:exc:`ModuleNotFoundError` is raised when the module being reloaded lacks
a :class:`~importlib.machinery.ModuleSpec`.
+ .. versionchanged:: 3.14
+ If *module* is a lazy module that has not yet been materialized (i.e.,
+ loaded via :class:`importlib.util.LazyLoader` and not yet accessed),
+ calling :func:`reload` is a no-op and returns the module unchanged.
+ This prevents the reload from unintentionally triggering the lazy load.
+
.. warning::
This function is not thread-safe. Calling it from multiple threads can result
in unexpected behavior. It's recommended to use the :class:`threading.Lock`
The module must have been successfully imported before.
"""
+ # If a LazyModule has not yet been materialized, reload is a no-op.
+ if importlib_util := sys.modules.get('importlib.util'):
+ if lazy_module_type := getattr(importlib_util, '_LazyModule', None):
+ if isinstance(module, lazy_module_type):
+ return module
try:
name = module.__spec__.name
except AttributeError:
from test.support import threading_helper
from test.test_importlib import util as test_util
+# Make sure sys.modules[util] is in sync with the import.
+# That is needed as other tests may reload util.
+sys.modules['importlib.util'] = util
class CollectInit:
sys.modules['json'] = module
loader.exec_module(module)
- # Trigger load with attribute lookup, ensure expected behavior
+ # Trigger load with attribute lookup, ensure expected behavior.
test_load = module.loads('{}')
self.assertEqual(test_load, {})
with self.assertRaises(AttributeError):
del module.CONSTANT
+ def test_reload(self):
+ # Reloading a lazy module that hasn't been materialized is a no-op.
+ module = self.new_module()
+ sys.modules[TestingImporter.module_name] = module
+
+ # Change the source code to add a new attribute.
+ TestingImporter.source_code = 'attr = 42\nnew_attr = 123\n__name__ = {!r}'.format(TestingImporter.mutated_name)
+ self.assertIsInstance(module, util._LazyModule)
+
+ # Reload the module (should be a no-op since not materialized).
+ reloaded = importlib.reload(module)
+ self.assertIs(reloaded, module)
+ self.assertIsInstance(module, util._LazyModule)
+
+ # Access the new attribute (should trigger materialization, and new_attr should exist).
+ self.assertEqual(module.attr, 42)
+ self.assertNotIsInstance(module, util._LazyModule)
+ self.assertTrue(hasattr(module, 'new_attr'))
+ self.assertEqual(module.new_attr, 123)
+
if __name__ == '__main__':
unittest.main()