在执行 Python 脚本时替换 Python 扩展模块时出现问题

发布于 2024-09-04 18:51:33 字数 449 浏览 3 评论 0原文

我正在尝试解决以下问题:假设我有一个 Python 脚本(我们称其为 Test.py),它使用 C++ 扩展模块(通过 SWIG 创建,我们称该模块为“示例”)。我在同一目录中有 Test.py、Example.py 和 _Example.so。

现在,在运行 Test.py 的过程中,我想对示例模块进行更改,重新编译(这将覆盖现有的 .so),并使用命令正常停止仍在使用旧版本的 Test.py模块(Test.py 需要进行一些清理工作,其中使用了示例模块中定义的一些内容),然后使用新版本的模块再次启动它。在我的例子中,优雅地停止 Test.py 然后重新编译模块不是一个选择。

问题是,一旦 _Example.so 被覆盖并且 Test.py 尝试访问示例模块中定义的任何内容(同时正常停止),我就会遇到分段错误。解决此问题的一种方法是通过在末尾附加版本号来显式命名示例模块,但我想知道是否有更好的解决方案(我不想导入Example_1_0)?

I'm trying to solve the following problem: Say I have a Python script (let's call it Test.py) which uses a C++ extension module (made via SWIG, let's call the module "Example"). I have Test.py, Example.py, and _Example.so in the same directory.

Now, in the middle of running Test.py, I want to make a change to my Example module, recompile (which will overwrite the existing .so), and use a command to gracefully stop Test.py which is still using the old version of the module (Test.py has some cleaning up to do, which uses some stuff which is defined in the Example module), then start it up again, using the new version of the module. Gracefully stopping Test.py and THEN recompiling the module is not an option in my case.

The problem is, as soon as _Example.so is overwritten and Test.py tries to access anything defined in the Example module (while gracefully stopping), I get a segmentation fault. One solution to this is to explicitly name the Example module by appending a version number at the end, but I was wondering if there was a better solution (I don't want to be importing Example_1_0)?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

如果没结果 2024-09-11 18:51:34

您可以在启动 Test.py 时,将 Example.* 文件复制到该实例唯一的临时文件夹(看看 tempfile.mkdtemp,它可以创建安全、唯一的文件夹),添加到sys.path,然后导入Example;并在 Test.py 关闭时在清理阶段删除该文件夹 (shutils.rmtree)。

这意味着 Test.py 的每个实例都将在其自己的示例模块副本上运行,不会干扰其他实例,并且仅在重新启动时才会更新到新的实例。

为此,您需要 Example.* 文件不要与 Test.py 位于同一文件夹中,否则导入将首先获取这些文件。只需将它们存储在子文件夹中就可以了。

You could, on starting Test.py, copy the Example.* files to a temp folder unique for that instance (take a look at tempfile.mkdtemp, it can create safe, unique folders), add that to sys.path and then import Example; and on Test.py shutdown remove that folder (shutils.rmtree) at the cleanup stage.

This would mean that each instance of Test.py would run on its own copy of the Example module, not interfering with the others, and would update to the new one only upon relaunch.

You would need the Example.* files not to be on the same folder as Test.py for this, otherwise the import would get those first. Just storing them on a subfolder should be fine.

我恋#小黄人 2024-09-11 18:51:34

您可以编译为临时名称 (Example_1_0),但一旦 Test.py 停止,重命名为 _Example.so 并然后重新启动 Test.py。


编辑:

由于您正在运行多个实例,您可能会考虑使用某种类型的 stack/generator/symlink 线程将它们链接在一起,以在 _Example.so 上执行您自己的“垃圾收集”:

您可以运行主测试器脚本,使用子进程启动 Test.py 脚本。每个 Test.py 都可以将 _ExampleXXX.so 作为命令行参数。然后,您保留每个 .so 文件的引用计数 - 当引用计数降至零时,该版本的模块将被消除,并且该进程将使用最新版本的 _Example.so 重新生成。

这可能有点棘手,但您可能只能使用

while True:
    #Do stuff
    for p in myprocesses:
        retcode = p.poll() # Set to [None][1] if the process hasn't finished
        # Do something with the return code

或一些类似的逻辑。

What you could is compile to the temporary name (Example_1_0) but as soon as the Test.py is stopped, rename to _Example.so and then restart Test.py.


Edit:

Since you're running multiple instances, you might consider using some type of stack/generator/symlink threading to link it all together to do your own "garbage collection" on _Example.so:

You could have the main tester script running, launching the Test.py scripts with subprocess. Each Test.py could take _ExampleXXX.so as a command line argument. Then you keep a reference count to each .so file - when the refcount drops to zero, that version of the module is eliminated and the process is respawned with the newest version of _Example.so.

It might be a bit tricky, but you may just be able to use

while True:
    #Do stuff
    for p in myprocesses:
        retcode = p.poll() # Set to [None][1] if the process hasn't finished
        # Do something with the return code

or some similar logic.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文