【问题标题】:dynamic import of dynamically created modules fails动态创建模块的动态导入失败
【发布时间】:2020-03-31 16:45:56
【问题描述】:

背景:

  • 一个守护进程创建包 p1(例如带有__init__.py 文件的目录)

  • 然后在 p1 中创建一个模块 m1。

  • 然后守护进程想要从 m1 导入,它的功能(到目前为止一直)。

  • 然后在 p1 中创建了一个模块 m2,并且守护进程想要从 m2 导入失败(大多数时候,但不是总是)并出现 ModuleNotFoundError。

查看最后重现问题的小测试脚本。

对我来说非常奇怪的是,在测试脚本中导入 m2 有时会起作用。我想知道是否有一些包内容缓存。如果是这样,如何防止或重新触发它。

(从 sys.modules 中重新加载 p1 和删除 p1 都不起作用)

"""
(python --version: Python 3.8.2)
Dynamic import of dynamically created modules right after creation.
"""

from shutil import rmtree
from os import path, chdir, mkdir, listdir
from importlib import import_module, reload
import sys

# for test-package creation
P1 = 'p1'
INIT = '__init__.py'
INIT_CONTENT = """\
# -*- coding: utf-8 -*-
"""

# for first test-module creation
M1 = 'm1'
M1_CONTENT = """\
# -*- coding: utf-8 -*-

answer = 42
"""

# for second test-module creation
M2 = 'm2'
M2_CONTENT = """\
# -*- coding: utf-8 -*-

hello = 'world'
"""

chdir(path.dirname(__file__))    # make sure we are in the right directory

if path.isdir(P1):
    rmtree(P1)                   # always start off under the same conditions


mkdir(P1)                        # create test-package and first test-module
with open(path.join(P1, INIT), 'w') as f:
    f.write(INIT_CONTENT)
with open(path.join(P1, M1+'.py'), 'w') as f:
    f.write(M1_CONTENT)

# import from the just created module; this worked always so far
from p1.m1 import answer

print(f'{answer=}')

with open(path.join(P1, M2+'.py'), 'w') as f:
    f.write(M2_CONTENT)          # create the second test-module

# check current directory, file and module structure
print('wd-content:', ', '.join(listdir()))
print('p1-content:', ', '.join(listdir(P1)))
print('p1-modlues:', ', '.join([m for m in sys.modules if m.startswith(P1)]))
# reload(sys.modules[P1])  # neither a reload
# del sys.modules[P1]      # nor a deletion of p1 does the trick

# here it most of the time fails (but NOT all the time)
# so far if it fails it fails in all three variants
# so far if it works the 'from ...'-import works already
try:
    from p1.m2 import hello
except ModuleNotFoundError:
    try:
        hello = getattr(import_module(f'{P1}.{M2}'), 'hello')
    except ModuleNotFoundError:
        try:
            hello = getattr(__import__(f'{P1}.{M2}', fromlist=[None]), 'hello')
        except ModuleNotFoundError:
            raise
        else:
            print("__import__-import worked")
    else:
        print("import_module-import worked")
else:
    print("'from ... '-import worked")

print(f'{hello=}')

【问题讨论】:

  • 推测:这听起来像是一种竞争条件。导入 p1 时,只存在 m1。如果 m2 的创建速度足够快,那么全局模块名称表还没有完成更新,m2 就在那里。否则,它已经导入了模块和the module is imported only once per interpreter session。看看上面 6.1.1 的注释是否有帮助:重新导入模块。另见this old question
  • 谢谢。我不明白你的意思。 m2 没有按照 6.1.1 中的说明进行更改,但它已创建并且不在 sys.modules 中,例如它无法重新加载。重新加载包含 m2 的包 p1 没有我的问题中提到的效果。
  • 创建m2 后,print(dir(p1)) 状态是什么?
  • print(dir(sys.modules[P1])) ` ['builtins', 'cached', 'doc', 'file', 'loader', 'name', 'package', ' path', 'spec', 'm1']` 很有趣,这是脚本成功运行的输出。我在'print('p1-modlues ...'之后添加了你的行

标签: python


【解决方案1】:

正如我所想 :) 从 'sys.path_importer_cache' 中删除 'p1' 的条目似乎可以解决问题。

【讨论】:

    猜你喜欢
    • 2019-06-12
    • 1970-01-01
    • 2011-02-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-07-25
    • 2011-04-17
    • 2023-02-01
    相关资源
    最近更新 更多