pycore v1.0.5
PyCore
PyCore是为NodeJs开发的模块,通过PyCore您可以轻松实现JavaScript与Python的交互,充分利用libuv线程池以及异步特性提高开发和执行的效率。JavaScript可以同步或异步执行Python语句和调用Python函数;在Python中同样可以执行JavaScript语句和调用JavaScript的函数。PyCore主要是为了将HTML+CSS+JavaScript/vuejs等前端技术作为Python程序的界面,PyCore可以将Python和Electron结合,通过Python直接与网页DOM元素进行交互,另外在nodejs中进行开发时可以用python来定制异步多线程操作。
一、Nodejs与python交互的方案
在nodejs中调用python有多种方法,一是使用childprocess通过进程通信就可以实现,这种方式适合简单的操作并不适合复杂的交互;二是采用网络编程,python开启服务器然后在nodejs中请求数据,如果是使用http服务器将不能实现全双工通信,采用websoket可以全双工通信但相对复杂而且需要保持连接,浏览器限制websoket通道数量。网络编程虽然可以实现数据交换,但是实际上效率并不高且占用网络资源和端口。
PyCore整合nodejs和python,在底层上cpython解释器与JavaScript引擎直接交互,这种交互方式能够提高执行效率还非常可靠,经测试通过PyCore创建百万级异步操作仍然可以轻松执行。
二、扩展异步操作
JavaScript是单线程执行,因此当CPU密集型操作时会阻塞主线程执行,JavaScript引入异步执行的操作,将密集型操作分交给其他线程来执行,执行完毕后再将结果返回给JavaScript主线程。异步多线程执行大大提高了单线程执行效率,浏览器中JavaScript可以将网络请求交给浏览器来做,异步线程操作的内容相对比较固定。PyCore引入Python后,JavaScript将可以把密集型操作交给异步独立线程的Python来操作,执行完毕后再将结果返回给JavaScript主线程。
三、跨平台
PyCore目前支持的主流操作系统和CPU架构。
- windows: x86 / x64
- linux: x64 / ARM64
- macos: x64
四、Python版本
当前仅支持Python3.10版本,后续将增加其他版本。
五、使用
(一)安装
npm install pycore
安装pycore模块
(二)引用
const pycore = require('pycore');
加载使用pycore
(三)、函数与参数
1.在JavaScript中调用的函数
模块函数 | 参数 | 说明 |
---|---|---|
init() | JSON | 解释器初始化函数 |
import() | string | import用python模块名创建对象 |
import('app').callSync | string, array | 模块对象同步调用python函数 |
import('app').call | string, array, function,function | 模块对象异步调用python函数 |
runScriptSync() | string | 同步执行python语句 |
runScript() | string | 异步执行python语句 |
release() | 释放解释器 |
2.Python中调用Pycore模块函数
模块函数 | 参数 | 说明 |
---|---|---|
pycore.callJS | target='函数名', args=() | 调用JS函数 |
pycore.runScript | string | 执行JS语句 |
(三)初始化
PyCore在使用前必须初始化解释器,通过添加python的环境路径,虚拟环境路径以及项目python脚本目录等路径到PyCore中,cpython解释器初始化环境才能够执行python语句和调用函数。
pycore.init({
"python_version":"3.10",
"python_home":"C:/Users/Admin/.pyenv/pyenv-win/versions/3.10.6",
"program_name":"python",
"base_prefix":"C:/Users/Admin/.pyenv/pyenv-win/versions/3.10.6",
"base_exec_prefix":"C:/Users/Admin/.pyenv/pyenv-win/versions/3.10.6",
"base_executable":"C:/Users/Admin/.pyenv/pyenv-win/versions/3.10.6/python.exe",
"prefix":"pyscript/venv",
"exec_prefix":"pyscript/venv",
"executable":"pyscript/venv/Scripts/python.exe",
"pythonpath_env":"pyscript/venv/Lib/site-packages",
"module_search_paths":["./", "pyscript"],
"encoding":"utf-8"
});
字段说明
字段 | 说明 |
---|---|
python_version | Python使用的版本 |
python_home | Python安装目录 |
program_name | python主程序名,windows是python,linux和macos系统python为2.7版本,python3为3.X版本 |
base_prefix | Python安装目录 |
base_exec_prefix | Python安装目录 |
base_executable | python主程序绝对路径 |
prefix | 虚拟环境目录,当不使用虚拟环境则设置为base_prefix |
exec_prefix | 虚拟环境目录,当不使用虚拟环境则设置为base_exec_prefix |
executable | 虚拟环境目录python主程序绝对路径,当不使用虚拟环境则设置为base_executable |
pythonpath_env | 虚拟环境模块目录 |
module_search_paths | 搜索模块的路径 |
encoding | 解释器编码,一般默认utf-8即可 |
(四)释放
pycore被初始化后在结束程序是需要使用release释放,当使用node程序执行js文件时,执行完毕后进程不会结束必须使用release()释放结束。electron中初始化pycore跟随主进程结束。
六、依赖动态库
- windows
pycore必须要python安装目录下的python3.dll
和python310.dll
,程序搜索动态库的目录有:程序当前目录、path环境变量的路径目录,system32目录,windows目录。如果python为默认安装安装目录已经添加在path环境变量中这可以直接启动程序,否则可以考虑将python3.dll/python310.dll文件拷贝到nodejs项目目录。
- linux
linux系统需要libpython3.10.so.1.0
动态库,系统直接安装的python动态库一般在/usr/lib/python3.10/config-3.10-x86_64-linux-gnu
目录下,如果是conda或者pyenv安装的python则在其对应目录下。pycore隐式加载动态库,搜索路径为程序当前目录(nodejs项目为项目目录),从/etc/ld.so.cache
文件中的路径搜索。/etc/ld.so.cache
实际上是一个缓存文件,它由/etc/ld.so.conf.d
目录下的所有文件生成,如果要添加新的动态库搜索路径,可以在该目录下创建.conf
后缀文件(名称任意),在文件内添加路径后执行ldconfig
命令就可以更新/etc/ld.so.cache
,动态库搜索路径就生效了。可以直接将libpython3.10.so.1.0动态库拷贝到nodejs项目目录。
- macos
macos必须libpython3.10.dylib
动态库,如果是conda或者pyenv安装的python则在其对应目录下找动态库,如果安装官方的python安装包,其路径为/Library/Frameworks/Python.framework/Versions/3.10/lib
。
因为python版本比较多,目前pycore仅针对python3.10进行开发,如果机器没有python3.10,建议是将python3.10免安装版与nodejs项目集成,或者使用pyenv安装python3.10
七、DOM元素
PyCore在electron中可以操作渲染器的DOM元素,pycore是nodejs模块,在electron中使用要么electron开启集成node环境,要么在preload中预加载pycore模块然后暴露API给渲染器JS使用。不管哪种方式他们都是共享window全局对象,所以都可以对DOM进行操作。
八、案例
github
https://github.com/supercoderlee/pycore-quick-start
index.js
const pycore = require('pycore')
// PyCore初始化
// 根据本机器的python3.10安装路径来配置环境
pycore.init({
"python_version":"3.10",
"python_home":"C:/Users/Admin/.pyenv/pyenv-win/versions/3.10.6",
"program_name":"python",
"base_prefix":"C:/Users/Admin/.pyenv/pyenv-win/versions/3.10.6",
"base_exec_prefix":"C:/Users/Admin/.pyenv/pyenv-win/versions/3.10.6",
"base_executable":"C:/Users/Admin/.pyenv/pyenv-win/versions/3.10.6/python.exe",
"prefix":"pyscript/venv",
"exec_prefix":"pyscript/venv",
"executable":"pyscript/venv/Scripts/python.exe",
"pythonpath_env":"pyscript/venv/Lib/site-packages",
"module_search_paths":["./", "pyscript"],
"encoding":"utf-8"
});
// Python调用的JS函数
// 必须是name = function(){}或者name = () => {}方式定义函数,否则无法在Python调用
sayHello = function (num1, num2) {
let total = num1 + num2;
console.log('Main SayHello total:' + total);
return ++total;
}
// 执行Python语句
pycore.runScriptSync("print('main run pyscript')");
pycore.runScript("print('main run pyscript')");
// 创建Python模块对象
const pyApp = pycore.import('app');
// 同步调用Python函数
let res = pyApp.callSync('sum', [1, 9]);
console.log(res);
// 异步调用Python函数
pyApp.call('callJS', [2, 6],
function (data) {
console.log(data);
},
function (error) {
console.log(error);
}
);
pyapp.py
import pycore
def sum(num1, num2):
return num1 + num2
def callBack(data):
pycore.runScript("console.log('callBack data:" + str(data) + "');")
return data # 该函数return返回值,在JS中为空的JS回调函数接收,将不会有任何操作
def callJS(num1, num2):
state = pycore.callJS(target='sayHello', args=(num1, num2), callback=(__name__, 'callBack')) # 返回0表示失败,1为成功
return num1 + num2