【问题标题】:Import a module relative to a file path导入相对于文件路径的模块
【发布时间】:2017-12-28 16:28:37
【问题描述】:

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

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

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

例子:

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

我希望source.yaml 将外部模块引用为.myModule

【问题讨论】:

  • 您能否举例说明您正在努力的路径?我不确定您所说的“YAML 文件的操作系统文件路径”是什么意思。

标签: python python-import python-3.3


【解决方案1】:

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

  • /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.pyfoo/__init__.py)。
  • 您可能需要根据具体需要对其进行更新,但这里是基础知识。

【讨论】:

  • 我希望能够将 foo 导入为 .foopackage.foo 而不是 .foopackage/foo.py。不幸的是,这是一个硬性要求,因为系统已经支持通常的点路径样式的绝对包路径。此外,这里使用的一些函数似乎在 Python 3.3 中不可用。
  • 好,我今天看看
  • 完成 - 让我知道它是否符合您的需要(至少如果您有足够的能力处理它)
猜你喜欢
  • 1970-01-01
  • 2023-04-03
  • 2010-09-21
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-12-01
相关资源
最近更新 更多