如何使用 importlib 加载带有自定义全局变量的 Python 模块?
How do I load a Python module with custom globals using importlib?
我正在尝试在 Python 中组装一个小型构建系统,为我的 C++ 项目生成 Ninja 文件。它的行为应该类似于CMake;也就是说,bldfile.py
脚本定义规则和目标,并可选择通过调用 bld.subdir()
递归到一个或多个目录中。每个 bldfile.py
脚本都有一个对应的 bld.File
对象。当 bldfile.py
脚本正在执行时,bld
全局应该被预定义为该文件的 bld.File
实例, 但仅在该模块的范围内 .
此外,我想以某种方式利用 Python 的字节码缓存,但是 .pyc
文件应该存储在构建输出目录中而不是 __pycache__
bldfile.py
脚本旁边的目录。
我知道我应该使用 importlib
(需要 Python 3.4+ 即可),但我不确定如何:
- 使用自定义全局变量加载并执行模块文件。
- 重新使用字节码缓存基础结构。
如有任何帮助,我们将不胜感激!
我研究了 importlib
的源代码,因为我不打算制作可重用的 Loader
,所以看起来有很多不必要的复杂性。所以我只是决定用 types.ModuleType
创建一个模块,将 bld
添加到模块的 __dict__
,用 compile
编译和缓存字节码,并用 [=16] 执行模块=].在低层次上,这基本上就是 importutil
所做的一切。
在执行之前将全局变量注入模块是一个有趣的想法。但是,我认为它与Zen of Python的几个点相冲突。特别是,它需要在模块中编写依赖于未明确定义、导入或以其他方式获取的全局值的代码 - 除非您知道调用模块所需的特定过程。
对于特定的用例,这可能是一个显而易见或巧妙的解决方案,但它不是很直观。通常,(Python) 代码应该是显式的。因此,我会寻求一种将参数显式传递给执行代码的解决方案。听起来像函数?右:
bldfile.py
def exec(bld):
print('Working with bld:', bld)
# ...
调用模块:
# set bld
# Option 1: static import
import bldfile
bldfile.exec(bld)
# Option 2: dynamic import if bldfile.py is located dynamically
import importlib.util
spec = importlib.util.spec_from_file_location("unique_name", "subdir/subsubdir/bldfile.py")
module = importlib.util.module_from_spec(spec)
spec.loader.exec_module(module)
module.exec(bld)
这样导入模块时不会执行任何代码(除了函数定义)。 exec
函数需要显式调用,当查看 exec
中的代码时,很清楚 bld
来自哪里。
可以通过使用加载其全局变量的虚拟模块来克服可能性的缺乏。
#service.py
module = importlib.import_module('userset')
module.user = user
module = importlib.import_module('config')
#config.py
from userset import *
#now you can use user from service.py
我正在尝试在 Python 中组装一个小型构建系统,为我的 C++ 项目生成 Ninja 文件。它的行为应该类似于CMake;也就是说,bldfile.py
脚本定义规则和目标,并可选择通过调用 bld.subdir()
递归到一个或多个目录中。每个 bldfile.py
脚本都有一个对应的 bld.File
对象。当 bldfile.py
脚本正在执行时,bld
全局应该被预定义为该文件的 bld.File
实例, 但仅在该模块的范围内 .
此外,我想以某种方式利用 Python 的字节码缓存,但是 .pyc
文件应该存储在构建输出目录中而不是 __pycache__
bldfile.py
脚本旁边的目录。
我知道我应该使用 importlib
(需要 Python 3.4+ 即可),但我不确定如何:
- 使用自定义全局变量加载并执行模块文件。
- 重新使用字节码缓存基础结构。
如有任何帮助,我们将不胜感激!
我研究了 importlib
的源代码,因为我不打算制作可重用的 Loader
,所以看起来有很多不必要的复杂性。所以我只是决定用 types.ModuleType
创建一个模块,将 bld
添加到模块的 __dict__
,用 compile
编译和缓存字节码,并用 [=16] 执行模块=].在低层次上,这基本上就是 importutil
所做的一切。
在执行之前将全局变量注入模块是一个有趣的想法。但是,我认为它与Zen of Python的几个点相冲突。特别是,它需要在模块中编写依赖于未明确定义、导入或以其他方式获取的全局值的代码 - 除非您知道调用模块所需的特定过程。
对于特定的用例,这可能是一个显而易见或巧妙的解决方案,但它不是很直观。通常,(Python) 代码应该是显式的。因此,我会寻求一种将参数显式传递给执行代码的解决方案。听起来像函数?右:
bldfile.py
def exec(bld):
print('Working with bld:', bld)
# ...
调用模块:
# set bld
# Option 1: static import
import bldfile
bldfile.exec(bld)
# Option 2: dynamic import if bldfile.py is located dynamically
import importlib.util
spec = importlib.util.spec_from_file_location("unique_name", "subdir/subsubdir/bldfile.py")
module = importlib.util.module_from_spec(spec)
spec.loader.exec_module(module)
module.exec(bld)
这样导入模块时不会执行任何代码(除了函数定义)。 exec
函数需要显式调用,当查看 exec
中的代码时,很清楚 bld
来自哪里。
可以通过使用加载其全局变量的虚拟模块来克服可能性的缺乏。
#service.py
module = importlib.import_module('userset')
module.user = user
module = importlib.import_module('config')
#config.py
from userset import *
#now you can use user from service.py