是否可以在CPython中以编程方式构造堆栈(一个或多个堆栈帧)并在任意代码点开始执行?想象一下以下场景:
您有一个工作流引擎,其中工作流可以在Python中编写脚本,其中包含一些构造(例如分支,等待/加入),这些构造是对工作流引擎的调用.
阻塞调用(例如等待或连接)在具有某种持久性后备存储的事件调度引擎中设置侦听器条件.
您有一个工作流脚本,它调用引擎中的等待条件,等待稍后将发出信号的某些条件.这将在事件调度引擎中设置侦听器.
工作流脚本的状态,包括程序计数器(或等效状态)的相关堆栈帧是持久的 - 因为等待条件可能在几天或几个月后发生.
在此期间,工作流引擎可能会停止并重新启动,这意味着必须能够以编程方式存储和重新构建工作流脚本的上下文.
事件调度引擎触发等待条件获取的事件.
工作流引擎读取序列化状态并堆栈并使用堆栈重构线程.然后在调用等待服务的点继续执行.
问题
可以使用未修改的Python解释器吗?更好的是,有人能指出一些可能涵盖此类事物的文档,或者以编程方式构造堆栈帧并在代码块中间某处开始执行的代码示例吗?
编辑:为了澄清'未修改的python解释器',我不介意使用C API(在PyThreadState中是否有足够的信息来执行此操作?)但我不想去探讨Python解释器的内部并且有建立一个修改过的.
更新:从一些初步调查,可以获得执行上下文PyThreadState_Get()
.这将返回PyThreadState
(定义在pystate.h
)中的线程状态,该状态具有对堆栈帧的引用frame
.堆栈帧保存在一个结构类型中PyFrameObject
,定义为frameobject.h
. PyFrameObject
有一个字段f_lasti
(bobince的道具),它有一个程序计数器,表示为从代码块开头的偏移量.
这最后是一个好消息,因为它意味着只要您保留实际编译的代码块,您就应该能够根据需要为尽可能多的堆栈帧重建本地,并重新启动代码.我想说这意味着它在理论上是可能的,而不必制作一个修改过的python interpereter,虽然这意味着代码仍然可能会非常繁琐并与特定版本的解释器紧密耦合.
剩下的三个问题是:
事务状态和'saga'回滚,这可能是通过一种用于构建O/R映射器的元类黑客来完成的.我确实曾经构建过一次原型,因此我对如何实现这一目标有了一个很好的了解.
强大地序列化事务状态和任意本地.这可以通过读取__locals__
(可从堆栈帧获得)并以编程方式构建对pickle的调用来完成.但是,我不知道在那里可能有什么,如果有的话.
版本控制和升级工作流程.这有点棘手,因为系统没有为工作流节点提供任何符号锚.我们所拥有的只是锚点为了做到这一点,人们必须确定所有入口点的偏移并将它们映射到新版本.可能手动完成,但我怀疑自动化很难.如果您想支持此功能,这可能是最大的障碍.
更新2: ( PyCodeObject
)code.h
有地址的列表(f_lasti
) - >在行号的映射PyCodeObject.co_lnotab
(如果错在这里指正).这可以用于促进迁移过程以将工作流更新到新版本,因为冻结的指令指针可以映射到新脚本中的适当位置,根据行号完成.仍然相当混乱,但更有希望.
更新3:我认为答案可能是Stackless Python. 您可以暂停任务并将其序列化.我还没有弄清楚这是否也适用于堆栈.
普通Python发行版中包含的expat python绑定是以编程方式构造堆栈帧.但要注意,它依赖于未记录的私有API.
http://svn.python.org/view/python/trunk/Modules/pyexpat.c?rev=64048&view=auto
你通常想要的是延续,我看到它已经是这个问题的标签.
如果您能够使用系统中的所有代码,您可能希望尝试这样做而不是处理解释器堆栈内部.我不确定这种情况会持续多久.
http://www.ps.uni-sb.de/~duchier/python/continuations.html
在实践中,我将构建您的工作流引擎,以便您的脚本将操作对象提交给管理器.管理员可以在任何时候挑选一组操作并允许它们被加载并再次开始执行(通过恢复提交动作).
换句话说:创建自己的应用程序级堆栈.