diff --git a/.travis.yml b/.travis.yml index b4129d7..0949de8 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,36 +1,30 @@ dist: trusty language: python +addons: + firefox: "latest" + python: - "2.7" - "3.3" - "3.4" - "3.5" + - "3.6" env: global: - - "SLIMERJSLAUNCHER=$(which firefox)" - - "DISPLAY=:99.0" - - "PATH=$TRAVIS_BUILD_DIR/slimerjs:$PATH" - "JDK=oracle8" jdk: - oraclejdk8 install: - - "sh -e /etc/init.d/xvfb start" - - "echo 'Installing Slimer'" - - "wget http://download.slimerjs.org/releases/0.9.6/slimerjs-0.9.6-linux-x86_64.tar.bz2" - - "tar xvf slimerjs-*.tar.bz2" - - "rm slimerjs-*.tar.bz2" - - "mv slimerjs-* slimerjs" - "sudo apt-get install -y oracle-java8-installer phantomjs libmozjs-24-bin" - "sudo ln -s /usr/bin/js24 /usr/bin/js" - "./install_development.sh" - "python setup.py install" script: - - "slimerjs --version" - "jjs -v < /dev/null" - "js --help" - "node --version && node --help" diff --git a/execjs/__init__.py b/execjs/__init__.py index 394a357..2b573df 100755 --- a/execjs/__init__.py +++ b/execjs/__init__.py @@ -41,16 +41,16 @@ get_from_environment = execjs._runtimes.get_from_environment -def eval(source): - return get().eval(source) -eval.__doc__= AbstractRuntime.eval.__doc__ +def eval(source, cwd=None): + return get().eval(source, cwd) +eval.__doc__ = AbstractRuntime.eval.__doc__ -def exec_(source): - return get().exec_(source) -exec_.__doc__= AbstractRuntime.exec_.__doc__ +def exec_(source, cwd=None): + return get().exec_(source, cwd) +exec_.__doc__ = AbstractRuntime.exec_.__doc__ -def compile(source): - return get().compile(source) -compile.__doc__= AbstractRuntime.compile.__doc__ +def compile(source, cwd=None): + return get().compile(source, cwd) +compile.__doc__ = AbstractRuntime.compile.__doc__ diff --git a/execjs/_exceptions.py b/execjs/_exceptions.py index 876cb22..e11f947 100644 --- a/execjs/_exceptions.py +++ b/execjs/_exceptions.py @@ -3,7 +3,11 @@ class Error(Exception): class RuntimeError(Error): - pass + def __init__(self, status, stdout, stderr): + Error.__init__(self, status, stdout, stderr) + self.status = status + self.stdout = stdout + self.stderr = stderr class ProgramError(Error): diff --git a/execjs/_external_runtime.py b/execjs/_external_runtime.py index dceec83..6489084 100644 --- a/execjs/_external_runtime.py +++ b/execjs/_external_runtime.py @@ -44,7 +44,7 @@ def is_available(self): return self._available def _compile(self, source, cwd=None): - return self.Context(self, source, cwd=cwd, tempfile=tempfile) + return self.Context(self, source, cwd=cwd, tempfile=self._tempfile) def _binary(self): if not hasattr(self, "_binary_cache"): @@ -92,7 +92,10 @@ def _exec_with_pipe(self, source): p = None try: p = Popen(cmd, stdin=PIPE, stdout=PIPE, stderr=PIPE, cwd=self._cwd, universal_newlines=True) - stdoutdata, stderrdata = p.communicate(input=source) + input = self._compile(source) + if six.PY2: + input = input.encode(sys.getfilesystemencoding()) + stdoutdata, stderrdata = p.communicate(input=input) ret = p.wait() finally: del p @@ -110,7 +113,7 @@ def _exec_with_tempfile(self, source): p = None try: - p = Popen(cmd, stdout=PIPE, stderr=PIPE, cwd=self._cwd) + p = Popen(cmd, stdout=PIPE, stderr=PIPE, cwd=self._cwd, universal_newlines=True) stdoutdata, stderrdata = p.communicate() ret = p.wait() finally: @@ -123,7 +126,7 @@ def _exec_with_tempfile(self, source): def _fail_on_non_zero_status(self, status, stdoutdata, stderrdata): if status != 0: - raise exceptions.RuntimeError("stdout: {}, stderr: {}".format(repr(stdoutdata), repr(stderrdata))) + raise exceptions.RuntimeError(status=status, stdout=stdoutdata, stderr=stderrdata) def _compile(self, source): runner_source = self._runtime._runner_source @@ -145,12 +148,11 @@ def _compile(self, 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 + raise exceptions.ProgramError else: ret = json.loads(output_last_line) if len(ret) == 1: @@ -159,8 +161,6 @@ def _extract_result(self, output): if status == "ok": return value - elif value.startswith('SyntaxError:'): - raise exceptions.RuntimeError(value) else: raise exceptions.ProgramError(value) @@ -241,7 +241,8 @@ def jsc(): return ExternalRuntime( name="JavaScriptCore", command=["/System/Library/Frameworks/JavaScriptCore.framework/Versions/A/Resources/jsc"], - runner_source=_runner_sources.JavaScriptCore + runner_source=_runner_sources.JavaScriptCore, + tempfile=True ) @@ -249,7 +250,8 @@ def spidermonkey(): return ExternalRuntime( name="SpiderMonkey", command=["js"], - runner_source=_runner_sources.SpiderMonkey + runner_source=_runner_sources.SpiderMonkey, + tempfile=True ) @@ -267,7 +269,8 @@ def phantomjs(): return ExternalRuntime( name="PhantomJS", command=["phantomjs"], - runner_source=_runner_sources.PhantomJS + runner_source=_runner_sources.PhantomJS, + tempfile=True ) @@ -275,7 +278,8 @@ def slimerjs(): return ExternalRuntime( name="SlimerJS", command=["slimerjs"], - runner_source=_runner_sources.SlimerJS + runner_source=_runner_sources.SlimerJS, + tempfile=True ) @@ -283,5 +287,6 @@ def nashorn(): return ExternalRuntime( name="Nashorn", command=["jjs"], - runner_source=_runner_sources.Nashorn + runner_source=_runner_sources.Nashorn, + tempfile=True ) diff --git a/test_execjs.py b/test_execjs.py index 5e7f815..146865a 100755 --- a/test_execjs.py +++ b/test_execjs.py @@ -25,7 +25,7 @@ def test_nested_context_call(self): def test_context_call_missing_function(self): context = self.runtime.compile("") - with self.assertRaises(execjs.ProgramError): + with self.assertRaises(execjs.Error): context.call("missing") def test_exec(self): @@ -75,11 +75,11 @@ def test_compile_large_scripts(self): self.assertTrue(self.runtime.exec_(code)) def test_syntax_error(self): - with self.assertRaises(execjs.RuntimeError): + with self.assertRaises(execjs.Error): self.runtime.exec_(")") def test_thrown_exception(self): - with self.assertRaises(execjs.ProgramError): + with self.assertRaises(execjs.Error): self.runtime.exec_("throw 'hello'") def test_broken_substitutions(self): @@ -91,20 +91,17 @@ class DefaultRuntimeTest(unittest.TestCase, RuntimeTestBase): def setUp(self): self.runtime = execjs +class NodeRuntimeTest(unittest.TestCase, RuntimeTestBase): + def setUp(self): + self.runtime = execjs.get('Node') -for name, runtime in execjs.runtimes().items(): - if not runtime.is_available(): - continue - class_name = name.capitalize() + "RuntimeTest" - - def f(runtime=runtime): - class RuntimeTest(unittest.TestCase, RuntimeTestBase): - def setUp(self): - self.runtime = runtime - RuntimeTest.__name__ = str(class_name) # 2.x compatibility - return RuntimeTest - exec("{class_name} = f()".format(class_name=class_name)) +class NashornRuntimeTest(unittest.TestCase, RuntimeTestBase): + def setUp(self): + self.runtime = execjs.get('Nashorn') +class PhantomJSRuntimeTest(unittest.TestCase, RuntimeTestBase): + def setUp(self): + self.runtime = execjs.get('PhantomJS') class CommonTest(unittest.TestCase): def test_empty_path_environ(self):