Visitar URL original
fix: run PymodeRun with activated virtualenv interpreter · python-mode/python-mode@593ac51 · GitHub
Skip to content

Commit 593ac51

Browse files
Copilotdiraol
andauthored
fix: run PymodeRun with activated virtualenv interpreter
Agent-Logs-Url: https://github.com/python-mode/python-mode/sessions/990a6238-68c1-475c-b384-5afbd8f07a7f Co-authored-by: diraol <192702+diraol@users.noreply.github.com>
1 parent b875597 commit 593ac51

3 files changed

Lines changed: 134 additions & 32 deletions

File tree

‎doc/pymode.txt‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -268,6 +268,9 @@ Set path to virtualenv manually *'g:pymode_virtualenv_path'
268268
Commands:
269269
*:PymodeRun* -- Run current buffer or selection
270270

271+
When a virtualenv is activated with |:PymodeVirtualenv|, *:PymodeRun* uses the
272+
virtualenv's Python executable.
273+
271274
Turn on the run code script *'g:pymode_run'*
272275
>
273276
let g:pymode_run = 1

‎pymode/run.py‎

Lines changed: 90 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
""" Code runnning support. """
2+
import os
3+
import subprocess
24
import sys
5+
import tempfile
36
from io import StringIO
47
from 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+
68127
def __prepare_lines(line1, line2):
69128

70129
lines = [l.rstrip() for l in env.lines[int(line1) - 1:int(line2)]]

‎tests/vader/commands.vader‎

Lines changed: 41 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,46 @@ Execute (Test PymodeRun command):
8686
Assert 1, 'PymodeRun executed without producing a run buffer'
8787
endif
8888

89+
Execute (Test PymodeRun uses active virtualenv python):
90+
if exists(':PymodeRun') && exists(':PymodeVirtualenv')
91+
let pycmd = executable('python3') ? 'python3' : (executable('python') ? 'python' : '')
92+
if !empty(pycmd)
93+
let temp_dir = tempname()
94+
call mkdir(temp_dir, 'p')
95+
let venv_dir = temp_dir . '/.venv'
96+
let sample_file = temp_dir . '/run_from_venv.py'
97+
98+
call writefile(['import sys', 'print(sys.executable)'], sample_file)
99+
100+
call system(pycmd . ' -m venv ' . shellescape(venv_dir))
101+
if v:shell_error == 0
102+
execute 'edit ' . fnameescape(sample_file)
103+
let g:pymode_virtualenv = 1
104+
execute 'PymodeVirtualenv ' . string(venv_dir)
105+
PymodeRun
106+
107+
let run_buffer = bufnr('__run__')
108+
if run_buffer != -1
109+
execute 'buffer ' . run_buffer
110+
let run_output = substitute(join(getline(1, '$'), "\n"), '\\', '/', 'g')
111+
let expected_python = has('win32') || has('win64') ? venv_dir . '/Scripts/python.exe' : venv_dir . '/bin/python'
112+
Assert stridx(run_output, expected_python) >= 0, 'PymodeRun should execute with virtualenv python executable'
113+
else
114+
Assert 0, 'PymodeRun should create run buffer when using virtualenv'
115+
endif
116+
else
117+
Assert 1, 'Unable to create test virtualenv in this environment - test skipped'
118+
endif
119+
120+
call delete(sample_file)
121+
call delete(temp_dir, 'rf')
122+
else
123+
Assert 1, 'No python executable available to create virtualenv - test skipped'
124+
endif
125+
else
126+
Assert 1, 'PymodeRun/PymodeVirtualenv command not available - test skipped'
127+
endif
128+
89129
# Test PymodeLint command
90130
Given python (Python code with lint issues):
91131
import math, sys;
@@ -275,4 +315,4 @@ Execute (Test PymodeRun with pymoderun_sample.py):
275315
endif
276316
else
277317
Assert 1, 'PymodeRun command not available - test skipped'
278-
endif
318+
endif

0 commit comments

Comments
 (0)