From f495a822632f4d617b0362dcd024c36c0c158f21 Mon Sep 17 00:00:00 2001 From: OMOTO Kenji Date: Sun, 8 Nov 2015 00:50:24 +0900 Subject: [PATCH 01/48] Updated README.md --- README.md | 51 ++++++++++++++++++++++++++------------------- README.rst | 61 +++++++++++++++++++++++++++++++++--------------------- 2 files changed, 67 insertions(+), 45 deletions(-) diff --git a/README.md b/README.md index 3b7a601..efb4c93 100644 --- a/README.md +++ b/README.md @@ -5,8 +5,7 @@ PyExecJS Run JavaScript code from Python. PyExecJS is a porting of ExecJS from Ruby. -PyExecJS automatically picks the best runtime available to evaluate your JavaScript program, -then returns the result to you as a Python object. +PyExecJS **automatically** picks the best runtime available to evaluate your JavaScript program. A short example: @@ -21,24 +20,23 @@ A short example: >>> ctx.call("add", 1, 2) 3 -Of course, you can pick particular JavaScript runtime by get() function: +The pros of PyExecJS is that you do not need take care of JavaScript environment. +Especially, it works in Windows environment without installing extra libraries. - >>> default = execjs.get() # the automatically picked runtime - >>> default.eval("1 + 2") - 3 - >>> jscript = execjs.get("JScript") - >>> jscript.eval("1 + 2") - 3 - >>> node = execjs.get("Node") - >>> node.eval("1 + 2") - 3 +One of cons of PyExecJS is performance. PyExecJS communicate JavaScript runtime by text and it is slow. +The other cons is that it does not fully support runtime specific features. -If EXECJS_RUNTIME environment variable is specified, PyExecJS pick the JavaScript runtime as a default: +[PyV8](https://code.google.com/p/pyv8/) might be better choice for some use case. - >>> #execjs.get().name # this value is depends on your environment. - >>> os.environ["EXECJS_RUNTIME"] = "Node" - >>> execjs.get().name - 'Node.js (V8)' +# Installation + + $ pip install PyExecJS + +or + + $ easy_install PyExecJS + +# Details PyExecJS supports these runtimes: @@ -50,14 +48,25 @@ PyExecJS supports these runtimes: * [SlimerJS](http://slimerjs.org/) * [PhantomJS](http://phantomjs.org/) -# Installation - $ pip install PyExecJS +If `EXECJS_RUNTIME` environment variable is specified, PyExecJS pick the JavaScript runtime as a default: -or + >>> execjs.get().name # this value is depends on your environment. + >>> os.environ["EXECJS_RUNTIME"] = "Node" + >>> execjs.get().name + 'Node.js (V8)' - $ easy_install PyExecJS +You can choose JavaScript runtime by `execjs.get()`: + >>> default = execjs.get() # the automatically picked runtime + >>> default.eval("1 + 2") + 3 + >>> jscript = execjs.get("JScript") + >>> jscript.eval("1 + 2") + 3 + >>> node = execjs.get("Node") + >>> node.eval("1 + 2") + 3 # License diff --git a/README.rst b/README.rst index bd39ef9..53092dd 100644 --- a/README.rst +++ b/README.rst @@ -3,9 +3,8 @@ PyExecJS Run JavaScript code from Python. -PyExecJS is a porting of ExecJS from Ruby. PyExecJS automatically picks -the best runtime available to evaluate your JavaScript program, then -returns the result to you as a Python object. +PyExecJS is a porting of ExecJS from Ruby. PyExecJS **automatically** +picks the best runtime available to evaluate your JavaScript program. A short example: @@ -22,29 +21,32 @@ A short example: >>> ctx.call("add", 1, 2) 3 -Of course, you can pick particular JavaScript runtime by get() function: +The pros of PyExecJS is that you do not need take care of JavaScript +environment. Especially, it works in Windows environment without +installing extra libraries. + +One of cons of PyExecJS is performance. PyExecJS communicate JavaScript +runtime by text and it is slow. The other cons is that it does not fully +support runtime specific features. + +`PyV8 `__ might be better choice for +some use case. + +Installation +============ :: - >>> default = execjs.get() # the automatically picked runtime - >>> default.eval("1 + 2") - 3 - >>> jscript = execjs.get("JScript") - >>> jscript.eval("1 + 2") - 3 - >>> node = execjs.get("Node") - >>> node.eval("1 + 2") - 3 + $ pip install PyExecJS -If EXECJS\_RUNTIME environment variable is specified, PyExecJS pick the -JavaScript runtime as a default: +or :: - >>> #execjs.get().name # this value is depends on your environment. - >>> os.environ["EXECJS_RUNTIME"] = "Node" - >>> execjs.get().name - 'Node.js (V8)' + $ easy_install PyExecJS + +Details +======= PyExecJS supports these runtimes: @@ -59,18 +61,29 @@ PyExecJS supports these runtimes: - `SlimerJS `__ - `PhantomJS `__ -Installation -============ +If ``EXECJS_RUNTIME`` environment variable is specified, PyExecJS pick +the JavaScript runtime as a default: :: - $ pip install PyExecJS + >>> #execjs.get().name # this value is depends on your environment. + >>> os.environ["EXECJS_RUNTIME"] = "Node" + >>> execjs.get().name + 'Node.js (V8)' -or +You can choose JavaScript runtime by ``execjs.get()``: :: - $ easy_install PyExecJS + >>> default = execjs.get() # the automatically picked runtime + >>> default.eval("1 + 2") + 3 + >>> jscript = execjs.get("JScript") + >>> jscript.eval("1 + 2") + 3 + >>> node = execjs.get("Node") + >>> node.eval("1 + 2") + 3 License ======= From 641e8152fb7860d2387248f33a4d6383a1f3c613 Mon Sep 17 00:00:00 2001 From: OMOTO Kenji Date: Sun, 8 Nov 2015 01:21:25 +0900 Subject: [PATCH 02/48] Exported _json2_source() to _json2.py --- execjs/__init__.py | 9 ++------- execjs/_json2.py | 5 +++++ 2 files changed, 7 insertions(+), 7 deletions(-) create mode 100644 execjs/_json2.py diff --git a/execjs/__init__.py b/execjs/__init__.py index 3c3a07f..d83fa7d 100755 --- a/execjs/__init__.py +++ b/execjs/__init__.py @@ -31,6 +31,7 @@ import tempfile from subprocess import Popen, PIPE, STDOUT import json +import execjs._json2 try: from collections import OrderedDict @@ -142,12 +143,6 @@ def _is_windows(): return platform.system() == 'Windows' -def _json2_source(): - # The folowing code is json2.js(https://github.com/douglascrockford/JSON-js). - # It is compressed by YUI Compressor Online(http://yui.2clics.net/). - - return 'var JSON;if(!JSON){JSON={}}(function(){function f(n){return n<10?"0"+n:n}if(typeof Date.prototype.toJSON!=="function"){Date.prototype.toJSON=function(key){return isFinite(this.valueOf())?this.getUTCFullYear()+"-"+f(this.getUTCMonth()+1)+"-"+f(this.getUTCDate())+"T"+f(this.getUTCHours())+":"+f(this.getUTCMinutes())+":"+f(this.getUTCSeconds())+"Z":null};String.prototype.toJSON=Number.prototype.toJSON=Boolean.prototype.toJSON=function(key){return this.valueOf()}}var cx=/[\\u0000\\u00ad\\u0600-\\u0604\\u070f\\u17b4\\u17b5\\u200c-\\u200f\\u2028-\\u202f\\u2060-\\u206f\\ufeff\\ufff0-\\uffff]/g,escapable=/[\\\\\\"\\x00-\\x1f\\x7f-\\x9f\\u00ad\\u0600-\\u0604\\u070f\\u17b4\\u17b5\\u200c-\\u200f\\u2028-\\u202f\\u2060-\\u206f\\ufeff\\ufff0-\\uffff]/g,gap,indent,meta={"\\b":"\\\\b","\\t":"\\\\t","\\n":"\\\\n","\\f":"\\\\f","\\r":"\\\\r",\'"\':\'\\\\"\',"\\\\":"\\\\\\\\"},rep;function quote(string){escapable.lastIndex=0;return escapable.test(string)?\'"\'+string.replace(escapable,function(a){var c=meta[a];return typeof c==="string"?c:"\\\\u"+("0000"+a.charCodeAt(0).toString(16)).slice(-4)})+\'"\':\'"\'+string+\'"\'}function str(key,holder){var i,k,v,length,mind=gap,partial,value=holder[key];if(value&&typeof value==="object"&&typeof value.toJSON==="function"){value=value.toJSON(key)}if(typeof rep==="function"){value=rep.call(holder,key,value)}switch(typeof value){case"string":return quote(value);case"number":return isFinite(value)?String(value):"null";case"boolean":case"null":return String(value);case"object":if(!value){return"null"}gap+=indent;partial=[];if(Object.prototype.toString.apply(value)==="[object Array]"){length=value.length;for(i=0;i Date: Sun, 8 Nov 2015 01:33:42 +0900 Subject: [PATCH 03/48] Decode PATH variable to treat non-ascii charactors --- execjs/__init__.py | 14 ++++++++++++-- setup.py | 3 +-- 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/execjs/__init__.py b/execjs/__init__.py index d83fa7d..65e85d0 100755 --- a/execjs/__init__.py +++ b/execjs/__init__.py @@ -22,6 +22,7 @@ 3 ''' +import sys import os import os.path import re @@ -31,6 +32,9 @@ import tempfile from subprocess import Popen, PIPE, STDOUT import json + +import six + import execjs._json2 try: @@ -143,9 +147,14 @@ def _is_windows(): return platform.system() == 'Windows' +def _decode_if_not_text(s): + if isinstance(s, six.text_type): + return s + return s.decode(sys.getfilesystemencoding()) + def _find_executable(prog, pathext=("",)): - pathlist = os.environ.get('PATH', '').split(os.pathsep) + pathlist = _decode_if_not_text(os.environ.get('PATH', '')).split(os.pathsep) for dir in pathlist: for ext in pathext: @@ -167,7 +176,8 @@ def _which(command): args = command[1:] if _is_windows(): - path = _find_executable(name, os.environ.get("PATHEXT", "").split(os.pathsep)) + pathext = _decode_if_not_text(os.environ.get("PATHEXT", "")) + path = _find_executable(name, pathext.split(os.pathsep)) else: path = _find_executable(name) diff --git a/setup.py b/setup.py index 574782c..605c65b 100755 --- a/setup.py +++ b/setup.py @@ -13,10 +13,9 @@ with io.open('README.rst', encoding='ascii') as fp: long_description = fp.read() +install_requires = ["six"] if sys.version_info < (2, 7): install_requires = "argparse ordereddict".split() -else: - install_requires = [] setup( packages=find_packages(), From 7fa4e8d4510084664916ec21529395aa5860ee4d Mon Sep 17 00:00:00 2001 From: John Gibson Date: Wed, 24 Feb 2016 14:28:40 -0500 Subject: [PATCH 04/48] Added Java 8's built-in JavaScript engine as a supported runtime. --- .travis.yml | 20 +++++++++++++++++++- README.md | 1 + README.rst | 4 +++- execjs/__init__.py | 25 +++++++++++++++++++++++++ 4 files changed, 48 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index 2205848..f098daa 100644 --- a/.travis.yml +++ b/.travis.yml @@ -8,7 +8,25 @@ python: - "3.4" env: - - "SLIMERJSLAUNCHER=$(which firefox) DISPLAY=:99.0 PATH=$TRAVIS_BUILD_DIR/slimerjs:$PATH" + global: + - "SLIMERJSLAUNCHER=$(which firefox)" + - "DISPLAY=:99.0" + - "PATH=$TRAVIS_BUILD_DIR/slimerjs:$PATH" + +matrix: + include: + - python: "2.7" + env: JDK=oracle8 + addons: + apt: + packages: + - oracle-java8-installer + - python: "3.4" + env: JDK=oracle8 + addons: + apt: + packages: + - oracle-java8-installer before_script: - "sh -e /etc/init.d/xvfb start" diff --git a/README.md b/README.md index efb4c93..27c7846 100644 --- a/README.md +++ b/README.md @@ -47,6 +47,7 @@ PyExecJS supports these runtimes: * [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 If `EXECJS_RUNTIME` environment variable is specified, PyExecJS pick the JavaScript runtime as a default: diff --git a/README.rst b/README.rst index 53092dd..8424f60 100644 --- a/README.rst +++ b/README.rst @@ -60,13 +60,15 @@ PyExecJS supports these runtimes: (JScript) - `SlimerJS `__ - `PhantomJS `__ +- `Nashorn `__ + - Included with Oracle Java 8 If ``EXECJS_RUNTIME`` environment variable is specified, PyExecJS pick the JavaScript runtime as a default: :: - >>> #execjs.get().name # this value is depends on your environment. + >>> execjs.get().name # this value is depends on your environment. >>> os.environ["EXECJS_RUNTIME"] = "Node" >>> execjs.get().name 'Node.js (V8)' diff --git a/execjs/__init__.py b/execjs/__init__.py index 65e85d0..f21f4d1 100755 --- a/execjs/__init__.py +++ b/execjs/__init__.py @@ -577,3 +577,28 @@ def convert(cls, obj): }); phantom.exit(); """) + +_runtimes['Nashorn'] = ExternalRuntime( + name="Nashorn", + command=["jjs"], + runner_source=r"""(function(program, execJS) { execJS(program) })(function() { #{source} +}, function(program) { + #{json2_source} + var output; + try { + result = program(); + print(""); + if (typeof result == 'undefined' && result !== null) { + print('["ok"]'); + } else { + try { + print(JSON.stringify(['ok', result])); + } catch (err) { + print('["err"]'); + } + } + } catch (err) { + print(JSON.stringify(['err', '' + err])); + } +}); +""") From 5001801b8b275f9653b7193380c5641269c2469f Mon Sep 17 00:00:00 2001 From: OMOTO Kenji Date: Thu, 28 Jan 2016 18:25:06 +0900 Subject: [PATCH 05/48] Separated __init__.py to modules --- execjs/__init__.py | 571 ++-------------------------------- execjs/_misc.py | 19 ++ execjs/_runner_sources.py | 130 ++++++++ execjs/_runtimes.py | 89 ++++++ execjs/external_runtime.py | 217 +++++++++++++ execjs/pyqtruntime.py | 106 +++++++ execjs/pyv8runtime.py | 89 ++++++ execjs/unavailable_runtime.py | 18 ++ install_development.sh | 1 + test_execjs.py | 13 +- 10 files changed, 697 insertions(+), 556 deletions(-) create mode 100644 execjs/_misc.py create mode 100644 execjs/_runner_sources.py create mode 100644 execjs/_runtimes.py create mode 100644 execjs/external_runtime.py create mode 100644 execjs/pyqtruntime.py create mode 100644 execjs/pyv8runtime.py create mode 100644 execjs/unavailable_runtime.py diff --git a/execjs/__init__.py b/execjs/__init__.py index f21f4d1..d7d9149 100755 --- a/execjs/__init__.py +++ b/execjs/__init__.py @@ -1,14 +1,13 @@ #!/usr/bin/env python3 # -*- coding: ascii -*- -from __future__ import unicode_literals, division, with_statement -''' - Run JavaScript code from Python. +'''r +Run JavaScript code from Python. - PyExecJS is a porting of ExecJS from Ruby. - PyExecJS automatically picks the best runtime available to evaluate your JavaScript program, - then returns the result to you as a Python object. +PyExecJS is a porting of ExecJS from Ruby. +PyExecJS automatically picks the best runtime available to evaluate your JavaScript program, +then returns the result to you as a Python object. - A short example: +A short example: >>> import execjs >>> execjs.eval("'red yellow blue'.split(' ')") @@ -21,31 +20,17 @@ >>> ctx.call("add", 1, 2) 3 ''' +from __future__ import unicode_literals, division, with_statement -import sys -import os -import os.path -import re -import stat -import io -import platform -import tempfile -from subprocess import Popen, PIPE, STDOUT -import json - -import six - -import execjs._json2 +import execjs._runtimes +import execjs.external_runtime +ExternalRuntime = execjs.external_runtime.ExternalRuntime -try: - from collections import OrderedDict -except ImportError: - from ordereddict import OrderedDict __all__ = """ get register runtimes get_from_environment exec_ eval compile - ExternalRuntime Context - Error RuntimeError ProgramError RuntimeUnavailable + ExternalRuntime + Error RuntimeError ProgramError RuntimeUnavailableError """.split() @@ -61,70 +46,15 @@ class ProgramError(Error): pass -class RuntimeUnavailable(RuntimeError): +class RuntimeUnavailableError(RuntimeError): pass -def register(name, runtime): - '''Register a JavaScript runtime.''' - _runtimes[name] = runtime - - -def get(name=None): - """ - Return a appropriate JavaScript runtime. - If name is specified, return the runtime. - """ - if name is None: - return _auto_detect() - - try: - runtime = runtimes()[name] - except KeyError: - raise RuntimeUnavailable("{name} runtime is not defined".format(name=name)) - else: - if not runtime.is_available(): - raise RuntimeUnavailable( - "{name} runtime is not available on this system".format(name=runtime.name)) - return runtime - - -def runtimes(): - """return a dictionary of all supported JavaScript runtimes.""" - return dict(_runtimes) - - -def available_runtimes(): - """return a dictionary of all supported JavaScript runtimes which is usable""" - return dict((name, runtime) for name, runtime in _runtimes.items() if runtime.is_available()) - - -def _auto_detect(): - runtime = get_from_environment() - if runtime is not None: - return runtime - - for runtime in _runtimes.values(): - if runtime.is_available(): - return runtime - - raise RuntimeUnavailable("Could not find a JavaScript runtime.") - - -def get_from_environment(): - ''' - Return the JavaScript runtime that is specified in EXECJS_RUNTIME environment variable. - If EXECJS_RUNTIME environment variable is empty or invalid, return None. - ''' - try: - name = os.environ["EXECJS_RUNTIME"] - except KeyError: - return None - - if not name: - #name is None or empty str - return None - return get(name) +register = execjs._runtimes.register +get = execjs._runtimes.get +runtimes = execjs._runtimes.runtimes +available_runtimes = execjs._runtimes.available_runtimes +get_from_environment = execjs._runtimes.get_from_environment def eval(source): @@ -137,468 +67,3 @@ def exec_(source): def compile(source): return get().compile(source) - - -def _root(): - return os.path.abspath(os.path.dirname(__file__)) - - -def _is_windows(): - return platform.system() == 'Windows' - - -def _decode_if_not_text(s): - if isinstance(s, six.text_type): - return s - return s.decode(sys.getfilesystemencoding()) - - -def _find_executable(prog, pathext=("",)): - 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): - 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 - - -class ExternalRuntime: - def __init__(self, name, command, runner_source, encoding='utf8'): - self._name = name - if isinstance(command, str): - command = [command] - self._command = command - self._runner_source = runner_source - self._encoding = encoding - - 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 exec_(self, source): - if not self.is_available(): - raise RuntimeUnavailable() - return self.Context(self).exec_(source) - - def eval(self, source): - if not self.is_available(): - raise RuntimeUnavailable() - return self.Context(self).eval(source) - - def compile(self, source): - if not self.is_available(): - raise RuntimeUnavailable() - return self.Context(self, source) - - def is_available(self): - return self._binary() is not None - - def runner_source(self): - return self._runner_source - - def _binary(self): - """protected""" - if not hasattr(self, "_binary_cache"): - self._binary_cache = _which(self._command) - return self._binary_cache - - def _execfile(self, filename): - """protected""" - cmd = self._binary() + [filename] - - p = None - try: - p = Popen(cmd, stdout=PIPE, stderr=STDOUT) - stdoutdata, stderrdata = p.communicate() - ret = p.wait() - finally: - del p - - if ret == 0: - return stdoutdata - else: - raise RuntimeError(stdoutdata) - - class Context: - def __init__(self, runtime, source=''): - self._runtime = runtime - self._source = source - - 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 - - (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)) - output = self._runtime._execfile(filename) - finally: - os.remove(filename) - - output = output.decode(self._runtime._encoding) - output = output.replace("\r\n", "\n").replace("\r", "\n") - return self._extract_result(output.split("\n")[-2]) - - def call(self, identifier, *args): - args = json.dumps(args) - return self.eval("{identifier}.apply(this, {args})".format(identifier=identifier, args=args)) - - def _compile(self, source): - """protected""" - runner_source = self._runtime.runner_source() - - replacements = { - '#{source}': lambda: source, - '#{encoded_source}': lambda: json.dumps( - "(function(){ " + - encode_unicode_codepoints(source) + - " })()" - ), - '#{json2_source}': execjs._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_last_line): - """protected""" - 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 RuntimeError(value) - else: - raise ProgramError(value) - - -def encode_unicode_codepoints(str): - r""" - >>> encode_unicode_codepoints("a") == 'a' - True - >>> ascii = ''.join(chr(i) for i in range(0x80)) - >>> encode_unicode_codepoints(ascii) == ascii - True - >>> encode_unicode_codepoints('\u4e16\u754c') == '\\u4e16\\u754c' - True - """ - codepoint_format = '\\u{0:04x}'.format - - def codepoint(m): - return codepoint_format(ord(m.group(0))) - - return re.sub('[^\x00-\x7f]', codepoint, str) - - -class PyV8Runtime: - def __init__(self): - try: - import PyV8 - except ImportError: - self._is_available = False - else: - self._is_available = True - - @property - def name(self): - return "PyV8" - - def exec_(self, source): - return self.Context().exec_(source) - - def eval(self, source): - return self.Context().eval(source) - - def compile(self, source): - return self.Context(source) - - def is_available(self): - return self._is_available - - class Context: - def __init__(self, source=""): - self._source = source - - def exec_(self, source): - source = '''\ - (function() {{ - {0}; - {1}; - }})()'''.format( - encode_unicode_codepoints(self._source), - encode_unicode_codepoints(source) - ) - source = str(source) - - import PyV8 - import contextlib - #backward compatibility - with contextlib.nested(PyV8.JSContext(), PyV8.JSEngine()) as (ctxt, engine): - js_errors = (PyV8.JSError, IndexError, ReferenceError, SyntaxError, TypeError) - try: - script = engine.compile(source) - except js_errors as e: - raise RuntimeError(e) - try: - value = script.run() - except js_errors as e: - raise ProgramError(e) - return self.convert(value) - - def eval(self, source): - return self.exec_('return ' + encode_unicode_codepoints(source)) - - def call(self, identifier, *args): - args = json.dumps(args) - return self.eval("{identifier}.apply(this, {args})".format(identifier=identifier, args=args)) - - @classmethod - def convert(cls, obj): - from PyV8 import _PyV8 - if isinstance(obj, bytes): - return obj.decode('utf8') - if isinstance(obj, _PyV8.JSArray): - return [cls.convert(v) for v in obj] - elif isinstance(obj, _PyV8.JSFunction): - return None - elif isinstance(obj, _PyV8.JSObject): - ret = {} - for k in obj.keys(): - v = cls.convert(obj[k]) - if v is not None: - ret[cls.convert(k)] = v - return ret - else: - return obj - - -_runtimes = OrderedDict() -_runtimes['PyV8'] = PyV8Runtime() - -for command in ["nodejs", "node"]: - _runtimes["Node"] = runtime = ExternalRuntime( - name="Node.js (V8)", - command=[command], - encoding='UTF-8', - runner_source=r"""(function(program, execJS) { execJS(program) })(function() { #{source} -}, function(program) { - var output; - var print = function(string) { - process.stdout.write('' + string + '\n'); - }; - try { - result = program(); - print('') - if (typeof result == 'undefined' && result !== null) { - print('["ok"]'); - } else { - try { - print(JSON.stringify(['ok', result])); - } catch (err) { - print('["err"]'); - } - } - } catch (err) { - print(JSON.stringify(['err', '' + err])); - } -});""", - ) - if runtime.is_available(): - break - - -_runtimes['JavaScriptCore'] = ExternalRuntime( - name="JavaScriptCore", - command=["/System/Library/Frameworks/JavaScriptCore.framework/Versions/A/Resources/jsc"], - runner_source=r"""(function(program, execJS) { execJS(program) })(function() { - return eval(#{encoded_source}); -}, function(program) { - var output; - try { - result = program(); - print(""); - if (typeof result == 'undefined' && result !== null) { - print('["ok"]'); - } else { - try { - print(JSON.stringify(['ok', result])); - } catch (err) { - print('["err"]'); - } - } - } catch (err) { - print(JSON.stringify(['err', '' + err])); - } -}); -""" -) - - -_runtimes['SpiderMonkey'] = _runtimes['Spidermonkey'] = ExternalRuntime( - name="SpiderMonkey", - command=["js"], - runner_source=r"""(function(program, execJS) { execJS(program) })(function() { #{source} -}, function(program) { - #{json2_source} - var output; - try { - result = program(); - print(""); - if (typeof result == 'undefined' && result !== null) { - print('["ok"]'); - } else { - try { - print(JSON.stringify(['ok', result])); - } catch (err) { - print('["err"]'); - } - } - } catch (err) { - print(JSON.stringify(['err', '' + err])); - } -}); -""") - - -_runtimes['JScript'] = ExternalRuntime( - name="JScript", - command=["cscript", "//E:jscript", "//Nologo"], - encoding="ascii", - runner_source=r"""(function(program, execJS) { execJS(program) })(function() { - return eval(#{encoded_source}); -}, function(program) { - #{json2_source} - var output, print = function(string) { - string = string.replace(/[^\x00-\x7f]/g, function(ch){ - return '\\u' + ('0000' + ch.charCodeAt(0).toString(16)).slice(-4); - }); - WScript.Echo(string); - }; - try { - result = program(); - print("") - if (typeof result == 'undefined' && result !== null) { - print('["ok"]'); - } else { - try { - print(JSON.stringify(['ok', result])); - } catch (err) { - print('["err"]'); - } - } - } catch (err) { - print(JSON.stringify(['err', err.name + ': ' + err.message])); - } -}); -""" -) - - -for _name, _command in [ - ['PhantomJS', 'phantomjs'], - ['SlimerJS', 'slimerjs'], -]: - _runtimes[_name] = ExternalRuntime( - name=_name, - command=[_command], - runner_source=r""" -(function(program, execJS) { execJS(program) })(function() { - return eval(#{encoded_source}); -}, function(program) { - var output; - var print = function(string) { - console.log('' + string); - }; - try { - result = program(); - print('') - if (typeof result == 'undefined' && result !== null) { - print('["ok"]'); - } else { - try { - print(JSON.stringify(['ok', result])); - } catch (err) { - print('["err"]'); - } - } - } catch (err) { - print(JSON.stringify(['err', '' + err])); - } -}); -phantom.exit(); -""") - -_runtimes['Nashorn'] = ExternalRuntime( - name="Nashorn", - command=["jjs"], - runner_source=r"""(function(program, execJS) { execJS(program) })(function() { #{source} -}, function(program) { - #{json2_source} - var output; - try { - result = program(); - print(""); - if (typeof result == 'undefined' && result !== null) { - print('["ok"]'); - } else { - try { - print(JSON.stringify(['ok', result])); - } catch (err) { - print('["err"]'); - } - } - } catch (err) { - print(JSON.stringify(['err', '' + err])); - } -}); -""") diff --git a/execjs/_misc.py b/execjs/_misc.py new file mode 100644 index 0000000..af38a02 --- /dev/null +++ b/execjs/_misc.py @@ -0,0 +1,19 @@ +import re + + +def encode_unicode_codepoints(str): + r""" + >>> encode_unicode_codepoints("a") == 'a' + True + >>> ascii = ''.join(chr(i) for i in range(0x80)) + >>> encode_unicode_codepoints(ascii) == ascii + True + >>> encode_unicode_codepoints('\u4e16\u754c') == '\\u4e16\\u754c' + True + """ + codepoint_format = '\\u{0:04x}'.format + + def codepoint(m): + return codepoint_format(ord(m.group(0))) + + return re.sub('[^\x00-\x7f]', codepoint, str) diff --git a/execjs/_runner_sources.py b/execjs/_runner_sources.py new file mode 100644 index 0000000..a90a745 --- /dev/null +++ b/execjs/_runner_sources.py @@ -0,0 +1,130 @@ +#!/usr/bin/env python3 +# -*- coding: ascii -*- +from __future__ import unicode_literals, division, with_statement + + +Node = r"""(function(program, execJS) { execJS(program) })(function() { #{source} +}, function(program) { + var output; + var print = function(string) { + process.stdout.write('' + string + '\n'); + }; + try { + result = program(); + print('') + if (typeof result == 'undefined' && result !== null) { + print('["ok"]'); + } else { + try { + print(JSON.stringify(['ok', result])); + } catch (err) { + print('["err"]'); + } + } + } catch (err) { + print(JSON.stringify(['err', '' + err])); + } +});""" + + +JavaScriptCore = r"""(function(program, execJS) { execJS(program) })(function() { + return eval(#{encoded_source}); +}, function(program) { + var output; + try { + result = program(); + print(""); + if (typeof result == 'undefined' && result !== null) { + print('["ok"]'); + } else { + try { + print(JSON.stringify(['ok', result])); + } catch (err) { + print('["err"]'); + } + } + } catch (err) { + print(JSON.stringify(['err', '' + err])); + } +}); +""" + +SpiderMonkey = r"""(function(program, execJS) { execJS(program) })(function() { #{source} +}, function(program) { + #{json2_source} + var output; + try { + result = program(); + print(""); + if (typeof result == 'undefined' && result !== null) { + print('["ok"]'); + } else { + try { + print(JSON.stringify(['ok', result])); + } catch (err) { + print('["err"]'); + } + } + } catch (err) { + print(JSON.stringify(['err', '' + err])); + } +}); +""" +Nashorn = SpiderMonkey + +JScript = r"""(function(program, execJS) { execJS(program) })(function() { + return eval(#{encoded_source}); +}, function(program) { + #{json2_source} + var output, print = function(string) { + string = string.replace(/[^\x00-\x7f]/g, function(ch){ + return '\\u' + ('0000' + ch.charCodeAt(0).toString(16)).slice(-4); + }); + WScript.Echo(string); + }; + try { + result = program(); + print("") + if (typeof result == 'undefined' && result !== null) { + print('["ok"]'); + } else { + try { + print(JSON.stringify(['ok', result])); + } catch (err) { + print('["err"]'); + } + } + } catch (err) { + print(JSON.stringify(['err', err.name + ': ' + err.message])); + } +}); +""" + +PhantomJS = r""" +(function(program, execJS) { execJS(program) })(function() { + return eval(#{encoded_source}); +}, function(program) { + var output; + var print = function(string) { + console.log('' + string); + }; + try { + result = program(); + print('') + if (typeof result == 'undefined' && result !== null) { + print('["ok"]'); + } else { + try { + print(JSON.stringify(['ok', result])); + } catch (err) { + print('["err"]'); + } + } + } catch (err) { + print(JSON.stringify(['err', '' + err])); + } +}); +phantom.exit(); +""" + +SlimerJS = PhantomJS diff --git a/execjs/_runtimes.py b/execjs/_runtimes.py new file mode 100644 index 0000000..a2b765d --- /dev/null +++ b/execjs/_runtimes.py @@ -0,0 +1,89 @@ +import os.path + +try: + from collections import OrderedDict +except ImportError: + from ordereddict import OrderedDict + +import execjs +import execjs.external_runtime as external_runtime +import execjs.pyv8runtime as pyv8runtime + + +def register(name, runtime): + '''Register a JavaScript runtime.''' + _runtimes[name] = runtime + + +def get(name=None): + """ + Return a appropriate JavaScript runtime. + If name is specified, return the runtime. + """ + if name is None: + return _auto_detect() + + try: + runtime = runtimes()[name] + except KeyError: + raise execjs.RuntimeUnavailable("{name} runtime is not defined".format(name=name)) + else: + if not runtime.is_available(): + raise execjs.RuntimeUnavailable( + "{name} runtime is not available on this system".format(name=runtime.name)) + return runtime + + +def runtimes(): + """return a dictionary of all supported JavaScript runtimes.""" + return dict(_runtimes) + + +def available_runtimes(): + """return a dictionary of all supported JavaScript runtimes which is usable""" + return dict((name, runtime) for name, runtime in _runtimes.items() if runtime.is_available()) + + +def _auto_detect(): + runtime = get_from_environment() + if runtime is not None: + return runtime + + for runtime in _runtimes.values(): + if runtime.is_available(): + return runtime + + raise execjs.RuntimeUnavailable("Could not find a JavaScript runtime.") + + +def get_from_environment(): + ''' + Return the JavaScript runtime that is specified in EXECJS_RUNTIME environment variable. + If EXECJS_RUNTIME environment variable is empty or invalid, return None. + ''' + try: + name = os.environ["EXECJS_RUNTIME"] + except KeyError: + return None + + if not name: + return None + return get(name) + + +_runtimes = OrderedDict() + +register('PyV8', pyv8runtime.PyV8Runtime()) + +if external_runtime.node.is_available(): + register("Node", external_runtime.node) +else: + register("Node", external_runtime.nodejs) + +register('JavaScriptCore', external_runtime.jsc) +register('SpiderMonkey', external_runtime.spidermonkey) +register('Spidermonkey', external_runtime.spidermonkey) +register('JScript', external_runtime.jscript) +register("PhantomJS", external_runtime.phantomjs) +register("SlimerJS", external_runtime.slimerjs) +register('Nashorn', external_runtime.nashorn) diff --git a/execjs/external_runtime.py b/execjs/external_runtime.py new file mode 100644 index 0000000..b350196 --- /dev/null +++ b/execjs/external_runtime.py @@ -0,0 +1,217 @@ +from subprocess import Popen, PIPE, STDOUT +import io +import json +import os +import os.path +import platform +import re +import stat +import sys +import tempfile + +import six + +import execjs +import execjs.unavailable_runtime +import execjs._json2 as _json2 +import execjs._runner_sources as _runner_sources +from execjs._misc import encode_unicode_codepoints + + +class ExternalRuntime: + def __init__(self, name, command, runner_source, encoding='utf8'): + self._name = name + if isinstance(command, str): + command = [command] + self._command = command + self._runner_source = runner_source + self._encoding = encoding + + if self._binary() is None: + self.__class__ = execjs.unavailable_runtime.UnavailableRuntime + + 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 exec_(self, source): + return self.Context(self).exec_(source) + + def eval(self, source): + return self.Context(self).eval(source) + + def compile(self, source): + return self.Context(self, source) + + def is_available(self): + return True + + def runner_source(self): + return self._runner_source + + def _binary(self): + """protected""" + if not hasattr(self, "_binary_cache"): + self._binary_cache = _which(self._command) + return self._binary_cache + + def _execfile(self, filename): + """protected""" + cmd = self._binary() + [filename] + + p = None + try: + p = Popen(cmd, stdout=PIPE, stderr=STDOUT) + stdoutdata, stderrdata = p.communicate() + ret = p.wait() + finally: + del p + + if ret == 0: + return stdoutdata + else: + raise execjs.RuntimeError(stdoutdata) + + class Context: + def __init__(self, runtime, source=''): + self._runtime = runtime + self._source = source + + 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 + + (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)) + output = self._runtime._execfile(filename) + finally: + os.remove(filename) + + output = output.decode(self._runtime._encoding) + output = output.replace("\r\n", "\n").replace("\r", "\n") + return self._extract_result(output.split("\n")[-2]) + + def call(self, identifier, *args): + args = json.dumps(args) + return self.eval("{identifier}.apply(this, {args})".format(identifier=identifier, args=args)) + + def _compile(self, source): + """protected""" + 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_last_line): + """protected""" + 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 execjs.RuntimeError(value) + else: + raise execjs.ProgramError(value) + + +def _is_windows(): + return platform.system() == 'Windows' + + +def _decode_if_not_text(s): + if isinstance(s, six.text_type): + return s + return s.decode(sys.getfilesystemencoding()) + + +def _find_executable(prog, pathext=("",)): + 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): + 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 + + +node = ExternalRuntime(name="Node.js (V8)", command=['node'], encoding='UTF-8', runner_source=_runner_sources.Node) +nodejs = ExternalRuntime(name="Node.js (V8)", command=['nodejs'], encoding='UTF-8', runner_source=_runner_sources.Node) + +jsc = ExternalRuntime( + name="JavaScriptCore", + command=["/System/Library/Frameworks/JavaScriptCore.framework/Versions/A/Resources/jsc"], + runner_source=_runner_sources.JavaScriptCore +) + +spidermonkey = ExternalRuntime(name="SpiderMonkey", command=["js"], runner_source=_runner_sources.SpiderMonkey) + +jscript = ExternalRuntime( + name="JScript", + command=["cscript", "//E:jscript", "//Nologo"], + encoding="ascii", + runner_source=_runner_sources.JScript +) + +phantomjs = ExternalRuntime(name="PhantomJS", command=["phantomjs"], runner_source=_runner_sources.PhantomJS) +slimerjs = ExternalRuntime(name="SlimerJS", command=["slimerjs"], runner_source=_runner_sources.SlimerJS) +nashorn = ExternalRuntime(name="Nashorn", command=["jjs"], runner_source=_runner_sources.Nashorn) diff --git a/execjs/pyqtruntime.py b/execjs/pyqtruntime.py new file mode 100644 index 0000000..e7a74d0 --- /dev/null +++ b/execjs/pyqtruntime.py @@ -0,0 +1,106 @@ +import sys +from PyQt4 import QtCore, QtGui, QtWebKit + +import execjs + +class WebPage(QtWebKit.QWebPage): + def javaScriptConsoleMessage(self, msg, line, source): + print '%s line %d: %s' % (source, line, msg) + +url = 'http://localhost/test.html' +app = QtGui.QApplication([]) +browser = QtWebKit.QWebView() +page = WebPage() +browser.setPage(page) +browser.load(QtCore.QUrl(url)) +browser.show() +sys.exit(app.exec_()) + + +class PySideRuntime: + def __init__(self): + try: + import PySide.QWebKit + except ImportError: + self._is_available = False + else: + self._is_available = True + + @property + def name(self): + return "PySide" + + def exec_(self, source): + self.ensure_available() + return self.Context().exec_(source) + + def eval(self, source): + self.ensure_available() + return self.Context().eval(source) + + def compile(self, source): + self.ensure_available() + return self.Context(source) + + def is_available(self): + return self._is_available + + def ensure_available(self): + if not self.is_available(): + raise execjs.RuntimeError + + class Context: + def __init__(self, source=""): + self._source = source + + def exec_(self, source): + source = '''\ + (function() {{ + {0}; + {1}; + }})()'''.format( + encode_unicode_codepoints(self._source), + encode_unicode_codepoints(source) + ) + source = str(source) + + import PyV8 + import contextlib + #backward compatibility + with contextlib.nested(PyV8.JSContext(), PyV8.JSEngine()) as (ctxt, engine): + js_errors = (PyV8.JSError, IndexError, ReferenceError, SyntaxError, TypeError) + try: + script = engine.compile(source) + except js_errors as e: + raise RuntimeError(e) + try: + value = script.run() + except js_errors as e: + raise ProgramError(e) + return self.convert(value) + + def eval(self, source): + return self.exec_('return ' + encode_unicode_codepoints(source)) + + def call(self, identifier, *args): + args = json.dumps(args) + return self.eval("{identifier}.apply(this, {args})".format(identifier=identifier, args=args)) + + @classmethod + def convert(cls, obj): + from PyV8 import _PyV8 + if isinstance(obj, bytes): + return obj.decode('utf8') + if isinstance(obj, _PyV8.JSArray): + return [cls.convert(v) for v in obj] + elif isinstance(obj, _PyV8.JSFunction): + return None + elif isinstance(obj, _PyV8.JSObject): + ret = {} + for k in obj.keys(): + v = cls.convert(obj[k]) + if v is not None: + ret[cls.convert(k)] = v + return ret + else: + return obj diff --git a/execjs/pyv8runtime.py b/execjs/pyv8runtime.py new file mode 100644 index 0000000..53a2ee3 --- /dev/null +++ b/execjs/pyv8runtime.py @@ -0,0 +1,89 @@ +import json +import contextlib + +import execjs +import execjs.unavailable_runtime +from execjs._misc import encode_unicode_codepoints + +try: + import PyV8 +except ImportError: + _pyv8_available = False +else: + _pyv8_available = True + + +class PyV8Runtime: + def __init__(self): + if not _pyv8_available: + self.__class__ = execjs.unavailable_runtime.UnavailableRuntime + + @property + def name(self): + return "PyV8" + + def exec_(self, source): + return self.Context().exec_(source) + + def eval(self, source): + return self.Context().eval(source) + + def compile(self, source): + return self.Context(source) + + def is_available(self): + return True + + class Context: + def __init__(self, source=""): + self._source = source + + def exec_(self, source): + source = '''\ + (function() {{ + {0}; + {1}; + }})()'''.format( + encode_unicode_codepoints(self._source), + encode_unicode_codepoints(source) + ) + source = str(source) + + # backward compatibility + with contextlib.nested(PyV8.JSContext(), PyV8.JSEngine()) as (ctxt, engine): + js_errors = (PyV8.JSError, IndexError, ReferenceError, SyntaxError, TypeError) + try: + script = engine.compile(source) + except js_errors as e: + raise execjs.RuntimeError(e) + try: + value = script.run() + except js_errors as e: + raise execjs.ProgramError(e) + return self.convert(value) + + def eval(self, source): + return self.exec_('return ' + encode_unicode_codepoints(source)) + + def call(self, identifier, *args): + args = json.dumps(args) + return self.eval("{identifier}.apply(this, {args})".format(identifier=identifier, args=args)) + + @classmethod + def convert(cls, obj): + from PyV8 import _PyV8 + if isinstance(obj, bytes): + return obj.decode('utf8') + if isinstance(obj, _PyV8.JSArray): + return [cls.convert(v) for v in obj] + elif isinstance(obj, _PyV8.JSFunction): + return None + elif isinstance(obj, _PyV8.JSObject): + ret = {} + for k in obj.keys(): + v = cls.convert(obj[k]) + if v is not None: + ret[cls.convert(k)] = v + return ret + else: + return obj diff --git a/execjs/unavailable_runtime.py b/execjs/unavailable_runtime.py new file mode 100644 index 0000000..71efe4a --- /dev/null +++ b/execjs/unavailable_runtime.py @@ -0,0 +1,18 @@ +import execjs + + +class UnavailableRuntime: + def __init__(self, name): + self.name = name + + def exec_(self, source): + raise execjs.RuntimeUnavailableError + + def eval(self, source): + raise execjs.RuntimeUnavailableError + + def compile(self, source): + raise execjs.RuntimeUnavailableError + + def is_available(self): + return False diff --git a/install_development.sh b/install_development.sh index abcf210..7a05729 100755 --- a/install_development.sh +++ b/install_development.sh @@ -3,3 +3,4 @@ python -c "import sys; sys.version_info >= (2, 7) or sys.exit(1)" if [ $? -ne 0 ]; then pip install unittest2 fi +pip install six diff --git a/test_execjs.py b/test_execjs.py index dce8d00..7f87ded 100755 --- a/test_execjs.py +++ b/test_execjs.py @@ -4,14 +4,21 @@ import sys import os +import doctest +import execjs +import execjs.external_runtime +import execjs.pyv8runtime +import execjs.unavailable_runtime +import execjs._json2 +import execjs._misc +import execjs._runner_sources +import execjs._runtimes + if sys.version_info < (2, 7): import unittest2 as unittest else: import unittest -import doctest -import execjs - class RuntimeTestBase: def test_context_call(self): From e257f7a29e218822d50a6246d92b945edb14ec7c Mon Sep 17 00:00:00 2001 From: OMOTO Kenji Date: Sun, 13 Mar 2016 19:34:00 +0900 Subject: [PATCH 06/48] Include stderr to RuntimeError --- execjs/external_runtime.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/execjs/external_runtime.py b/execjs/external_runtime.py index b350196..b43668e 100644 --- a/execjs/external_runtime.py +++ b/execjs/external_runtime.py @@ -1,4 +1,4 @@ -from subprocess import Popen, PIPE, STDOUT +from subprocess import Popen, PIPE import io import json import os @@ -67,7 +67,7 @@ def _execfile(self, filename): p = None try: - p = Popen(cmd, stdout=PIPE, stderr=STDOUT) + p = Popen(cmd, stdout=PIPE, stderr=PIPE) stdoutdata, stderrdata = p.communicate() ret = p.wait() finally: @@ -76,7 +76,7 @@ def _execfile(self, filename): if ret == 0: return stdoutdata else: - raise execjs.RuntimeError(stdoutdata) + raise execjs.RuntimeError("stdout: {}, stderr: {}".format(repr(stdoutdata), repr(stderrdata))) class Context: def __init__(self, runtime, source=''): From 5584f8d5b0910c5a1959d843fc693839abe7ac73 Mon Sep 17 00:00:00 2001 From: OMOTO Kenji Date: Sun, 13 Mar 2016 20:09:12 +0900 Subject: [PATCH 07/48] Removed Python 2.6 and added 3.5 to TravisCI target --- .travis.yml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index f098daa..f18861e 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,11 +1,10 @@ language: python python: - - "2.6" - "2.7" - - "3.2" - "3.3" - "3.4" + - "3.5" env: global: From 620d90a5847abfde876d03d82fc108306a429075 Mon Sep 17 00:00:00 2001 From: OMOTO Kenji Date: Sun, 13 Mar 2016 19:27:53 +0900 Subject: [PATCH 08/48] Merged Java8 test in Travis CI --- .travis.yml | 17 +++-------------- 1 file changed, 3 insertions(+), 14 deletions(-) diff --git a/.travis.yml b/.travis.yml index f18861e..e7ea6a9 100644 --- a/.travis.yml +++ b/.travis.yml @@ -11,21 +11,10 @@ env: - "SLIMERJSLAUNCHER=$(which firefox)" - "DISPLAY=:99.0" - "PATH=$TRAVIS_BUILD_DIR/slimerjs:$PATH" + - "JDK=oracle8" -matrix: - include: - - python: "2.7" - env: JDK=oracle8 - addons: - apt: - packages: - - oracle-java8-installer - - python: "3.4" - env: JDK=oracle8 - addons: - apt: - packages: - - oracle-java8-installer +jdk: + - oraclejdk8 before_script: - "sh -e /etc/init.d/xvfb start" From 523f221ff5e3baa34c68e6f922b2c254e0f48cbf Mon Sep 17 00:00:00 2001 From: OMOTO Kenji Date: Sun, 13 Mar 2016 20:21:03 +0900 Subject: [PATCH 09/48] Fix SlimerJS tests fail --- .travis.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index e7ea6a9..d5df2d4 100644 --- a/.travis.yml +++ b/.travis.yml @@ -19,7 +19,7 @@ jdk: before_script: - "sh -e /etc/init.d/xvfb start" - "echo 'Installing Slimer'" - - "wget http://download.slimerjs.org/releases/0.9.3/slimerjs-0.9.3-linux-x86_64.tar.bz2" + - "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" @@ -29,4 +29,5 @@ install: - "python setup.py install" script: + - "slimerjs --version" - "python setup.py test" From 5ba52f5cbb5f1725f8c0b731775482dd884e409b Mon Sep 17 00:00:00 2001 From: OMOTO Kenji Date: Sun, 13 Mar 2016 21:18:53 +0900 Subject: [PATCH 10/48] Fix doctest fails with Python 2.x --- test_execjs.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/test_execjs.py b/test_execjs.py index 7f87ded..83636c9 100755 --- a/test_execjs.py +++ b/test_execjs.py @@ -3,8 +3,9 @@ from __future__ import unicode_literals import sys import os - import doctest +import six + import execjs import execjs.external_runtime import execjs.pyv8runtime @@ -135,7 +136,8 @@ def test_runtime_availability(self): def load_tests(loader, tests, ignore): - tests.addTests(doctest.DocTestSuite(execjs)) + if six.PY3: + tests.addTests(doctest.DocTestSuite(execjs)) return tests From 9c06816bd8f1ce402f9a577d86fdbab74141854a Mon Sep 17 00:00:00 2001 From: OMOTO Kenji Date: Sun, 13 Mar 2016 21:25:01 +0900 Subject: [PATCH 11/48] Version 1.2.0 --- README.md | 10 ++++++++-- README.rst | 11 +++++++++-- setup.py | 12 ++++++------ 3 files changed, 23 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index 27c7846..488e4ac 100644 --- a/README.md +++ b/README.md @@ -71,12 +71,18 @@ You can choose JavaScript runtime by `execjs.get()`: # License -Copyright (c) 2012 Omoto Kenji. -Copyright (c) 2011 Sam Stephenson and Josh Peek. +Copyright (c) 2016 Omoto Kenji. +Copyright (c) 2011 Sam Stephenson and Josh Peek. (As a author of ExecJS) Released under the MIT license. See `LICENSE` for details. # Changes + +## 1.2.0 +- Supported Python 3.5 +- Supported Nashorn(Java 8 JavaScript engine) as runtime +- Dropped support for Python 2.6 and 3.2 + ## 1.1.0 - Supported Python 3.4 - Supported SlimerJS as runtime diff --git a/README.rst b/README.rst index 8424f60..895c993 100644 --- a/README.rst +++ b/README.rst @@ -90,14 +90,21 @@ You can choose JavaScript runtime by ``execjs.get()``: License ======= -Copyright (c) 2012 Omoto Kenji. Copyright (c) 2011 Sam Stephenson and -Josh Peek. +Copyright (c) 2016 Omoto Kenji. Copyright (c) 2011 Sam Stephenson and +Josh Peek. (As a author of ExecJS) Released under the MIT license. See ``LICENSE`` for details. Changes ======= +1.2.0 +----- + +- Supported Python 3.5 +- Supported Nashorn(Java 8 JavaScript engine) as runtime +- Dropped support for Python 2.6 and 3.2 + 1.1.0 ----- diff --git a/setup.py b/setup.py index 605c65b..f704b74 100755 --- a/setup.py +++ b/setup.py @@ -1,14 +1,15 @@ #!/usr/bin/env python3 # -*- coding: ascii -*- from __future__ import division, with_statement -version = '1.1.0' +from setuptools import setup, find_packages +import sys +import io + +version = '1.2.0' author = "Omoto Kenji" license = "MIT License" author_email = 'doloopwhile@gmail.com' -from setuptools import setup, find_packages -import sys -import io with io.open('README.rst', encoding='ascii') as fp: long_description = fp.read() @@ -35,12 +36,11 @@ 'License :: OSI Approved :: MIT License', 'Programming Language :: Python', "Programming Language :: Python :: 2", - "Programming Language :: Python :: 2.6", "Programming Language :: Python :: 2.7", "Programming Language :: Python :: 3", - 'Programming Language :: Python :: 3.2', 'Programming Language :: Python :: 3.3', 'Programming Language :: Python :: 3.4', + 'Programming Language :: Python :: 3.5', 'Programming Language :: JavaScript', ], install_requires=install_requires, From f8f4f8e2d79465af7a47ca25deb738b4f78929ed Mon Sep 17 00:00:00 2001 From: OMOTO Kenji Date: Sun, 13 Mar 2016 21:28:28 +0900 Subject: [PATCH 12/48] [README] Reordered chapters --- README.md | 37 ++++++++++++++++++------------------- README.rst | 53 +++++++++++++++++++++++++++-------------------------- 2 files changed, 45 insertions(+), 45 deletions(-) diff --git a/README.md b/README.md index 488e4ac..efaa82f 100644 --- a/README.md +++ b/README.md @@ -20,13 +20,16 @@ A short example: >>> ctx.call("add", 1, 2) 3 -The pros of PyExecJS is that you do not need take care of JavaScript environment. -Especially, it works in Windows environment without installing extra libraries. - -One of cons of PyExecJS is performance. PyExecJS communicate JavaScript runtime by text and it is slow. -The other cons is that it does not fully support runtime specific features. +# Supported runtimes -[PyV8](https://code.google.com/p/pyv8/) might be better choice for some use case. +* [PyV8](http://code.google.com/p/pyv8/) - A python wrapper for Google V8 engine, +* [Node.js](http://nodejs.org/) +* 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 # Installation @@ -38,18 +41,6 @@ or # Details -PyExecJS supports these runtimes: - -* [PyV8](http://code.google.com/p/pyv8/) - A python wrapper for Google V8 engine, -* [Node.js](http://nodejs.org/) -* 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 - - If `EXECJS_RUNTIME` environment variable is specified, PyExecJS pick the JavaScript runtime as a default: >>> execjs.get().name # this value is depends on your environment. @@ -69,6 +60,14 @@ You can choose JavaScript runtime by `execjs.get()`: >>> node.eval("1 + 2") 3 +The pros of PyExecJS is that you do not need take care of JavaScript environment. +Especially, it works in Windows environment without installing extra libraries. + +One of cons of PyExecJS is performance. PyExecJS communicate JavaScript runtime by text and it is slow. +The other cons is that it does not fully support runtime specific features. + +[PyV8](https://code.google.com/p/pyv8/) might be better choice for some use case. + # License Copyright (c) 2016 Omoto Kenji. @@ -76,7 +75,7 @@ Copyright (c) 2011 Sam Stephenson and Josh Peek. (As a author of ExecJS) Released under the MIT license. See `LICENSE` for details. -# Changes +# Changelog ## 1.2.0 - Supported Python 3.5 diff --git a/README.rst b/README.rst index 895c993..f51f6fc 100644 --- a/README.rst +++ b/README.rst @@ -21,16 +21,21 @@ A short example: >>> ctx.call("add", 1, 2) 3 -The pros of PyExecJS is that you do not need take care of JavaScript -environment. Especially, it works in Windows environment without -installing extra libraries. - -One of cons of PyExecJS is performance. PyExecJS communicate JavaScript -runtime by text and it is slow. The other cons is that it does not fully -support runtime specific features. +Supported runtimes +================== -`PyV8 `__ might be better choice for -some use case. +- `PyV8 `__ - A python wrapper for + Google V8 engine, +- `Node.js `__ +- Apple JavaScriptCore - Included with Mac OS X +- `Mozilla SpiderMonkey `__ +- `Microsoft Windows Script + Host `__ + (JScript) +- `SlimerJS `__ +- `PhantomJS `__ +- `Nashorn `__ + - Included with Oracle Java 8 Installation ============ @@ -48,21 +53,6 @@ or Details ======= -PyExecJS supports these runtimes: - -- `PyV8 `__ - A python wrapper for - Google V8 engine, -- `Node.js `__ -- Apple JavaScriptCore - Included with Mac OS X -- `Mozilla SpiderMonkey `__ -- `Microsoft Windows Script - Host `__ - (JScript) -- `SlimerJS `__ -- `PhantomJS `__ -- `Nashorn `__ - - Included with Oracle Java 8 - If ``EXECJS_RUNTIME`` environment variable is specified, PyExecJS pick the JavaScript runtime as a default: @@ -87,6 +77,17 @@ You can choose JavaScript runtime by ``execjs.get()``: >>> node.eval("1 + 2") 3 +The pros of PyExecJS is that you do not need take care of JavaScript +environment. Especially, it works in Windows environment without +installing extra libraries. + +One of cons of PyExecJS is performance. PyExecJS communicate JavaScript +runtime by text and it is slow. The other cons is that it does not fully +support runtime specific features. + +`PyV8 `__ might be better choice for +some use case. + License ======= @@ -95,8 +96,8 @@ Josh Peek. (As a author of ExecJS) Released under the MIT license. See ``LICENSE`` for details. -Changes -======= +Changelog +========= 1.2.0 ----- From 256e6bc1a5c82fad55b51ed4eacbb468a5cbd038 Mon Sep 17 00:00:00 2001 From: Gabe Rives-Corbett Date: Wed, 1 Apr 2015 12:03:23 -0400 Subject: [PATCH 13/48] Support raw result --- execjs/external_runtime.py | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/execjs/external_runtime.py b/execjs/external_runtime.py index b43668e..516907e 100644 --- a/execjs/external_runtime.py +++ b/execjs/external_runtime.py @@ -19,13 +19,14 @@ class ExternalRuntime: - def __init__(self, name, command, runner_source, encoding='utf8'): + def __init__(self, name, command, runner_source, encoding='utf8', raw_result=False): self._name = name if isinstance(command, str): command = [command] self._command = command self._runner_source = runner_source self._encoding = encoding + self._raw_result = raw_result if self._binary() is None: self.__class__ = execjs.unavailable_runtime.UnavailableRuntime @@ -105,9 +106,11 @@ def exec_(self, source): finally: os.remove(filename) - output = output.decode(self._runtime._encoding) - output = output.replace("\r\n", "\n").replace("\r", "\n") - return self._extract_result(output.split("\n")[-2]) + if not self._runtime._raw_result: + output = output.decode(self._runtime._encoding) + output = output.replace("\r\n", "\n").replace("\r", "\n") + output = self._extract_result(output.split("\n")[-2]) + return output def call(self, identifier, *args): args = json.dumps(args) From 6b29922f7baf0a9f312cc2f78eebf3d22319990d Mon Sep 17 00:00:00 2001 From: OMOTO Kenji Date: Sun, 13 Mar 2016 22:10:22 +0900 Subject: [PATCH 14/48] Fixed Nashorn test in travis-ci The test was not executed. Added a step to print Nashorn's version --- .travis.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.travis.yml b/.travis.yml index d5df2d4..fb120aa 100644 --- a/.travis.yml +++ b/.travis.yml @@ -23,6 +23,7 @@ before_script: - "tar xvf slimerjs-*.tar.bz2" - "rm slimerjs-*.tar.bz2" - "mv slimerjs-* slimerjs" + - "sudo apt-get install oracle-java8-installer" install: - "./install_development.sh" @@ -30,4 +31,5 @@ install: script: - "slimerjs --version" + - "jjs -v < /dev/null" - "python setup.py test" From 982d7926d374e6015d9d6f4cddc4c0c3475a67f1 Mon Sep 17 00:00:00 2001 From: OMOTO Kenji Date: Sun, 13 Mar 2016 22:29:48 +0900 Subject: [PATCH 15/48] Revert "Merge branch 'grivescorbett-raw-result'" raw_result feature does not look useful. This reverts commit 36229ea65dd2921d1c09eeeb5c85d96047988962, reversing changes made to a4890baa09cf4ab8c63f32584e181f21c5c546a1. --- execjs/external_runtime.py | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/execjs/external_runtime.py b/execjs/external_runtime.py index 516907e..b43668e 100644 --- a/execjs/external_runtime.py +++ b/execjs/external_runtime.py @@ -19,14 +19,13 @@ class ExternalRuntime: - def __init__(self, name, command, runner_source, encoding='utf8', raw_result=False): + def __init__(self, name, command, runner_source, encoding='utf8'): self._name = name if isinstance(command, str): command = [command] self._command = command self._runner_source = runner_source self._encoding = encoding - self._raw_result = raw_result if self._binary() is None: self.__class__ = execjs.unavailable_runtime.UnavailableRuntime @@ -106,11 +105,9 @@ def exec_(self, source): finally: os.remove(filename) - if not self._runtime._raw_result: - output = output.decode(self._runtime._encoding) - output = output.replace("\r\n", "\n").replace("\r", "\n") - output = self._extract_result(output.split("\n")[-2]) - return output + output = output.decode(self._runtime._encoding) + output = output.replace("\r\n", "\n").replace("\r", "\n") + return self._extract_result(output.split("\n")[-2]) def call(self, identifier, *args): args = json.dumps(args) From 78b0eb61bbe7c1e21551f533bda07cb7b46e41f9 Mon Sep 17 00:00:00 2001 From: OMOTO Kenji Date: Sun, 13 Mar 2016 22:24:03 +0900 Subject: [PATCH 16/48] Removed unused file --- execjs/pyqtruntime.py | 106 ------------------------------------------ 1 file changed, 106 deletions(-) delete mode 100644 execjs/pyqtruntime.py diff --git a/execjs/pyqtruntime.py b/execjs/pyqtruntime.py deleted file mode 100644 index e7a74d0..0000000 --- a/execjs/pyqtruntime.py +++ /dev/null @@ -1,106 +0,0 @@ -import sys -from PyQt4 import QtCore, QtGui, QtWebKit - -import execjs - -class WebPage(QtWebKit.QWebPage): - def javaScriptConsoleMessage(self, msg, line, source): - print '%s line %d: %s' % (source, line, msg) - -url = 'http://localhost/test.html' -app = QtGui.QApplication([]) -browser = QtWebKit.QWebView() -page = WebPage() -browser.setPage(page) -browser.load(QtCore.QUrl(url)) -browser.show() -sys.exit(app.exec_()) - - -class PySideRuntime: - def __init__(self): - try: - import PySide.QWebKit - except ImportError: - self._is_available = False - else: - self._is_available = True - - @property - def name(self): - return "PySide" - - def exec_(self, source): - self.ensure_available() - return self.Context().exec_(source) - - def eval(self, source): - self.ensure_available() - return self.Context().eval(source) - - def compile(self, source): - self.ensure_available() - return self.Context(source) - - def is_available(self): - return self._is_available - - def ensure_available(self): - if not self.is_available(): - raise execjs.RuntimeError - - class Context: - def __init__(self, source=""): - self._source = source - - def exec_(self, source): - source = '''\ - (function() {{ - {0}; - {1}; - }})()'''.format( - encode_unicode_codepoints(self._source), - encode_unicode_codepoints(source) - ) - source = str(source) - - import PyV8 - import contextlib - #backward compatibility - with contextlib.nested(PyV8.JSContext(), PyV8.JSEngine()) as (ctxt, engine): - js_errors = (PyV8.JSError, IndexError, ReferenceError, SyntaxError, TypeError) - try: - script = engine.compile(source) - except js_errors as e: - raise RuntimeError(e) - try: - value = script.run() - except js_errors as e: - raise ProgramError(e) - return self.convert(value) - - def eval(self, source): - return self.exec_('return ' + encode_unicode_codepoints(source)) - - def call(self, identifier, *args): - args = json.dumps(args) - return self.eval("{identifier}.apply(this, {args})".format(identifier=identifier, args=args)) - - @classmethod - def convert(cls, obj): - from PyV8 import _PyV8 - if isinstance(obj, bytes): - return obj.decode('utf8') - if isinstance(obj, _PyV8.JSArray): - return [cls.convert(v) for v in obj] - elif isinstance(obj, _PyV8.JSFunction): - return None - elif isinstance(obj, _PyV8.JSObject): - ret = {} - for k in obj.keys(): - v = cls.convert(obj[k]) - if v is not None: - ret[cls.convert(k)] = v - return ret - else: - return obj From 411f60976c9e1e2d4bd806006752c092542c11b2 Mon Sep 17 00:00:00 2001 From: OMOTO Kenji Date: Sun, 13 Mar 2016 22:33:33 +0900 Subject: [PATCH 17/48] Refactored _extract_result --- execjs/external_runtime.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/execjs/external_runtime.py b/execjs/external_runtime.py index b43668e..99495de 100644 --- a/execjs/external_runtime.py +++ b/execjs/external_runtime.py @@ -105,9 +105,7 @@ def exec_(self, source): finally: os.remove(filename) - output = output.decode(self._runtime._encoding) - output = output.replace("\r\n", "\n").replace("\r", "\n") - return self._extract_result(output.split("\n")[-2]) + return self._extract_result(output) def call(self, identifier, *args): args = json.dumps(args) @@ -133,8 +131,12 @@ def _compile(self, source): return runner_source - def _extract_result(self, output_last_line): + def _extract_result(self, output): """protected""" + 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: From a5722a602763c48214c0ba1a46d1ebe34c0ce569 Mon Sep 17 00:00:00 2001 From: OMOTO Kenji Date: Sun, 13 Mar 2016 22:54:38 +0900 Subject: [PATCH 18/48] Removed UnavailableRuntime and added AbstractRuntime --- execjs/_abstract_runtime.py | 30 ++++++++++++++++ execjs/external_runtime.py | 68 ++++++++++++++++++----------------- execjs/pyv8runtime.py | 20 ++++++----- execjs/unavailable_runtime.py | 18 ---------- test_execjs.py | 7 ---- 5 files changed, 78 insertions(+), 65 deletions(-) create mode 100644 execjs/_abstract_runtime.py delete mode 100644 execjs/unavailable_runtime.py diff --git a/execjs/_abstract_runtime.py b/execjs/_abstract_runtime.py new file mode 100644 index 0000000..c8f70bc --- /dev/null +++ b/execjs/_abstract_runtime.py @@ -0,0 +1,30 @@ +import execjs + + +class AbstructRuntime: + def exec_(self, source): + if not self.is_available(): + raise execjs.RuntimeUnavailableError + return self._exec_(source) + + def eval(self, source): + if not self.is_available(): + raise execjs.RuntimeUnavailableError + return self._eval(source) + + def compile(self, source): + if not self.is_available(): + raise execjs.RuntimeUnavailableError + return self._compile(source) + + def is_available(self): + raise NotImplementedError + + def _exec_(self, source, cwd): + raise NotImplementedError + + def _compile(self, source, cwd): + raise NotImplementedError + + def _eval(self, source, cwd): + raise NotImplementedError diff --git a/execjs/external_runtime.py b/execjs/external_runtime.py index 99495de..ea4b2d0 100644 --- a/execjs/external_runtime.py +++ b/execjs/external_runtime.py @@ -12,13 +12,13 @@ import six import execjs -import execjs.unavailable_runtime +import execjs._abstract_runtime as abstract_runtime import execjs._json2 as _json2 import execjs._runner_sources as _runner_sources from execjs._misc import encode_unicode_codepoints -class ExternalRuntime: +class ExternalRuntime(abstract_runtime.AbstructRuntime): def __init__(self, name, command, runner_source, encoding='utf8'): self._name = name if isinstance(command, str): @@ -27,8 +27,7 @@ def __init__(self, name, command, runner_source, encoding='utf8'): self._runner_source = runner_source self._encoding = encoding - if self._binary() is None: - self.__class__ = execjs.unavailable_runtime.UnavailableRuntime + self._available = self._binary() is not None def __str__(self): return "{class_name}({runtime_name})".format( @@ -40,45 +39,29 @@ def __str__(self): def name(self): return self._name - def exec_(self, source): + def is_available(self): + return self._available + + def _exec_(self, source): + """protected""" return self.Context(self).exec_(source) - def eval(self, source): + def _eval(self, source): + """protected""" return self.Context(self).eval(source) - def compile(self, source): + def _compile(self, source): + """protected""" return self.Context(self, source) - def is_available(self): - return True - - def runner_source(self): - return self._runner_source - def _binary(self): """protected""" if not hasattr(self, "_binary_cache"): self._binary_cache = _which(self._command) return self._binary_cache - def _execfile(self, filename): - """protected""" - cmd = self._binary() + [filename] - - p = None - try: - p = Popen(cmd, stdout=PIPE, stderr=PIPE) - stdoutdata, stderrdata = p.communicate() - ret = p.wait() - finally: - del p - - if ret == 0: - return stdoutdata - else: - raise execjs.RuntimeError("stdout: {}, stderr: {}".format(repr(stdoutdata), repr(stderrdata))) - class Context: + """protected""" def __init__(self, runtime, source=''): self._runtime = runtime self._source = source @@ -101,7 +84,7 @@ def exec_(self, source): try: with io.open(filename, "w+", encoding=self._runtime._encoding) as fp: fp.write(self._compile(source)) - output = self._runtime._execfile(filename) + output = self._execfile(filename) finally: os.remove(filename) @@ -111,9 +94,26 @@ def call(self, identifier, *args): args = json.dumps(args) return self.eval("{identifier}.apply(this, {args})".format(identifier=identifier, args=args)) + def _execfile(self, filename): + """protected""" + cmd = self._runtime._binary() + [filename] + + p = None + try: + p = Popen(cmd, stdout=PIPE, stderr=PIPE) + stdoutdata, stderrdata = p.communicate() + ret = p.wait() + finally: + del p + + if ret == 0: + return stdoutdata + else: + raise execjs.RuntimeError("stdout: {}, stderr: {}".format(repr(stdoutdata), repr(stderrdata))) + def _compile(self, source): """protected""" - runner_source = self._runtime.runner_source() + runner_source = self._runtime._runner_source replacements = { '#{source}': lambda: source, @@ -154,16 +154,19 @@ def _extract_result(self, output): 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: @@ -179,6 +182,7 @@ def _find_executable(prog, pathext=("",)): def _which(command): + """protected""" if isinstance(command, str): command = [command] command = list(command) diff --git a/execjs/pyv8runtime.py b/execjs/pyv8runtime.py index 53a2ee3..5e06bc6 100644 --- a/execjs/pyv8runtime.py +++ b/execjs/pyv8runtime.py @@ -2,7 +2,7 @@ import contextlib import execjs -import execjs.unavailable_runtime +import execjs._abstract_runtime as abstract_runtime from execjs._misc import encode_unicode_codepoints try: @@ -13,28 +13,32 @@ _pyv8_available = True -class PyV8Runtime: +class PyV8Runtime(abstract_runtime.AbstructRuntime): def __init__(self): - if not _pyv8_available: - self.__class__ = execjs.unavailable_runtime.UnavailableRuntime + pass @property def name(self): return "PyV8" - def exec_(self, source): + def _exec_(self, source): + """protected""" return self.Context().exec_(source) - def eval(self, source): + def _eval(self, source): + """protected""" return self.Context().eval(source) - def compile(self, source): + def _compile(self, source): + """protected""" return self.Context(source) def is_available(self): - return True + return _pyv8_available class Context: + """protected""" + def __init__(self, source=""): self._source = source diff --git a/execjs/unavailable_runtime.py b/execjs/unavailable_runtime.py deleted file mode 100644 index 71efe4a..0000000 --- a/execjs/unavailable_runtime.py +++ /dev/null @@ -1,18 +0,0 @@ -import execjs - - -class UnavailableRuntime: - def __init__(self, name): - self.name = name - - def exec_(self, source): - raise execjs.RuntimeUnavailableError - - def eval(self, source): - raise execjs.RuntimeUnavailableError - - def compile(self, source): - raise execjs.RuntimeUnavailableError - - def is_available(self): - return False diff --git a/test_execjs.py b/test_execjs.py index 83636c9..1e8057b 100755 --- a/test_execjs.py +++ b/test_execjs.py @@ -7,13 +7,6 @@ import six import execjs -import execjs.external_runtime -import execjs.pyv8runtime -import execjs.unavailable_runtime -import execjs._json2 -import execjs._misc -import execjs._runner_sources -import execjs._runtimes if sys.version_info < (2, 7): import unittest2 as unittest From aa542362bd881b1a40b059cd58d734d2a68a73e4 Mon Sep 17 00:00:00 2001 From: OMOTO Kenji Date: Sun, 13 Mar 2016 23:10:25 +0900 Subject: [PATCH 19/48] Renamed private sub-packages --- execjs/__init__.py | 4 ++-- execjs/{external_runtime.py => _external_runtime.py} | 0 execjs/{pyv8runtime.py => _pyv8runtime.py} | 0 execjs/_runtimes.py | 4 ++-- 4 files changed, 4 insertions(+), 4 deletions(-) rename execjs/{external_runtime.py => _external_runtime.py} (100%) rename execjs/{pyv8runtime.py => _pyv8runtime.py} (100%) diff --git a/execjs/__init__.py b/execjs/__init__.py index d7d9149..f148308 100755 --- a/execjs/__init__.py +++ b/execjs/__init__.py @@ -23,8 +23,8 @@ from __future__ import unicode_literals, division, with_statement import execjs._runtimes -import execjs.external_runtime -ExternalRuntime = execjs.external_runtime.ExternalRuntime +import execjs._external_runtime as external_runtime +ExternalRuntime = external_runtime.ExternalRuntime __all__ = """ diff --git a/execjs/external_runtime.py b/execjs/_external_runtime.py similarity index 100% rename from execjs/external_runtime.py rename to execjs/_external_runtime.py diff --git a/execjs/pyv8runtime.py b/execjs/_pyv8runtime.py similarity index 100% rename from execjs/pyv8runtime.py rename to execjs/_pyv8runtime.py diff --git a/execjs/_runtimes.py b/execjs/_runtimes.py index a2b765d..1fb8cb3 100644 --- a/execjs/_runtimes.py +++ b/execjs/_runtimes.py @@ -6,8 +6,8 @@ from ordereddict import OrderedDict import execjs -import execjs.external_runtime as external_runtime -import execjs.pyv8runtime as pyv8runtime +import execjs._external_runtime as external_runtime +import execjs._pyv8runtime as pyv8runtime def register(name, runtime): From 79e4ddb33d1efeac3b128d7edcf761b0deac97b4 Mon Sep 17 00:00:00 2001 From: OMOTO Kenji Date: Sun, 13 Mar 2016 23:42:37 +0900 Subject: [PATCH 20/48] Add cwd parameter --- execjs/_abstract_runtime.py | 18 +++++++++--------- execjs/_external_runtime.py | 17 +++++++++-------- execjs/_pyv8runtime.py | 6 +++--- 3 files changed, 21 insertions(+), 20 deletions(-) diff --git a/execjs/_abstract_runtime.py b/execjs/_abstract_runtime.py index c8f70bc..2c92e14 100644 --- a/execjs/_abstract_runtime.py +++ b/execjs/_abstract_runtime.py @@ -2,29 +2,29 @@ class AbstructRuntime: - def exec_(self, source): + def exec_(self, source, cwd=None): if not self.is_available(): raise execjs.RuntimeUnavailableError - return self._exec_(source) + return self._exec_(source, cwd=cwd) - def eval(self, source): + def eval(self, source, cwd=None): if not self.is_available(): raise execjs.RuntimeUnavailableError - return self._eval(source) + return self._eval(source, cwd=cwd) - def compile(self, source): + def compile(self, source, cwd=None): if not self.is_available(): raise execjs.RuntimeUnavailableError - return self._compile(source) + return self._compile(source, cwd=cwd) def is_available(self): raise NotImplementedError - def _exec_(self, source, cwd): + def _exec_(self, source, cwd=None): raise NotImplementedError - def _compile(self, source, cwd): + def _compile(self, source, cwd=None): raise NotImplementedError - def _eval(self, source, cwd): + def _eval(self, source, cwd=None): raise NotImplementedError diff --git a/execjs/_external_runtime.py b/execjs/_external_runtime.py index ea4b2d0..b6de135 100644 --- a/execjs/_external_runtime.py +++ b/execjs/_external_runtime.py @@ -42,17 +42,17 @@ def name(self): def is_available(self): return self._available - def _exec_(self, source): + def _exec_(self, source, cwd=None): """protected""" - return self.Context(self).exec_(source) + return self.Context(self, cwd=cwd).exec_(source) - def _eval(self, source): + def _eval(self, source, cwd=None): """protected""" - return self.Context(self).eval(source) + return self.Context(self, cwd=cwd).eval(source) - def _compile(self, source): + def _compile(self, source, cwd=None): """protected""" - return self.Context(self, source) + return self.Context(self, source, cwd=cwd) def _binary(self): """protected""" @@ -62,9 +62,10 @@ def _binary(self): class Context: """protected""" - def __init__(self, runtime, source=''): + def __init__(self, runtime, source='', cwd=None): self._runtime = runtime self._source = source + self._cwd = cwd def eval(self, source): if not source.strip(): @@ -100,7 +101,7 @@ def _execfile(self, filename): p = None try: - p = Popen(cmd, stdout=PIPE, stderr=PIPE) + p = Popen(cmd, stdout=PIPE, stderr=PIPE, cwd=self._cwd) stdoutdata, stderrdata = p.communicate() ret = p.wait() finally: diff --git a/execjs/_pyv8runtime.py b/execjs/_pyv8runtime.py index 5e06bc6..5a94d0a 100644 --- a/execjs/_pyv8runtime.py +++ b/execjs/_pyv8runtime.py @@ -21,15 +21,15 @@ def __init__(self): def name(self): return "PyV8" - def _exec_(self, source): + def _exec_(self, source, cwd=None): """protected""" return self.Context().exec_(source) - def _eval(self, source): + def _eval(self, source, cwd=None): """protected""" return self.Context().eval(source) - def _compile(self, source): + def _compile(self, source, cwd=None): """protected""" return self.Context(source) From 10877070ad58d0e6b3aae25b50eeedc4812ceb25 Mon Sep 17 00:00:00 2001 From: OMOTO Kenji Date: Sun, 13 Mar 2016 23:56:48 +0900 Subject: [PATCH 21/48] Version 1.3.0 --- README.md | 3 +++ README.rst | 5 +++++ setup.py | 2 +- 3 files changed, 9 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index efaa82f..7a8615b 100644 --- a/README.md +++ b/README.md @@ -77,6 +77,9 @@ Released under the MIT license. See `LICENSE` for details. # Changelog +## 1.3.0 +- Added `cwd` argument. + ## 1.2.0 - Supported Python 3.5 - Supported Nashorn(Java 8 JavaScript engine) as runtime diff --git a/README.rst b/README.rst index f51f6fc..05c80bc 100644 --- a/README.rst +++ b/README.rst @@ -99,6 +99,11 @@ Released under the MIT license. See ``LICENSE`` for details. Changelog ========= +1.3.0 +----- + +- Added ``cwd`` argument. + 1.2.0 ----- diff --git a/setup.py b/setup.py index f704b74..81412bd 100755 --- a/setup.py +++ b/setup.py @@ -5,7 +5,7 @@ import sys import io -version = '1.2.0' +version = '1.3.0' author = "Omoto Kenji" license = "MIT License" author_email = 'doloopwhile@gmail.com' From 45530fad63a6a054d401aeb35c3c8148cad9e1de Mon Sep 17 00:00:00 2001 From: James Broadhead Date: Wed, 16 Mar 2016 00:02:44 +0000 Subject: [PATCH 22/48] remove cyclic dep on package - remove a cyclic dependency on the root package by extracting exception definitions and using package-relative imports - fix some missed renames of RuntimeUnavailableError - flip the __name__ comparison (pylint best-practise advice) --- execjs/__init__.py | 17 +---------------- execjs/_abstract_runtime.py | 8 ++++---- execjs/_exceptions.py | 14 ++++++++++++++ execjs/_external_runtime.py | 8 ++++---- execjs/_pyv8runtime.py | 6 +++--- execjs/_runtimes.py | 8 ++++---- 6 files changed, 30 insertions(+), 31 deletions(-) create mode 100644 execjs/_exceptions.py diff --git a/execjs/__init__.py b/execjs/__init__.py index f148308..aad8f80 100755 --- a/execjs/__init__.py +++ b/execjs/__init__.py @@ -22,6 +22,7 @@ ''' from __future__ import unicode_literals, division, with_statement +from execjs._exceptions import Error, RuntimeError, ProgramError, RuntimeUnavailableError import execjs._runtimes import execjs._external_runtime as external_runtime ExternalRuntime = external_runtime.ExternalRuntime @@ -34,22 +35,6 @@ """.split() -class Error(Exception): - pass - - -class RuntimeError(Error): - pass - - -class ProgramError(Error): - pass - - -class RuntimeUnavailableError(RuntimeError): - pass - - register = execjs._runtimes.register get = execjs._runtimes.get runtimes = execjs._runtimes.runtimes diff --git a/execjs/_abstract_runtime.py b/execjs/_abstract_runtime.py index 2c92e14..1209cb0 100644 --- a/execjs/_abstract_runtime.py +++ b/execjs/_abstract_runtime.py @@ -1,20 +1,20 @@ -import execjs +import execjs._exceptions as exceptions class AbstructRuntime: def exec_(self, source, cwd=None): if not self.is_available(): - raise execjs.RuntimeUnavailableError + raise exceptions.RuntimeUnavailableError return self._exec_(source, cwd=cwd) def eval(self, source, cwd=None): if not self.is_available(): - raise execjs.RuntimeUnavailableError + raise exceptions.RuntimeUnavailableError return self._eval(source, cwd=cwd) def compile(self, source, cwd=None): if not self.is_available(): - raise execjs.RuntimeUnavailableError + raise exceptions.RuntimeUnavailableError return self._compile(source, cwd=cwd) def is_available(self): diff --git a/execjs/_exceptions.py b/execjs/_exceptions.py new file mode 100644 index 0000000..876cb22 --- /dev/null +++ b/execjs/_exceptions.py @@ -0,0 +1,14 @@ +class Error(Exception): + pass + + +class RuntimeError(Error): + pass + + +class ProgramError(Error): + pass + + +class RuntimeUnavailableError(RuntimeError): + pass diff --git a/execjs/_external_runtime.py b/execjs/_external_runtime.py index b6de135..8e1a40c 100644 --- a/execjs/_external_runtime.py +++ b/execjs/_external_runtime.py @@ -11,10 +11,10 @@ import six -import execjs import execjs._abstract_runtime as abstract_runtime import execjs._json2 as _json2 import execjs._runner_sources as _runner_sources +import execjs._exceptions as exceptions from execjs._misc import encode_unicode_codepoints @@ -110,7 +110,7 @@ def _execfile(self, filename): if ret == 0: return stdoutdata else: - raise execjs.RuntimeError("stdout: {}, stderr: {}".format(repr(stdoutdata), repr(stderrdata))) + raise exceptions.RuntimeError("stdout: {}, stderr: {}".format(repr(stdoutdata), repr(stderrdata))) def _compile(self, source): """protected""" @@ -149,9 +149,9 @@ def _extract_result(self, output): if status == "ok": return value elif value.startswith('SyntaxError:'): - raise execjs.RuntimeError(value) + raise exceptions.RuntimeError(value) else: - raise execjs.ProgramError(value) + raise exceptions.ProgramError(value) def _is_windows(): diff --git a/execjs/_pyv8runtime.py b/execjs/_pyv8runtime.py index 5a94d0a..46f65d2 100644 --- a/execjs/_pyv8runtime.py +++ b/execjs/_pyv8runtime.py @@ -1,8 +1,8 @@ import json import contextlib -import execjs import execjs._abstract_runtime as abstract_runtime +import execjs._exceptions as exceptions from execjs._misc import encode_unicode_codepoints try: @@ -59,11 +59,11 @@ def exec_(self, source): try: script = engine.compile(source) except js_errors as e: - raise execjs.RuntimeError(e) + raise exceptions.RuntimeError(e) try: value = script.run() except js_errors as e: - raise execjs.ProgramError(e) + raise exceptions.ProgramError(e) return self.convert(value) def eval(self, source): diff --git a/execjs/_runtimes.py b/execjs/_runtimes.py index 1fb8cb3..e3688fe 100644 --- a/execjs/_runtimes.py +++ b/execjs/_runtimes.py @@ -5,9 +5,9 @@ except ImportError: from ordereddict import OrderedDict -import execjs import execjs._external_runtime as external_runtime import execjs._pyv8runtime as pyv8runtime +import execjs._exceptions as exceptions def register(name, runtime): @@ -26,10 +26,10 @@ def get(name=None): try: runtime = runtimes()[name] except KeyError: - raise execjs.RuntimeUnavailable("{name} runtime is not defined".format(name=name)) + raise exceptions.RuntimeUnavailableError("{name} runtime is not defined".format(name=name)) else: if not runtime.is_available(): - raise execjs.RuntimeUnavailable( + raise exceptions.RuntimeUnavailableError( "{name} runtime is not available on this system".format(name=runtime.name)) return runtime @@ -53,7 +53,7 @@ def _auto_detect(): if runtime.is_available(): return runtime - raise execjs.RuntimeUnavailable("Could not find a JavaScript runtime.") + raise exceptions.RuntimeUnavailableError("Could not find a JavaScript runtime.") def get_from_environment(): From 6e78cdac6ec577d1b68614f4add91c2e6419c2a1 Mon Sep 17 00:00:00 2001 From: OMOTO Kenji Date: Sun, 20 Mar 2016 13:24:47 +0900 Subject: [PATCH 23/48] Created AbstractRuntimeContext and added docstring --- execjs/__init__.py | 7 ++-- execjs/_abstract_runtime.py | 27 ++++++++++++++- execjs/_abstract_runtime_context.py | 53 +++++++++++++++++++++++++++++ execjs/_external_runtime.py | 27 +++++++-------- execjs/_pyv8runtime.py | 22 ++++++------ 5 files changed, 108 insertions(+), 28 deletions(-) create mode 100644 execjs/_abstract_runtime_context.py diff --git a/execjs/__init__.py b/execjs/__init__.py index f148308..fec7e76 100755 --- a/execjs/__init__.py +++ b/execjs/__init__.py @@ -23,8 +23,8 @@ from __future__ import unicode_literals, division, with_statement import execjs._runtimes -import execjs._external_runtime as external_runtime -ExternalRuntime = external_runtime.ExternalRuntime +from execjs._external_runtime import ExternalRuntime +from execjs._abstract_runtime import AbstractRuntime __all__ = """ @@ -59,11 +59,14 @@ class RuntimeUnavailableError(RuntimeError): def eval(source): return get().eval(source) +eval.__doc__= AbstractRuntime.eval.__doc__ def exec_(source): return get().exec_(source) +exec_.__doc__= AbstractRuntime.exec_.__doc__ def compile(source): return get().compile(source) +compile.__doc__= AbstractRuntime.compile.__doc__ diff --git a/execjs/_abstract_runtime.py b/execjs/_abstract_runtime.py index 2c92e14..9076d85 100644 --- a/execjs/_abstract_runtime.py +++ b/execjs/_abstract_runtime.py @@ -1,30 +1,55 @@ import execjs +from abc import ABCMeta, abstractmethod +import six -class AbstructRuntime: +@six.add_metaclass(ABCMeta) +class AbstractRuntime(object): + ''' + Abstract base class for runtime class. + ''' def exec_(self, source, cwd=None): + '''Execute source by JavaScript runtime and return all output to stdout as a string. + + source -- JavaScript code to execute. + cwd -- Directory where call JavaScript runtime. It may be ignored for some runtime. + ''' if not self.is_available(): raise execjs.RuntimeUnavailableError return self._exec_(source, cwd=cwd) def eval(self, source, cwd=None): + '''Evaluate source in JavaScript runtime. + + source -- JavaScript code to evaluate. + cwd -- Directory where call JavaScript runtime. It may be ignored for some runtime. + ''' if not self.is_available(): raise execjs.RuntimeUnavailableError return self._eval(source, cwd=cwd) def compile(self, source, cwd=None): + '''Bulk source as a context object. The source can be used to execute another code. + + source -- JavaScript code to bulk. + cwd -- Directory where call JavaScript runtime. It may be ignored for some runtime. + ''' if not self.is_available(): raise execjs.RuntimeUnavailableError return self._compile(source, cwd=cwd) + @abstractmethod def is_available(self): raise NotImplementedError + @abstractmethod def _exec_(self, source, cwd=None): raise NotImplementedError + @abstractmethod def _compile(self, source, cwd=None): raise NotImplementedError + @abstractmethod def _eval(self, source, cwd=None): raise NotImplementedError diff --git a/execjs/_abstract_runtime_context.py b/execjs/_abstract_runtime_context.py new file mode 100644 index 0000000..f645cf5 --- /dev/null +++ b/execjs/_abstract_runtime_context.py @@ -0,0 +1,53 @@ +import execjs +from abc import ABCMeta, abstractmethod +import six + + +@six.add_metaclass(ABCMeta) +class AbstractRuntimeContext(object): + ''' + Abstract base class for runtime context class. + ''' + def exec_(self, source): + '''Execute source by JavaScript runtime and return all output to stdout as a string. + + source -- JavaScript code to execute. + ''' + if not self.is_available(): + raise execjs.RuntimeUnavailableError + return self._exec_(source) + + def eval(self, source): + '''Evaluate source in JavaScript runtime. + + source -- JavaScript code to evaluate. + ''' + if not self.is_available(): + raise execjs.RuntimeUnavailableError + return self._eval(source) + + def call(self, name, *args): + '''Call a JavaScript function in context. + + name -- Name of funtion object to call + args -- Arguments for the funtion object + ''' + if not self.is_available(): + raise execjs.RuntimeUnavailableError + return self._call(name, *args) + + @abstractmethod + def is_available(self): + raise NotImplementedError + + @abstractmethod + def _exec_(self, source): + raise NotImplementedError + + @abstractmethod + def _eval(self, source): + raise NotImplementedError + + @abstractmethod + def _call(self, name, *args): + raise NotImplementedError diff --git a/execjs/_external_runtime.py b/execjs/_external_runtime.py index b6de135..2fd2f22 100644 --- a/execjs/_external_runtime.py +++ b/execjs/_external_runtime.py @@ -12,13 +12,15 @@ import six import execjs -import execjs._abstract_runtime as abstract_runtime +from execjs._abstract_runtime import AbstractRuntime +from execjs._abstract_runtime_context import AbstractRuntimeContext import execjs._json2 as _json2 import execjs._runner_sources as _runner_sources from execjs._misc import encode_unicode_codepoints -class ExternalRuntime(abstract_runtime.AbstructRuntime): +class ExternalRuntime(AbstractRuntime): + '''Runtime to execute codes with external command.''' def __init__(self, name, command, runner_source, encoding='utf8'): self._name = name if isinstance(command, str): @@ -43,31 +45,31 @@ def is_available(self): return self._available def _exec_(self, source, cwd=None): - """protected""" return self.Context(self, cwd=cwd).exec_(source) def _eval(self, source, cwd=None): - """protected""" return self.Context(self, cwd=cwd).eval(source) def _compile(self, source, cwd=None): - """protected""" return self.Context(self, source, cwd=cwd) def _binary(self): - """protected""" if not hasattr(self, "_binary_cache"): self._binary_cache = _which(self._command) return self._binary_cache - class Context: - """protected""" + class Context(AbstractRuntimeContext): + # protected + def __init__(self, runtime, source='', cwd=None): self._runtime = runtime self._source = source self._cwd = cwd - def eval(self, source): + def is_available(self): + return self._runtime.is_available() + + def _eval(self, source): if not source.strip(): data = "''" else: @@ -76,7 +78,7 @@ def eval(self, source): code = 'return eval({data})'.format(data=data) return self.exec_(code) - def exec_(self, source): + def _exec_(self, source): if self._source: source = self._source + '\n' + source @@ -91,12 +93,11 @@ def exec_(self, source): return self._extract_result(output) - def call(self, identifier, *args): + def _call(self, identifier, *args): args = json.dumps(args) return self.eval("{identifier}.apply(this, {args})".format(identifier=identifier, args=args)) def _execfile(self, filename): - """protected""" cmd = self._runtime._binary() + [filename] p = None @@ -113,7 +114,6 @@ def _execfile(self, filename): raise execjs.RuntimeError("stdout: {}, stderr: {}".format(repr(stdoutdata), repr(stderrdata))) def _compile(self, source): - """protected""" runner_source = self._runtime._runner_source replacements = { @@ -133,7 +133,6 @@ def _compile(self, source): return runner_source def _extract_result(self, output): - """protected""" output = output.decode(self._runtime._encoding) output = output.replace("\r\n", "\n").replace("\r", "\n") output_last_line = output.split("\n")[-2] diff --git a/execjs/_pyv8runtime.py b/execjs/_pyv8runtime.py index 5a94d0a..fa44b15 100644 --- a/execjs/_pyv8runtime.py +++ b/execjs/_pyv8runtime.py @@ -2,7 +2,8 @@ import contextlib import execjs -import execjs._abstract_runtime as abstract_runtime +from execjs._abstract_runtime import AbstractRuntime +from execjs._abstract_runtime_context import AbstractRuntimeContext from execjs._misc import encode_unicode_codepoints try: @@ -13,7 +14,8 @@ _pyv8_available = True -class PyV8Runtime(abstract_runtime.AbstructRuntime): +class PyV8Runtime(AbstractRuntime): + '''Runtime to execute codes with PyV8.''' def __init__(self): pass @@ -22,27 +24,25 @@ def name(self): return "PyV8" def _exec_(self, source, cwd=None): - """protected""" return self.Context().exec_(source) def _eval(self, source, cwd=None): - """protected""" return self.Context().eval(source) def _compile(self, source, cwd=None): - """protected""" return self.Context(source) def is_available(self): return _pyv8_available - class Context: - """protected""" - + class Context(AbstractRuntimeContext): def __init__(self, source=""): self._source = source - def exec_(self, source): + def is_available(self): + return _pyv8_available + + def _exec_(self, source): source = '''\ (function() {{ {0}; @@ -66,10 +66,10 @@ def exec_(self, source): raise execjs.ProgramError(e) return self.convert(value) - def eval(self, source): + def _eval(self, source): return self.exec_('return ' + encode_unicode_codepoints(source)) - def call(self, identifier, *args): + def _call(self, identifier, *args): args = json.dumps(args) return self.eval("{identifier}.apply(this, {args})".format(identifier=identifier, args=args)) From c4e58bb5c59393e143d642d7db6ed0b1bf49f130 Mon Sep 17 00:00:00 2001 From: OMOTO Kenji Date: Sun, 20 Mar 2016 14:52:12 +0900 Subject: [PATCH 24/48] Added methods for AbstractRuntime --- execjs/_abstract_runtime.py | 22 +++++----------------- execjs/_external_runtime.py | 6 ------ execjs/_pyv8runtime.py | 6 ------ 3 files changed, 5 insertions(+), 29 deletions(-) diff --git a/execjs/_abstract_runtime.py b/execjs/_abstract_runtime.py index 9076d85..0ff424d 100644 --- a/execjs/_abstract_runtime.py +++ b/execjs/_abstract_runtime.py @@ -12,27 +12,23 @@ def exec_(self, source, cwd=None): '''Execute source by JavaScript runtime and return all output to stdout as a string. source -- JavaScript code to execute. - cwd -- Directory where call JavaScript runtime. It may be ignored for some runtime. + cwd -- Directory where call JavaScript runtime. It may be ignored in some derived class. ''' - if not self.is_available(): - raise execjs.RuntimeUnavailableError - return self._exec_(source, cwd=cwd) + return self.compile('', cwd=cwd).exec_(source) def eval(self, source, cwd=None): '''Evaluate source in JavaScript runtime. source -- JavaScript code to evaluate. - cwd -- Directory where call JavaScript runtime. It may be ignored for some runtime. + cwd -- Directory where call JavaScript runtime. It may be ignored in some derived class. ''' - if not self.is_available(): - raise execjs.RuntimeUnavailableError - return self._eval(source, cwd=cwd) + return self.compile('', cwd=cwd).eval(source) def compile(self, source, cwd=None): '''Bulk source as a context object. The source can be used to execute another code. source -- JavaScript code to bulk. - cwd -- Directory where call JavaScript runtime. It may be ignored for some runtime. + cwd -- Directory where call JavaScript runtime. It may be ignored in some derived class. ''' if not self.is_available(): raise execjs.RuntimeUnavailableError @@ -42,14 +38,6 @@ def compile(self, source, cwd=None): def is_available(self): raise NotImplementedError - @abstractmethod - def _exec_(self, source, cwd=None): - raise NotImplementedError - @abstractmethod def _compile(self, source, cwd=None): raise NotImplementedError - - @abstractmethod - def _eval(self, source, cwd=None): - raise NotImplementedError diff --git a/execjs/_external_runtime.py b/execjs/_external_runtime.py index 2fd2f22..f4f1814 100644 --- a/execjs/_external_runtime.py +++ b/execjs/_external_runtime.py @@ -44,12 +44,6 @@ def name(self): def is_available(self): return self._available - def _exec_(self, source, cwd=None): - return self.Context(self, cwd=cwd).exec_(source) - - def _eval(self, source, cwd=None): - return self.Context(self, cwd=cwd).eval(source) - def _compile(self, source, cwd=None): return self.Context(self, source, cwd=cwd) diff --git a/execjs/_pyv8runtime.py b/execjs/_pyv8runtime.py index fa44b15..3011ed2 100644 --- a/execjs/_pyv8runtime.py +++ b/execjs/_pyv8runtime.py @@ -23,12 +23,6 @@ def __init__(self): def name(self): return "PyV8" - def _exec_(self, source, cwd=None): - return self.Context().exec_(source) - - def _eval(self, source, cwd=None): - return self.Context().eval(source) - def _compile(self, source, cwd=None): return self.Context(source) From 53c71d13dd437a98103da71f095b666e053db87b Mon Sep 17 00:00:00 2001 From: OMOTO Kenji Date: Sat, 26 Mar 2016 16:12:56 +0900 Subject: [PATCH 25/48] Added tests --- .travis.yml | 1 + test_execjs.py | 4 ++++ 2 files changed, 5 insertions(+) diff --git a/.travis.yml b/.travis.yml index fb120aa..d6faa28 100644 --- a/.travis.yml +++ b/.travis.yml @@ -33,3 +33,4 @@ script: - "slimerjs --version" - "jjs -v < /dev/null" - "python setup.py test" + - "python -m execjs --print-available-runtimes" diff --git a/test_execjs.py b/test_execjs.py index 1e8057b..437255d 100755 --- a/test_execjs.py +++ b/test_execjs.py @@ -127,6 +127,10 @@ def test_runtime_availability(self): r = execjs.ExternalRuntime("success", ["python"], "") self.assertTrue(r.is_available()) + def test_attributes_export(self): + for name in execjs.__all__: + self.assertTrue(hasattr(execjs, name), "{} is not defined".format(name)) + def load_tests(loader, tests, ignore): if six.PY3: From 74f0ae63d244eb4901cb86da4125f26a005d2211 Mon Sep 17 00:00:00 2001 From: OMOTO Kenji Date: Sat, 26 Mar 2016 16:59:44 +0900 Subject: [PATCH 26/48] refs #45 Fixed --print-available-runtimes fail in Python 2.7 --- execjs/__main__.py | 1 + 1 file changed, 1 insertion(+) diff --git a/execjs/__main__.py b/execjs/__main__.py index 07eac07..ccf67de 100755 --- a/execjs/__main__.py +++ b/execjs/__main__.py @@ -1,5 +1,6 @@ #!/usr/bin/env python3 # -*- coding: ascii -*- +from __future__ import unicode_literals import sys import io from argparse import ArgumentParser, Action, SUPPRESS From c08b83b71684003ec0943ec0cf2361cba39f5d83 Mon Sep 17 00:00:00 2001 From: OMOTO Kenji Date: Sat, 26 Mar 2016 17:10:02 +0900 Subject: [PATCH 27/48] Version 1.3.1 --- README.md | 3 +++ setup.py | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 7a8615b..c4e792b 100644 --- a/README.md +++ b/README.md @@ -77,6 +77,9 @@ Released under the MIT license. See `LICENSE` for details. # Changelog +## 1.3.1 +- Fixed `--print-available-runtimes` fails in Python 2.7. + ## 1.3.0 - Added `cwd` argument. diff --git a/setup.py b/setup.py index 81412bd..3fc8960 100755 --- a/setup.py +++ b/setup.py @@ -5,7 +5,7 @@ import sys import io -version = '1.3.0' +version = '1.3.1' author = "Omoto Kenji" license = "MIT License" author_email = 'doloopwhile@gmail.com' From 8167c086b56bc73859467011722c57ca97dd0b9a Mon Sep 17 00:00:00 2001 From: OMOTO Kenji Date: Sun, 24 Apr 2016 15:47:49 +0900 Subject: [PATCH 28/48] Install phantomjs --- .travis.yml | 6 ++---- install_development.sh | 1 - 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/.travis.yml b/.travis.yml index d6faa28..fe068ac 100644 --- a/.travis.yml +++ b/.travis.yml @@ -16,16 +16,14 @@ env: jdk: - oraclejdk8 -before_script: +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 oracle-java8-installer" - -install: + - "sudo apt-get install -y oracle-java8-installer phantomjs" - "./install_development.sh" - "python setup.py install" diff --git a/install_development.sh b/install_development.sh index 7a05729..abcf210 100755 --- a/install_development.sh +++ b/install_development.sh @@ -3,4 +3,3 @@ python -c "import sys; sys.version_info >= (2, 7) or sys.exit(1)" if [ $? -ne 0 ]; then pip install unittest2 fi -pip install six From 2c540b992ab0ef5cb263220dbe86c1255b333499 Mon Sep 17 00:00:00 2001 From: OMOTO Kenji Date: Sun, 8 May 2016 12:35:26 +0900 Subject: [PATCH 29/48] Fixed required libraries --- execjs/_runtimes.py | 6 +----- setup.py | 6 +----- 2 files changed, 2 insertions(+), 10 deletions(-) diff --git a/execjs/_runtimes.py b/execjs/_runtimes.py index e3688fe..79fa220 100644 --- a/execjs/_runtimes.py +++ b/execjs/_runtimes.py @@ -1,9 +1,5 @@ import os.path - -try: - from collections import OrderedDict -except ImportError: - from ordereddict import OrderedDict +from collections import OrderedDict import execjs._external_runtime as external_runtime import execjs._pyv8runtime as pyv8runtime diff --git a/setup.py b/setup.py index 3fc8960..96b5dd9 100755 --- a/setup.py +++ b/setup.py @@ -14,10 +14,6 @@ with io.open('README.rst', encoding='ascii') as fp: long_description = fp.read() -install_requires = ["six"] -if sys.version_info < (2, 7): - install_requires = "argparse ordereddict".split() - setup( packages=find_packages(), include_package_data=True, @@ -43,6 +39,6 @@ 'Programming Language :: Python :: 3.5', 'Programming Language :: JavaScript', ], - install_requires=install_requires, + install_requires=["six==1.10.0"], test_suite="test_execjs", ) From acb55831312e0340a730cb948b1d23c12f40c90c Mon Sep 17 00:00:00 2001 From: OMOTO Kenji Date: Sun, 8 May 2016 12:47:58 +0900 Subject: [PATCH 30/48] Install spidermonkey in travis-ci --- .travis.yml | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index fe068ac..ef8de2b 100644 --- a/.travis.yml +++ b/.travis.yml @@ -23,12 +23,15 @@ install: - "tar xvf slimerjs-*.tar.bz2" - "rm slimerjs-*.tar.bz2" - "mv slimerjs-* slimerjs" - - "sudo apt-get install -y oracle-java8-installer phantomjs" + - "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" - - "python setup.py test" + - "js --help" + - "node --version && node --help" - "python -m execjs --print-available-runtimes" + - "python setup.py test" From db5ac145d41151ccae46f7a1833d8f76c928e707 Mon Sep 17 00:00:00 2001 From: OMOTO Kenji Date: Sun, 8 May 2016 12:53:08 +0900 Subject: [PATCH 31/48] Use ubuntu trusty in travis-ci --- .travis.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.travis.yml b/.travis.yml index ef8de2b..b4129d7 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,3 +1,4 @@ +dist: trusty language: python python: From ce6c4d37ae888fd8592a6ba24d63bdf9a248fcac Mon Sep 17 00:00:00 2001 From: OMOTO Kenji Date: Sun, 8 May 2016 13:39:57 +0900 Subject: [PATCH 32/48] Added execjs.runtime_names --- README.md | 6 ++- execjs/_runtimes.py | 84 ++++++++++++++++++++--------------------- execjs/runtime_names.py | 8 ++++ 3 files changed, 53 insertions(+), 45 deletions(-) create mode 100644 execjs/runtime_names.py diff --git a/README.md b/README.md index c4e792b..098dc00 100644 --- a/README.md +++ b/README.md @@ -53,10 +53,12 @@ You can choose JavaScript runtime by `execjs.get()`: >>> default = execjs.get() # the automatically picked runtime >>> default.eval("1 + 2") 3 - >>> jscript = execjs.get("JScript") + >>> import execjs.runtime_names + >>> jscript = execjs.get(execjs.runtime_names.JScript) >>> jscript.eval("1 + 2") 3 - >>> node = execjs.get("Node") + >>> import execjs.runtime_names + >>> node = execjs.get(execjs.runtime_names.Node) >>> node.eval("1 + 2") 3 diff --git a/execjs/_runtimes.py b/execjs/_runtimes.py index 79fa220..957b55f 100644 --- a/execjs/_runtimes.py +++ b/execjs/_runtimes.py @@ -1,6 +1,6 @@ import os.path -from collections import OrderedDict +import execjs.runtime_names as runtime_names import execjs._external_runtime as external_runtime import execjs._pyv8runtime as pyv8runtime import execjs._exceptions as exceptions @@ -8,7 +8,7 @@ def register(name, runtime): '''Register a JavaScript runtime.''' - _runtimes[name] = runtime + _runtimes.append((name, runtime)) def get(name=None): @@ -17,17 +17,8 @@ def get(name=None): If name is specified, return the runtime. """ if name is None: - return _auto_detect() - - try: - runtime = runtimes()[name] - except KeyError: - raise exceptions.RuntimeUnavailableError("{name} runtime is not defined".format(name=name)) - else: - if not runtime.is_available(): - raise exceptions.RuntimeUnavailableError( - "{name} runtime is not available on this system".format(name=runtime.name)) - return runtime + return get_from_environment() or _find_available_runtime() + return _find_runtime_by_name(name) def runtimes(): @@ -37,19 +28,7 @@ def runtimes(): def available_runtimes(): """return a dictionary of all supported JavaScript runtimes which is usable""" - return dict((name, runtime) for name, runtime in _runtimes.items() if runtime.is_available()) - - -def _auto_detect(): - runtime = get_from_environment() - if runtime is not None: - return runtime - - for runtime in _runtimes.values(): - if runtime.is_available(): - return runtime - - raise exceptions.RuntimeUnavailableError("Could not find a JavaScript runtime.") + return dict((name, runtime) for name, runtime in _runtimes if runtime.is_available()) def get_from_environment(): @@ -57,29 +36,48 @@ def get_from_environment(): Return the JavaScript runtime that is specified in EXECJS_RUNTIME environment variable. If EXECJS_RUNTIME environment variable is empty or invalid, return None. ''' - try: - name = os.environ["EXECJS_RUNTIME"] - except KeyError: + name = os.environ.get("EXECJS_RUNTIME", "") + if not name: return None - if not name: + try: + return _find_runtime_by_name(name) + except exceptions.RuntimeUnavailableError: return None - return get(name) -_runtimes = OrderedDict() +def _find_available_runtime(): + for _, runtime in _runtimes: + if runtime.is_available(): + return runtime + raise exceptions.RuntimeUnavailableError("Could not find an available JavaScript runtime.") + + +def _find_runtime_by_name(name): + for runtime_name, runtime in _runtimes: + if runtime_name.lower() == name.lower(): + break + else: + raise exceptions.RuntimeUnavailableError("{name} runtime is not defined".format(name=name)) + + if not runtime.is_available(): + raise exceptions.RuntimeUnavailableError( + "{name} runtime is not available on this system".format(name=runtime.name)) + return runtime + + +_runtimes = [] -register('PyV8', pyv8runtime.PyV8Runtime()) +register(runtime_names.PyV8, pyv8runtime.PyV8Runtime()) if external_runtime.node.is_available(): - register("Node", external_runtime.node) + register(runtime_names.Node, external_runtime.node) else: - register("Node", external_runtime.nodejs) - -register('JavaScriptCore', external_runtime.jsc) -register('SpiderMonkey', external_runtime.spidermonkey) -register('Spidermonkey', external_runtime.spidermonkey) -register('JScript', external_runtime.jscript) -register("PhantomJS", external_runtime.phantomjs) -register("SlimerJS", external_runtime.slimerjs) -register('Nashorn', external_runtime.nashorn) + register(runtime_names.Node, external_runtime.nodejs) + +register(runtime_names.JavaScriptCore, external_runtime.jsc) +register(runtime_names.SpiderMonkey, external_runtime.spidermonkey) +register(runtime_names.JScript, external_runtime.jscript) +register(runtime_names.PhantomJS, external_runtime.phantomjs) +register(runtime_names.SlimerJS, external_runtime.slimerjs) +register(runtime_names.Nashorn, external_runtime.nashorn) diff --git a/execjs/runtime_names.py b/execjs/runtime_names.py new file mode 100644 index 0000000..cb85226 --- /dev/null +++ b/execjs/runtime_names.py @@ -0,0 +1,8 @@ +PyV8 = "PyV8" +Node = "Node" +JavaScriptCore = "JavaScriptCore" +SpiderMonkey = "SpiderMonkey" +JScript = "JScript" +PhantomJS = "PhantomJS" +SlimerJS = "SlimerJS" +Nashorn = "Nashorn" From 074dad5179c99e44e5b738caec86771c8f42286a Mon Sep 17 00:00:00 2001 From: OMOTO Kenji Date: Sun, 8 May 2016 14:20:02 +0900 Subject: [PATCH 33/48] Sort output of --print-runtime-available by priority --- execjs/__init__.py | 1 - execjs/__main__.py | 7 ++----- execjs/_runtimes.py | 8 ++------ test_execjs.py | 4 +++- 4 files changed, 7 insertions(+), 13 deletions(-) diff --git a/execjs/__init__.py b/execjs/__init__.py index fbea3de..394a357 100755 --- a/execjs/__init__.py +++ b/execjs/__init__.py @@ -38,7 +38,6 @@ register = execjs._runtimes.register get = execjs._runtimes.get runtimes = execjs._runtimes.runtimes -available_runtimes = execjs._runtimes.available_runtimes get_from_environment = execjs._runtimes.get_from_environment diff --git a/execjs/__main__.py b/execjs/__main__.py index ccf67de..28cf92f 100755 --- a/execjs/__main__.py +++ b/execjs/__main__.py @@ -19,11 +19,8 @@ def __init__(self, option_strings, dest=SUPPRESS, default=SUPPRESS, help=None): ) def __call__(self, parser, namespace, values, option_string=None): - buffer = io.StringIO() - for name, runtime in sorted(execjs.runtimes().items()): - if runtime.is_available(): - buffer.write(name + "\n") - parser.exit(message=buffer.getvalue()) + message = "".join(name + "\n" for name, runtime in execjs.runtimes().items() if runtime.is_available()) + parser.exit(message=message) def main(): diff --git a/execjs/_runtimes.py b/execjs/_runtimes.py index 957b55f..a911a28 100644 --- a/execjs/_runtimes.py +++ b/execjs/_runtimes.py @@ -1,4 +1,5 @@ import os.path +from collections import OrderedDict import execjs.runtime_names as runtime_names import execjs._external_runtime as external_runtime @@ -23,12 +24,7 @@ def get(name=None): def runtimes(): """return a dictionary of all supported JavaScript runtimes.""" - return dict(_runtimes) - - -def available_runtimes(): - """return a dictionary of all supported JavaScript runtimes which is usable""" - return dict((name, runtime) for name, runtime in _runtimes if runtime.is_available()) + return OrderedDict(_runtimes) def get_from_environment(): diff --git a/test_execjs.py b/test_execjs.py index 437255d..5e7f815 100755 --- a/test_execjs.py +++ b/test_execjs.py @@ -92,7 +92,9 @@ def setUp(self): self.runtime = execjs -for name, runtime in execjs.available_runtimes().items(): +for name, runtime in execjs.runtimes().items(): + if not runtime.is_available(): + continue class_name = name.capitalize() + "RuntimeTest" def f(runtime=runtime): From ceca27db18f00399b96d9f24b8ee4df651ca09d7 Mon Sep 17 00:00:00 2001 From: OMOTO Kenji Date: Sun, 8 May 2016 15:02:05 +0900 Subject: [PATCH 34/48] Made external_runtime.xxx to be functions --- execjs/_external_runtime.py | 93 ++++++++++++++++++++++++++++--------- execjs/_runtimes.py | 21 ++++----- 2 files changed, 80 insertions(+), 34 deletions(-) diff --git a/execjs/_external_runtime.py b/execjs/_external_runtime.py index 5e219ed..61e6e6d 100644 --- a/execjs/_external_runtime.py +++ b/execjs/_external_runtime.py @@ -192,24 +192,75 @@ def _which(command): return [path] + args -node = ExternalRuntime(name="Node.js (V8)", command=['node'], encoding='UTF-8', runner_source=_runner_sources.Node) -nodejs = ExternalRuntime(name="Node.js (V8)", command=['nodejs'], encoding='UTF-8', runner_source=_runner_sources.Node) - -jsc = ExternalRuntime( - name="JavaScriptCore", - command=["/System/Library/Frameworks/JavaScriptCore.framework/Versions/A/Resources/jsc"], - runner_source=_runner_sources.JavaScriptCore -) - -spidermonkey = ExternalRuntime(name="SpiderMonkey", command=["js"], runner_source=_runner_sources.SpiderMonkey) - -jscript = ExternalRuntime( - name="JScript", - command=["cscript", "//E:jscript", "//Nologo"], - encoding="ascii", - runner_source=_runner_sources.JScript -) - -phantomjs = ExternalRuntime(name="PhantomJS", command=["phantomjs"], runner_source=_runner_sources.PhantomJS) -slimerjs = ExternalRuntime(name="SlimerJS", command=["slimerjs"], runner_source=_runner_sources.SlimerJS) -nashorn = ExternalRuntime(name="Nashorn", command=["jjs"], runner_source=_runner_sources.Nashorn) +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 + ) + + +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 + ) diff --git a/execjs/_runtimes.py b/execjs/_runtimes.py index a911a28..a3ccd85 100644 --- a/execjs/_runtimes.py +++ b/execjs/_runtimes.py @@ -64,16 +64,11 @@ def _find_runtime_by_name(name): _runtimes = [] -register(runtime_names.PyV8, pyv8runtime.PyV8Runtime()) - -if external_runtime.node.is_available(): - register(runtime_names.Node, external_runtime.node) -else: - register(runtime_names.Node, external_runtime.nodejs) - -register(runtime_names.JavaScriptCore, external_runtime.jsc) -register(runtime_names.SpiderMonkey, external_runtime.spidermonkey) -register(runtime_names.JScript, external_runtime.jscript) -register(runtime_names.PhantomJS, external_runtime.phantomjs) -register(runtime_names.SlimerJS, external_runtime.slimerjs) -register(runtime_names.Nashorn, external_runtime.nashorn) +register(runtime_names.PyV8, pyv8runtime.PyV8Runtime()) +register(runtime_names.Node, external_runtime.node()) +register(runtime_names.JavaScriptCore, external_runtime.jsc()) +register(runtime_names.SpiderMonkey, external_runtime.spidermonkey()) +register(runtime_names.JScript, external_runtime.jscript()) +register(runtime_names.PhantomJS, external_runtime.phantomjs()) +register(runtime_names.SlimerJS, external_runtime.slimerjs()) +register(runtime_names.Nashorn, external_runtime.nashorn()) From 30c365838b434cddde346ec8d65ba211b83b206c Mon Sep 17 00:00:00 2001 From: OMOTO Kenji Date: Sun, 8 May 2016 15:29:37 +0900 Subject: [PATCH 35/48] Added option to call runtime using stdin/pipes instead of temp directory --- execjs/_external_runtime.py | 58 +++++++++++++++++++++++++------------ 1 file changed, 39 insertions(+), 19 deletions(-) diff --git a/execjs/_external_runtime.py b/execjs/_external_runtime.py index 61e6e6d..fb558d8 100644 --- a/execjs/_external_runtime.py +++ b/execjs/_external_runtime.py @@ -19,13 +19,14 @@ class ExternalRuntime(AbstractRuntime): '''Runtime to execute codes with external command.''' - def __init__(self, name, command, runner_source, encoding='utf8'): + 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 @@ -43,7 +44,7 @@ def is_available(self): return self._available def _compile(self, source, cwd=None): - return self.Context(self, source, cwd=cwd) + return self.Context(self, source, cwd=cwd, tempfile=tempfile) def _binary(self): if not hasattr(self, "_binary_cache"): @@ -53,10 +54,11 @@ def _binary(self): class Context(AbstractRuntimeContext): # protected - def __init__(self, runtime, source='', cwd=None): + 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() @@ -74,35 +76,53 @@ def _exec_(self, source): if self._source: source = self._source + '\n' + 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)) - output = self._execfile(filename) - finally: - os.remove(filename) - + 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)) + return self._eval("{identifier}.apply(this, {args})".format(identifier=identifier, args=args)) - def _execfile(self, filename): - cmd = self._runtime._binary() + [filename] + def _exec_with_pipe(self, source): + cmd = self._runtime._binary() p = None try: - p = Popen(cmd, stdout=PIPE, stderr=PIPE, cwd=self._cwd) - stdoutdata, stderrdata = p.communicate() + 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 - if ret == 0: + 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 - else: + 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): From be5535b0c0396aaab674e95b8b300a65b7d9d64f Mon Sep 17 00:00:00 2001 From: OMOTO Kenji Date: Sun, 8 May 2016 15:29:37 +0900 Subject: [PATCH 36/48] Added option to call runtime using stdin/pipes instead of temp directory --- execjs/_external_runtime.py | 61 +++++++++++++++++++++++++------------ 1 file changed, 41 insertions(+), 20 deletions(-) diff --git a/execjs/_external_runtime.py b/execjs/_external_runtime.py index 61e6e6d..dceec83 100644 --- a/execjs/_external_runtime.py +++ b/execjs/_external_runtime.py @@ -19,13 +19,14 @@ class ExternalRuntime(AbstractRuntime): '''Runtime to execute codes with external command.''' - def __init__(self, name, command, runner_source, encoding='utf8'): + 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 @@ -43,7 +44,7 @@ def is_available(self): return self._available def _compile(self, source, cwd=None): - return self.Context(self, source, cwd=cwd) + return self.Context(self, source, cwd=cwd, tempfile=tempfile) def _binary(self): if not hasattr(self, "_binary_cache"): @@ -53,10 +54,11 @@ def _binary(self): class Context(AbstractRuntimeContext): # protected - def __init__(self, runtime, source='', cwd=None): + 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() @@ -74,35 +76,53 @@ def _exec_(self, source): if self._source: source = self._source + '\n' + 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)) - output = self._execfile(filename) - finally: - os.remove(filename) - + 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)) + return self._eval("{identifier}.apply(this, {args})".format(identifier=identifier, args=args)) - def _execfile(self, filename): - cmd = self._runtime._binary() + [filename] + def _exec_with_pipe(self, source): + cmd = self._runtime._binary() p = None try: - p = Popen(cmd, stdout=PIPE, stderr=PIPE, cwd=self._cwd) - stdoutdata, stderrdata = p.communicate() + 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 - if ret == 0: + 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 - else: + 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): @@ -238,7 +258,8 @@ def jscript(): name="JScript", command=["cscript", "//E:jscript", "//Nologo"], encoding="ascii", - runner_source=_runner_sources.JScript + runner_source=_runner_sources.JScript, + tempfile=True ) From 2a4b56e31bd03c0b4cb47c0d52aecac6c038efb7 Mon Sep 17 00:00:00 2001 From: OMOTO Kenji Date: Sun, 8 May 2016 16:06:01 +0900 Subject: [PATCH 37/48] Version 1.4.0 --- README.md | 5 +++++ README.rst | 20 +++++++++++++++++--- setup.py | 2 +- 3 files changed, 23 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 098dc00..c820530 100644 --- a/README.md +++ b/README.md @@ -79,6 +79,11 @@ Released under the MIT license. See `LICENSE` for details. # Changelog +## 1.4.0 +- Fixed required libraries. +- Fixed order of output of `--print-available-runtimes`. +- Execute some JavaScript runtime with pipe/stdin (without temporary file). + ## 1.3.1 - Fixed `--print-available-runtimes` fails in Python 2.7. diff --git a/README.rst b/README.rst index 05c80bc..e84efa9 100644 --- a/README.rst +++ b/README.rst @@ -70,10 +70,12 @@ You can choose JavaScript runtime by ``execjs.get()``: >>> default = execjs.get() # the automatically picked runtime >>> default.eval("1 + 2") 3 - >>> jscript = execjs.get("JScript") + >>> import execjs.runtime_names + >>> jscript = execjs.get(execjs.runtime_names.JScript) >>> jscript.eval("1 + 2") 3 - >>> node = execjs.get("Node") + >>> import execjs.runtime_names + >>> node = execjs.get(execjs.runtime_names.Node) >>> node.eval("1 + 2") 3 @@ -99,6 +101,19 @@ Released under the MIT license. See ``LICENSE`` for details. Changelog ========= +1.4.0 +----- + +- Fixed required libraries. +- Fixed order of output of ``--print-available-runtimes``. +- Execute some JavaScript runtime with pipe/stdin (without temporary + file). + +1.3.1 +----- + +- Fixed ``--print-available-runtimes`` fails in Python 2.7. + 1.3.0 ----- @@ -152,4 +167,3 @@ Changelog ----- - First release. - diff --git a/setup.py b/setup.py index 96b5dd9..f09e310 100755 --- a/setup.py +++ b/setup.py @@ -5,7 +5,7 @@ import sys import io -version = '1.3.1' +version = '1.4.0' author = "Omoto Kenji" license = "MIT License" author_email = 'doloopwhile@gmail.com' From cba8720f2155ad8e93b424ef4aa9ff2bc3241dc6 Mon Sep 17 00:00:00 2001 From: OMOTO Kenji Date: Mon, 6 Jun 2016 22:06:37 +0900 Subject: [PATCH 38/48] Do compile sources in _exec_with_pipe --- execjs/_exceptions.py | 6 +++++- execjs/_external_runtime.py | 31 ++++++++++++++++++------------- test_execjs.py | 4 ++-- 3 files changed, 25 insertions(+), 16 deletions(-) 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..ac3a145 100755 --- a/test_execjs.py +++ b/test_execjs.py @@ -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.ProgramError): self.runtime.exec_(")") def test_thrown_exception(self): - with self.assertRaises(execjs.ProgramError): + with self.assertRaises(execjs.RuntimeError): self.runtime.exec_("throw 'hello'") def test_broken_substitutions(self): From 8783e86120ce1b28fb589e66be3b2213434acd93 Mon Sep 17 00:00:00 2001 From: OMOTO Kenji Date: Mon, 6 Jun 2016 22:08:48 +0900 Subject: [PATCH 39/48] Added cwd argument to module-level functions --- execjs/__init__.py | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) 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__ From 68de4b666855adcfc5bf840fb5019ec03566aaa6 Mon Sep 17 00:00:00 2001 From: OMOTO Kenji Date: Sat, 16 Sep 2017 17:16:02 +0900 Subject: [PATCH 40/48] Temporary removed tests with SlimerJS --- .travis.yml | 14 ++++---------- test_execjs.py | 27 ++++++++++++--------------- 2 files changed, 16 insertions(+), 25 deletions(-) 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/test_execjs.py b/test_execjs.py index ac3a145..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.ProgramError): + with self.assertRaises(execjs.Error): self.runtime.exec_(")") def test_thrown_exception(self): - with self.assertRaises(execjs.RuntimeError): + 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): From 3006696972fb0420aa24c2c421656208504d6ff0 Mon Sep 17 00:00:00 2001 From: OMOTO Kenji Date: Sat, 16 Sep 2017 17:12:06 +0900 Subject: [PATCH 41/48] Fixed wrong use of exception and ambiguous exception names --- execjs/__init__.py | 8 +++++++- execjs/_exceptions.py | 20 +++++++++++++------- execjs/_external_runtime.py | 22 ++++++++++++---------- execjs/_pyv8runtime.py | 2 +- 4 files changed, 33 insertions(+), 19 deletions(-) diff --git a/execjs/__init__.py b/execjs/__init__.py index 2b573df..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 diff --git a/execjs/_exceptions.py b/execjs/_exceptions.py index e11f947..0723550 100644 --- a/execjs/_exceptions.py +++ b/execjs/_exceptions.py @@ -1,18 +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 ProcessExitedWithNonZeroStatus(RuntimeError): def __init__(self, status, stdout, stderr): - Error.__init__(self, status, stdout, stderr) + RuntimeError.__init__(self, status, stdout, stderr) self.status = status self.stdout = stdout self.stderr = stderr - +# Errors due to JS script. +# ex. Script has syntax error, executed and raised exception. class ProgramError(Error): pass - - -class RuntimeUnavailableError(RuntimeError): - pass diff --git a/execjs/_external_runtime.py b/execjs/_external_runtime.py index 6489084..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 @@ -126,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(status=status, stdout=stdoutdata, stderr=stderrdata) + raise ProcessExitedWithNonZeroStatus(status=status, stdout=stdoutdata, stderr=stderrdata) def _compile(self, source): runner_source = self._runtime._runner_source @@ -151,18 +156,15 @@ def _extract_result(self, output): output = output.replace("\r\n", "\n").replace("\r", "\n") output_last_line = output.split("\n")[-2] - if not output_last_line: - raise exceptions.ProgramError - 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 else: - raise exceptions.ProgramError(value) + raise ProgramError(value) def _is_windows(): diff --git a/execjs/_pyv8runtime.py b/execjs/_pyv8runtime.py index c5edcff..2f93a2c 100644 --- a/execjs/_pyv8runtime.py +++ b/execjs/_pyv8runtime.py @@ -53,7 +53,7 @@ def _exec_(self, source): 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: From 16211f20fa409f9e486763acd6f146d6c15a7940 Mon Sep 17 00:00:00 2001 From: OMOTO Kenji Date: Mon, 6 Jun 2016 22:12:27 +0900 Subject: [PATCH 42/48] Version 1.4.1 --- README.md | 5 +++++ README.rst | 7 +++++++ setup.py | 2 +- 3 files changed, 13 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index c820530..8baab56 100644 --- a/README.md +++ b/README.md @@ -79,6 +79,11 @@ Released under the MIT license. See `LICENSE` for details. # Changelog +## 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..79a2e11 100644 --- a/README.rst +++ b/README.rst @@ -101,6 +101,13 @@ Released under the MIT license. See ``LICENSE`` for details. Changelog ========= +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/setup.py b/setup.py index f09e310..4ed25d4 100755 --- a/setup.py +++ b/setup.py @@ -5,7 +5,7 @@ import sys import io -version = '1.4.0' +version = '1.4.1' author = "Omoto Kenji" license = "MIT License" author_email = 'doloopwhile@gmail.com' From 47fc67ea9c7494b329b1b9bf0ecab06663bdcf99 Mon Sep 17 00:00:00 2001 From: OMOTO Kenji Date: Sun, 17 Sep 2017 11:42:17 +0900 Subject: [PATCH 43/48] Dropped first-class suuports for some runtimes --- README.md | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 8baab56..b9eb9df 100644 --- a/README.md +++ b/README.md @@ -22,14 +22,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 From 2ce8d692b4071f504ea35d38b1ecfeb7a1750d84 Mon Sep 17 00:00:00 2001 From: OMOTO Kenji Date: Tue, 19 Sep 2017 10:03:18 +0900 Subject: [PATCH 44/48] Ease version requirement of six --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 4ed25d4..e897014 100755 --- a/setup.py +++ b/setup.py @@ -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", ) From f63beab270411f9b82f19b1a43698ea8e0c920af Mon Sep 17 00:00:00 2001 From: OMOTO Kenji Date: Wed, 8 Nov 2017 13:02:29 +0900 Subject: [PATCH 45/48] Version 1.5.0 --- README.md | 3 +++ README.rst | 20 ++++++++++++++++---- setup.py | 2 +- 3 files changed, 20 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index b9eb9df..36454db 100644 --- a/README.md +++ b/README.md @@ -84,6 +84,9 @@ 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. diff --git a/README.rst b/README.rst index 79a2e11..c26d955 100644 --- a/README.rst +++ b/README.rst @@ -24,18 +24,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 +108,11 @@ Released under the MIT license. See ``LICENSE`` for details. Changelog ========= +1.5.0 +----- + +- Eased version requirement for six. + 1.4.1 ----- diff --git a/setup.py b/setup.py index e897014..d15dad4 100755 --- a/setup.py +++ b/setup.py @@ -5,7 +5,7 @@ import sys import io -version = '1.4.1' +version = '1.5.0' author = "Omoto Kenji" license = "MIT License" author_email = 'doloopwhile@gmail.com' From 998553d04e4226695b8cf7a0b29ea701fe314a24 Mon Sep 17 00:00:00 2001 From: OMOTO Kenji Date: Wed, 27 Dec 2017 15:49:10 +0900 Subject: [PATCH 46/48] Removed contextlib.nested --- execjs/_pyv8runtime.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/execjs/_pyv8runtime.py b/execjs/_pyv8runtime.py index 2f93a2c..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,7 +47,7 @@ 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) From 025e9fb9d444653fadaca12395835d05c9dd4400 Mon Sep 17 00:00:00 2001 From: OMOTO Kenji Date: Thu, 18 Jan 2018 13:20:05 +0900 Subject: [PATCH 47/48] Version 1.5.1, EOL --- README.md | 12 ++++++++++-- README.rst | 14 ++++++++++++-- setup.py | 2 +- 3 files changed, 23 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 36454db..bf4b296 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,15 @@ -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. 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. diff --git a/README.rst b/README.rst index c26d955..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. diff --git a/setup.py b/setup.py index d15dad4..68689ea 100755 --- a/setup.py +++ b/setup.py @@ -5,7 +5,7 @@ import sys import io -version = '1.5.0' +version = '1.5.1' author = "Omoto Kenji" license = "MIT License" author_email = 'doloopwhile@gmail.com' From e300f0a8120c0b7b70eed0758c3c85a9bd1a7b9f Mon Sep 17 00:00:00 2001 From: OMOTO Kenji Date: Mon, 19 Mar 2018 13:15:05 +0900 Subject: [PATCH 48/48] Update README.md --- README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index bf4b296..e89124c 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,8 @@ PyExecJS (EOL) # End of life -This library is no longer maintananced. Bugs are not be fixed (even if they are trivial or essential). +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.