代码之家  ›  专栏  ›  技术社区  ›  Thom Smith

相对于文件路径导入模块

  •  0
  • Thom Smith  · 技术社区  · 7 年前

    我正在开发一个宏引擎来转换YAML文件。这些YAML文件包含我正在使用的Python模块的路径 importlib . 我希望最终用户能够指定以开头的相对路径 . ,以及要相对于YAML文件解析的这些路径。(这样,用户可以轻松地将YAML文件和相关模块发送到目录或zip文件中。)

    我宁愿不修改 sys.path 如果可能,但这不是一个硬性要求(我可以使用上下文管理器来修补/取消修补)。

    我知道如何使用 importlib.import_module(name, package) 要导入 name 相对于虚线路径 package . 但在这里,我有一个指向YAML文件的OS文件路径,它不是Python模块。这能做到吗?

    例子:

    • 我的脚本位于 ~/bin/macroengine.py
    • YAML文件位于 ~/example/source.yaml
    • 外部模块位于 ~/example/myModule.py

    我想要 source.yaml 将外部模块引用为 .myModule .

    1 回复  |  直到 7 年前
        1
  •  2
  •   Arount    7 年前

    以下是我用来测试的文件系统路径:

    • /tmp/stack/ymport/content.yaml :

      afile: .foo.bar.baz.afile
      amodule: .egg.bacon
      
    • /tmp/stack/ymport/foo/bar/baz/afile.py :

      variable = 'A FILE'
      
    • /tmp/stack/ymport/egg/bacon/__init__.py :

      variable = 'A MODULE'
      

    Python脚本:

    import os
    import yaml
    from importlib.machinery import SourceFileLoader
    
    
    def ymport(module_name, base_dir=None):
        '''
        Import module from relative path.
            module_name    Name / path-string of the module (foo.bar.baz)
            base_dir       Base directory to find module (default './')
    
        If module can not be found as file (foo/bar/baz.py) it will try to import it
        as module (foo/bar/baz/__init__.py).
    
        Returns module instance
        '''
    
        if base_dir is None:
            base_dir = './'
    
        base_path = relative_to_absolute(module_to_os_path(module_name), base_dir)
        file_path = '{}.py'.format(base_path)
    
    
        try:
            return SourceFileLoader(module_name, file_path).load_module()
        # If more obvious path didn't works, try to import path as module (__init__.py)
        except FileNotFoundError:
            module_path = '{}/__init__.py'.format(base_path)
            try:
                return SourceFileLoader(module_name, module_path).load_module()
            except FileNotFoundError:
                # Make obvious we tried 2 differents paths
                raise FileNotFoundError("No such files or directories '{}', '{}'".format(
                    file_path, module_path
                ))
    
    
    def module_to_os_path(module_name):
        '''
        Parse module path (foo.bar.baz) into filesystem path (foo/bar/baz)
        '''
        if module_name.startswith('.'):
            module_name = module_name[1:]
    
        return module_name.replace('.', os.sep)
    
    
    def relative_to_absolute(path, base):
        return os.path.join(base, path)
    
    
    # Let's try it
    with open('/tmp/stack/ymport/content.yaml') as fh:
        base_path = os.path.dirname(fh.name)
        data = yaml.load(fh.read())
    
        for name, path in data.items():
            module = ymport(path, base_path)
            print(module.variable)
    

    输出:

    A FILE
    A MODULE
    

    Import from absolute filesystem path 作为参考。


    一些注意事项:

    • 为Python3.3完成。x个
    • 它允许您同时加载模块和文件( foo.py vs公司 foo/__init__.py ).
    • 您可能需要根据具体需要对其进行更新,但基本信息就在这里。