如何让Python程序加载并使用几个不同的同名包?
How to enable the Python program to load and use a few different packages with the same name?
我正在开发一个 Python 控制硬件的应用程序。硬件由基于 FPGA 的系统组成。从控制的角度来看,它们的内部结构由自动化系统 AGWB ( http://github.com/wzab/agwb.git ) 管理。
每个系统的描述都被编译成一个 Python 模块“agwb”,其中包含一些子模块。
加载模块后,它用于生成处理与关联系统的进一步通信的对象。
当我的应用程序与几个不同的系统一起工作时,它必须加载几个不同的“agwb”模块(当然,每个模块都来自不同的目录)。
通过在 sys.path
的开头添加该目录(并在加载模块后删除它),可以轻松地从指定目录加载模块。
不幸的是,Python 只导入模块“agwb”一次。稍后尝试导入它会导致使用先前加载的一个。
使用 importlib.reload
也无济于事。
在阅读 https://docs.python.org/3/reference/import.html 之后,我找到了以下解决方案:
import sys
import importlib
def load_agwb(lddir,top):
sys.path.insert(0,lddir)
importlib.import_module('agwb')
# Get the class for the top entity
res = getattr(sys.modules['agwb'],top)()
sys.path.remove(lddir)
sys.modules.pop('agwb')
mods_to_remove = []
for mod in sys.modules.keys():
if mod.find('agwb.') == 0:
mods_to_remove.append(mod)
for mod in mods_to_remove:
sys.modules.pop(mod)
return res
at1 = load_agwb('./t1','top_v0')
at2 = load_agwb('./t2','top_v0')
print("Call at1.show")
at1.show()
print("Call at2.show")
at2.show()
目录“./t1”和“./t2”包含两个不同版本的“agwb”模块。
执行脚本后,我们得到以下输出,证明确实加载并同时使用了两个不同版本的“agwb”:
Call at1.show
object from t1, desc= That's version from directory t1
type ID= 1111
Call at2.show
object from t2, desc= That's version from directory t2
type ID= 2222
完整的最小演示程序(作为 shar 存档)可在 https://pastebin.com/ZGZj8qV1 获得(不幸的是,我无法将文件附加到我的问题)。
我的问题来了:
- 上述方法是否是 Python 中可靠、合法的解决方案?
- 也许它工作只是偶然,可能会在下一版本的 Python 中停止工作?
- 是否有其他更“官方”的方法来解决我的问题?
我根据 pavel's
评论创建了一个答案(非常感谢!)。
我已经使用临时目录和符号链接来解决又长又复杂的路径问题。以下是结果:
import sys
import os
import importlib
import tempfile
# Directory with description of the first board
dir1 = './xt1/very/long/path'
# Directory with description of the second board
dir2 = './xt2/completely/different/path'
# Name of the top entity in the design
top = 'top_v0'
# Create a temporary directory
tmpdir = tempfile.TemporaryDirectory()
# Add it to the Python path
sys.path.insert(0,tmpdir.name)
# Create symlinks to the directories with descriptions of the boards
os.symlink(os.path.abspath(dir1),tmpdir.name+"/t1")
os.symlink(os.path.abspath(dir2),tmpdir.name+"/t2")
# Import description of the first board as agwb
import t1.agwb as agwb
# use the imported agwb
at1 = getattr(agwb,top)()
# if the name of the top entity is constant, you can do it easier
bt1 = agwb.top_v0()
# Import description of the second board as agwb
import t2.agwb as agwb
# use the imported agwb
at2 = getattr(agwb,top)()
# if the name of the top entity is constant, you can do it easier
bt2 = agwb.top_v0()
# Now we can remove the temporary directory from the python path
sys.path.remove(tmpdir.name)
# We can even delete it (otherwise it will be deleted when Python session ends)
tmpdir.cleanup()
# Check if both versions are loaded and available
print("Call at1.show")
at1.show()
print("Call at2.show")
at2.show()
print("Call bt1.show")
bt1.show()
print("Call bt2.show")
bt2.show()
和以前一样,整个 minimal reproducer
可以在 https://pastebin.com/GrRUh8U9 的 shar 格式中找到。
我正在开发一个 Python 控制硬件的应用程序。硬件由基于 FPGA 的系统组成。从控制的角度来看,它们的内部结构由自动化系统 AGWB ( http://github.com/wzab/agwb.git ) 管理。
每个系统的描述都被编译成一个 Python 模块“agwb”,其中包含一些子模块。 加载模块后,它用于生成处理与关联系统的进一步通信的对象。
当我的应用程序与几个不同的系统一起工作时,它必须加载几个不同的“agwb”模块(当然,每个模块都来自不同的目录)。
通过在 sys.path
的开头添加该目录(并在加载模块后删除它),可以轻松地从指定目录加载模块。
不幸的是,Python 只导入模块“agwb”一次。稍后尝试导入它会导致使用先前加载的一个。
使用 importlib.reload
也无济于事。
在阅读 https://docs.python.org/3/reference/import.html 之后,我找到了以下解决方案:
import sys
import importlib
def load_agwb(lddir,top):
sys.path.insert(0,lddir)
importlib.import_module('agwb')
# Get the class for the top entity
res = getattr(sys.modules['agwb'],top)()
sys.path.remove(lddir)
sys.modules.pop('agwb')
mods_to_remove = []
for mod in sys.modules.keys():
if mod.find('agwb.') == 0:
mods_to_remove.append(mod)
for mod in mods_to_remove:
sys.modules.pop(mod)
return res
at1 = load_agwb('./t1','top_v0')
at2 = load_agwb('./t2','top_v0')
print("Call at1.show")
at1.show()
print("Call at2.show")
at2.show()
目录“./t1”和“./t2”包含两个不同版本的“agwb”模块。
执行脚本后,我们得到以下输出,证明确实加载并同时使用了两个不同版本的“agwb”:
Call at1.show
object from t1, desc= That's version from directory t1
type ID= 1111
Call at2.show
object from t2, desc= That's version from directory t2
type ID= 2222
完整的最小演示程序(作为 shar 存档)可在 https://pastebin.com/ZGZj8qV1 获得(不幸的是,我无法将文件附加到我的问题)。
我的问题来了:
- 上述方法是否是 Python 中可靠、合法的解决方案?
- 也许它工作只是偶然,可能会在下一版本的 Python 中停止工作?
- 是否有其他更“官方”的方法来解决我的问题?
我根据 pavel's
评论创建了一个答案(非常感谢!)。
我已经使用临时目录和符号链接来解决又长又复杂的路径问题。以下是结果:
import sys
import os
import importlib
import tempfile
# Directory with description of the first board
dir1 = './xt1/very/long/path'
# Directory with description of the second board
dir2 = './xt2/completely/different/path'
# Name of the top entity in the design
top = 'top_v0'
# Create a temporary directory
tmpdir = tempfile.TemporaryDirectory()
# Add it to the Python path
sys.path.insert(0,tmpdir.name)
# Create symlinks to the directories with descriptions of the boards
os.symlink(os.path.abspath(dir1),tmpdir.name+"/t1")
os.symlink(os.path.abspath(dir2),tmpdir.name+"/t2")
# Import description of the first board as agwb
import t1.agwb as agwb
# use the imported agwb
at1 = getattr(agwb,top)()
# if the name of the top entity is constant, you can do it easier
bt1 = agwb.top_v0()
# Import description of the second board as agwb
import t2.agwb as agwb
# use the imported agwb
at2 = getattr(agwb,top)()
# if the name of the top entity is constant, you can do it easier
bt2 = agwb.top_v0()
# Now we can remove the temporary directory from the python path
sys.path.remove(tmpdir.name)
# We can even delete it (otherwise it will be deleted when Python session ends)
tmpdir.cleanup()
# Check if both versions are loaded and available
print("Call at1.show")
at1.show()
print("Call at2.show")
at2.show()
print("Call bt1.show")
bt1.show()
print("Call bt2.show")
bt2.show()
和以前一样,整个 minimal reproducer
可以在 https://pastebin.com/GrRUh8U9 的 shar 格式中找到。