【发布时间】: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