11""" Code runnning support. """
2+ import os
3+ import subprocess
24import sys
5+ import tempfile
36from io import StringIO
47from re import compile as re
58
@@ -25,46 +28,102 @@ def run_code():
2528 elif encoding .match (lines [1 ]):
2629 lines .pop (1 )
2730
28- context = dict (
29- __name__ = '__main__' ,
30- __file__ = env .var ('expand("%:p")' ),
31- input = env .user_input ,
32- raw_input = env .user_input )
31+ python_cmd = __get_virtualenv_python ()
32+ if python_cmd :
33+ result = __run_with_external_python (python_cmd , lines )
34+ if result is not None :
35+ output , err = result
36+ else :
37+ python_cmd = None
38+
39+ if not python_cmd :
40+ context = dict (
41+ __name__ = '__main__' ,
42+ __file__ = env .var ('expand("%:p")' ),
43+ input = env .user_input ,
44+ raw_input = env .user_input )
45+
46+ sys .stdout , stdout_ = StringIO (), sys .stdout
47+ sys .stderr , stderr_ = StringIO (), sys .stderr
48+
49+ try :
50+ code = compile ('\n ' .join (lines ) + '\n ' , env .curbuf .name , 'exec' )
51+ sys .path .insert (0 , env .curdir )
52+ exec (code , context ) # noqa
53+ sys .path .pop (0 )
54+
55+ except SystemExit as e :
56+ if e .code :
57+ # A non-false code indicates abnormal termination.
58+ # A false code will be treated as a
59+ # successful run, and the error will be hidden from Vim
60+ env .error ("Script exited with code %s" % e .code )
61+ return env .stop ()
62+
63+ except Exception :
64+ import traceback
65+ err = traceback .format_exc ()
66+
67+ else :
68+ err = sys .stderr .getvalue ()
69+
70+ output = sys .stdout .getvalue ()
71+ sys .stdout , sys .stderr = stdout_ , stderr_
3372
34- sys .stdout , stdout_ = StringIO (), sys .stdout
35- sys .stderr , stderr_ = StringIO (), sys .stderr
36-
37- try :
38- code = compile ('\n ' .join (lines ) + '\n ' , env .curbuf .name , 'exec' )
39- sys .path .insert (0 , env .curdir )
40- exec (code , context ) # noqa
41- sys .path .pop (0 )
42-
43- except SystemExit as e :
44- if e .code :
45- # A non-false code indicates abnormal termination.
46- # A false code will be treated as a
47- # successful run, and the error will be hidden from Vim
48- env .error ("Script exited with code %s" % e .code )
49- return env .stop ()
50-
51- except Exception :
52- import traceback
53- err = traceback .format_exc ()
54-
55- else :
56- err = sys .stderr .getvalue ()
57-
58- output = sys .stdout .getvalue ()
5973 output = env .prepare_value (output , dumps = False )
60- sys .stdout , sys .stderr = stdout_ , stderr_
6174
6275 errors += [er for er in err .splitlines () if er and "<string>" not in er ]
6376
6477 env .let ('l:traceback' , errors [2 :])
6578 env .let ('l:output' , [s for s in output .splitlines ()])
6679
6780
81+ def __get_virtualenv_python ():
82+ path = env .var ('g:pymode_virtualenv_enabled' , silence = True , default = '' )
83+ if not path :
84+ return None
85+
86+ if os .name == 'nt' :
87+ candidates = [
88+ os .path .join (path , 'Scripts' , 'python.exe' ),
89+ os .path .join (path , 'Scripts' , 'python' ),
90+ ]
91+ else :
92+ candidates = [os .path .join (path , 'bin' , 'python' )]
93+
94+ for candidate in candidates :
95+ if os .path .isfile (candidate ) and os .access (candidate , os .X_OK ):
96+ return candidate
97+
98+ return None
99+
100+
101+ def __run_with_external_python (python_cmd , lines ):
102+ source = '\n ' .join (lines ) + '\n '
103+ temp_file_path = None
104+ try :
105+ with tempfile .NamedTemporaryFile (
106+ mode = 'w' , suffix = '.py' , delete = False , encoding = 'utf-8'
107+ ) as temp_file :
108+ temp_file .write (source )
109+ temp_file_path = temp_file .name
110+
111+ process = subprocess .Popen (
112+ [python_cmd , temp_file_path ],
113+ stdout = subprocess .PIPE ,
114+ stderr = subprocess .PIPE ,
115+ cwd = env .curdir ,
116+ universal_newlines = True )
117+ output , err = process .communicate ()
118+ except OSError :
119+ return None
120+ finally :
121+ if temp_file_path and os .path .exists (temp_file_path ):
122+ os .unlink (temp_file_path )
123+
124+ return output , err
125+
126+
68127def __prepare_lines (line1 , line2 ):
69128
70129 lines = [l .rstrip () for l in env .lines [int (line1 ) - 1 :int (line2 )]]
0 commit comments