⚡️ autoimport: Explicit Lazy Module Imports
autoimport defers execution of a Python module until its first attribute access. It builds on Python's standard
importlib loader protocol, preserving the canonical module object
and its identity in sys.modules.
The ultralytics-autoimport package supports Python 3.8 through 3.14.
Installation
pip install ultralytics-autoimport
Usage
Replace a module import with an explicit lazy assignment:
import time
from autoimport import lazy_import
t0 = time.perf_counter()
torch = lazy_import("torch") # Finds torch now, but does not execute torch/__init__.py yet
print(f"Setup: {time.perf_counter() - t0:.3f}s")
t1 = time.perf_counter()
torch.cuda.is_available() # First attribute access executes and initializes torch
print(f"First use: {time.perf_counter() - t1:.3f}s")
torch.tensor([1, 2, 3]) # Later attributes use the fully initialized module normally
Aliases and dotted modules are explicit strings:
np = lazy_import("numpy")
linalg = lazy_import("numpy.linalg")
If the requested module is already imported, lazy_import() returns the existing object unchanged. Module discovery
happens immediately, so a misspelled or unavailable module raises ModuleNotFoundError at the lazy_import() call.
For dotted names, Python may import parent packages while finding the requested child module.
Scope and Limitations
lazy_import() deliberately supports modules only. It does not emulate arbitrary from ... import ... bindings:
# Supported
pathlib = lazy_import("pathlib")
path = pathlib.Path("models")
# Not provided: transparent lazy classes, functions, or constants
# Path = lazy_import("pathlib.Path")
Deferring a module also defers its import-time side effects and any exception raised while executing its body. Those
effects or errors occur on first attribute access instead of at the assignment. If code immediately accesses the module
after lazy_import(), there is no startup benefit.
First execution of lazy module bodies is serialized process-wide. This prevents two lazy modules from deadlocking each other during concurrent circular initialization, but it also means unrelated first accesses cannot initialize in parallel. Module bodies run while that shared reentrant lock is held, so applications should avoid coordinating lazy first access with other threads during import-time synchronization.
The discovered loader must support exec_module(), and its module object must permit __class__ reassignment. These are
the same compatibility requirements as Python's importlib.util.LazyLoader; incompatible custom loaders fail during
lazy_import() instead of returning a partial proxy.
Versions before 0.1.0 exposed a with lazy(): context manager that replaced builtins.__import__. That design could not
preserve normal Python behavior for aliases, function-local imports, dotted imports, or objects imported with
from ... import ..., and it affected imports in every thread. Replace it with explicit module assignments.
Python 3.15 and PEP 810
Python 3.15 adds native lazy imports through accepted PEP 810. Its interpreter-supported syntax can lazily bind both modules and imported attributes:
lazy from pathlib import Path
lazy import torch
PEP 810 also provides a migration form that keeps ordinary import statements:
>>> __lazy_modules__ = {"numpy", "torch"}
>>> import numpy as np
>>> import torch
On Python 3.15, the listed imports are lazy. Earlier Python versions ignore __lazy_modules__ and import them eagerly,
which lets libraries adopt the declaration before dropping older versions. Native lazy imports are the preferred
long-term solution because the interpreter can replace a lazy binding with any real module, class, function, or constant
before Python code uses it. autoimport remains a narrow module-only option for Python 3.8 through 3.14.
Development
uv pip install -e .
python -m unittest discover tests -v
ruff format .
ruff check .
License
Ultralytics offers AGPL-3.0 and Enterprise licenses. See LICENSE and the Ultralytics licensing page.
For bug reports and feature requests, open a GitHub issue.
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file ultralytics_autoimport-0.1.0.tar.gz.
File metadata
- Download URL: ultralytics_autoimport-0.1.0.tar.gz
- Upload date:
- Size: 46.3 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
0ca669e339dadd8f3fbff475b91e4b288ba2098258b871094fb6d42c27102df6
|
|
| MD5 |
ec6121c660220580cb4cfde5580a9c4d
|
|
| BLAKE2b-256 |
e11860a0772e6c0f2f1b9c32cd971376f572f330f1599ccc1de14ce299604e09
|
Provenance
The following attestation bundles were made for ultralytics_autoimport-0.1.0.tar.gz:
Publisher:
publish.yml on ultralytics/autoimport
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
ultralytics_autoimport-0.1.0.tar.gz -
Subject digest:
0ca669e339dadd8f3fbff475b91e4b288ba2098258b871094fb6d42c27102df6 - Sigstore transparency entry: 2327456056
- Sigstore integration time:
-
Permalink:
ultralytics/autoimport@d23358a70bb57bf22ad46dc0a7856a48ca1feb03 -
Branch / Tag:
refs/heads/main - Owner: https://github.com/ultralytics
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@d23358a70bb57bf22ad46dc0a7856a48ca1feb03 -
Trigger Event:
push
-
Statement type:
File details
Details for the file ultralytics_autoimport-0.1.0-py3-none-any.whl.
File metadata
- Download URL: ultralytics_autoimport-0.1.0-py3-none-any.whl
- Upload date:
- Size: 29.7 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
82c3b7dac1f33c90ae84ed12be38bcc95dceb883f092d2489ba1b360a781c4ec
|
|
| MD5 |
ff6f6936cf147a92a87541480ca46465
|
|
| BLAKE2b-256 |
9200eca985c2297a6a4efe7bb2d60be0dfb687aea7aa647235d4dc674fcc1248
|
Provenance
The following attestation bundles were made for ultralytics_autoimport-0.1.0-py3-none-any.whl:
Publisher:
publish.yml on ultralytics/autoimport
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
ultralytics_autoimport-0.1.0-py3-none-any.whl -
Subject digest:
82c3b7dac1f33c90ae84ed12be38bcc95dceb883f092d2489ba1b360a781c4ec - Sigstore transparency entry: 2327456149
- Sigstore integration time:
-
Permalink:
ultralytics/autoimport@d23358a70bb57bf22ad46dc0a7856a48ca1feb03 -
Branch / Tag:
refs/heads/main - Owner: https://github.com/ultralytics
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@d23358a70bb57bf22ad46dc0a7856a48ca1feb03 -
Trigger Event:
push
-
Statement type: