diff --git a/.travis.yml b/.travis.yml
index 2205848..0949de8 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -1,26 +1,32 @@
+dist: trusty
language: python
+addons:
+ firefox: "latest"
+
python:
- - "2.6"
- "2.7"
- - "3.2"
- "3.3"
- "3.4"
+ - "3.5"
+ - "3.6"
env:
- - "SLIMERJSLAUNCHER=$(which firefox) DISPLAY=:99.0 PATH=$TRAVIS_BUILD_DIR/slimerjs:$PATH"
+ global:
+ - "JDK=oracle8"
-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"
- - "tar xvf slimerjs-*.tar.bz2"
- - "rm slimerjs-*.tar.bz2"
- - "mv slimerjs-* slimerjs"
+jdk:
+ - oraclejdk8
install:
+ - "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:
+ - "jjs -v < /dev/null"
+ - "js --help"
+ - "node --version && node --help"
+ - "python -m execjs --print-available-runtimes"
- "python setup.py test"
diff --git a/README.md b/README.md
index 3b7a601..e89124c 100644
--- a/README.md
+++ b/README.md
@@ -1,12 +1,20 @@
-PyExecJS
-========
+PyExecJS (EOL)
+==============
[](https://travis-ci.org/doloopwhile/PyExecJS)
+# End of life
+
+This library is no longer maintananced ([Reason](https://gist.github.com/doloopwhile/8c6ec7dd4703e8a44e559411cb2ea221)).
+Bugs are not be fixed (even if they are trivial or essential).
+
+We suggest to use other library or to make a fork.
+
+---
+
Run JavaScript code from Python.
PyExecJS is a porting of ExecJS from Ruby.
-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,34 +29,21 @@ A short example:
>>> ctx.call("add", 1, 2)
3
-Of course, you can pick particular JavaScript runtime by get() function:
-
- >>> 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
-
-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.
- >>> os.environ["EXECJS_RUNTIME"] = "Node"
- >>> execjs.get().name
- 'Node.js (V8)'
+# Supported runtimes
-PyExecJS supports these 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/)
+* [Mozilla SpiderMonkey](http://www.mozilla.org/js/spidermonkey/)
# Installation
@@ -58,15 +53,70 @@ or
$ easy_install PyExecJS
+# Details
+
+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.
+ >>> os.environ["EXECJS_RUNTIME"] = "Node"
+ >>> execjs.get().name
+ 'Node.js (V8)'
+
+You can choose JavaScript runtime by `execjs.get()`:
+
+ >>> default = execjs.get() # the automatically picked runtime
+ >>> default.eval("1 + 2")
+ 3
+ >>> import execjs.runtime_names
+ >>> jscript = execjs.get(execjs.runtime_names.JScript)
+ >>> jscript.eval("1 + 2")
+ 3
+ >>> import execjs.runtime_names
+ >>> node = execjs.get(execjs.runtime_names.Node)
+ >>> 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) 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
+# Changelog
+
+## 1.5.0
+- Eased version requirement for six.
+
+## 1.4.1
+- Fixed arguments of module-level functions.
+- Fixed bug of execution with pipe.
+- Fixed wrong excption is raised.
+
+## 1.4.0
+- Fixed required libraries.
+- Fixed order of output of `--print-available-runtimes`.
+- 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
+- Added `cwd` argument.
+
+## 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 bd39ef9..e3a5e83 100644
--- a/README.rst
+++ b/README.rst
@@ -1,11 +1,20 @@
-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.
-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,42 +31,28 @@ A short example:
>>> ctx.call("add", 1, 2)
3
-Of course, you can pick particular JavaScript runtime by get() function:
-
-::
-
- >>> 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
-
-If EXECJS\_RUNTIME environment variable is specified, PyExecJS pick the
-JavaScript runtime as a default:
+Supported runtimes
+==================
-::
-
- >>> #execjs.get().name # this value is depends on your environment.
- >>> os.environ["EXECJS_RUNTIME"] = "Node"
- >>> execjs.get().name
- 'Node.js (V8)'
-
-PyExecJS supports these 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 `__
+- `Mozilla SpiderMonkey `__
Installation
============
@@ -72,16 +67,93 @@ or
$ easy_install PyExecJS
+Details
+=======
+
+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.
+ >>> os.environ["EXECJS_RUNTIME"] = "Node"
+ >>> execjs.get().name
+ 'Node.js (V8)'
+
+You can choose JavaScript runtime by ``execjs.get()``:
+
+::
+
+ >>> default = execjs.get() # the automatically picked runtime
+ >>> default.eval("1 + 2")
+ 3
+ >>> import execjs.runtime_names
+ >>> jscript = execjs.get(execjs.runtime_names.JScript)
+ >>> jscript.eval("1 + 2")
+ 3
+ >>> import execjs.runtime_names
+ >>> node = execjs.get(execjs.runtime_names.Node)
+ >>> 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
=======
-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
-=======
+Changelog
+=========
+
+1.5.0
+-----
+
+- Eased version requirement for six.
+
+1.4.1
+-----
+
+- Fixed arguments of module-level functions.
+- Fixed bug of execution with pipe.
+- Fixed wrong excption is raised.
+
+1.4.0
+-----
+
+- Fixed required libraries.
+- Fixed order of output of ``--print-available-runtimes``.
+- 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
+-----
+
+- Added ``cwd`` argument.
+
+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
-----
@@ -124,4 +196,3 @@ Changes
-----
- First release.
-
diff --git a/execjs/__init__.py b/execjs/__init__.py
index 3c3a07f..98a4bea 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,554 +20,43 @@
>>> ctx.call("add", 1, 2)
3
'''
+from __future__ import unicode_literals, division, with_statement
-import os
-import os.path
-import re
-import stat
-import io
-import platform
-import tempfile
-from subprocess import Popen, PIPE, STDOUT
-import json
+from execjs._exceptions import (
+ Error,
+ RuntimeError,
+ ProgramError,
+ RuntimeUnavailableError,
+)
+
+import execjs._runtimes
+from execjs._external_runtime import ExternalRuntime
+from execjs._abstract_runtime import AbstractRuntime
-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()
-class Error(Exception):
- pass
-
-
-class RuntimeError(Error):
- pass
-
-
-class ProgramError(Error):
- pass
-
-
-class RuntimeUnavailable(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)
-
-
-def eval(source):
- return get().eval(source)
-
-
-def exec_(source):
- return get().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 _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>> 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]));
- }
-});
-"""
-)
+def exec_(source, cwd=None):
+ return get().exec_(source, cwd)
+exec_.__doc__ = AbstractRuntime.exec_.__doc__
-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();
-""")
+def compile(source, cwd=None):
+ return get().compile(source, cwd)
+compile.__doc__ = AbstractRuntime.compile.__doc__
diff --git a/execjs/__main__.py b/execjs/__main__.py
index 07eac07..28cf92f 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
@@ -18,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/_abstract_runtime.py b/execjs/_abstract_runtime.py
new file mode 100644
index 0000000..3096b4b
--- /dev/null
+++ b/execjs/_abstract_runtime.py
@@ -0,0 +1,43 @@
+from abc import ABCMeta, abstractmethod
+import six
+import execjs._exceptions as exceptions
+
+
+@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 in some derived class.
+ '''
+ 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 in some derived class.
+ '''
+ 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 in some derived class.
+ '''
+ if not self.is_available():
+ raise exceptions.RuntimeUnavailableError
+ return self._compile(source, cwd=cwd)
+
+ @abstractmethod
+ def is_available(self):
+ raise NotImplementedError
+
+ @abstractmethod
+ def _compile(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/_exceptions.py b/execjs/_exceptions.py
new file mode 100644
index 0000000..0723550
--- /dev/null
+++ b/execjs/_exceptions.py
@@ -0,0 +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):
+ 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
diff --git a/execjs/_external_runtime.py b/execjs/_external_runtime.py
new file mode 100644
index 0000000..2b05c16
--- /dev/null
+++ b/execjs/_external_runtime.py
@@ -0,0 +1,294 @@
+from subprocess import Popen, PIPE
+import io
+import json
+import os
+import os.path
+import platform
+import re
+import stat
+import sys
+import tempfile
+import six
+import execjs._json2 as _json2
+import execjs._runner_sources as _runner_sources
+
+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
+
+
+class ExternalRuntime(AbstractRuntime):
+ '''Runtime to execute codes with external command.'''
+ def __init__(self, name, command, runner_source, encoding='utf8', tempfile=False):
+ self._name = name
+ if isinstance(command, str):
+ command = [command]
+ self._command = command
+ self._runner_source = runner_source
+ self._encoding = encoding
+ self._tempfile = tempfile
+
+ self._available = self._binary() is not None
+
+ def __str__(self):
+ return "{class_name}({runtime_name})".format(
+ class_name=type(self).__name__,
+ runtime_name=self._name,
+ )
+
+ @property
+ def name(self):
+ return self._name
+
+ def is_available(self):
+ return self._available
+
+ def _compile(self, source, cwd=None):
+ return self.Context(self, source, cwd=cwd, tempfile=self._tempfile)
+
+ def _binary(self):
+ if not hasattr(self, "_binary_cache"):
+ self._binary_cache = _which(self._command)
+ return self._binary_cache
+
+ class Context(AbstractRuntimeContext):
+ # protected
+
+ def __init__(self, runtime, source='', cwd=None, tempfile=False):
+ self._runtime = runtime
+ self._source = source
+ self._cwd = cwd
+ self._tempfile = tempfile
+
+ def is_available(self):
+ return self._runtime.is_available()
+
+ def _eval(self, source):
+ if not source.strip():
+ data = "''"
+ else:
+ data = "'('+" + json.dumps(source, ensure_ascii=True) + "+')'"
+
+ code = 'return eval({data})'.format(data=data)
+ return self.exec_(code)
+
+ def _exec_(self, source):
+ if self._source:
+ source = self._source + '\n' + source
+
+ if self._tempfile:
+ output = self._exec_with_tempfile(source)
+ else:
+ output = self._exec_with_pipe(source)
+ return self._extract_result(output)
+
+ def _call(self, identifier, *args):
+ args = json.dumps(args)
+ return self._eval("{identifier}.apply(this, {args})".format(identifier=identifier, args=args))
+
+ def _exec_with_pipe(self, source):
+ cmd = self._runtime._binary()
+
+ p = None
+ try:
+ p = Popen(cmd, stdin=PIPE, stdout=PIPE, stderr=PIPE, cwd=self._cwd, universal_newlines=True)
+ input = self._compile(source)
+ if six.PY2:
+ input = input.encode(sys.getfilesystemencoding())
+ stdoutdata, stderrdata = p.communicate(input=input)
+ ret = p.wait()
+ finally:
+ del p
+
+ self._fail_on_non_zero_status(ret, stdoutdata, stderrdata)
+ return stdoutdata
+
+ def _exec_with_tempfile(self, source):
+ (fd, filename) = tempfile.mkstemp(prefix='execjs', suffix='.js')
+ os.close(fd)
+ try:
+ with io.open(filename, "w+", encoding=self._runtime._encoding) as fp:
+ fp.write(self._compile(source))
+ cmd = self._runtime._binary() + [filename]
+
+ p = None
+ try:
+ p = Popen(cmd, stdout=PIPE, stderr=PIPE, cwd=self._cwd, universal_newlines=True)
+ stdoutdata, stderrdata = p.communicate()
+ ret = p.wait()
+ finally:
+ del p
+
+ self._fail_on_non_zero_status(ret, stdoutdata, stderrdata)
+ return stdoutdata
+ finally:
+ os.remove(filename)
+
+ def _fail_on_non_zero_status(self, status, stdoutdata, stderrdata):
+ if status != 0:
+ raise ProcessExitedWithNonZeroStatus(status=status, stdout=stdoutdata, stderr=stderrdata)
+
+ def _compile(self, source):
+ runner_source = self._runtime._runner_source
+
+ replacements = {
+ '#{source}': lambda: source,
+ '#{encoded_source}': lambda: json.dumps(
+ "(function(){ " +
+ encode_unicode_codepoints(source) +
+ " })()"
+ ),
+ '#{json2_source}': _json2._json2_source,
+ }
+
+ pattern = "|".join(re.escape(k) for k in replacements)
+
+ runner_source = re.sub(pattern, lambda m: replacements[m.group(0)](), runner_source)
+
+ return runner_source
+
+ def _extract_result(self, output):
+ output = output.replace("\r\n", "\n").replace("\r", "\n")
+ output_last_line = output.split("\n")[-2]
+
+ ret = json.loads(output_last_line)
+ if len(ret) == 1:
+ ret = [ret[0], None]
+ status, value = ret
+
+ if status == "ok":
+ return value
+ else:
+ raise ProgramError(value)
+
+
+def _is_windows():
+ """protected"""
+ return platform.system() == 'Windows'
+
+
+def _decode_if_not_text(s):
+ """protected"""
+ if isinstance(s, six.text_type):
+ return s
+ return s.decode(sys.getfilesystemencoding())
+
+
+def _find_executable(prog, pathext=("",)):
+ """protected"""
+ pathlist = _decode_if_not_text(os.environ.get('PATH', '')).split(os.pathsep)
+
+ for dir in pathlist:
+ for ext in pathext:
+ filename = os.path.join(dir, prog + ext)
+ try:
+ st = os.stat(filename)
+ except os.error:
+ continue
+ if stat.S_ISREG(st.st_mode) and (stat.S_IMODE(st.st_mode) & 0o111):
+ return filename
+ return None
+
+
+def _which(command):
+ """protected"""
+ if isinstance(command, str):
+ command = [command]
+ command = list(command)
+ name = command[0]
+ args = command[1:]
+
+ if _is_windows():
+ pathext = _decode_if_not_text(os.environ.get("PATHEXT", ""))
+ path = _find_executable(name, pathext.split(os.pathsep))
+ else:
+ path = _find_executable(name)
+
+ if not path:
+ return None
+ return [path] + args
+
+
+def node():
+ r = node_node()
+ if r.is_available():
+ return r
+ return node_nodejs()
+
+
+def node_node():
+ return ExternalRuntime(
+ name="Node.js (V8)",
+ command=['node'],
+ encoding='UTF-8',
+ runner_source=_runner_sources.Node
+ )
+
+
+def node_nodejs():
+ return ExternalRuntime(
+ name="Node.js (V8)",
+ command=['nodejs'],
+ encoding='UTF-8',
+ runner_source=_runner_sources.Node
+ )
+
+
+def jsc():
+ return ExternalRuntime(
+ name="JavaScriptCore",
+ command=["/System/Library/Frameworks/JavaScriptCore.framework/Versions/A/Resources/jsc"],
+ runner_source=_runner_sources.JavaScriptCore,
+ tempfile=True
+ )
+
+
+def spidermonkey():
+ return ExternalRuntime(
+ name="SpiderMonkey",
+ command=["js"],
+ runner_source=_runner_sources.SpiderMonkey,
+ tempfile=True
+ )
+
+
+def jscript():
+ return ExternalRuntime(
+ name="JScript",
+ command=["cscript", "//E:jscript", "//Nologo"],
+ encoding="ascii",
+ runner_source=_runner_sources.JScript,
+ tempfile=True
+ )
+
+
+def phantomjs():
+ return ExternalRuntime(
+ name="PhantomJS",
+ command=["phantomjs"],
+ runner_source=_runner_sources.PhantomJS,
+ tempfile=True
+ )
+
+
+def slimerjs():
+ return ExternalRuntime(
+ name="SlimerJS",
+ command=["slimerjs"],
+ runner_source=_runner_sources.SlimerJS,
+ tempfile=True
+ )
+
+
+def nashorn():
+ return ExternalRuntime(
+ name="Nashorn",
+ command=["jjs"],
+ runner_source=_runner_sources.Nashorn,
+ tempfile=True
+ )
diff --git a/execjs/_json2.py b/execjs/_json2.py
new file mode 100644
index 0000000..a6e4f81
--- /dev/null
+++ b/execjs/_json2.py
@@ -0,0 +1,5 @@
+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>> 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/_pyv8runtime.py b/execjs/_pyv8runtime.py
new file mode 100644
index 0000000..06ddcb7
--- /dev/null
+++ b/execjs/_pyv8runtime.py
@@ -0,0 +1,86 @@
+import json
+
+import execjs._exceptions as exceptions
+from execjs._abstract_runtime import AbstractRuntime
+from execjs._abstract_runtime_context import AbstractRuntimeContext
+from execjs._misc import encode_unicode_codepoints
+
+try:
+ import PyV8
+except ImportError:
+ _pyv8_available = False
+else:
+ _pyv8_available = True
+
+
+class PyV8Runtime(AbstractRuntime):
+ '''Runtime to execute codes with PyV8.'''
+ def __init__(self):
+ pass
+
+ @property
+ def name(self):
+ return "PyV8"
+
+ def _compile(self, source, cwd=None):
+ return self.Context(source)
+
+ def is_available(self):
+ return _pyv8_available
+
+ class Context(AbstractRuntimeContext):
+ def __init__(self, source=""):
+ self._source = source
+
+ def is_available(self):
+ return _pyv8_available
+
+ def _exec_(self, source):
+ source = '''\
+ (function() {{
+ {0};
+ {1};
+ }})()'''.format(
+ encode_unicode_codepoints(self._source),
+ encode_unicode_codepoints(source)
+ )
+ source = str(source)
+
+ # backward compatibility
+ with PyV8.JSContext() as ctxt, PyV8.JSEngine() as engine:
+ js_errors = (PyV8.JSError, IndexError, ReferenceError, SyntaxError, TypeError)
+ try:
+ script = engine.compile(source)
+ except js_errors as e:
+ raise exceptions.ProgramError(e)
+ try:
+ value = script.run()
+ except js_errors as e:
+ raise exceptions.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/_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..a3ccd85
--- /dev/null
+++ b/execjs/_runtimes.py
@@ -0,0 +1,74 @@
+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
+
+
+def register(name, runtime):
+ '''Register a JavaScript runtime.'''
+ _runtimes.append((name, runtime))
+
+
+def get(name=None):
+ """
+ Return a appropriate JavaScript runtime.
+ If name is specified, return the runtime.
+ """
+ if name is None:
+ return get_from_environment() or _find_available_runtime()
+ return _find_runtime_by_name(name)
+
+
+def runtimes():
+ """return a dictionary of all supported JavaScript runtimes."""
+ return OrderedDict(_runtimes)
+
+
+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.
+ '''
+ name = os.environ.get("EXECJS_RUNTIME", "")
+ if not name:
+ return None
+
+ try:
+ return _find_runtime_by_name(name)
+ except exceptions.RuntimeUnavailableError:
+ return None
+
+
+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(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())
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"
diff --git a/setup.py b/setup.py
index 574782c..68689ea 100755
--- a/setup.py
+++ b/setup.py
@@ -1,23 +1,19 @@
#!/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.5.1'
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()
-if sys.version_info < (2, 7):
- install_requires = "argparse ordereddict".split()
-else:
- install_requires = []
-
setup(
packages=find_packages(),
include_package_data=True,
@@ -36,14 +32,13 @@
'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,
+ install_requires=["six >= 1.10.0"],
test_suite="test_execjs",
)
diff --git a/test_execjs.py b/test_execjs.py
index dce8d00..146865a 100755
--- a/test_execjs.py
+++ b/test_execjs.py
@@ -3,15 +3,16 @@
from __future__ import unicode_literals
import sys
import os
+import doctest
+import six
+
+import execjs
if sys.version_info < (2, 7):
import unittest2 as unittest
else:
import unittest
-import doctest
-import execjs
-
class RuntimeTestBase:
def test_context_call(self):
@@ -24,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):
@@ -74,11 +75,11 @@ def test_compile_large_scripts(self):
self.assertTrue(self.runtime.exec_(code))
def test_syntax_error(self):
- with self.assertRaises(execjs.RuntimeError):
+ with self.assertRaises(execjs.Error):
self.runtime.exec_(")")
def test_thrown_exception(self):
- with self.assertRaises(execjs.ProgramError):
+ with self.assertRaises(execjs.Error):
self.runtime.exec_("throw 'hello'")
def test_broken_substitutions(self):
@@ -90,18 +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.available_runtimes().items():
- 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):
@@ -126,9 +126,14 @@ 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):
- tests.addTests(doctest.DocTestSuite(execjs))
+ if six.PY3:
+ tests.addTests(doctest.DocTestSuite(execjs))
return tests