我不知道怎么称呼它,如果有人能想到更好的标题,请告诉我,我会重命名这个问题。
这不是现实生活中的例子,但如果我能解决这种情况,我会在更大的项目中使用它。假设我必须像下面描述的那样做,我不能改变这个想法,我需要找到适合它的解决方案。目前我不允许展示原始项目的更多细节。
主意:
所以,假设我正在基于 YAPSY 插件创建类似 cron 的东西。我想将插件存储在某个目录中,有时我的守护进程会从该目录中收集所有插件,调用它们的方法并再休眠一段时间。这些插件应该能够访问一些单例,它将存储一些数据,例如插件使用的 URL 等。我还在同一进程中运行 TCP 服务器,它将修改这个单例,所以我可以在运行时自定义行为。TCP 服务器应该对单例具有读/写访问权限,并且插件应该具有只读访问权限。可能有许多单例(我的意思是,许多类表现为单例,一个类的实例不多,呃)插件可读并且可以由 TCP 服务器修改。调用插件方法时会在调用它们之前生成一些值,
问题:
如何授予对插件的只读访问权限?我想要完全隔离,所以如果singleton
有字段x
,它是对象,那么singleton.x
(like singleton.x.y
) 的字段也应该是插件的只读字段。只读意味着插件应该能够修改这些字段,但它不会对运行时的其余部分产生任何影响,因此当插件方法返回时,单例(及其字段和它们的字段等)应该与在运行插件方法之前,所以它不是真正的只读。此外,插件可能以并发方式运行,并释放 GIL 一段时间(它们可能有 IO 操作,或者只是使用 time.sleep())。
- 编辑 -
解决方案必须是多平台的,并且至少可以在 Linux、Windows 和 MacOS 上运行。
- /编辑 -
方法:
我可以尝试在单例方法中检查堆栈以查看是否有任何调用者是插件,如果是,则存储任何修改字段的原始值。然后,在插件方法调用之后,我将使用函数 restore() 将单例恢复到运行插件之前的状态。
我可以尝试在另一个进程中运行插件方法,使用多处理,将所有单例传递(很容易做到,通过使用元类来跟踪所有这些,并在新进程中重建它们,或者在某处显式存储单例)到子进程。
我可以尝试将
globals()
and包装locals()
到一些dict
s 中,这将执行与第 (1) 点类似的技巧(恢复原始值),或者将深度复制所有全局变量和局部变量,并exec
使用插件方法的代码(而不是字符串,我知道这是不安全的)。
为什么上面的方法不起作用?
(1):堆栈检查通常是错误的,我想说在这种情况下是非常错误的。此外,每次调用后恢复变量可能会非常昂贵,因为插件会进行许多修改。此外,插件方法可能以并发方式运行,因此我需要在每次发布 GIL 时恢复原始值,并在获取 GIL 时恢复插件范围的值 - 这会造成很大的伤害(你能想象实现这个吗?在这一刻,我不能,我对此并不感到抱歉)。
(2): YAPSY 插件不可挑选,因此我无法将它们发送到子进程。
(3):exec()
不会执行带有自由变量的代码,它看不到调用它的范围,所以我需要找到插件函数的所有自由变量(我会使用包装器,在运行时生成,像这样:
def no_arg_plugin_call():
plugin.method(something, from_, locals_)
并通过no_args_plugin_call.__code__
) 并将它们存储在 Wrapped 中locals()
。此外,整个环境的深拷贝将与(1)中一样昂贵。
PS。“字段”是指“属性”,因为我(不幸的是)在 Java 等方面长大。
聚苯乙烯。如果您听说过任何类似于 YAPSY 的插件系统(它必须具有所有功能并且是轻量级的)并且会生成可挑选的实例,那对我来说就足够了;)