Skip to content

Commit 4fbde9c

Browse files
committed
Defer inspect import to reduce import time by ~24%
Move `import inspect` from module level in `_compat.py` and `_make.py` into the functions that use it. Lazy-load `converters` and `validators` submodules from `attr/__init__.py` and `attrs/__init__.py` since they trigger class building (which invokes inspect) at import time. inspect is only used in three places, all at class-build time: - _compat._AnnotationExtractor.__init__() for signature extraction - _make.py line 709 for __attrs_pre_init__ arg detection - _make.py line 931 for cached_property return annotations Benchmark (Python 3.13, PYTHONDONTWRITEBYTECODE=1, 50 runs): - Before: 85.0ms ± 20.4ms - After: 64.7ms ± 12.0ms - Improvement: ~20ms (24%) All 1385 tests pass.
1 parent 0f758fe commit 4fbde9c

4 files changed

Lines changed: 22 additions & 4 deletions

File tree

src/attr/__init__.py

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
from functools import partial
88
from typing import Callable, Literal, Protocol
99

10-
from . import converters, exceptions, filters, setters, validators
10+
from . import exceptions, filters, setters
1111
from ._cmp import cmp_using
1212
from ._config import get_run_validators, set_run_validators
1313
from ._funcs import asdict, assoc, astuple, has, resolve_types
@@ -78,13 +78,23 @@ class AttrsInstance(Protocol):
7878
]
7979

8080

81+
_LAZY_SUBMODULES = {"converters", "validators"}
82+
83+
8184
def _make_getattr(mod_name: str) -> Callable:
8285
"""
8386
Create a metadata proxy for packaging information that uses *mod_name* in
8487
its warnings and errors.
8588
"""
8689

8790
def __getattr__(name: str) -> str:
91+
if name in _LAZY_SUBMODULES:
92+
import importlib
93+
94+
mod = importlib.import_module(f".{name}", mod_name)
95+
globals()[name] = mod
96+
return mod
97+
8898
if name not in ("__version__", "__version_info__"):
8999
msg = f"module {mod_name} has no attribute {name}"
90100
raise AttributeError(msg)

src/attr/_compat.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
# SPDX-License-Identifier: MIT
22

3-
import inspect
43
import platform
54
import sys
65
import threading
@@ -46,6 +45,8 @@ class _AnnotationExtractor:
4645
__slots__ = ["sig"]
4746

4847
def __init__(self, callable):
48+
import inspect # noqa: PLC0415
49+
4950
try:
5051
self.sig = inspect.signature(callable)
5152
except (ValueError, TypeError): # inspect failed
@@ -55,6 +56,8 @@ def get_first_param_type(self):
5556
"""
5657
Return the type annotation of the first argument if it's not empty.
5758
"""
59+
import inspect # noqa: PLC0415
60+
5861
if not self.sig:
5962
return None
6063

@@ -68,6 +71,8 @@ def get_return_type(self):
6871
"""
6972
Return the return type if it's not empty.
7073
"""
74+
import inspect # noqa: PLC0415
75+
7176
if (
7277
self.sig
7378
and self.sig.return_annotation is not inspect.Signature.empty

src/attr/_make.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@
66
import contextlib
77
import copy
88
import enum
9-
import inspect
109
import itertools
1110
import linecache
1211
import sys
@@ -705,6 +704,8 @@ def __init__(
705704
if self._has_pre_init:
706705
# Check if the pre init method has more arguments than just `self`
707706
# We want to pass arguments if pre init expects arguments
707+
import inspect # noqa: PLC0415
708+
708709
pre_init_func = cls.__attrs_pre_init__
709710
pre_init_signature = inspect.signature(pre_init_func)
710711
self._pre_init_has_args = len(pre_init_signature.parameters) > 1
@@ -920,6 +921,8 @@ def _create_slots_class(self):
920921
# To know to update them.
921922
additional_closure_functions_to_update = []
922923
if cached_properties:
924+
import inspect # noqa: PLC0415
925+
923926
class_annotations = _get_annotations(self._cls)
924927
for name, func in cached_properties.items():
925928
# Add cached properties to names for slotting.

src/attrs/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@
2525
from attr._make import ClassProps
2626
from attr._next_gen import asdict, astuple, inspect
2727

28-
from . import converters, exceptions, filters, setters, validators
28+
from . import exceptions, filters, setters
2929

3030

3131
__all__ = [

0 commit comments

Comments
 (0)