将变量注入导入命名空间 [英] Injecting variables into an import namespace

查看:35
本文介绍了将变量注入导入命名空间的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

为了说明我想要做什么,假设我有一个位于 ./testmod.py 中的模块 testmod.这个模块的全部内容是

x = 测试

我希望能够使用 importlib 或任何其他内置库中的任何可用工具成功将此模块导入 Python.

显然从当前目录执行一个简单的import testmod语句会导致错误:NameError: name 'test' is not defined.

我认为可能将 globalslocals 正确地传递给 __import__ 会修改正在运行的脚本内的环境,但它不会:

<预><代码>>>>testmod = __import__('testmod', globals={'test': 'globals'}, locals={'test': 'locals'})回溯(最近一次调用最后一次):文件<stdin>",第 1 行,位于 <module>文件/home/jfoxrabi/testmod.py",第 1 行,在 <module> 中x = 测试NameError:未定义名称test"

我以不同的方式设置了 test 的值,以便我可以看到 testmod.x 来自哪个 dict testmod.x 如果这有效.

由于这些似乎都不起作用,我被卡住了.甚至有可能完成我想要做的事情吗?我猜是的,因为这是 Python,而不是 Sparta.

我在 Anaconda 上使用 Python 3.5.我非常不想使用外部库.

更新:原因

我将一个模块作为配置文件导入到我的程序中.我不使用 JSON 或 INI 的原因是我希望 Python 解释器的完整范围可用于从表达式计算配置中的值.我想在程序中预先计算某些值以进行这些计算.

虽然我知道这与调用 eval 一样糟糕(我也在我的程序中这样做),但我暂时不关心安全方面.但是,如果这确实是 XY 的情况,我非常愿意接受更好的解决方案.

解决方案

我想出了一个基于这个答案 和 importlib 文档.基本上,我可以在加载之前访问模块对象,方法是使用对 importlib 的正确调用顺序:

from importlib.util import spec_from_file_location, module_from_specfrom os.path import splitext, basename定义加载配置(文件名):test = '这是一个测试'name = splitext(basename(fileName))[0]spec = spec_from_file_location(名称,文件名)配置 = module_from_spec(规格)config.test = 测试spec.loader.exec_module(config)返回配置testmod = loadConfig('./testmod.py')

这比修改builtins要好一点,后者可能会在程序的其他部分产生意想不到的后果,并且还可能限制我可以传入模块的名称.

我决定将所有配置项放入一个在加载时可访问的字段中,我将其命名为 config.这允许我在 testmod 中执行以下操作:

if 'test' in config:x = 配置['测试']

加载器现在看起来像这样:

from importlib.util import spec_from_file_location, module_from_specfrom os.path import splitext, basenamedef loadConfig(fileName, **kwargs):name = splitext(basename(fileName))[0]spec = spec_from_file_location(名称,文件名)配置 = module_from_spec(规格)config.config = kwargsspec.loader.exec_module(config)返回配置testmod = loadConfig('./testmod.py', test='这是一个测试')

To illustrate what I am trying to do, let's say I have a module testmod that lives in ./testmod.py. The entire contents of this module is

x = test

I would like to be able to successfully import this module into Python, using any of the tools available in importlib or any other built in library.

Obviously doing a simple import testmod statement from the current directory results in an error: NameError: name 'test' is not defined.

I thought that maybe passing either globals or locals to __import__ correctly would modify the environment inside the script being run, but it does not:

>>> 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

I was setting the value of test differently so I could see which dict testmod.x came from if this worked.

Since neither of these seems to work, I am stuck. Is it even possible to accomplish what I am trying to do? I would guess that yes, since this is Python, not Sparta.

I am using Python 3.5 on Anaconda. I would very much prefer not to use external libraries.

Update: The Why

I am importing a module into my program as a configuration file. The reason that I am not using JSON or INI is that I would like to have the full scope of Python's interpreter available to compute the values in the config from expressions. I would like to have certain values that I compute before-hand in the program available to do those calculations.

While I am aware of the fact that this is about as bad as calling eval (I do that too in my program), I am not concerned with the security aspect for the time being. I am, however, quite willing to entertain better solutions should this indeed turn out to be a case of XY.

解决方案

I came up with a solution based on this answer and the importlib docs. Basically, I have access to the module object before it is loaded by using the correct sequence of calls to 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')

This is a bit better than modifying builtins, which may have unintended consequences in other parts of the program, and may also restrict the names I can pass in to the module.

I decided to put all the configuration items into a single field accessible at load time, which I named config. This allows me to do the following in testmod:

if 'test' in config:
    x = config['test']

The loader now looks like this:

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')

这篇关于将变量注入导入命名空间的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆