Python:更改导入文件类型的优先级(.py before.so)
如果我从同时包含Python:更改导入文件类型的优先级(.py before.so),python,python-3.x,import,python-import,python-importlib,Python,Python 3.x,Import,Python Import,Python Importlib,如果我从同时包含A.py和A.so的目录中导入,将导入.so文件。我想更改导入文件类型的顺序,以便.py优先于.so,尽管只是暂时的,即在代码行I和j之间。这当然可以通过一些魔法来实现 目前,我通过将.py复制到一个单独的目录中,将该目录预先添加到sys.path中,然后进行导入来解决这个问题,这太糟糕了 为什么需要? .so文件是.py文件的cython编译版本。我正在cython上进行一些自定义代码转换,为此我需要导入.py源代码,即使存在“等效的”。因此 测试设置 下面是一个简单的测试设置
A.py
和A.so
的目录中导入,将导入.so
文件。我想更改导入文件类型的顺序,以便.py
优先于.so
,尽管只是暂时的,即在代码行I
和j
之间。这当然可以通过一些魔法来实现
目前,我通过将.py
复制到一个单独的目录中,将该目录预先添加到sys.path
中,然后进行导入来解决这个问题,这太糟糕了
为什么需要?
.so
文件是.py
文件的cython编译版本。我正在cython上进行一些自定义代码转换,为此我需要导入.py
源代码,即使存在“等效的”。因此
测试设置
下面是一个简单的测试设置
#A.py
进口B
#B.py
进口C
打印('hello from B')
#C.py
通过
运行pythona.py
成功地从B.py
打印出消息。现在添加B.so
(因为.so
文件的内容是不相关的,让B.so
真正成为文本文件就可以了):
现在python A.py
失败了。虽然这是现代的处理方式,但到目前为止,我只知道如何使用不推荐使用的模块直接导入特定文件。将A.py
更新为
#A.py
进口小商品
B=导入加载\源('B','B.py')
使它再次工作。但是,引入C.so
会再次破坏它,因为对.py
而不是.so
的查找不会在导入机制中全局注册:
# C.so
this is a fake binary
注意,在本例中,我只允许编辑A.py
。我需要Python3.8的解决方案,但我怀疑3.x的任何解决方案也适用于3.8。我现在有了一个可行的解决方案。它有点粗糙,但我认为它很结实
结果是,sys.path\u importer\u cache
存储各种查找程序,这些查找程序依次存储list
加载程序,这些加载程序由import
按顺序提取。这些加载程序存储为2元组,第一个元素正好是给定加载程序处理的文件扩展名
我只需遍历加载程序的所有列表
,并将那些带有.so
扩展名的加载程序推到列表
的后面,以实现尽可能低的优先级(我可以完全删除它们,但无法导入任何.so
文件)。我跟踪对sys.path\u importer\u cache的更改,并在完成特殊导入后撤消这些更改。所有这些都被整齐地包装在一个上下文管理器中:
导入集合、上下文库、系统
@contextlib.contextmanager
def禁用_加载器(外部):
ext='.'+ext.lstrip('.'))
#将ext extension的所有装载机推到后面
编辑=集合.defaultdict(列表)
path\u importer\u cache=list(sys.path\u importer\u cache.values())
对于i,枚举中的查找器(路径\导入器\缓存):
loaders=getattr(查找器,'.\u loaders',无)
如果装载机为无:
持续
对于j,枚举中的加载器(加载器):
如果j+len(编辑[i])==len(装载机):
打破
如果加载程序[0]!=提取:
持续
#找到ext扩展的加载程序。
#向后推。
装载机.附加(装载机.pop(j))
编辑[i]。追加(j)
尝试:
#将控件返回给调用方
产量
最后:
#撤消对路径导入器缓存的更改
对于i,在edits.items()中编辑:
加载程序=路径\u导入程序\u缓存[i]。\u加载程序
对于反向(编辑)中的j:
insert(j,loaders.pop())
#演示导入失败
尝试:
导入
例外情况除外,如e:
打印(e)
#演示解决方案
使用disable_loader('.so'):
导入
#用声明证明(通缉)外部故障
进口A2
请注意,要使import A2
正常失败,您需要复制测试设置,以便您还具有A2.py
、B2.py
、C2.py
、B2.so
,它们以与原始测试文件相同的方式相互导入
在进行更改之前,只需进行一次完整的备份copy.deepcopy(sys.path\u importer\u cache)
,然后将此备份粘贴到sys
,就可以摆脱涉及编辑的有些复杂的簿记工作。在上面的有限测试中,它确实起作用,但由于导入机器的各个部分可能包含对不同嵌套对象的引用,我认为只使用变异更安全。“尽管只是暂时的,即在代码行I和j之间”:不;这难道不会让整个事情变得毫无意义,因为一旦模块被导入,它就不会被重新导入吗?@CristiFati不,这就是我需要的。我在函数中加载模块,做一些事情,然后从函数返回。
# C.so
this is a fake binary