我使用Python 3来模拟另一个工具提供的Python脚本接口。(在编写此工具的脚本时,您可以运行Python脚本,它可以执行import mytool
访问脚本API。)
我已经实现了这个工具公开的接口,并且希望编写一个您将调用的加载程序,例如:
python run_mytool_script.py dump_menu_items.py
此加载程序将允许您在不实际安装工具的情况下与工具的某些部分功能进行交互。理想情况下,这应该允许我运行为该工具设计的现有脚本,而无需修改。
在run_mytool_script.py
中,我希望:
exec
脚本dump_menu_items.py
但是,我不太清楚如何创建导入钩子。如何安装钩子,以便在脚本执行mytool
时将模拟脚本接口公开为import mytool
?
注意,模拟脚本接口必须在运行时初始化,所以安装一个名为mytool
的包就不起作用了。
发布于 2017-07-06 19:38:16
有几种方法可以做到这一点。让我们从最复杂的一个开始--一个完全动态地创建mytool
。您可以使用imp
模块创建一个新模块,然后定义它的结构,最后将其添加到全局模块列表中,以便在同一个解释器堆栈中运行的所有内容都可以导入它:
run_mytool_script.py
import imp
import sys
# lets first deal with loading of our external script so we don't waste cycles if a script
# was not provided as an argument
pending_script = None # hold a script path we should execute
if __name__ == "__main__": # make sure we're running this script directly, not imported
if len(sys.argv) > 1: # we need at least one argument to tell us which script to run
pending_script = sys.argv[1] # set the script to run as the first argument
else:
print("Please provide a path for a script to run") # a script was not provided
exit(1)
# now lets create the `mytool` module dynamically
mytool = imp.new_module("mytool") # create a new module
def hello(name): # define a local function
print("Hello {}!".format(name))
mytool.__dict__["hello"] = hello # add the function to the `mytool` module
sys.modules["mytool"] = mytool # add 'mytool' to the global list so it can be imported
# now everything running in the same Python interpreter stack is able to import mytool
# lets run our `pending_script` if we're running the main script
if pending_script:
try:
execfile(pending_script) # run the script
except NameError: # Python 3.x doesn't have execfile, use the exec function instead
with open(pending_script, "r") as f:
exec(f.read()) # read and run the script
现在,您可以创建另一个脚本,该脚本信任您在通过代理脚本运行时存在一个mytool
模块,例如:
dump_menu_items.py
import mytool
mytool.hello("Dump Menu Items")
现在当你运行它时:
$ python run_mytool_script.py dump_menu_items.py
Hello Dump Menu Items!
虽然整洁,但这是一项相当繁琐的任务--我建议您在run_mytool_script.py
所在的文件夹中创建一个适当的run_mytool_script.py
模块,让run_mytool_script.py
初始化所需的所有内容,然后使用脚本的最后一部分来运行外部脚本--这要容易得多,通常也是一种更好的方法。
发布于 2017-07-06 19:37:57
按照cPython测试这里的示例,我提出了以下可能的解决方案。我会更新这篇文章,因为我意识到它的利弊。
class HookedImporter(importlib.abc.MetaPathFinder, importlib.abc.Loader):
def __init__(self, hooks=None):
self.hooks = hooks
def find_spec(self, name, path, target=None):
if name not in self.hooks:
return None
spec = importlib.util.spec_from_loader(name, self)
return spec
def create_module(self, spec):
# req'd in 3.6
logger.info('hooking import: %s', spec.name)
module = importlib.util._Module(spec.name)
mod = self.hooks[spec.name]
for attr in dir(mod):
if attr.startswith('__'):
continue
module.__dict__[attr] = getattr(mod, attr)
return module
def exec_module(self, module):
# module is already loaded (imported by line `import idb` above),
# so no need to re-execute.
#
# req'd in 3.6.
return
def install(self):
sys.meta_path.insert(0, self)
..。稍后的某个地方..。
api = mytool.from_config(...)
hooks = {
'mytool': api.mytool,
}
importer = HookedImporter(hooks=hooks)
importer.install()
with open(args.script_path, 'rb') as f:
g = {
'__name__': '__main__',
}
g.update(hooks)
exec(f.read(), g)
https://stackoverflow.com/questions/44956289
复制相似问题