问题分析
因为有需要通过Python项目代码目录中的 node.exe
执行指定的 JavaScript 代码,但发现 PyExecJS 总是报出 execjs._exceptions.RuntimeUnavailableError: Node.js (V8)_local runtime is not available on this system
这个错误。
经过仔细阅读源码,发现 PyExecJS 默认只能运行系统环境变量中设置好的目录下的 NodeJS 环境,主要原因在于 PyExecJS 是通过两个分别名为 _binary
和 _which
的函数来获取 Node 程序的执行路径,而 _which
函数被设计为遍历系统环境变量 Path
中设定的目录,并从中寻找 node.exe
程序,从而获取到其完整路径的方法。
这两个函数的设计原型如下:
class ExternalRuntime(AbstractRuntime):
def _binary(self):
if not hasattr(self, "_binary_cache"):
self._binary_cache = _which(self._command)
return self._binary_cache
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
解决方法
这个问题解决起来很简单,只需要在创建好 ExternalRuntime
后,对其 _binary_cache
和 _available
两个属性进行强制修改即可。
_binary_cache
是 PyExecJS 在运行 JavaScript 代码时,调用的 NodeJs 程序的路径(代码第 14 行,需要修改为需要使用的 NodeJs 的路径,相对路径、绝对路径均可)_available
是 PyExecJS 的_binary_cache
是否可以调用的标识,直接置为True
即可
# 导入 PyExecJS 模块
import execjs
import execjs._runner_sources as _runner_sources
# 创建 JavaScript 运行时 对象
# 参数中的 command 并不重要,传入空字符串即可
local_node_runtime = execjs.ExternalRuntime(
name="Node.js (V8) local",
command='',
encoding='UTF-8',
runner_source=_runner_sources.Node
)
# 这里是重点,需要强制性修改
local_node_runtime._binary_cache = ['./node.exe']
local_node_runtime._available = True
# 将刚创建好的 JavaScript 运行时 注册至 PyExecJS 中
execjs.register('local_node', local_node_runtime)
# 待运行的 JavaScript 代码
js_code = '''
function sum(a, b) {
return a + b
}
'''
# 通过 execjs 的 get 方法,即可调用刚才注册到 PyExecJS 的 JavaScript 运行时
ctx = execjs.get('local_node').compile(js_code)
result = ctx.call('add', 1, 2)