将python脚本添加到非python应用程序的"正确"方法

kur*_*ige 21 c++ python scripting plugins desktop-application

我目前正在使用python中编写的插件为用户添加扩展桌面应用程序(C++)功能的能力.

天真的方法很简单.嵌入python静态库并按照分散在Web上的任意数量的教程来描述如何初始化和调用python文件,并且你已经完成了很多工作.

然而...

我正在寻找的更像是Blender所做的.Blender可以通过python脚本完全自定义,它需要一个外部 python可执行文件.(即,python实际上并没有嵌入到blender可执行文件中.)因此,当然,在编写blender脚本时,可以包含site-packages目录中已有的任何模块.不是那个建议,因为这会限制脚本的可移植性.

所以,我想知道的是,如果已经有办法让你的蛋糕也吃了它.我想要一个使用以下内容的插件系统:

  • 嵌入式python解释器.

    Blender的方法的缺点是它迫使你在你的系统上全局安装一个特定的,可能过时的python版本.有一个嵌入式解释器允许我控制正在使用的python版本.

  • 防火墙插件.

    virtualenv每个插件的一些等价物; 允许他们安装他们需要或想要的所有模块,但要将它们与其他插件中的可能冲突分开.也许zc.buildout是这里更好的人选,但同样,我给建议公开.对于实现这一目标的最佳方式,我有点不知所措.

  • 尽可能无痛......

    对于用户.只要大部分内容对插件编写者尽可能透明,我愿意付出额外的努力.


如果你们中的任何人有这方面的经验,你们的帮助将非常感激.:)


编辑: 基本上,我想要的简短版本是简单的virtualenv,但没有捆绑的python解释器,以及一种以编程方式激活特定"虚拟环境"的方法,就像zc.buildoutsys.path操作(sys.path[0:0] = [...]技巧)一样.

双方virtualenvzc.buildout包含我想要的部分,但也产生重新定位的建立是我,或插件开发者可以简单地压缩并发送到另一台计算机.

简单地操作.pth文件,或sys.path直接在脚本中操作,从我的应用程序执行,让我在那里中途.但是,当编译模块是必要的时候,例如PIL,这还不够.

Noa*_*ins 8

实现此目的的一种有效方法是使用消息传递/通信流程体系结构,允许您使用Python实现目标,但不限制自己使用Python.

------------------------------------
| App  <--> Ext. API <--> Protocol | <--> (Socket) <--> API.py <--> Script
------------------------------------
Run Code Online (Sandbox Code Playgroud)

此图试图显示以下内容:您的应用程序使用消息传递与外部进程(例如Python)进行通信.这在本地计算机上很有效,并且可以移植,因为您定义了自己的协议.您必须为用户提供的唯一功能是实现自定义API的Python库,并使用用户脚本和应用程序之间的Send-Receive通信循环进行通信.

定义应用程序的外部API

应用程序的外部API描述了外部进程必须能够与之交互的所有功能.例如,如果您希望Python脚本能够在应用程序中绘制红色圆圈,则外部API可能包括绘图(对象,颜色,位置).

定义通信协议

这是外部进程通过其外部API与您的应用程序通信的协议.流行的选择可能是XML-RPC,SunRPC,JSON或您自己的自定义协议和数据格式.这里的选择需要足够您的API.例如,如果要传输二进制数据,则JSON可能需要base64编码,而SunRPC则采用二进制通信.

构建应用程序的消息系统

这就像在通信协议中接收消息的无限循环一样简单,为应用程序内的请求提供服务,并通过相同的套接字/通道进行回复.例如,如果您选择JSON,那么您将收到一条消息,其中包含执行Draw(对象,颜色,位置)的指令.执行请求后,您将回复该请求.

为Python(或其他任何东西)构建消息传递库

这甚至更简单.同样,这是一个代表库用户发送和接收消息的循环(即您的用户编写Python脚本).该库必须做的唯一事情是为应用程序的外部API提供编程接口,并将请求转换为您的通信协议,所有这些都隐藏在您的用户之外.

例如,使用Unix套接字将非常快.

插件/应用程序Rendezvous

发现应用程序插件的常见做法是指定应放置插件的"众所周知"目录.例如,这可能是:

~/.myapp/plugins
Run Code Online (Sandbox Code Playgroud)

下一步是让您的应用程序查看此目录中是否存在插件.您的应用程序应该有一些智能,以便能够区分适用于您的应用程序的实际脚本的Python脚本.

假设您的通信协议指定每个脚本将使用JSON通过StdInput/StdOuput进行通信.一种简单有效的方法是在您的协议中指定脚本第一次运行时将MAGIC_ID发送到标准输出.也就是说,您的应用程序会读取第一个,例如8个字节,并查找将其标识为脚本的特定64位值.

此外,您应该在外部API中包含允许脚本识别自身的方法.例如,脚本应该能够通过外部API通知应用程序,例如名称,描述,功能,期望,基本上通知应用程序它是什么,以及它将要做什么.