该库不再维护。错误不会被修复(即使它们是微不足道的或必不可少的)。
我们建议使用其他库或创建一个分支。
从 Python 运行 JavaScript 代码。
PyExecJS 是从 Ruby 移植 ExecJS。PyExecJS会自动 选择可用于评估 JavaScript 程序的最佳运行时。
一个简短的例子:
>>> import execjs
>>> execjs.eval("'red yellow blue'.split(' ')")
['red', 'yellow', 'blue']
>>> ctx = execjs.compile("""
... function add(x, y) {
... return x + y;
... }
... """)
>>> ctx.call("add", 1, 2)
3
$ pip install PyExecJS
或者
$ easy_install PyExecJS
如果指定了EXECJS_RUNTIME环境变量,PyExecJS 会选择 JavaScript 运行时作为默认值:
>>> execjs.get().name # this value is depends on your environment.
>>> os.environ["EXECJS_RUNTIME"] = "Node"
>>> execjs.get().name
'Node.js (V8)'
您可以通过execjs.get()选择 JavaScript 运行时:
>>> 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
PyExecJS 的优点是你不需要照顾 JavaScript 环境。特别是,它无需安装额外的库即可在 Windows 环境中运行。
PyExecJS 的缺点之一是性能。PyExecJS 通过文本与 JavaScript 运行时通信,速度很慢。另一个缺点是它不完全支持特定于运行时的功能。
对于某些用例,PyV8可能是更好的选择。
python 运行 execjs 出现异常:pyexecjs UnicodeEncodeError: 'gbk' codec can't encode character '\xa0' in position 68162:
解决办法1:编辑 "C:\Python36\Lib\subprocess.py" 修改 encoding=None 为 encoding="utf-8"
解决办法2:安装bug修复版本:PyExecJS2,项目地址:pip install PyExecJS2==1.6.1
一切都是在 100% 纯 Python 中完成的,因此安装和使用起来非常容易。支持 Python 2 & 3。完全支持 ECMAScript 5.1,ECMA 6 支持仍处于试验阶段。
简单示例:
>>> import js2py
>>> js2py.eval_js('console.log( "Hello World!" )')
'Hello World!'
>>> add = js2py.eval_js('function add(a, b) {return a + b}')
>>> add(1, 2) + 3
6
>>> add.constructor
function Function() { [python code] }
>>> js2py.require('underscore')
'function _(obj) { [python code] }'
您还可以导入大量节点模块,就好像它们是用 Python 编写的一样!例如,这里我们导入一个纯 JS 库crypto-js:
>>> CryptoJS = js2py.require('crypto-js')
>>> data = [{'id': 1}, {'id': 2}]
>>> JSON = js2py.eval_js('JSON')
>>> ciphertext = CryptoJS.AES.encrypt(JSON.stringify(data), 'secret key 123')
>>> bytes = CryptoJS.AES.decrypt(ciphertext.toString(), 'secret key 123')
>>> decryptedData = JSON.parse(bytes.toString(CryptoJS.enc.Utf8)).to_list()
>>> decryptedData
[{u'id': 1}, {u'id': 2}]
现在还支持 JavaScript 6(仍处于试验阶段):
>>> js2py.eval_js6('let a = () => 11; a()')
11
JavaScript 6 支持是通过使用 Js2Py 翻译名为Babel 的javascript 库来实现的。Babel 将 JS 6 翻译成 JS 5,然后 Js2Py 将 JS 5 翻译成 Python。唯一的缺点是翻译的 babel.js 大约有 4 MB,导入这么长的 Python 文件大约需要 15 秒!
翻译 JavaScript 文件:
# this will translate example.js to example.py
>>> js2py.translate_file('example.js', 'example.py')
# example.py can be now imported and used!
>>> from example import example
>>> example.someFunction()
...
ECMA 5.1 的每个功能都已实现(“with”语句除外):
>>> js2py.eval_js("Object.prototype.toString.call(Function('s', 'return s+arguments[1]')(new Date(), 7).__proto__)")
[object String]
不幸的是,尽管 Js2Py 通常可用于翻译巨大的 Js 文件(超过 50k 行),但在极少数情况下,您可能会遇到一些意想不到的问题(例如 javascript 调用带有 300 个参数的函数 - python 只允许 255 个)。这些问题很难用当前的翻译方法解决。我将尝试在不久的将来实现一个解释器,它有望修复所有边缘情况。
pip install js2py
js_file = r'xxx.js'
with open(js_file, 'r', encoding='UTF-8') as f:
js_code = f.read()
#实例化解析js对象
context = js2py.EvalJs()
#js转换为python代码
context.execute(js_code)
#调⽤js中的o函数,t为o的参数
result = context.o(t)
print(result)
可以使用 EvalJs 从 JS 范围访问所有变量。此外,如果将 Python 对象添加到作用域,则可以使用 JavaScript 代码中的 Python 对象。在这个例子中,我们将使用 Python 内置的求和函数对 JS 数组中的元素求和。它将位于 python_sum 之下。
# Adding Python built-in sum to the JS context:
>>> context = js2py.EvalJs({'python_sum': sum})
>>> js_code = '''
var a = 10
function f(x) {return x*x}
'''
>>> context.execute(js_code)
# Get value of variable a:
>>> context.a
10
# context.f behaves just like js function so you can supply more than 1 argument. '9'*'9' in javascript is 81.
>>> context.f('9', 0)
81
# context.f has all attributes of normal JavaScript object
>>> context.f.toString()
u'function f(x) { [python code] }'
>>> context.f.bind
function bind(thisArg) { [python code] }
# You can also add variables to the context:
>>> context.foo = [1,2,3] # context.foo is now Js Array object and behaves just like javascript array!
>>> context.foo.push(4)
4
>>> context.foo.to_list() # convert to python list
[1, 2, 3, 4]
# You can use Python objects that you put inside the context!
>>> context.eval('python_sum(new Array(1, 2, 3))')
6
您还可以像这样在 JavaScript 中启用 require 支持:
>>> context = js2py.EvalJs(enable_require=True)
>>> context.eval("require('esprima').parse('var a = 1')")
作为一个有趣的实验项目,我还实现了一个基于 VM 的 JavaScript(是的——在这个 repo 中有 2 个独立的 JS 实现)。它比基于翻译的版本功能完整且速度更快。下面你可以看到一个带有漂亮调试视图的演示(字节码+执行序列):
>>> from js2py.internals import seval
>>> seval.eval_js_vm("try {throw 3+3} catch (e) {console.log(e)}", debug=True)
[LOAD_UNDEFINED(),
JUMP(4,),
LABEL(1,),
LOAD_UNDEFINED(),
POP(),
LOAD_NUMBER(3.0,),
LOAD_NUMBER(3.0,),
BINARY_OP('+',),
THROW(),
NOP(),
LABEL(2,),
LOAD_UNDEFINED(),
POP(),
LOAD('console',),
LOAD('e',),
LOAD_N_TUPLE(1,),
CALL_METHOD_DOT('log',),
NOP(),
LABEL(3,),
LOAD_UNDEFINED(),
NOP(),
LABEL(4,),
TRY_CATCH_FINALLY(1, 2, 'e', 3, False, 4)]
0 LOAD_UNDEFINED()
1 JUMP(4,)
18 TRY_CATCH_FINALLY(1, 2, 'e', 3, False, 4)
ctx entry (from:2, to:9)
2 LOAD_UNDEFINED()
3 POP()
4 LOAD_NUMBER(3.0,)
5 LOAD_NUMBER(3.0,)
6 BINARY_OP('+',)
7 THROW()
ctx exit (js errors)
ctx entry (from:9, to:16)
9 LOAD_UNDEFINED()
10 POP()
11 LOAD('console',)
12 LOAD('e',)
13 LOAD_N_TUPLE(1,)
14 CALL_METHOD_DOT('log',)
6
15 NOP()
ctx exit (normal)
这只是一种好奇心,我不建议在实践中使用 VM(需要更多改进)。
主要有3个限制:
它们在实践中通常不是大问题。在实践中,更多的问题是不幸的是有时确实会发生的次要边缘情况。如果发现错误,请报告错误。
Js2Py 能够成功翻译和运行巨大的 JS 库,如 Babel(100k+ loc)、esprima、crypto-js 等。您可以通过导入任何受支持的 npm 包来自己尝试js2py.require('your_package')。
在 Js2Py 中,所有 JavaScript 对象都是 PyJs 对象的子类。例如 JS Number 由 PyJsNumber 类表示。js2py.eval_js 和 js2py.EvalJs 自动尝试将 PyJs 类型转换为内置 python 类型。因此,例如,如果您执行:
>>> js2py.eval_js('var a = "hello"; a')
eval_js 将返回 unicode 类型 (u"hello")。但是,对于复杂类型,这种转换是不可能的,因此会返回 JsObjectWrapper。看转换表JsType -> PyType:
Boolean -> bool
String -> unicode (str in Python 3)
Number -> float (or int/long if whole number)
undefined -> None
null -> None
OTHER -> JsObjectWrapper
JsObjectWrapper 支持:getitem、getattr、setitem、setattr、repr 和 call。此外,如果您想将其转换为内置 python 类型,它还具有 to_list 和 to_dict 方法。
>>> js = js2py.eval_js('d = {a:1, b:2}')
>>> js
{a: 1, b: 2}
>>> type(js)
<class 'js2py.base.JsObjectWrapper'>
>>> js.a
1
>>> js['a']
1
>>> js.b = 20
>>> js
{a: 1, b: 20}
>>> js['c'] = 30
>>> js.to_dict()
{u'a': 1, 'c': 30, u'b': 20}
另外,当然你可以使用 Js2Py 来解析(tree 和 esprima.js 中一样)和翻译 JavaScript
>>> js2py.parse_js('var $ = 5')
{
"body": [
{
"declarations": [
{
"id": {
"name": "$",
"type": "Identifier"
},
"init": {
"raw": "5",
"type": "Literal",
"value": 5
},
"type": "VariableDeclarator"
}
],
"kind": "var",
"type": "VariableDeclaration"
}
],
"type": "Program"
}
>>> print(js2py.translate_js('var $ = 5'))
from js2py.pyjs import *
# setting scope
var = Scope( JS_BUILTINS )
set_global_object(var)
# Code follows:
var.registers(['$'])
var.put('$', Js(5.0))
最后,Js2Py 还支持使用“pyimport”语句从 JavaScript 导入任何 Python 代码:
>>> x = """pyimport urllib;
var result = urllib.urlopen('https://www.google.com/').read();
console.log(result.length)
"""
>>> js2py.eval_js(x)
18211
import subprocess
p = subprocess.Popen("mode", stdout=subprocess.PIPE, universal_newlines=True).stdout.read()