相对于文件路径导入模块
Import a module relative to a file path
我正在开发一个转换 YAML 文件的宏引擎。这些 YAML 文件包含我使用 importlib
导入的 Python 模块的路径。我希望最终用户能够指定以 .
开头的相对路径,并希望这些路径相对于 YAML 文件进行解析。 (这样,用户可以轻松地在目录或 zip 文件中发送 YAML 文件和相关模块。)
如果可能,我不想修改 sys.path
,但这不是硬性要求(我可以使用上下文管理器来 patch/unpatch 它)。
我知道如何使用 importlib.import_module(name, package)
相对于虚线路径 package
导入 name
。但是在这里,我有一个指向 YAML 文件的 OS 文件路径,它不是 Python 模块。这能做到吗?
示例:
- 我的脚本在
~/bin/macroengine.py
- YAML 文件位于
~/example/source.yaml
- 外部模块在
~/example/myModule.py
我希望 source.yaml
将外部模块引用为 .myModule
。
这是我用来测试的文件系统路径:
/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
)。
- 您可能需要根据具体需要进行更新,但基本知识都在这里。
我正在开发一个转换 YAML 文件的宏引擎。这些 YAML 文件包含我使用 importlib
导入的 Python 模块的路径。我希望最终用户能够指定以 .
开头的相对路径,并希望这些路径相对于 YAML 文件进行解析。 (这样,用户可以轻松地在目录或 zip 文件中发送 YAML 文件和相关模块。)
如果可能,我不想修改 sys.path
,但这不是硬性要求(我可以使用上下文管理器来 patch/unpatch 它)。
我知道如何使用 importlib.import_module(name, package)
相对于虚线路径 package
导入 name
。但是在这里,我有一个指向 YAML 文件的 OS 文件路径,它不是 Python 模块。这能做到吗?
示例:
- 我的脚本在
~/bin/macroengine.py
- YAML 文件位于
~/example/source.yaml
- 外部模块在
~/example/myModule.py
我希望 source.yaml
将外部模块引用为 .myModule
。
这是我用来测试的文件系统路径:
/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
vsfoo/__init__.py
)。 - 您可能需要根据具体需要进行更新,但基本知识都在这里。