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/README.md b/README.md index c820530..e89124c 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,16 @@ -PyExecJS -======== +PyExecJS (EOL) +============== [![Build Status](https://travis-ci.org/doloopwhile/PyExecJS.svg?branch=travis-ci)](https://travis-ci.org/doloopwhile/PyExecJS) +# End of life + +This library is no longer maintananced ([Reason](https://gist.github.com/doloopwhile/8c6ec7dd4703e8a44e559411cb2ea221)). +Bugs are not be fixed (even if they are trivial or essential). + +We suggest to use other library or to make a fork. + +--- + Run JavaScript code from Python. PyExecJS is a porting of ExecJS from Ruby. @@ -22,14 +31,19 @@ A short example: # Supported runtimes +## First-class support (runtime class is provided and tested) + * [PyV8](http://code.google.com/p/pyv8/) - A python wrapper for Google V8 engine, * [Node.js](http://nodejs.org/) +* [PhantomJS](http://phantomjs.org/) +* [Nashorn](http://docs.oracle.com/javase/8/docs/technotes/guides/scripting/nashorn/intro.html#sthref16) - Included with Oracle Java 8 + +## Second-class support (runtime class is privided but not tested) + * Apple JavaScriptCore - Included with Mac OS X -* [Mozilla SpiderMonkey](http://www.mozilla.org/js/spidermonkey/) * [Microsoft Windows Script Host](http://msdn.microsoft.com/en-us/library/9bbdkx3k.aspx) (JScript) * [SlimerJS](http://slimerjs.org/) -* [PhantomJS](http://phantomjs.org/) -* [Nashorn](http://docs.oracle.com/javase/8/docs/technotes/guides/scripting/nashorn/intro.html#sthref16) - Included with Oracle Java 8 +* [Mozilla SpiderMonkey](http://www.mozilla.org/js/spidermonkey/) # Installation @@ -79,6 +93,14 @@ Released under the MIT license. See `LICENSE` for details. # Changelog +## 1.5.0 +- Eased version requirement for six. + +## 1.4.1 +- Fixed arguments of module-level functions. +- Fixed bug of execution with pipe. +- Fixed wrong excption is raised. + ## 1.4.0 - Fixed required libraries. - Fixed order of output of `--print-available-runtimes`. diff --git a/README.rst b/README.rst index e84efa9..e3a5e83 100644 --- a/README.rst +++ b/README.rst @@ -1,5 +1,15 @@ -PyExecJS -======== +PyExecJS (EOL) +============== + +End of life +=========== + +This library is no longer maintananced. Bugs are not be fixed (even if +they are trivial or essential). + +We suggest to use other library or to make a fork. + +-------------- Run JavaScript code from Python. @@ -24,18 +34,25 @@ A short example: Supported runtimes ================== +First-class support (runtime class is provided and tested) +---------------------------------------------------------- + - `PyV8 `__ - A python wrapper for Google V8 engine, - `Node.js `__ +- `PhantomJS `__ +- `Nashorn `__ + - Included with Oracle Java 8 + +Second-class support (runtime class is privided but not tested) +--------------------------------------------------------------- + - Apple JavaScriptCore - Included with Mac OS X -- `Mozilla SpiderMonkey `__ - `Microsoft Windows Script Host `__ (JScript) - `SlimerJS `__ -- `PhantomJS `__ -- `Nashorn `__ - - Included with Oracle Java 8 +- `Mozilla SpiderMonkey `__ Installation ============ @@ -101,6 +118,18 @@ Released under the MIT license. See ``LICENSE`` for details. Changelog ========= +1.5.0 +----- + +- Eased version requirement for six. + +1.4.1 +----- + +- Fixed arguments of module-level functions. +- Fixed bug of execution with pipe. +- Fixed wrong excption is raised. + 1.4.0 ----- diff --git a/execjs/__init__.py b/execjs/__init__.py index 394a357..98a4bea 100755 --- a/execjs/__init__.py +++ b/execjs/__init__.py @@ -22,7 +22,13 @@ ''' from __future__ import unicode_literals, division, with_statement -from execjs._exceptions import Error, RuntimeError, ProgramError, RuntimeUnavailableError +from execjs._exceptions import ( + Error, + RuntimeError, + ProgramError, + RuntimeUnavailableError, +) + import execjs._runtimes from execjs._external_runtime import ExternalRuntime from execjs._abstract_runtime import AbstractRuntime @@ -41,16 +47,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..0723550 100644 --- a/execjs/_exceptions.py +++ b/execjs/_exceptions.py @@ -1,14 +1,24 @@ +# Abstract base error classes class Error(Exception): pass - +# Abstract class that represents errors of runtime engine. +# ex. Specified runtime engine is not installed, runtime engine aborted (by its bugs). +# By the way "RuntimeError" is bad name because it is confusing with the standard exception. class RuntimeError(Error): pass +# Concrete runtime error classes +class RuntimeUnavailableError(RuntimeError): pass -class ProgramError(Error): - pass +class ProcessExitedWithNonZeroStatus(RuntimeError): + def __init__(self, status, stdout, stderr): + RuntimeError.__init__(self, status, stdout, stderr) + self.status = status + self.stdout = stdout + self.stderr = stderr - -class RuntimeUnavailableError(RuntimeError): +# Errors due to JS script. +# ex. Script has syntax error, executed and raised exception. +class ProgramError(Error): pass diff --git a/execjs/_external_runtime.py b/execjs/_external_runtime.py index dceec83..2b05c16 100644 --- a/execjs/_external_runtime.py +++ b/execjs/_external_runtime.py @@ -11,7 +11,12 @@ import six import execjs._json2 as _json2 import execjs._runner_sources as _runner_sources -import execjs._exceptions as exceptions + +from execjs._exceptions import ( + ProcessExitedWithNonZeroStatus, + ProgramError +) + from execjs._abstract_runtime import AbstractRuntime from execjs._abstract_runtime_context import AbstractRuntimeContext from execjs._misc import encode_unicode_codepoints @@ -44,7 +49,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 +97,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 +118,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 +131,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 ProcessExitedWithNonZeroStatus(status=status, stdout=stdoutdata, stderr=stderrdata) def _compile(self, source): runner_source = self._runtime._runner_source @@ -145,24 +153,18 @@ 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 - else: - ret = json.loads(output_last_line) - if len(ret) == 1: - ret = [ret[0], None] - status, value = ret + 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) + raise ProgramError(value) def _is_windows(): @@ -241,7 +243,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 +252,8 @@ def spidermonkey(): return ExternalRuntime( name="SpiderMonkey", command=["js"], - runner_source=_runner_sources.SpiderMonkey + runner_source=_runner_sources.SpiderMonkey, + tempfile=True ) @@ -267,7 +271,8 @@ def phantomjs(): return ExternalRuntime( name="PhantomJS", command=["phantomjs"], - runner_source=_runner_sources.PhantomJS + runner_source=_runner_sources.PhantomJS, + tempfile=True ) @@ -275,7 +280,8 @@ def slimerjs(): return ExternalRuntime( name="SlimerJS", command=["slimerjs"], - runner_source=_runner_sources.SlimerJS + runner_source=_runner_sources.SlimerJS, + tempfile=True ) @@ -283,5 +289,6 @@ def nashorn(): return ExternalRuntime( name="Nashorn", command=["jjs"], - runner_source=_runner_sources.Nashorn + runner_source=_runner_sources.Nashorn, + tempfile=True ) diff --git a/execjs/_pyv8runtime.py b/execjs/_pyv8runtime.py index c5edcff..06ddcb7 100644 --- a/execjs/_pyv8runtime.py +++ b/execjs/_pyv8runtime.py @@ -1,5 +1,4 @@ import json -import contextlib import execjs._exceptions as exceptions from execjs._abstract_runtime import AbstractRuntime @@ -48,12 +47,12 @@ def _exec_(self, source): source = str(source) # backward compatibility - with contextlib.nested(PyV8.JSContext(), PyV8.JSEngine()) as (ctxt, engine): + with PyV8.JSContext() as ctxt, PyV8.JSEngine() as engine: js_errors = (PyV8.JSError, IndexError, ReferenceError, SyntaxError, TypeError) try: script = engine.compile(source) except js_errors as e: - raise exceptions.RuntimeError(e) + raise exceptions.ProgramError(e) try: value = script.run() except js_errors as e: diff --git a/setup.py b/setup.py index f09e310..68689ea 100755 --- a/setup.py +++ b/setup.py @@ -5,7 +5,7 @@ import sys import io -version = '1.4.0' +version = '1.5.1' author = "Omoto Kenji" license = "MIT License" author_email = 'doloopwhile@gmail.com' @@ -39,6 +39,6 @@ 'Programming Language :: Python :: 3.5', 'Programming Language :: JavaScript', ], - install_requires=["six==1.10.0"], + install_requires=["six >= 1.10.0"], test_suite="test_execjs", ) 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):