pythonrpc-pyserver

Call python internal in nodejs. The python server side.


Keywords
JavaScript, rpc
License
MIT
Install
pip install pythonrpc-pyserver==0.0.1

Documentation

使用前提:使用这个程序造成的一切后果开发者不承担任何形式的义务或责任, 您应该主动学习,对使用这个程序的后果自行承担风险,遵守相关法律法规, 否则请马上终止这个使用这个程序。

pythonrpc

架构图

在 JavaScript 环境中使用影子对象调用 python 的模块、实例、属性和方法。

Calling python module, instance, attribute or function in JavaScript Runtime with a shadowed object.

用法示例

\1. 首先要安装 pythonrpc-jsclient 和 pythonrpc-pyserver。(见下文)

并不是采用将完整的 Python 嵌入到 JavaScript 的运行时的实现,而是需要开启一个特定的 Python 服务器,然后 JavaScript 运行时和这个 Python 服务器建立 http 通信。这样子的一个优点是两套运行环境是分离的,Python环境可以单独关闭、重启、重置,而且多个JavaScript可以而且只需要关联到一个Python进程等等。

\2. 开启 Python 运行时服务器。(见下文)

\3. 在 JavaScript 中打开一个 Session 。调用分在不同的 Session 上,在 JavaScript 环境中的操作,Python 服务器会自动管理、自主运行、自己操作。

在 JavaScript 中通过 open_session 打开一个新的 Session :

async function foo(){
	var session = await open_session('http://127.0.0.1:23345');
	// ...
}

open_session 返回的是一个封装了的 Session 类。这个类包括如下的这些方法:

表达式(Session) 用途
$string = session.prefix() 返回这个 session 的前缀,即 python 运行时服务器的地址。
$number = session.sessionid() 返回这个 session 的 sessionid 。同一个 Session 在 JavaScript 和 Python 的 sessionid 数值相等。
$promise = session.require_module(name) 要求 Python 环境载入名字为 name 的模块,并将 JavaScript环境中可以操作这个模块的 $mod 对象通过 $promise.resolve($mod) 。
$promise = session.del() 清除 python 环境中与这个 session 有关的引用。
                                                                        

理解 Session 的概念很重要。直观来看, Session 就像是一座桥梁,可以把一些调用 python 环境的需要传递到 JavaScript 环境中,并且 python 环境的一些东西通过 Session 传回 JavaScript 环境中。

\4. 通过影子对象操纵 python 环境中的对象。

表达式 (shadowed object) 用途
$number = $x.id() 返回 $x 所对应的python对象的 id 。这个值等于 python 中的 id(obj) 的值。
$promise = $x.attr(name) 返回 $x 所对应的python对象的名为 name 的属性的影子对象。即对应于 python 中的 obj.attr 。
$promise = $x.call(args, kwargs) 控制$x 所对应的python对象以args和kwargs做一次函数执行。即对应于 python 中的 fun(*args, **kwargs) ,并接收结果。
$promise = $x.del_ref() 切断 $x 所对应的python对象和影子对象的关联。如果不手动切断关联,可能会导致python环境的内存浪费,但在相关的 session.del() 启动的时候也会自动清理。
                                                                        

在 $promise = session.require_module(name) 中,通过 $promise.resolve($mod) 的 $mod 对象就是一个影子对象。例如下面这段 python 代码:

import random
x = random.randint(0, 50)
pushToJavaScript(x)

JavaScript 的这段代码将会操纵 python 环境中的进程,实现与其类似的效果:

async function getRandomFromPython(){
	var session = await open_session('http://127.0.0.1:23345');
	// 打开了 session
	
	var random = await session.require_module('random');
	// python中将会 
	//     > import random
	// 这里的 random 是一个操作python中的random的影子对象
	
	var randint = await random.attr('randint');
	// 获得python中的random.randint的影子对象
	
	var x = await randint.call([0, 50]);
	console.log(x);
	// 调用 random.randint(0, 50) ,并将结果记录在 x 中。
	
}

\5. 没有了!如果不需要再使用 python 运行时,直接将相关的python服务器关闭即可。

安装

现在已经可以通过 pip 安装 python 服务器:

pip install pythonrpc_pyserver

在 JavaScript 中的安装:

cd jsworkspace
npm init # 初始化 package.json
npm install pythonrpc-jsclient # 安装

开启 python 服务器:

python -c "from pythonrpc_pyserver import app; app.run(port=8888)"

打开 node 测试一下!比如这段代码:

// on node 
open_session = require('pythonrpc-jsclient').open_session;
async function getRandomFromPython(){
	var session = await open_session('http://127.0.0.1:8888');
	console.log('session', session);
	var random = await session.require_module('random');
	console.log('random shadow', random);
	var randint = await random.attr('randint');
	console.log('randint shadow', randint);
	var x = await randint.call([0, 50]);
	console.log('x', x);	
}
getRandomFromPython()

常见问答

(1)

问:现在的开发大概是什么一个状态?这些已经可以用于生产环境了吗?

答:现在主要是原型论证的状态,还有许多问题没有妥善讨论。不同的生产环境可能有不同的要求,不建议未经过论证就直接用在生产环境上,无论是从正确性、可用性还是稳定性来说。

(2)

问:怎么可能在影子对象和原对象之间保持一贯性?JavaScript的表达模式和Python的表达模式如何能够建立等价的完整的完备的关联?

答:一贯性是通过 http 协议的请求/响应过程来实现的。

可能不存在等价的完整的完备的JavaScript的表达模式和Python的表达模式的关联方法,在这里的机制是,以 JavaScript 的一些基本数据类型为主(number,boolean,string,array,dict,null),如果能够转换为 JavaScript 的这些基本数据类型,就进行转换;否则就记录在一张表里,然后通过 id 进行操作。

即使是这样,仍然会出现一些问题,例如循环引用的对象:

a = {}
b = {}
a['x'] = b
b['y'] = a

这类的对象如果要求返回到 JavaScript ,由于在进行 json 编码的时候语义会比较诡异,会采取一些消极的策略处理,因此不建议在python到JavaScript的返回值中使用这样子的数据结构。但只存在于python环境则没有关系。

长期来看,我们也不准备解决这个问题,而是直接填 null 。这是因为这个库主要是为了必要的在JavaScript 对python的调用,而不是可以完美地在JavaScript中整合python。

(3)

问:发现了一些很诡异的行为!还有bug!

答:这可能发生在 undefined 的情况。一般来说这些也是不打算解决的。但是你也可以把你的用例情况说一下,然后报个issues。

更多测试用例

可以访问 sample

文档翻译

欢迎文档翻译,可直接发 pull request 。

Welcome a translation for documentation, just send a pull request.