为 PyQt4.QtCore 导入挂钩
Import hooks for PyQt4.QtCore
我正在尝试设置一些 import hooks through sys.meta_path
, in a somewhat similar approach to this SO question。为此,我需要定义两个函数 find_module
和 load_module
,如上面 link 中所述。这是我的 load_module
函数,
import imp
def load_module(name, path):
fp, pathname, description = imp.find_module(name, path)
try:
module = imp.load_module(name, fp, pathname, description)
finally:
if fp:
fp.close()
return module
对于大多数模块都可以正常工作,但在使用 Python 2.7:
时对于 PyQt4.QtCore
会失败
name = "QtCore"
path = ['/usr/lib64/python2.7/site-packages/PyQt4']
mod = load_module(name, path)
哪个returns,
Traceback (most recent call last):
File "test.py", line 19, in <module>
mod = load_module(name, path)
File "test.py", line 13, in load_module
module = imp.load_module(name, fp, pathname, description)
SystemError: dynamic module not initialized properly
相同的代码在 Python 3.4 上运行良好(尽管理想情况下应该使用 imp
is getting deprecated and importlib
)。
我想这与SIP 动态模块初始化有关。 Python 2.7 还有什么我应该尝试的吗?
注意:这适用于 PyQt4
和 PyQt5
。
编辑:这可能与this question确实有关,
cd /usr/lib64/python2.7/site-packages/PyQt4
python2 -c 'import QtCore'
失败并出现同样的错误。我仍然不确定有什么解决方法...
Edit2:根据 @Nikita 对具体用例示例的请求,我想做的是重定向导入,所以当一个人做 import A
时,会发生 import B
。确实可以认为,为此,在 find_spec/find_module
中进行模块重命名然后使用默认值 load_module
就足够了。但是,不清楚在 Python 2 中哪里可以找到默认的 load_module
实现。我发现的最接近类似实现的是 future.standard_library.RenameImport
。看起来没有 importlib
从 Python 3 到 2.
的完整实现的反向移植
可在此 gist.
中找到重现此问题的导入挂钩的最小工作示例
UPD:这部分在答案更新后并不真正相关,因此请参阅下面的 UPD。
为什么不直接使用 importlib.import_module
,它在 Python 2.7 和 Python 3 中都可用:
#test.py
import importlib
mod = importlib.import_module('PyQt4.QtCore')
print(mod.__file__)
在 Ubuntu 14.04:
$ python2 test.py
/usr/lib/python2.7/dist-packages/PyQt4/QtCore.so
因为它是一个动态模块,正如错误中所说(实际文件是QtCore.so
),也可以看看imp.load_dynamic
。
另一个解决方案可能是强制执行模块初始化代码,但在我看来这太麻烦了,所以为什么不直接使用 importlib
.
UPD:pkgutil
中有些东西可能会有所帮助。我在评论中所说的,尝试像这样修改您的查找器:
import pkgutil
class RenameImportFinder(object):
def find_module(self, fullname, path=None):
""" This is the finder function that renames all imports like
PyQt4.module or PySide.module into PyQt4.module """
for backend_name in valid_backends:
if fullname.startswith(backend_name):
# just rename the import (That's what i thought about)
name_new = fullname.replace(backend_name, redirect_to_backend)
print('Renaming import:', fullname, '->', name_new, )
print(' Path:', path)
# (And here, don't create a custom loader, get one from the
# system, either by using 'pkgutil.get_loader' as suggested
# in PEP302, or instantiate 'pkgutil.ImpLoader').
return pkgutil.get_loader(name_new)
#(Original return statement, probably 'pkgutil.ImpLoader'
#instantiation should be inside 'RenameImportLoader' after
#'find_module()' call.)
#return RenameImportLoader(name_orig=fullname, path=path,
# name_new=name_new)
return None
目前无法测试上面的代码,请自行尝试。
P.S。请注意,在 Python 3 中为您工作的 imp.load_module()
是 deprecated since Python 3.3.
另一个解决方案是根本不使用钩子,而是包装 __import__
:
print(__import__)
valid_backends = ['shelve']
redirect_to_backend = 'pickle'
# Using closure with parameters
def import_wrapper(valid_backends, redirect_to_backend):
def wrapper(import_orig):
def import_mod(*args, **kwargs):
fullname = args[0]
for backend_name in valid_backends:
if fullname.startswith(backend_name):
fullname = fullname.replace(backend_name, redirect_to_backend)
args = (fullname,) + args[1:]
return import_orig(*args, **kwargs)
return import_mod
return wrapper
# Here it's important to assign to __import__ in __builtin__ and not
# local __import__, or it won't affect the import statement.
import __builtin__
__builtin__.__import__ = import_wrapper(valid_backends,
redirect_to_backend)(__builtin__.__import__)
print(__import__)
import shutil
import shelve
import re
import glob
print shutil.__file__
print shelve.__file__
print re.__file__
print glob.__file__
输出:
<built-in function __import__>
<function import_mod at 0x02BBCAF0>
C:\Python27\lib\shutil.pyc
C:\Python27\lib\pickle.pyc
C:\Python27\lib\re.pyc
C:\Python27\lib\glob.pyc
shelve
重命名为pickle
,pickle
默认导入机器,变量名shelve
.
当查找属于包的一部分的模块时,如 PyQt4.QtCore
,您必须递归地查找名称中没有 .
的每个部分。 imp.load_module
要求它的 name
参数是完整的模块名称,.
分隔包和模块名称。
因为 QtCore
是软件包的一部分,所以您应该改为 python -c 'import PyQt4.QtCore'
。这是加载模块的代码。
import imp
def load_module(name):
def _load_module(name, pkg=None, path=None):
rest = None
if '.' in name:
name, rest = name.split('.', 1)
find = imp.find_module(name, path)
if pkg is not None:
name = '{}.{}'.format(pkg, name)
try:
mod = imp.load_module(name, *find)
finally:
if find[0]:
find[0].close()
if rest is None:
return mod
return _load_module(rest, name, mod.__path__)
return _load_module(name)
测试;
print(load_module('PyQt4.QtCore').qVersion())
4.8.6
我正在尝试设置一些 import hooks through sys.meta_path
, in a somewhat similar approach to this SO question。为此,我需要定义两个函数 find_module
和 load_module
,如上面 link 中所述。这是我的 load_module
函数,
import imp
def load_module(name, path):
fp, pathname, description = imp.find_module(name, path)
try:
module = imp.load_module(name, fp, pathname, description)
finally:
if fp:
fp.close()
return module
对于大多数模块都可以正常工作,但在使用 Python 2.7:
时对于PyQt4.QtCore
会失败
name = "QtCore"
path = ['/usr/lib64/python2.7/site-packages/PyQt4']
mod = load_module(name, path)
哪个returns,
Traceback (most recent call last):
File "test.py", line 19, in <module>
mod = load_module(name, path)
File "test.py", line 13, in load_module
module = imp.load_module(name, fp, pathname, description)
SystemError: dynamic module not initialized properly
相同的代码在 Python 3.4 上运行良好(尽管理想情况下应该使用 imp
is getting deprecated and importlib
)。
我想这与SIP 动态模块初始化有关。 Python 2.7 还有什么我应该尝试的吗?
注意:这适用于 PyQt4
和 PyQt5
。
编辑:这可能与this question确实有关,
cd /usr/lib64/python2.7/site-packages/PyQt4
python2 -c 'import QtCore'
失败并出现同样的错误。我仍然不确定有什么解决方法...
Edit2:根据 @Nikita 对具体用例示例的请求,我想做的是重定向导入,所以当一个人做 import A
时,会发生 import B
。确实可以认为,为此,在 find_spec/find_module
中进行模块重命名然后使用默认值 load_module
就足够了。但是,不清楚在 Python 2 中哪里可以找到默认的 load_module
实现。我发现的最接近类似实现的是 future.standard_library.RenameImport
。看起来没有 importlib
从 Python 3 到 2.
可在此 gist.
中找到重现此问题的导入挂钩的最小工作示例UPD:这部分在答案更新后并不真正相关,因此请参阅下面的 UPD。
为什么不直接使用 importlib.import_module
,它在 Python 2.7 和 Python 3 中都可用:
#test.py
import importlib
mod = importlib.import_module('PyQt4.QtCore')
print(mod.__file__)
在 Ubuntu 14.04:
$ python2 test.py
/usr/lib/python2.7/dist-packages/PyQt4/QtCore.so
因为它是一个动态模块,正如错误中所说(实际文件是QtCore.so
),也可以看看imp.load_dynamic
。
另一个解决方案可能是强制执行模块初始化代码,但在我看来这太麻烦了,所以为什么不直接使用 importlib
.
UPD:pkgutil
中有些东西可能会有所帮助。我在评论中所说的,尝试像这样修改您的查找器:
import pkgutil
class RenameImportFinder(object):
def find_module(self, fullname, path=None):
""" This is the finder function that renames all imports like
PyQt4.module or PySide.module into PyQt4.module """
for backend_name in valid_backends:
if fullname.startswith(backend_name):
# just rename the import (That's what i thought about)
name_new = fullname.replace(backend_name, redirect_to_backend)
print('Renaming import:', fullname, '->', name_new, )
print(' Path:', path)
# (And here, don't create a custom loader, get one from the
# system, either by using 'pkgutil.get_loader' as suggested
# in PEP302, or instantiate 'pkgutil.ImpLoader').
return pkgutil.get_loader(name_new)
#(Original return statement, probably 'pkgutil.ImpLoader'
#instantiation should be inside 'RenameImportLoader' after
#'find_module()' call.)
#return RenameImportLoader(name_orig=fullname, path=path,
# name_new=name_new)
return None
目前无法测试上面的代码,请自行尝试。
P.S。请注意,在 Python 3 中为您工作的 imp.load_module()
是 deprecated since Python 3.3.
另一个解决方案是根本不使用钩子,而是包装 __import__
:
print(__import__)
valid_backends = ['shelve']
redirect_to_backend = 'pickle'
# Using closure with parameters
def import_wrapper(valid_backends, redirect_to_backend):
def wrapper(import_orig):
def import_mod(*args, **kwargs):
fullname = args[0]
for backend_name in valid_backends:
if fullname.startswith(backend_name):
fullname = fullname.replace(backend_name, redirect_to_backend)
args = (fullname,) + args[1:]
return import_orig(*args, **kwargs)
return import_mod
return wrapper
# Here it's important to assign to __import__ in __builtin__ and not
# local __import__, or it won't affect the import statement.
import __builtin__
__builtin__.__import__ = import_wrapper(valid_backends,
redirect_to_backend)(__builtin__.__import__)
print(__import__)
import shutil
import shelve
import re
import glob
print shutil.__file__
print shelve.__file__
print re.__file__
print glob.__file__
输出:
<built-in function __import__>
<function import_mod at 0x02BBCAF0>
C:\Python27\lib\shutil.pyc
C:\Python27\lib\pickle.pyc
C:\Python27\lib\re.pyc
C:\Python27\lib\glob.pyc
shelve
重命名为pickle
,pickle
默认导入机器,变量名shelve
.
当查找属于包的一部分的模块时,如 PyQt4.QtCore
,您必须递归地查找名称中没有 .
的每个部分。 imp.load_module
要求它的 name
参数是完整的模块名称,.
分隔包和模块名称。
因为 QtCore
是软件包的一部分,所以您应该改为 python -c 'import PyQt4.QtCore'
。这是加载模块的代码。
import imp
def load_module(name):
def _load_module(name, pkg=None, path=None):
rest = None
if '.' in name:
name, rest = name.split('.', 1)
find = imp.find_module(name, path)
if pkg is not None:
name = '{}.{}'.format(pkg, name)
try:
mod = imp.load_module(name, *find)
finally:
if find[0]:
find[0].close()
if rest is None:
return mod
return _load_module(rest, name, mod.__path__)
return _load_module(name)
测试;
print(load_module('PyQt4.QtCore').qVersion())
4.8.6