Skip to content

Commit 30c3658

Browse files
committed
Added option to call runtime using stdin/pipes instead of temp directory
1 parent ceca27d commit 30c3658

File tree

1 file changed

+39
-19
lines changed

1 file changed

+39
-19
lines changed

execjs/_external_runtime.py

Lines changed: 39 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -19,13 +19,14 @@
1919

2020
class ExternalRuntime(AbstractRuntime):
2121
'''Runtime to execute codes with external command.'''
22-
def __init__(self, name, command, runner_source, encoding='utf8'):
22+
def __init__(self, name, command, runner_source, encoding='utf8', tempfile=False):
2323
self._name = name
2424
if isinstance(command, str):
2525
command = [command]
2626
self._command = command
2727
self._runner_source = runner_source
2828
self._encoding = encoding
29+
self._tempfile = tempfile
2930

3031
self._available = self._binary() is not None
3132

@@ -43,7 +44,7 @@ def is_available(self):
4344
return self._available
4445

4546
def _compile(self, source, cwd=None):
46-
return self.Context(self, source, cwd=cwd)
47+
return self.Context(self, source, cwd=cwd, tempfile=tempfile)
4748

4849
def _binary(self):
4950
if not hasattr(self, "_binary_cache"):
@@ -53,10 +54,11 @@ def _binary(self):
5354
class Context(AbstractRuntimeContext):
5455
# protected
5556

56-
def __init__(self, runtime, source='', cwd=None):
57+
def __init__(self, runtime, source='', cwd=None, tempfile=False):
5758
self._runtime = runtime
5859
self._source = source
5960
self._cwd = cwd
61+
self._tempfile = tempfile
6062

6163
def is_available(self):
6264
return self._runtime.is_available()
@@ -74,35 +76,53 @@ def _exec_(self, source):
7476
if self._source:
7577
source = self._source + '\n' + source
7678

77-
(fd, filename) = tempfile.mkstemp(prefix='execjs', suffix='.js')
78-
os.close(fd)
79-
try:
80-
with io.open(filename, "w+", encoding=self._runtime._encoding) as fp:
81-
fp.write(self._compile(source))
82-
output = self._execfile(filename)
83-
finally:
84-
os.remove(filename)
85-
79+
if self._tempfile:
80+
output = self._exec_with_tempfile(source)
81+
else:
82+
output = self._exec_with_pipe(source)
8683
return self._extract_result(output)
8784

8885
def _call(self, identifier, *args):
8986
args = json.dumps(args)
90-
return self.eval("{identifier}.apply(this, {args})".format(identifier=identifier, args=args))
87+
return self._eval("{identifier}.apply(this, {args})".format(identifier=identifier, args=args))
9188

92-
def _execfile(self, filename):
93-
cmd = self._runtime._binary() + [filename]
89+
def _exec_with_pipe(self, source):
90+
cmd = self._runtime._binary()
9491

9592
p = None
9693
try:
97-
p = Popen(cmd, stdout=PIPE, stderr=PIPE, cwd=self._cwd)
98-
stdoutdata, stderrdata = p.communicate()
94+
p = Popen(cmd, stdin=PIPE, stdout=PIPE, stderr=PIPE, cwd=self._cwd, universal_newlines=True)
95+
stdoutdata, stderrdata = p.communicate(input=source)
9996
ret = p.wait()
10097
finally:
10198
del p
10299

103-
if ret == 0:
100+
self._fail_on_non_zero_status(ret, stdoutdata, stderrdata)
101+
return stdoutdata
102+
103+
def _exec_with_tempfile(self, source):
104+
(fd, filename) = tempfile.mkstemp(prefix='execjs', suffix='.js')
105+
os.close(fd)
106+
try:
107+
with io.open(filename, "w+", encoding=self._runtime._encoding) as fp:
108+
fp.write(self._compile(source))
109+
cmd = self._runtime._binary() + [filename]
110+
111+
p = None
112+
try:
113+
p = Popen(cmd, stdout=PIPE, stderr=PIPE, cwd=self._cwd)
114+
stdoutdata, stderrdata = p.communicate()
115+
ret = p.wait()
116+
finally:
117+
del p
118+
119+
self._fail_on_non_zero_status(ret, stdoutdata, stderrdata)
104120
return stdoutdata
105-
else:
121+
finally:
122+
os.remove(filename)
123+
124+
def _fail_on_non_zero_status(self, status, stdoutdata, stderrdata):
125+
if status != 0:
106126
raise exceptions.RuntimeError("stdout: {}, stderr: {}".format(repr(stdoutdata), repr(stderrdata)))
107127

108128
def _compile(self, source):

0 commit comments

Comments
 (0)