为什么在 Windows 上的 Python 3 下创建模块后导入失败?
Why does importing fail after creating a module under Python 3 on Windows?
以下代码尝试创建然后导入两个模块:
# coding: utf-8
import os
import time
# Remove the modules we're about to create if they already exist
def force_unlink(name):
try:
os.unlink(name)
except OSError:
pass
force_unlink("print1.py")
force_unlink("print1.pyc")
force_unlink("print2.py")
force_unlink("print2.pyc")
time.sleep(1)
# Create module 1 and module 2, then try to import them just afterwards
print("Creating module 1...")
with open("print1.py", "wb+") as fd:
fd.write(b'print("Imported module 1")')
import print1
print("Creating module 2...")
with open("print2.py", "wb+") as fd:
fd.write(b'print("Imported module 2")')
import print2
在 Windows 上,两个导入都在 Python 2 (2.7) 下工作,但在 Python 3(3.5 和 3.6)下不工作:
$ python2 reproduce.py
Creating module 1...
Imported module 1
Creating module 2...
Imported module 2
$ python3 reproduce.py
Creating module 1...
Imported module 1
Creating module 2...
Traceback (most recent call last):
File "reproduce.py", line 26, in <module>
import print2
ImportError: No module named 'print2'
在每个 import printX
调用之前添加 time.sleep(5)
使其工作。
这是为什么?
注意:这是 issue 我想弄明白的一个更简单的版本。
我想我知道发生了什么。新的 Python 3 导入机制 缓存 它在目录中找到的文件名。当目录的修改时间mtime
发生变化时,它会重新加载缓存。
查看 importlib._bootstrap_external.FileFinder.find_spec()
method implementation,其中包含:
try:
mtime = _path_stat(self.path or _os.getcwd()).st_mtime
except OSError:
mtime = -1
if mtime != self._path_mtime:
self._fill_cache()
self._path_mtime = mtime
此处 _path_stat
只是一个 os.stat()
调用,但已本地化以避免导入。 _fill_cache()
方法执行 os.listdir()
调用。
在某些 Windows 文件系统上,mtime
的分辨率非常低,最多 2 秒。对于您的情况,分辨率显然仍然足够低,以至于在您尝试加载第二个模块时 不会 更新缓存。尽管 NTFS 文件系统可以以 100ns 的增量记录时间,但实际上限制因素似乎是 Windows 系统时钟,据我所知通常限于 15ms 的分辨率。因此,如果您在写入 print1.py
的 15 毫秒内写入 print2.py
,那么 Python 将不会注意到。
Python 确实为您提供了清除此缓存的方法;使用 importlib.invalidate_caches()
method;这会将 FileFinder
实例上的 _path_mtime
属性重置回 -1
,强制进行新的 _fill_cache()
调用。
正如该函数的文档所述:
This function should be called if any modules are created/installed while your program is running to guarantee all finders will notice the new module’s existence.
以下代码尝试创建然后导入两个模块:
# coding: utf-8
import os
import time
# Remove the modules we're about to create if they already exist
def force_unlink(name):
try:
os.unlink(name)
except OSError:
pass
force_unlink("print1.py")
force_unlink("print1.pyc")
force_unlink("print2.py")
force_unlink("print2.pyc")
time.sleep(1)
# Create module 1 and module 2, then try to import them just afterwards
print("Creating module 1...")
with open("print1.py", "wb+") as fd:
fd.write(b'print("Imported module 1")')
import print1
print("Creating module 2...")
with open("print2.py", "wb+") as fd:
fd.write(b'print("Imported module 2")')
import print2
在 Windows 上,两个导入都在 Python 2 (2.7) 下工作,但在 Python 3(3.5 和 3.6)下不工作:
$ python2 reproduce.py
Creating module 1...
Imported module 1
Creating module 2...
Imported module 2
$ python3 reproduce.py
Creating module 1...
Imported module 1
Creating module 2...
Traceback (most recent call last):
File "reproduce.py", line 26, in <module>
import print2
ImportError: No module named 'print2'
在每个 import printX
调用之前添加 time.sleep(5)
使其工作。
这是为什么?
注意:这是 issue 我想弄明白的一个更简单的版本。
我想我知道发生了什么。新的 Python 3 导入机制 缓存 它在目录中找到的文件名。当目录的修改时间mtime
发生变化时,它会重新加载缓存。
查看 importlib._bootstrap_external.FileFinder.find_spec()
method implementation,其中包含:
try:
mtime = _path_stat(self.path or _os.getcwd()).st_mtime
except OSError:
mtime = -1
if mtime != self._path_mtime:
self._fill_cache()
self._path_mtime = mtime
此处 _path_stat
只是一个 os.stat()
调用,但已本地化以避免导入。 _fill_cache()
方法执行 os.listdir()
调用。
在某些 Windows 文件系统上,mtime
的分辨率非常低,最多 2 秒。对于您的情况,分辨率显然仍然足够低,以至于在您尝试加载第二个模块时 不会 更新缓存。尽管 NTFS 文件系统可以以 100ns 的增量记录时间,但实际上限制因素似乎是 Windows 系统时钟,据我所知通常限于 15ms 的分辨率。因此,如果您在写入 print1.py
的 15 毫秒内写入 print2.py
,那么 Python 将不会注意到。
Python 确实为您提供了清除此缓存的方法;使用 importlib.invalidate_caches()
method;这会将 FileFinder
实例上的 _path_mtime
属性重置回 -1
,强制进行新的 _fill_cache()
调用。
正如该函数的文档所述:
This function should be called if any modules are created/installed while your program is running to guarantee all finders will notice the new module’s existence.