In this test, I create a new directory (deleting it if it already exists), add it to sys.path
, add two simple Python files, and try to import them. I'm using Python 3.11.6 on Ubuntu 23.10.
import sys
from pathlib import Path
from shutil import rmtree
d = Path('/tmp/fleen')
try:
rmtree(d)
except FileNotFoundError:
pass
d.mkdir()
sys.path = [str(d)] + sys.path
(d / 'module1.py').write_text('x = 1')
import module1
assert module1.x == 1
(d / 'module2.py').write_text('x = 2')
print('file contains:', repr((d / 'module2.py').read_text()))
import module2
assert module2.x == 2
This prints file contains: 'x = 2'
, as expected, but then raises
Traceback (most recent call last):
File "example.py", line 16, in <module>
import module2
ModuleNotFoundError: No module named 'module2'
What's going on? Why can't import
see the file? Is there some sort of cache of each directory in sys.path
that I need to clear?
Edited to add: Unpredictably, this sometimes works, as if there's a race condition of some kind. This is mysterious because all the IO here is supposed to flush all buffers before going on to the next statement.
Some importers cache the modules they find. You have to invalidate the cache when another module is added. From importlib.import_module
If you are dynamically importing a module that was created since the interpreter began execution (e.g., created a Python source file), you may need to call invalidate_caches() in order for the new module to be noticed by the import system.
By adding a line to invalidate importer caches after writing the second file, the problem is solved
import sys
from pathlib import Path
from shutil import rmtree
import importlib
d = Path('/tmp/fleen')
try:
rmtree(d)
except FileNotFoundError:
pass
d.mkdir()
#sys.path = [str(d)] + sys.path
sys.path.insert(0, str(d))
(d / 'module1.py').write_text('x = 1')
import module1
assert module1.x == 1
(d / 'module2.py').write_text('x = 2')
importlib.invalidate_caches() # <== invalidate
print('file contains:', repr((d / 'module2.py').read_text()))
import module2
assert module2.x == 2
This wasn't needed for the first file because /tmp/fleen
hadn't been visited by the importer before the import.