Python:强制导入更喜欢 .py 而不是 .so

jmd*_*_dk 5 python import cython python-import python-3.x

我有一种情况,即相同的 Python 模块存在于两个不同版本的同一目录中;mymodule.pymymodule.so(我通过 Cython 从第一个获得后者,但这与我的问题无关)。当我从 Python 开始时

import mymodule
Run Code Online (Sandbox Code Playgroud)

它总是选择mymodule.so. 有时我真的想导入mymodule.py。我可以暂时移动mymodule.so到另一个位置,但是如果我同时运行另一个需要导入mymodule.so.

现在的问题是,如何让import喜欢.py过的文件.so,而不是相反?

这是我对解决方案的想法:我想象使用importlib并可能编辑sys.meta_path. 具体来说,我看到sys.meta_path[2]Hold_frozen_importlib_external.PathFinder用于导入外部模块,即这用于mymodule.pymymodule.so。如果我可以将其替换PathFinder为对文件类型使用相反顺序的类似方法,我将有一个解决方案。

我正在使用 Python 3.7,如果这会影响解决方案。

编辑

请注意,简单地读入mymodule.pyexec'ing 它们的源代码行是行不通的,因为mymodule.py它本身可能会导入同样存在于 a.py.soversion 中的其他模块(我也想导入.py这些模块的版本)。

jmd*_*_dk 1

使用这些笔记我想出了这个。它不太漂亮,但似乎很有效。

import glob, importlib, sys

def hook(name):
    if name != '.':
        raise ImportError()
    modnames = set(f.rstrip('.py') for f in glob.glob('*.py'))
    return Finder(modnames)
sys.path_hooks.insert(1, hook)
sys.path.insert(0, '.')

class Finder(object):
    def __init__(self, modnames):
        self.modnames = modnames
    def find_spec(self, modname, target=None):
        if modname in self.modnames:
            origin = './' + modname + '.py'
            loader = Loader()
            return importlib.util.spec_from_loader(modname, loader, origin=origin)
        else:
            return None

class Loader(object):
    def create_module(self, target):
        return None
    def exec_module(self, module):
        with open(module.__spec__.origin, 'r', encoding='utf-8') as f:
            code = f.read()
        compile(code, module.__spec__.origin, 'exec')
        exec(code, module.__dict__)
Run Code Online (Sandbox Code Playgroud)