我有一些字符串形式的代码,并希望在不写入磁盘的情况下制作一个模块。
当我尝试使用imp和StringIO对象来执行此操作时,我得到:
>>> imp.load_source('my_module', '', StringIO('print "hello world"'))
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: load_source() argument 3 must be file, not instance
>>> imp.load_module('my_module', StringIO('print "hello world"'), '', ('', '', 0))
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ValueError: load_module arg#2 should be a file or None
如何在没有实际文件的情况下创建模块?或者,如何在不写入磁盘的情况下将StringIO包装在文件中?
更新
注意:此问题在python3中也是一个问题。
我尝试加载的代码只是部分信任。我已经完成了它,并确定它不会导入任何东西或做任何我不喜欢的事情,但我不相信它足以运行它,当我有可以修改的局部变量,并且我不相信自己的代码可以避免我试图导入的代码。
我创建了一个只包含以下内容的空模块:
def load(code):
# Delete all local variables
globals()['code'] = code
del locals()['code']
# Run the code
exec(globals()['code'])
# Delete any global variables we've added
del globals()['load']
del globals()['code']
# Copy k so we can use it
if 'k' in locals():
globals()['k'] = locals()['k']
del locals()['k']
# Copy the rest of the variables
for k in locals().keys():
globals()[k] = locals()[k]
然后,您可以导入mymodule
并致电mymodule.load(code)
。这对我有用,因为我确保我加载的代码不使用globals
。此外,global
关键字只是一个解析器指令,不能引用exec之外的任何内容。
对于import
模块而言,如果没有写入磁盘,这真的太过分了,但如果你想做到这一点,我相信这是最好的方法。
答案 0 :(得分:52)
以下是如何将字符串作为模块导入( Python 2.x ):
import sys,imp
my_code = 'a = 5'
mymodule = imp.new_module('mymodule')
exec my_code in mymodule.__dict__
在 Python 3 中,exec是一个函数,所以这应该有效:
import sys,imp
my_code = 'a = 5'
mymodule = imp.new_module('mymodule')
exec(my_code, mymodule.__dict__)
现在访问模块属性(和函数,类等):
print(mymodule.a)
>>> 5
要忽略下次导入的任何尝试,请将模块添加到sys
:
sys.modules['mymodule'] = mymodule
答案 1 :(得分:4)
如果模块的代码位于字符串中,您可以放弃StringIO
并直接与exec
一起使用,如下图所示,文件名为dynmodule.py
。
适用于Python 2&amp; 3。
from __future__ import print_function
class _DynamicModule(object):
def load(self, code):
execdict = {'__builtins__': None} # optional, to increase safety
exec(code, execdict)
keys = execdict.get(
'__all__', # use __all__ attribute if defined
# else all non-private attributes
(key for key in execdict if not key.startswith('_')))
for key in keys:
setattr(self, key, execdict[key])
# replace this module object in sys.modules with empty _DynamicModule instance
# see Stack Overflow question:
# https://stackoverflow.com/questions/5365562/why-is-the-value-of-name-changing-after-assignment-to-sys-modules-name
import sys as _sys
_ref, _sys.modules[__name__] = _sys.modules[__name__], _DynamicModule()
if __name__ == '__main__':
import dynmodule # name of this module
import textwrap # for more readable code formatting in sample string
# string to be loaded can come from anywhere or be generated on-the-fly
module_code = textwrap.dedent("""\
foo, bar, baz = 5, 8, 2
def func():
return foo*bar + baz
__all__ = 'foo', 'bar', 'func' # 'baz' not included
""")
dynmodule.load(module_code) # defines module's contents
print('dynmodule.foo:', dynmodule.foo)
try:
print('dynmodule.baz:', dynmodule.baz)
except AttributeError:
print('no dynmodule.baz attribute was defined')
else:
print('Error: there should be no dynmodule.baz module attribute')
print('dynmodule.func() returned:', dynmodule.func())
输出:
dynmodule.foo: 5
no dynmodule.baz attribute was defined
dynmodule.func() returned: 42
在'__builtins__'
字典中将None
条目设置为execdict
会阻止代码直接执行任何内置函数,例如__import__
,因此使运行更安全。您可以通过有选择地向其添加您感觉良好和/或必需的内容来缓解该限制。
还可以添加您希望为代码提供的预定义实用程序和属性,从而为其运行创建自定义执行上下文。这类内容对于实现“插件”非常有用“或其他用户可扩展的架构。
答案 2 :(得分:3)
您可以简单地创建一个Module对象并将其填充到sys.modules中并将代码放入其中。
类似的东西:
import sys
from types import ModuleType
mod = ModuleType('mymodule')
sys.modules['mymodule'] = mod
exec(mycode, mod.__dict__)
答案 3 :(得分:1)
imp.new_module
但是使用short solution来自schlenk的types.ModuleType仍在python 3.7中工作
imp.new_module替换为importlib.util.module_from_spec
importlib.util.module_from_spec 优先于使用types.ModuleType来创建一个新模块 spec用于在模块上设置许多导入控制的属性 尽可能。
importlib.util.spec_from_loader 使用可用的加载程序API(例如InspectLoader.is_package())来 填写规范中所有丢失的信息。
但无论是长版还是短版,都只能设置:__builtins__, __doc__, __loader__, __name__, __package__, __spec__
import sys, importlib
my_name = 'my_module'
my_spec = importlib.util.spec_from_loader(my_name, loader=None)
my_module = importlib.util.module_from_spec(my_spec)
my_code = '''
def f():
print('f says hello')
'''
exec(my_code, my_module.__dict__)
sys.modules['my_module'] = my_module
my_module.f()
答案 4 :(得分:0)
documentation for imp.load_source
说(我的重点):
file参数是源文件,从头开始打开以作为文本阅读。 它当前必须是真实的文件对象,而不是用户定义的模拟文件的类。
...所以你可能会对这种方法不满意,我很害怕。
在这种情况下,或许eval
对你来说已经足够了吗?
这听起来似乎是一个相当令人惊讶的要求 - 如果你在问题中添加更多关于你真正试图解决的问题,它可能会有所帮助。
答案 5 :(得分:0)