Zope Deferred Import
zope.deferredimport allows you to perform imports of names that will only be resolved when used in the code. This utility library helps improve application startup time and manage potential circular dependencies by making imports lazy. The current version is 6.1.1, and it receives updates as part of the broader Zope Foundation ecosystem.
Warnings
- breaking Versions 6.x and above of `zope.deferredimport` require Python 3.10 or higher. Projects on older Python versions (e.g., 3.9 or earlier) must use an older version of the library (e.g., 5.x) or upgrade their Python interpreter.
- gotcha When a deferred-imported symbol is accessed but its underlying source cannot be found (e.g., the module doesn't exist, or the attribute is missing), the `AttributeError` will be raised at the *point of access*, not necessarily where `deferredImport` was called. This can make debugging unexpected `AttributeError`s tricky as the traceback doesn't immediately point to the source of the deferred definition.
- gotcha The performance benefits (faster startup time) of `zope.deferredimport` only materialize if the deferred symbols are *not* immediately accessed after the `deferredImport` call. If a symbol is accessed on the next line, the import happens immediately, largely negating the startup improvement for that specific symbol.
- gotcha Modules using `deferredImport` do not automatically populate their `__all__` attribute with the deferred symbols. This means that `from my_module import *` will not expose deferred names without explicit modification of `my_module.__all__`.
Install
-
pip install zope.deferredimport
Imports
- deferredImport
from zope.deferredimport import deferredImport
Quickstart
import sys
from zope.deferredimport import deferredImport
# Simulate a module that would be slow to import or cause a circular dependency
# In a real scenario, 'my_app.utils' might be a separate file/module.
sys.modules['my_app.utils'] = type('module', (object,), {
'complex_function': lambda: "Complex function executed!",
'helper_constant': 42,
'another_item': 'hello'
})
# Define deferred imports for a target module 'my_app.api'
deferredImport(
'my_app.api',
'my_app.utils:complex_function',
'my_app.utils:helper_constant',
'my_app.utils:non_existent_item', # This will raise AttributeError on access
)
# At this point, 'my_app.api' exists but its contents are not loaded (lazy)
print(f"Is 'my_app.api' in sys.modules? {'my_app.api' in sys.modules}")
print(f"Has 'complex_function' been imported yet? {'complex_function' in sys.modules.get('my_app.api', {}).__dict__}")
# Accessing 'complex_function' triggers its import from 'my_app.utils'
import my_app.api
print(f"Value of complex_function: {my_app.api.complex_function()}")
print(f"Has 'complex_function' been imported now? {'complex_function' in sys.modules.get('my_app.api', {}).__dict__}")
print(f"Value of helper_constant: {my_app.api.helper_constant}")
# Accessing a non-existent item will raise an AttributeError as expected
try:
my_app.api.non_existent_item
except AttributeError as e:
print(f"Expected error for non-existent item: {e}")
# Clean up sys.modules for example isolation (not typically done in app code)
del sys.modules['my_app.utils']
del sys.modules['my_app.api']