Почему происходит сбой импорта после создания модуля под Python 3 в Windows? - PullRequest
0 голосов
/ 22 октября 2018

Следующий код пытается создать, а затем импортировать два модуля:

# 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'

Добавление time.sleep(5) перед каждым import printX вызовом заставляет его работать.

Почему это так?

Примечание. Это более простая версия выпуск Я пытаюсь выяснить.

1 Ответ

0 голосов
/ 22 октября 2018

Мне кажется, я знаю, что происходит.Новый механизм импорта Python 3 кэширует имена файлов, которые он находит в каталогах.Он перезагрузит кэш, когда mtime, время изменения каталога, изменится.

См. Реализацию метода importlib._bootstrap_external.FileFinder.find_spec() , которая содержит:

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 может записывать время с шагом 100 нс, на практике ограничивающим фактором, по-видимому, являются системные часы Windows, которые, как я понимаю, обычно ограничены разрешением 15 мс.Так что если вы пишете print2.py в течение 15 мс после написания print1.py, то Python не заметит.

Python действительно дает вам возможность очистить этот кеш;используйте метод importlib.invalidate_caches() ;это приведет к сбросу атрибута _path_mtime в экземпляре FileFinder обратно на -1, что приведет к новому вызову _fill_cache().

Как указано в документации по функции:

ThisФункция должна вызываться, если какие-либо модули создаются / устанавливаются во время работы вашей программы, чтобы гарантировать, что все искатели заметят существование нового модуля.

...