cangjie_python_interop:仓颉-Python 互操作库的架构、构建与使用指南
开发工具【免费下载链接】cangjie_python_interop项目地址https://gitcode.com/Cangjie/cangjie_python_interop点击查看免费下载cangjie_python_interop仓颉-Python 互操作库为仓颉语言提供与 Python 的跨语言互操作能力覆盖双向类型转换、导入并调用 Python 模块、执行 Python 表达式与代码以及 Python 回调仓颉函数四大能力。本文以仓库的 README 为核心骨架结合src/ffi/py_interop源码、build/build.py构建脚本与tests/测试用例系统梳理该库的能力边界、目录结构、约束条件与从源码构建的完整流程帮助你从看懂文档走到能编译、能链接、能调用。一、库定位与核心能力如 README_zh.md 所述本库用于提供仓颉与 Python 跨语言互操作能力具体包括四类功能Python 数据类型与仓颉类型之间的互相转换——Bool、整型、浮点、字符串、元组、列表、字典、集合、切片等基础类型均有对应的桥接类型PyBool、PyLong、PyFloat、PyString、PyTuple、PyList、PyDict、PySet、PySlice在仓颉侧导入并使用 Python 模块——标准库、三方模块与用户自定义模块如numpy均可导入并使用模块内的函数、变量、类等所有可见对象执行 Python 表达式与 Python 代码——通过Python.eval()执行单个表达式并返回值通过Python.exec()执行完整代码块函数与类定义、模块导入、多行流程控制等Python 回调仓颉函数——构造PyCFunc对象后直接传递给 Python 侧调用支持 Python 侧各类数据作为回调入参。更完整的接口说明参见 用户指南 与 开发者指南API 参考文档位于 docs/libs/py_interop/py_interop_package_overview.md。二、仓库目录结构README 给出的目录结构如下cangjie_interop/ ├── build # 构建脚本 ├── docs # 介绍文档包括用户指南、开发者指南、api参考 ├── src │ ├── ffi │ └── py_interop # 仓颉-Python 互操作库源码 └── tests # 测试用例结合仓库实际内容各目录的职责如下路径内容与职责build/build.py构建入口脚本封装cjpm命令提供build/install/clean三个子命令src/ffi/py_interop互操作库全部源码共 14 个.cj文件、约 4000 行tests/py_interop按功能分类的测试用例cfunc、eval、exec、import、object、slice 等 12 个目录docs/user-guide.md、docs/developer-guide.md使用指南与开发者指南docs/libspy_interop包 API 参考从源码结构看src/ffi/py_interop下各文件与核心类型的对应关系较为直接源文件主要承载类型按源码命名推断python.cjPython解释器类、Version、PythonException、LogLevel、PythonLoggerPYLOGpyobj.cjPyObj基类成员访问、函数调用、运算符重载、asPyXxx类型转换pybase.cjPyBool/PyLong/PyFloat/PyString等基础标量类型pylist.cj、pytuple.cj、pydict.cj、pyset.cj、pyslice.cj容器类类型PyList/PyTuple/PyDict/PySet/PySlicepycfunc.cjPyCFuncPython 回调仓颉函数pymodule.cjPyModulePython 模块封装cjobj.cjCjObj接口及仓颉基础类型的toPyObj()扩展pythoncapi.cj、pythoncwapper.cjPython C API 的foreign func声明与 C 侧包装slab.cj内部内存分配支撑三、约束条件平台与后端限制README_zh.md 明确了当前的约束目前仅支持在 Linux 平台编译和使用并且仅支持仓颉编译器的 cjnative 后端。这一点在仓库中可以得到多处印证构建脚本 build/build.py 中有硬性校验非 Linux 或--target不为native时直接报错py_interop compile is only supported for native Linux且SUPPORTED_TARGET [native]只允许本机平台包配置文件 src/ffi/py_interop/cjpm.toml 中Linux 目标x86_64-unknown-linux-gnu通过--cfg py_interop_plat Linux注入平台宏源码中用When[py_interop_plat ohos]等条件编译区分平台行为同时该文件还包含aarch64-linux-ohos、x86_64-linux-ohos的交叉编译配置与 用户指南 中支持在 Linux 和 OpenHarmony 平台上仓颉调用 Python的表述相互对应。因此在使用前需要确认两点运行环境是 Linux且仓颉工程采用cjnative 后端。四、从源码编译构建开发者指南 给出的安装与使用流程如下这里结合 build/build.py 的实现补充细节。4.1 构建准备准备 Linux 平台的cangjie SDK并执行对应 SDK 的envsetup脚本确保 SDK 被正确配置使用python构建脚本方式编译需安装python3。4.2 构建步骤cd build python3 build.py build -t release-t, --build-type指定构建产物版本可选值为debug/release默认release。从 build/build.py 的源码看该命令实际是在src/ffi/py_interop目录下调用cjpm builddebug对应cjpm build --debugrelease对应cjpm build --release因此构建依赖 SDK 自带的cjpm工具链。src/ffi/py_interop/cjpm.toml 声明了包信息包名py_interop、版本1.0.0、cjc-version 0.53.21、output-type dynamic即产出动态库target-dir指向仓库根目录下的target。4.3 安装到指定目录# 默认安装到 target 目录 python3 build.py install # 通过 --prefix 指定安装目录 python3 build.py install --prefix ./output从源码实现build/build.py可以看到install会检查target/release/py_interop是否存在若不存在则提示先执行build存在时把两个编译产物拷贝到目标目录output/ |-- py_interop.cjo # cjo 文件仓颉包元数据 |-- libpy_interop.so # 共享库文件4.4 清理与更多构建选项python3 build.py cleanclean会清除target目录下的全部构建产物。build子命令还提供--target TARGET指定编译目标产物的运行平台默认native本机平台当前仅支持 Linux-h, --help打印build功能的帮助信息build.py顶层同样支持-h, --help。4.5 编译产物如何被使用编译并安装后用户工程在调用cjc时需要将py_interop目录同时加入链接路径与导入路径。仓库自身的测试配置 tests/test.cfg 给出了标准用法pyffi-link -l py_interop -L $CANGJIE_STDX_PATH/../../../py_interop --import-path $CANGJIE_STDX_PATH/../../../py_interop即编译命令形如cjc -l py_interop -L /path/to/py_interop --import-path /path/to/py_interop ./main.cj -o ./main。此外用户指南 特别提示执行 Python 互操作相关代码前建议将仓颉默认栈大小配置为至少 1MBexport cjStackSize1MB因为互操作过程会大量调用 Python 库的 native 代码超出默认 64KB 栈保护可能触发不可预期的结果。五、快速上手加载解释器与最小互操作示例README 指引读者参阅 docs 目录下的指南这里给出最小可运行示例来自 用户指南导入 Python 标准库sys模块并访问其version属性。import py_interop.* main(): Int64 { Python.load() var sys: PyObj Python.importMod(sys) println(sys[version]) Python.unload() return 0 }执行结果与指南一致具体版本号取决于系统安装的 Python3.10.12 (main, May 27 2025, 17:12:29) [GCC 11.4.0]使用py_interop库的标准流程为import py_interop.*导入库获得解释器类Python、全局日志对象PYLOG与PyObject类型体系调用Python.load()加载解释器资源进入互操作前的必须步骤完成后调用Python.unload()释放通过Python.importMod()/Python.eval()/Python.exec()/Python.getVersion()等接口调用 Python 能力用PyObj及其子类创建与操作 Python 对象通过PyCFunc注册仓颉函数实现 Python 反向调用。5.1 源码视角下的Python.load()python.cj 中load(path: String)的实现清晰呈现了加载链路用ReentrantMutexinitLock保护全局初始化envInit标志位保证加载/卸载全局只生效一次与文档多次调用仅第一次生效一致校验路径存在后调用loadSo(path)加载动态库再由readSym()读取符号表调用Py_Initialize()初始化 Python 解释器随后PyEval_SaveThread()保存并释放 GIL 线程状态setPyArgs()将仓颉可执行文件的参数传递给 Python最后从__builtins__中取出内置函数字典支撑Python.getBuiltinsAttr()。Python.isLoad()python.cj在加锁状态下读取envInitunload()则执行CJ_UnbindOSThread()解绑系统线程并清理资源未先load时unload仅打印 warn 并返回。5.2 Python 动态库的三步查找策略libpython的搜索顺序由无参load()内部调用getPythonPath()完成python.cj 的注释即完整描述了三步策略环境变量PYTHON_DYNLIBexport PYTHON_DYNLIB.../libpython3.9.so可执行文件依赖执行ldd $(which python3)从python3的动态库依赖中提取libpython路径源码中对应常量cmdExeDep见 python.cj。注意非动态库依赖的 Python源码编译未加--enable-shared无法被此步找到系统默认路径依次在/lib、/usr/lib、/usr/local/lib中查找文件名必须满足libpythonX.Y.so命名支持d.so、m.so、dm.so、.so等后缀。如果三步都失败会抛出PythonException如Cant get path from system lib path, load exit.。排查时可开启INFO级日志Python.load(loglevel: LogLevel.INFO)观察完整搜索过程。关于动态库获取用户指南 推荐从 Python 源码编译# 在Python源码路径下 ./configure --enable-shared --with-system-ffi --prefix/usr make make install用户指南建议 Python 3.9.2并声明支持读取 Python 3.0 以上版本当前源码中还定义了解释器版本上限常量TOP_MOJOR_VERSION 3、TOP_MINOR_VERSION 12python.cj可用于校验所链接动态库的版本兼容性。5.3 核心接口速览接口签名节选说明Python.loadstatic func load(loglevel!: LogLevel LogLevel.WARN)/load(path: String, loglevel!: LogLevel LogLevel.WARN)加载解释器支持无参自动查找或指定动态库绝对路径失败抛PythonExceptionPython.unloadstatic func unload(): Unit卸载解释器资源须与load一一对应Python.isLoadstatic func isLoad(): Bool判断 Python 库是否已加载Python.getVersionstatic func getVersion(): Version获取当前 Python 版本Version不可变结构提供getMajor/Minor/Micro、getVersion()三元组与toString未load时返回0.0.0Python.importModstatic func importMod(module: String): PyModule导入标准库/三方/自定义模块模块不存在时抛仓颉异常异常信息含ModuleNotFoundError详情Python.evalstatic func eval(cmd: String, module!: String __main__): PyObj执行单个表达式并返回值仅支持表达式赋值等语句会报SyntaxErrorcmd长度需在 (0, 1000]Python.execstatic func exec(code: String): Unit执行完整代码块定义、导入、多行流程控制Python.getBuiltinsAttrstatic func getBuiltinsAttr(attr: String): OptionPyObj按名称获取 Python 内置函数未找到返回NoneimportMod的模块路径规则来自 用户指南内置模块如sys、time无需路径配置标准库模块如os、json依赖 Python 安装路径Linux 默认/usr通常无需设置OpenHarmony 需设置PYTHONHOME三方与自定义模块需通过PYTHONPATH环境变量或运行时向sys.path插入路径来配置搜索路径。六、Python 对象体系与类型映射互操作基于 Python C API 开发所有跨语言数据交换统一通过PyObject结构体指针完成。仓颉侧将其封装为基类PyObj并按 Python 数据类型派生子类。用户指南 给出的双向类型映射表是理解整个对象体系的总纲仓颉类型到 Python 类型映射Cangjie TypeBridge TypePython TypeBoolPyBoolboolUInt8/Int8/Int16/UInt16/Int32/UInt32/Int64/UInt64PyLongintFloat32/Float64PyFloatfloatRune/StringPyStringstr-PyTupletupleArray T PyList T listHashMap K, V PyDict K, V dictHashSet T PySet T setRange T PySlice T slice(Array PyObj ) - PyObjPyCFuncbuiltin_function_or_methodPython 类型到仓颉类型映射Python TypeBridge TypeCangjie TypeboolPyBoolBoolintPyLongInt64/UInt64floatPyFloatFloat64strPyStringStringtuplePyTupleArray PyObj listPyList PyObj Array PyObj dictPyDict PyObj, PyObj HashMap PyObj, PyObj setPySet PyObj HashSet PyObj slicePySlice Int64 Range Int64 几条关键约定仓颉侧泛型容器PyListT、PyDictK,V、PySetT要求元素类型约束为PyFFIType接口的子类包括PyObj与实现CjObj的仓颉基础类型仓颉ArrayPyObj数组可直接构造PyTuplePython 侧的自定义类实例、函数、模块等一切可调用/可命名对象均以PyObj形式传入仓颉侧。PyObj基类pyobj.cj提供了通用操作isAvailable()判断封装指针是否可用[]重载实现成员读写写入不可用值会删除对应键()重载实现无参/多参/kargs命名参数调用参数支持CjObj与PyObj但不允许混用 - * / ** %运算符重载遵循 Python 语义非法组合会打印 warn 并返回不可用对象asPyLong/asPyFloat/asPyString/asPyBool/asPyTuple/asPyDict/asPyList/asPySet/asPySlice提供类型匹配转换类型不符时抛异常。七、Python 回调仓颉函数PyCFuncPyCFunc是双向互操作的另一半它允许把签名为(ArrayPyObj) - PyObj的仓颉函数注册给 Python 侧pycfunc.cj回调参数逐一按PyObj解析返回值需构造一个PyObj。func foo(args: ArrayPyObj): PyObj { let obj0 args[0].asPyLong() println(${obj0.toCjObj()}) PyListPyObj([PyLong(12345), PyString(ret)]) } main() { Python.load() let pyFunc PyCFunc(foo) // 注册回调 pyFunc(42, hello, [4, 5, 6]) // 仓颉侧也可直接调用 let mm Python.importMod(main) mmtestFoo // 传递给 Python 侧调用 Python.unload() 0 }在完整示例中用户指南Python 侧一次性传入int/str/tuple/dict/list/bool/float/set/slice/自定义类实例/函数对象十一种参数仓颉侧全部正确解析并返回[12345, ret, [1, 2, 3]]。CFunc函数入参和返回值支持的类型包括Bool/Int8/UInt8/Int16/UInt16/Int32/UInt32/Int64/UInt64/Float32/Float64/CPointerUnit/CString/Unit。八、并发、异常与全局资源并发模型。程序运行时全局仅加载一个 Python 解释器通过全局解释器锁GIL保证并发访问安全。互操作接口支持系统线程间并发但不支持仓颉协程间直接并发在spawn出的协程中调用互操作接口前需通过CJ_BindOSThread()将协程绑定到系统线程该foreign func的声明即位于 python.cj且load()内部也会为主线程自动绑定foreign func CJ_BindOSThread(): Int32 foreign func CJ_UnbindOSThread(): Int32 main() { Python.load() spawn { unsafe {CJ_BindOSThread()} var sys Python.importMod(sys) sys[version] unsafe {CJ_UnbindOSThread()} } Python.unload() 0 }未绑定系统线程的spawn在调用期间可能因线程切换抛出异常同时拉起超过 8 个绑定系统线程的spawn可能出现死锁用户指南将其标注为仓颉运行时已知问题。异常体系。库内所有异常统一为PythonExceptionpython.cj可通过try-catch捕获未捕获时打印堆栈并以返回值 1 退出。回调场景下仓颉回调函数中抛出的PythonException堆栈会与 Python 侧堆栈一同打印便于跨语言定位。全局日志。全局日志对象PYLOGPythonLogger实例定义于 python.cj默认打印等级为WARN提供debug/info/warn/error与log(level, msg)接口前缀区分级别如WARN: log warn不支持日志转储文件。load的loglevel参数不配置时会将PYLOG重置为WARN。九、测试体系tests/目录基于 cangjie_test_framework 测试框架组织该框架为独立仓库tests/test.sh 头部注释给出了配置依赖的方法。测试用例按功能划分为 12 个子目录tests/py_interop/ ├── cfunc/ # PyCFunc 回调含 refcount、spawn 场景 ├── compare/ # /! 与 hashCode ├── eval/ # eval 表达式执行含 evalTest.py ├── exec/ # exec 代码块执行 ├── global/ # load 生命周期、日志、解释器异常、spawn 并发 ├── import/ # 模块导入importTest.py、class/module 场景 ├── index/ # [] 下标操作 ├── iterator/ # PyObjIterator 迭代 ├── kargs_call/ # 命名参数调用 ├── object/ # 各基础/容器类型的构造与映射python_dict/list/set/tuple 等 ├── object_operation/ # 运算符重载 └── slice/ # PySlice 切片运行入口 tests/test.sh 调用测试框架主程序关键参数为--fail_exit --keep_temp --retry3 --timeout600 -j16测试清单 tests/testlist 以[ALL-TEST-CASE]全量选择py_interop目录tests/test.cfg 声明测试条件为LLVM/x86/Linux/Unix并在[env]中将当前目录前置到LD_LIBRARY_PATH。运行前需按test.sh注释要求先编译py_interop把py_interop.cjo与libpy_interop.so拷贝到$CANGJIE_STDX_PATH/../../../py_interop目录并 export 到LD_LIBRARY_PATH。十、开源协议与参与贡献本项目基于 Apache-2.0 with Runtime Library Exception 与 Attribution 4.0 International 双重许可发布源码文件头部同时声明了两者的版权与授权信息。项目欢迎提交 Issue 与任何形式的参与贡献构建与测试工具链则依赖同体系的 cangjie_test_framework 开源仓库见 tests/test.sh 中的说明。十一、小结cangjie_python_interop 以一个包py_interop、一个解释器门面Python类、一套对象桥接体系PyObj及子类为核心组织Python.load/unload管理全局唯一的解释器生命周期importMod/eval/exec打通仓颉到 Python 的调用通道PyObj对象体系与CjObj接口完成双向类型转换PyCFunc补全 Python 反向回调仓颉的闭环。使用上需要牢记三件事仅限 Linux cjnative 后端load与unload严格一一对应且全局只生效一次协程并发调用前必须绑定系统线程。掌握以上内容后即可按本文第四节的流程完成从源码构建到cjc -l py_interop -L ... --import-path ...链接、再到真实调用 Python 生态的完整链路。赞分享开发工具【免费下载链接】cangjie_python_interop项目地址https://gitcode.com/Cangjie/cangjie_python_interop点击查看免费下载相关推荐cangjie_python_interop 仓颉-Python 互操作库跨语言桥接能力与源码级实战指南cangjie_python_interop 仓颉 Python 互操作库跨语言桥接能力与源码级实战指南 本文围绕 cangjie_python_intero开发工具仓颉-C互操作FFI实战如何在仓颉中调用C库的完整指南仓颉 C互操作FFI实战如何在仓颉中调用C库的完整指南 想让仓颉程序直接调用成熟的 C 库吗仓颉编程语言通过 FFIForeign Function文档编程语言大麦自动抢票双端 Web 移动端自动化抢票完整指南3步跑通大麦自动抢票双端 Web 移动端自动化抢票完整指南3步跑通 ticket purchase 是一个用 Python 写的大麦自动抢票工具网页端用 SeGUI 自动化RPA创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考