tptimer/env/lib/python2.7/site-packages/execjs/_external_runtime.py

288 lines
8.0 KiB
Python
Raw Normal View History

2018-07-20 05:43:02 +00:00
from subprocess import Popen, PIPE
import io
import json
import os
import os.path
import platform
import re
import stat
import sys
import tempfile
import six
import execjs._json2 as _json2
import execjs._runner_sources as _runner_sources
import execjs._exceptions as exceptions
from execjs._abstract_runtime import AbstractRuntime
from execjs._abstract_runtime_context import AbstractRuntimeContext
from execjs._misc import encode_unicode_codepoints
class ExternalRuntime(AbstractRuntime):
'''Runtime to execute codes with external command.'''
def __init__(self, name, command, runner_source, encoding='utf8', tempfile=False):
self._name = name
if isinstance(command, str):
command = [command]
self._command = command
self._runner_source = runner_source
self._encoding = encoding
self._tempfile = tempfile
self._available = self._binary() is not None
def __str__(self):
return "{class_name}({runtime_name})".format(
class_name=type(self).__name__,
runtime_name=self._name,
)
@property
def name(self):
return self._name
def is_available(self):
return self._available
def _compile(self, source, cwd=None):
return self.Context(self, source, cwd=cwd, tempfile=tempfile)
def _binary(self):
if not hasattr(self, "_binary_cache"):
self._binary_cache = _which(self._command)
return self._binary_cache
class Context(AbstractRuntimeContext):
# protected
def __init__(self, runtime, source='', cwd=None, tempfile=False):
self._runtime = runtime
self._source = source
self._cwd = cwd
self._tempfile = tempfile
def is_available(self):
return self._runtime.is_available()
def _eval(self, source):
if not source.strip():
data = "''"
else:
data = "'('+" + json.dumps(source, ensure_ascii=True) + "+')'"
code = 'return eval({data})'.format(data=data)
return self.exec_(code)
def _exec_(self, source):
if self._source:
source = self._source + '\n' + source
if self._tempfile:
output = self._exec_with_tempfile(source)
else:
output = self._exec_with_pipe(source)
return self._extract_result(output)
def _call(self, identifier, *args):
args = json.dumps(args)
return self._eval("{identifier}.apply(this, {args})".format(identifier=identifier, args=args))
def _exec_with_pipe(self, source):
cmd = self._runtime._binary()
p = None
try:
p = Popen(cmd, stdin=PIPE, stdout=PIPE, stderr=PIPE, cwd=self._cwd, universal_newlines=True)
stdoutdata, stderrdata = p.communicate(input=source)
ret = p.wait()
finally:
del p
self._fail_on_non_zero_status(ret, stdoutdata, stderrdata)
return stdoutdata
def _exec_with_tempfile(self, source):
(fd, filename) = tempfile.mkstemp(prefix='execjs', suffix='.js')
os.close(fd)
try:
with io.open(filename, "w+", encoding=self._runtime._encoding) as fp:
fp.write(self._compile(source))
cmd = self._runtime._binary() + [filename]
p = None
try:
p = Popen(cmd, stdout=PIPE, stderr=PIPE, cwd=self._cwd)
stdoutdata, stderrdata = p.communicate()
ret = p.wait()
finally:
del p
self._fail_on_non_zero_status(ret, stdoutdata, stderrdata)
return stdoutdata
finally:
os.remove(filename)
def _fail_on_non_zero_status(self, status, stdoutdata, stderrdata):
if status != 0:
raise exceptions.RuntimeError("stdout: {}, stderr: {}".format(repr(stdoutdata), repr(stderrdata)))
def _compile(self, source):
runner_source = self._runtime._runner_source
replacements = {
'#{source}': lambda: source,
'#{encoded_source}': lambda: json.dumps(
"(function(){ " +
encode_unicode_codepoints(source) +
" })()"
),
'#{json2_source}': _json2._json2_source,
}
pattern = "|".join(re.escape(k) for k in replacements)
runner_source = re.sub(pattern, lambda m: replacements[m.group(0)](), runner_source)
return runner_source
def _extract_result(self, output):
output = output.decode(self._runtime._encoding)
output = output.replace("\r\n", "\n").replace("\r", "\n")
output_last_line = output.split("\n")[-2]
if not output_last_line:
status = value = None
else:
ret = json.loads(output_last_line)
if len(ret) == 1:
ret = [ret[0], None]
status, value = ret
if status == "ok":
return value
elif value.startswith('SyntaxError:'):
raise exceptions.RuntimeError(value)
else:
raise exceptions.ProgramError(value)
def _is_windows():
"""protected"""
return platform.system() == 'Windows'
def _decode_if_not_text(s):
"""protected"""
if isinstance(s, six.text_type):
return s
return s.decode(sys.getfilesystemencoding())
def _find_executable(prog, pathext=("",)):
"""protected"""
pathlist = _decode_if_not_text(os.environ.get('PATH', '')).split(os.pathsep)
for dir in pathlist:
for ext in pathext:
filename = os.path.join(dir, prog + ext)
try:
st = os.stat(filename)
except os.error:
continue
if stat.S_ISREG(st.st_mode) and (stat.S_IMODE(st.st_mode) & 0o111):
return filename
return None
def _which(command):
"""protected"""
if isinstance(command, str):
command = [command]
command = list(command)
name = command[0]
args = command[1:]
if _is_windows():
pathext = _decode_if_not_text(os.environ.get("PATHEXT", ""))
path = _find_executable(name, pathext.split(os.pathsep))
else:
path = _find_executable(name)
if not path:
return None
return [path] + args
def node():
r = node_node()
if r.is_available():
return r
return node_nodejs()
def node_node():
return ExternalRuntime(
name="Node.js (V8)",
command=['node'],
encoding='UTF-8',
runner_source=_runner_sources.Node
)
def node_nodejs():
return ExternalRuntime(
name="Node.js (V8)",
command=['nodejs'],
encoding='UTF-8',
runner_source=_runner_sources.Node
)
def jsc():
return ExternalRuntime(
name="JavaScriptCore",
command=["/System/Library/Frameworks/JavaScriptCore.framework/Versions/A/Resources/jsc"],
runner_source=_runner_sources.JavaScriptCore
)
def spidermonkey():
return ExternalRuntime(
name="SpiderMonkey",
command=["js"],
runner_source=_runner_sources.SpiderMonkey
)
def jscript():
return ExternalRuntime(
name="JScript",
command=["cscript", "//E:jscript", "//Nologo"],
encoding="ascii",
runner_source=_runner_sources.JScript,
tempfile=True
)
def phantomjs():
return ExternalRuntime(
name="PhantomJS",
command=["phantomjs"],
runner_source=_runner_sources.PhantomJS
)
def slimerjs():
return ExternalRuntime(
name="SlimerJS",
command=["slimerjs"],
runner_source=_runner_sources.SlimerJS
)
def nashorn():
return ExternalRuntime(
name="Nashorn",
command=["jjs"],
runner_source=_runner_sources.Nashorn
)