将变量注入导入命名空间
Injecting variables into an import namespace
为了说明我正在尝试做什么,假设我有一个模块 testmod
位于 ./testmod.py
中。该模块的全部内容是
x = test
我希望能够使用 importlib
中可用的任何工具或任何其他内置库将此模块成功导入 Python。
显然,从当前目录执行简单的 import testmod
语句会导致错误:NameError: name 'test' is not defined
.
我认为可能将 globals
或 locals
正确传递给 __import__
会修改脚本内的环境为 运行,但它不会:
>>> testmod = __import__('testmod', globals={'test': 'globals'}, locals={'test': 'locals'})
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "/home/jfoxrabi/testmod.py", line 1, in <module>
x = test
NameError: name 'test' is not defined
我设置了不同的 test 值,这样我就可以看到如果这有效的话 testmod.x
来自哪个字典。
由于这些似乎都不起作用,我被卡住了。甚至有可能完成我想做的事情吗?我想是的,因为这是 Python,而不是斯巴达。
我在 Anaconda 上使用 Python 3.5。我非常不想使用外部库。
更新:原因
我正在将一个模块作为配置文件导入到我的程序中。我不使用 JSON 或 INI 的原因是我希望 Python 的解释器的全部范围可用于从表达式计算配置中的值。我想在可用于执行这些计算的程序中预先计算某些值。
虽然我知道这与调用 eval
一样糟糕(我在我的程序中也这样做),但我暂时不关心安全方面的问题。但是,如果这确实是 XY 的情况,我非常愿意接受更好的解决方案。
您可以使用 Python 的内置函数来注入您自己的假内置 test
变量:
import builtins # __builtin__, no s, in Python 2
builtins.test = 5 # or whatever other placeholder value
import testmod
del builtins.test # clean up after ourselves
我想出了一个基于 this answer and the importlib docs 的解决方案。基本上,通过使用正确的 importlib
:
调用顺序,我可以在加载模块对象之前访问它
from importlib.util import spec_from_file_location, module_from_spec
from os.path import splitext, basename
def loadConfig(fileName):
test = 'This is a test'
name = splitext(basename(fileName))[0]
spec = spec_from_file_location(name, fileName)
config = module_from_spec(spec)
config.test = test
spec.loader.exec_module(config)
return config
testmod = loadConfig('./testmod.py')
这比修改 builtins
好一点,后者可能会在程序的其他部分产生意想不到的后果,并且可能还会限制我可以传递给模块的名称。
我决定将所有配置项放入加载时可访问的单个字段中,我将其命名为 config
。这允许我在 testmod
:
中执行以下操作
if 'test' in config:
x = config['test']
加载器现在看起来像这样:
from importlib.util import spec_from_file_location, module_from_spec
from os.path import splitext, basename
def loadConfig(fileName, **kwargs):
name = splitext(basename(fileName))[0]
spec = spec_from_file_location(name, fileName)
config = module_from_spec(spec)
config.config = kwargs
spec.loader.exec_module(config)
return config
testmod = loadConfig('./testmod.py', test='This is a test')
在发现自己多次使用它之后,我最终将这个功能添加到我维护的实用程序库中,haggis
. haggis.load.load_module
loads a text file as a module with injection, while haggis.load.module_as_dict
做了一个更高级的版本,将它加载为一个潜在的嵌套配置文件到 dict
.
为了说明我正在尝试做什么,假设我有一个模块 testmod
位于 ./testmod.py
中。该模块的全部内容是
x = test
我希望能够使用 importlib
中可用的任何工具或任何其他内置库将此模块成功导入 Python。
显然,从当前目录执行简单的 import testmod
语句会导致错误:NameError: name 'test' is not defined
.
我认为可能将 globals
或 locals
正确传递给 __import__
会修改脚本内的环境为 运行,但它不会:
>>> testmod = __import__('testmod', globals={'test': 'globals'}, locals={'test': 'locals'})
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "/home/jfoxrabi/testmod.py", line 1, in <module>
x = test
NameError: name 'test' is not defined
我设置了不同的 test 值,这样我就可以看到如果这有效的话 testmod.x
来自哪个字典。
由于这些似乎都不起作用,我被卡住了。甚至有可能完成我想做的事情吗?我想是的,因为这是 Python,而不是斯巴达。
我在 Anaconda 上使用 Python 3.5。我非常不想使用外部库。
更新:原因
我正在将一个模块作为配置文件导入到我的程序中。我不使用 JSON 或 INI 的原因是我希望 Python 的解释器的全部范围可用于从表达式计算配置中的值。我想在可用于执行这些计算的程序中预先计算某些值。
虽然我知道这与调用 eval
一样糟糕(我在我的程序中也这样做),但我暂时不关心安全方面的问题。但是,如果这确实是 XY 的情况,我非常愿意接受更好的解决方案。
您可以使用 Python 的内置函数来注入您自己的假内置 test
变量:
import builtins # __builtin__, no s, in Python 2
builtins.test = 5 # or whatever other placeholder value
import testmod
del builtins.test # clean up after ourselves
我想出了一个基于 this answer and the importlib docs 的解决方案。基本上,通过使用正确的 importlib
:
from importlib.util import spec_from_file_location, module_from_spec
from os.path import splitext, basename
def loadConfig(fileName):
test = 'This is a test'
name = splitext(basename(fileName))[0]
spec = spec_from_file_location(name, fileName)
config = module_from_spec(spec)
config.test = test
spec.loader.exec_module(config)
return config
testmod = loadConfig('./testmod.py')
这比修改 builtins
好一点,后者可能会在程序的其他部分产生意想不到的后果,并且可能还会限制我可以传递给模块的名称。
我决定将所有配置项放入加载时可访问的单个字段中,我将其命名为 config
。这允许我在 testmod
:
if 'test' in config:
x = config['test']
加载器现在看起来像这样:
from importlib.util import spec_from_file_location, module_from_spec
from os.path import splitext, basename
def loadConfig(fileName, **kwargs):
name = splitext(basename(fileName))[0]
spec = spec_from_file_location(name, fileName)
config = module_from_spec(spec)
config.config = kwargs
spec.loader.exec_module(config)
return config
testmod = loadConfig('./testmod.py', test='This is a test')
在发现自己多次使用它之后,我最终将这个功能添加到我维护的实用程序库中,haggis
. haggis.load.load_module
loads a text file as a module with injection, while haggis.load.module_as_dict
做了一个更高级的版本,将它加载为一个潜在的嵌套配置文件到 dict
.