gh-137855: Lazy import inspect module in dataclasses#144387
gh-137855: Lazy import inspect module in dataclasses#144387danielhollas wants to merge 17 commits intopython:mainfrom
inspect module in dataclasses#144387Conversation
|
|
||
| # If this is a wrapped function, unwrap it. | ||
| member = inspect.unwrap(member) | ||
| if not isinstance(member, type) and hasattr(member, '__wrapped__'): |
There was a problem hiding this comment.
This check was copied from the while loop in inspect.unwrap
|
Deferring the call to $ ./python -m cProfile -m _colorize | head 20
5604 function calls (5484 primitive calls) in 0.007 seconds
Ordered by: cumulative time
ncalls tottime percall cumtime percall filename:lineno(function)
9/1 0.004 0.000 0.007 0.007 {built-in method builtins.exec}
1 0.000 0.000 0.007 0.007 <string>:1(<module>)
1 0.000 0.000 0.007 0.007 <frozen runpy>:199(run_module)
1 0.000 0.000 0.007 0.007 <frozen runpy>:65(_run_code)
1 0.000 0.000 0.007 0.007 _colorize.py:1(<module>)
7 0.000 0.000 0.006 0.001 dataclasses.py:1432(wrap)
7 0.000 0.000 0.006 0.001 dataclasses.py:986(_process_class)
7 0.000 0.000 0.004 0.001 dataclasses.py:478(add_fns_to_class)
4 0.000 0.000 0.001 0.000 inspect.py:3343(signature)
4 0.000 0.000 0.001 0.000 inspect.py:3056(from_callable)
12/4 0.000 0.000 0.001 0.000 inspect.py:2437(_signature_from_callable)
4 0.000 0.000 0.001 0.000 inspect.py:2331(_signature_from_function)
71/11 0.000 0.000 0.000 0.000 annotationlib.py:907(get_annotations)
8 0.000 0.000 0.000 0.000 dataclasses.py:541(__annotate__)
17/9 0.000 0.000 0.000 0.000 annotationlib.py:1114(_get_and_call_annotate) |
ca36c68 to
6bc6199
Compare
|
I believe we should add a NEWS entry, because it is user-facing change (at least in the performance terms). |
|
As a side effect that may be worth noting, deferring @dataclass
class Example:
examples: list[Example]
print(Example.__doc__)Before: "Example(examples: list[ForwardRef('Example', is_class=True, owner=<class '__main__.Example'>)])"PR: 'Example(examples: list[__main__.Example])'Every dataclass is getting its own Following on from this, the Deferring Footnotes
|
They could; that would be nice. |
Ideally with the new annotations, uses of |
|
I'm not sure we should mix changes for |
|
@danielhollas If you open a new PR with the lazy imports and global regex I'm happy to merge those. I'd prefer someone else to review the autodoc stuff. |
|
@hugovk worth noting as it was one of the driving forces behind this that you lose a chunk of the benefit in One way of encouraging people to document their classes I guess 🙂 . |
Thanks, I could do, but note that deferring re without deferring inspect is pointless since inspect imports re as well. (and it also wouldn't help |
I was wrong, I forgot that I made |
|
Alright, #148379 has been merged (thanks Hugo!) so this PR is now only about lazy importing |




inspectmodule is slow to import (see #117865) and is dragging down dataclasses with it.There are currently only two uses of
inspectin dataclasses, but they are a bit tricky to inline since they are on a direct code path when the@dataclassdecorator is executed.inspect.signatureis used to autogenerate class docstring (if one is not provided already)inspect.unwrapis used in a rather esoteric code path only for slotted classes, added in gh-90562: Support zero argument super with dataclasses when slots=True #124455)For 1. I have used a descriptor protocol to generate the
__doc__attribute on demand (this is my first time messing with descriptors, apologies if I overlooked something).For 2. can be deferred by calling the unwrap functions only when really necessary (and hopefully this path is not common)
Benchmarks
./python -Ximporttime -c "import dataclasses"Before
After
Overall seems to be a solid 20-30% improvement.