是否可以在 CPython 中以编程方式构造一个堆栈(一个或多个堆栈帧)并在任意代码点开始执行?想象以下场景:
您有一个工作流引擎,其中的工作流可以用 Python 编写脚本,其中包含一些调用工作流引擎的结构(例如分支、等待/加入)。
阻塞调用(例如等待或加入)在具有某种持久后备存储的事件调度引擎中设置侦听器条件。
您有一个工作流脚本,它调用引擎中的等待条件,等待稍后将发出信号的某些条件。这会在事件分派引擎中设置侦听器。
工作流脚本的状态,包括程序计数器(或等效状态)在内的相关堆栈帧被保留 - 因为等待条件可能会在几天或几个月后发生。
在此期间,工作流引擎可能会停止并重新启动,这意味着必须能够以编程方式存储和重建工作流脚本的上下文。
事件调度引擎触发等待条件拾取的事件。
工作流引擎读取序列化状态和堆栈,并使用堆栈重建线程。然后它在调用等待服务的地方继续执行。
问题
这可以用未经修改的 Python 解释器来完成吗?更好的是,谁能指出一些可能涵盖此类事情的文档或以编程方式构造堆栈帧并在代码块中间某处开始执行的代码示例?
编辑:为了澄清“未修改的 python 解释器”,我不介意使用 C API(PyThreadState 中是否有足够的信息来做到这一点?)但我不想去探索 Python 解释器的内部结构并拥有建立一个修改过的。
更新:通过一些初步调查,可以使用PyThreadState_Get()
. 这将返回 a 中的线程状态PyThreadState
(在 中定义pystate.h
),该状态具有对 中的堆栈帧的引用frame
。堆栈帧保存在 struct typedef'd to 中PyFrameObject
,该结构定义在frameobject.h
. PyFrameObject
有一个字段f_lasti
(对bobince的道具),它有一个程序计数器,表示为距代码块开头的偏移量。
最后一点是个好消息,因为这意味着只要您保留实际编译的代码块,您就应该能够根据需要为尽可能多的堆栈帧重建局部变量并重新启动代码。我想说这意味着理论上可以不必修改python解释器,尽管这意味着代码仍然可能会与特定版本的解释器紧密耦合。
剩下的三个问题是:
事务状态和“传奇”回滚,这可能可以通过一种用于构建 O/R 映射器的元类黑客来完成。我确实构建了一次原型,所以我对如何实现这一点有一个很好的了解。
稳健地序列化事务状态和任意局部变量。这可以通过读取
__locals__
(可从堆栈帧中获得)并以编程方式构造对 pickle 的调用来完成。但是,我不知道这里可能存在什么问题(如果有的话)。工作流的版本控制和升级。这有点棘手,因为系统没有为工作流节点提供任何符号锚。我们只有锚点 为了做到这一点,我们必须识别所有入口点的偏移量并将它们映射到新版本。手动操作可能可行,但我怀疑很难自动化。如果您想支持此功能,这可能是最大的障碍。
更新 2: PyCodeObject
( code.h
) 中有一个 addr ( f_lasti
)-> 行号映射列表PyCodeObject.co_lnotab
(如果此处错误,请纠正我)。这可能用于促进将工作流更新到新版本的迁移过程,因为冻结的指令指针可以映射到新脚本中的适当位置,根据行号完成。仍然很混乱,但更有希望。
更新 3:我认为这个问题的答案可能是Stackless Python。 您可以暂停任务并将它们序列化。我还没有弄清楚这是否也适用于堆栈。