在从importlib导入的模块上使用“经典”导入的ModuleNotFoundError

时间:2019-08-23 10:55:39

标签: python import module python-importlib

TL,DR

我有:

mod = import_module('path.module')

之后,我想/需要做的事情:

from mod.script import func

但是那给了我:

ModuleNotFoundError: No module named 'mod.script'

警告:使用“ mod.script.func()”或类似的名称无法满足我的需要(项目约束),我搜索如何使用“ from”这样的语法[module_imported_from_importlib]导入XXX“

简介:

我需要将现有代码拆分到多个版本的不同文件夹中。目标是在应用程序中具有不同的部分,每个部分使用另一部分的指定版本。

示例树:

ref.py 
block1 
__init__.py
- v1
| __init__.py
|- __init__.py
|- script1.py
 block2
 __init__.py
- v1
| __init__.py
|-- __init__.py
|-- script2.py
- v2
|- __init__.py
|-- __init__.py
|-- script2.py

有了这个,我需要运行: /block1/v1/script1.py中的/block2/v1/script2.py函数

目标

我想做的是使用相同的语法指定script1应该在何处使用“ script2”(在v1或v2中,在block2中),但只指定不带版本的块(将会改变):

旧script1.py:

from script2 import <func>

新script1.py

from block2.script2 import <func>

代码

我已经尝试了很多事情,但都没有成功,现在我在这里似乎很接近解决方案,但是我找不到它(也许不可能吗?):

在block1 / v1 / init .py中:

from importlib import import_module, reload

MODULE = import_module('block2.v1') # With 'block2.v1' defined as a variable somewhere else (eg in ref.py)
reload(MODULE)

在block1 / v1 / script1.py中:

from block1.v1 import MODULE as block2
print(block2)
print(f'block2 : {dir(block2)}')
from block2.script2 import test

在block2 / v1 / init .py中:

from block2.v1 import script2

print(script2)

在block2 / v1 / script2.py中:

def test():
    print("hello")

#python block1 / v1 / script1.py的结果:

<module 'block2.v1.script2' from 'xxx/block2/v1/script2.py'>
<module 'block2.v1.script2' from 'xxx/block2/v1/script2.py'>
<module 'block2.v1' from 'xxx/block1/v1/__init__.py'>
block2 : ['__builtins__', '__cached__', '__doc__', '__file__', '__loader__', '__name__', '__package__', '__path__', '__spec__', 'script2']
Traceback (most recent call last):
  File "block1/v1/script1.py", line 17, in <module>
    from block2.script2 import test
ModuleNotFoundError: No module named 'block2.script2'

预期结果

很快,我希望在脚本1中具有此调用语法:

from block2.script2 import test
test()

能够运行我的测试功能

非常感谢您的帮助,我不知道这很清楚!

1 个答案:

答案 0 :(得分:0)

经过大量测试,我终于找到了不错的东西。

如果要使用带有自定义名称的自定义模块,最好的方法是创建一个模块,并给他指定您想要的名称:D

树:

/GLOBALCONFIG
|- /module_manager
  |- block1.py
  |- block2.py
  |- blocX..
- module_version_manager
/block1
|- /v1
   |- script1.py
|- /v2
   |- script1.py
/block2
|- /v1
   |- script2.py
|- /v2
   |- script2.py

为此,我在项目根目录下创建了一个名为“ GLOBALCONFIG”的目录,其中包含一个.py管理器,其内容如下:

from importlib import import_module
import sys
import os

block1 = sys.modules['block1'] = import_module('block1.v1')
block2 = sys.modules['block2'] = import_module('block2.v1')

使用此选项,我可以保留所有导入内容,仅在“管理器”文件中指定版本,例如:

block1 / v1 / script1.py:

from GLOBALCONFIG.module_manager.block1 import block2
from block2 import script2

如果我将script2放在block2 / v2或/ v3或任何版本中,我只需要更改正确的版本以将其用于block1.py管理器中的block2,代码仍然可以工作

此外,如果需要的话:
使用block2 / v1的block1 / v1
使用block2 / v2的block1 / v2

您可以创建一个引用全局配置中模块/版本的字典:

module_version_manager.py:

block1_modules_versions = {
    'block1.v1': {
        'block2':'block2.v1',
    }
    'block1.v2': {
        'block2':'block2.v2',
    }
}

block2_modules_versions = {
    'block2.v2': {
        'block1':'block1.v1',
    }
   'block2.v2': {
        'block1':'block1.v2',
    }
}

然后,在您的block1.py管理器中:

from GLOBALCONFIG.module_version_manager import block1_modules_versions
from importlib import import_module
import sys
import os

block1 = sys.modules['u1'] = import_module('.'.join(str.rsplit(sys.argv[0], "/")[3:5])) # give me the current version of the current block : block1.vX
block2 = sys.modules['block2'] = import_module(block1_modules_versions['.'.join(str.rsplit(sys.argv[0], "/")[3:5])]['block2']) # give me the block2 to use for the right block1.vX

在block1 / v1 / script1.py中:

import sys
sys.argv[0] = __file__ # [path]/block1/v1/script1 => current path of executed script giving the right folder and import the right modules
from GLOBALCONFIG.module_manager.block1 import block1, block2

block1 => block1 / v1
block2 => block2 / v1

但是这是神奇的把戏
在block1 / v2 /script1.py中:

import sys
sys.argv[0] = __file__
from GLOBALCONFIG.module_manager.block1 import block1, block2

block1 => block1 / v2
block2 => block2 / v2

版本不同但代码完全相同

FYI:强制使用当前文件路径的sys.argv [0] = 文件对我来说是强制性的,这是因为我的服务开始使用gunicon wsgi:app,该默认路径位于[venv] / bin / gunicorn,但没有这个限制,GLOBALCONFIG导入应该可以正常工作

此致