forked from ethanchewy/PythonBuddy
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathrunsubprocess.py
More file actions
116 lines (102 loc) · 3.97 KB
/
runsubprocess.py
File metadata and controls
116 lines (102 loc) · 3.97 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
"""Run a subprocess. Wrapper around the 'subprocess' module
with a hack to prevent bogus out-of-memory conditions in os.fork()
if the current process already grew very large.
"""
import sys
import gc
import os
from subprocess import PIPE, Popen
PY2 = (sys.version_info.major == 2)
if PY2:
text = unicode
else:
text = str
def run_subprocess(executable, args, env=None, cwd=None):
if isinstance(args, list):
args = [a.encode('latin1') if isinstance(a, text) else a
for a in args]
return _run(executable, args, env, cwd)
shell_default = False
if sys.platform == 'win32':
shell_default = True
def _run(executable, args, env, cwd):
# note that this function can be *overridden* below
# in some cases!
if sys.platform == 'win32':
executable = executable.replace('/','\\')
if isinstance(args, str):
args = str(executable) + ' ' + args
shell = True
else:
if args is None:
args = [str(executable)]
else:
args = [str(executable)] + args
# shell=True on unix-like is a known security vulnerability, but
# on windows shell=True does not properly propogate the env dict
shell = shell_default
# Just before spawning the subprocess, do a gc.collect(). This
# should help if we are running on top of PyPy, if the subprocess
# is going to need a lot of RAM and we are using a lot too.
gc.collect()
pipe = Popen(args, stdout=PIPE, stderr=PIPE, shell=shell, env=env, cwd=cwd)
stdout, stderr = pipe.communicate()
if (sys.platform == 'win32' and pipe.returncode == 1 and
'is not recognized' in stderr):
# Setting shell=True on windows messes up expected exceptions
raise EnvironmentError(stderr)
return pipe.returncode, stdout, stderr
if __name__ == '__main__':
while True:
gc.collect()
operation = sys.stdin.readline()
if not operation:
sys.exit()
assert operation.startswith('(')
args = eval(operation)
try:
results = _run(*args)
except EnvironmentError as e:
results = (None, str(e))
sys.stdout.write('%r\n' % (results,))
sys.stdout.flush()
if sys.platform != 'win32' and hasattr(os, 'fork') and not os.getenv("PYPY_DONT_RUN_SUBPROCESS", None):
# do this at import-time, when the process is still tiny
_source = os.path.dirname(os.path.abspath(__file__))
_source = os.path.join(_source, 'runsubprocess.py') # and not e.g. '.pyc'
def spawn_subprocess():
global _child, child_stdin, child_stdout
_child = Popen([sys.executable, _source], bufsize=0,
stdin=PIPE, stdout=PIPE, close_fds=True)
if PY2:
child_stdin = _child.stdin
child_stdout = _child.stdout
else:
# create TextIOWrappers which (hopefully) have the same newline
# behavior as the child's stdin / stdout
from io import TextIOWrapper
child_stdin = TextIOWrapper(_child.stdin,
newline=sys.stdin.newlines,
write_through=True)
child_stdout = TextIOWrapper(_child.stdout,
newline=sys.stdout.newlines)
spawn_subprocess()
def cleanup_subprocess():
global _child, child_stdin, child_stdout
_child = None
child_stdin = None
child_stdout = None
import atexit; atexit.register(cleanup_subprocess)
def _run(*args):
try:
child_stdin.write('%r\n' % (args,))
except (OSError, IOError):
# lost the child. Try again...
spawn_subprocess()
child_stdin.write('%r\n' % (args,))
results = child_stdout.readline()
assert results.startswith('(')
results = eval(results)
if results[0] is None:
raise OSError('%s: %s\nargs=%r' % (args[0], results[1], args))
return results