PyGTK的“gdk”模块问题

1 投票
1 回答
1419 浏览
提问于 2025-04-18 00:16

Python的gtk模块在导入一个叫gdk的模块时,做了一些奇怪的操作。实际上,哪里都找不到一个真正的gdk模块。gtk/__init__.py模块的开头是这样的:

# load the required modules:
import gobject as _gobject

ver = getattr(_gobject, 'pygobject_version', ())
if ver < (2, 11, 1):
    raise ImportError(
        "PyGTK requires PyGObject 2.11.1 or higher, but %s was found" % (ver,))

if 'gtk._gtk' in sys.modules:
    _gtk = sys.modules['gtk._gtk']
else:
    from gtk import _gtk

import gdk

显然,gdk就像雅典娜一样,从gtk模块里包含的_gtk.so共享库中突然出现。这样做没问题,但我现在遇到的问题是,使用weechat时,第一次加载一个使用gtk的Python脚本是成功的,但之后就会出现像这样的错误信息:

python: loading script "/home/username/.weechat/python/lnotify.py"
python: stdout/stderr: Traceback (most recent call last):
python: stdout/stderr:   File "/home/username/.weechat/python/lnotify.py", line 20, in <module>
python: stdout/stderr:     import weechat, string, pynotify
python: stdout/stderr:   File "/usr/lib64/python2.7/site-packages/gtk-2.0/pynotify/__init__.py", line 19, in <module>
python: stdout/stderr:     import gtk
python: stdout/stderr:   File "/usr/lib64/python2.7/site-packages/gtk-2.0/gtk/__init__.py", line 42, in <module>
python: stdout/stderr:     import gdk
python: stdout/stderr: ImportError: No module named gdk
python: unable to parse file "/home/username/.weechat/python/lnotify.py"

所以,似乎发生了一些事情,让gtk模块突然找不到共享库里的gdk模块。我甚至不知道该从哪里开始排查这个问题,因为我不太确定是什么原因导致了这种符号解析的问题。

你之前见过这样的情况吗?

1 个回答

1

这个问题的根源在于,每个C扩展模块在一个进程中只会加载一次,而WeeChat在这个进程里使用了多个解释器实例,每个脚本一个。这种情况和扩展模块的工作方式不太兼容,原因有很多。

具体来说,模块的初始化只会在第一次导入扩展模块gtk._gtk时运行,正如你所提到的,这时候gtk.gdk才会被创建。从那以后,每次尝试import gtk时,都会发现_gtk已经被加载了,因此在新的解释器上下文中只会复制一份它的模块字典。这意味着你可以访问gtk._gtk,但扩展的初始化函数不会再被调用,这就是为什么后续的解释器会缺少gtk.gdk的原因。

你可以通过不使用WeeChat或子解释器,简单地导入gtk,然后从sys.modules中删除引用,再次导入gtk来重现这种行为:

import sys
import gtk
for mod in sys.modules.keys():
    if mod.startswith('gtk'):
        sys.modules.pop(mod)
import gtk
# throws "ImportError: No module named gdk"

解决这个问题并不简单。你可以尝试:

  1. 修复PyGTK,使其使用一种与在不同解释器上下文中(重新)加载兼容的初始化程序
  2. 在WeeChat中为每个执行的脚本使用fork(),以提供真正的隔离(但在Windows上该怎么办呢?),这样可以避免这个问题,但会增加额外的开销。

撰写回答