GV

GeezerVison

The World Wide Web, restored to 800 pixels of glory.

Home|About|Gaming cabinet|Archives|How it works|Starter sites

On this site

  • Sign in
  • ActionsCI/CD and automation
  • CodespacesCloud development environments
  • All Features
  • Documentation
  • Training
  • Certifications
  • Build with Copilot
  • Customize Copilot
  • Integrations
  • Trending repos
  • GitHub Sponsors
  • Developer articles
  • Enterprises

This view

  • Original page
  • New address

GeezerVison is showing github.com — gh-137855: Lazy import `inspect` module in dataclasses by danielhollas · Pull Request #144387 · python/cpython

Conversation

@danielhollas

@danielhollas danielhollas commented Feb 2, 2026 •
edited
Loading

Uh oh!

There was an error while loading. Please reload this page.

Copy link
Copy Markdown
Contributor

inspect module is slow to import (see #117865) and is dragging down dataclasses with it.

There are currently only two uses of inspect in dataclasses, but they are a bit tricky to inline since they are on a direct code path when the @dataclass decorator is executed.

  1. inspect.signature is used to autogenerate class docstring (if one is not provided already)
  2. inspect.unwrap is 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

image
❯ hyperfine -w 10 './python -c "import dataclasses"' 
Benchmark 1: ./python -c "import dataclasses"
  Time (mean ± σ):      15.4 ms ±   1.8 ms    [User: 11.6 ms, System: 3.6 ms]
  Range (min … max):    13.1 ms …  20.2 ms    201 runs

After

image
❯ hyperfine -w 10 './python -c "import dataclasses"' 
Benchmark 1: ./python -c "import dataclasses"
  Time (mean ± σ):      11.0 ms ±   1.5 ms    [User: 7.7 ms, System: 3.1 ms]
  Range (min … max):     9.1 ms …  15.0 ms    271 runs

Overall seems to be a solid 20-30% improvement.

  • Issue: Improve import time of various stdlib modules #137855

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions
@danielhollas
danielhollas requested a review from ericvsmith as a code owner February 2, 2026 05:28
@bedevere-app bedevere-app Bot added the awaiting review label Feb 2, 2026
@bedevere-app bedevere-app Bot mentioned this pull request Feb 2, 2026
Improve import time of various stdlib modules #137855
Closed
danielhollas
danielhollas commented Feb 2, 2026
View reviewed changes
Comment thread Lib/dataclasses.py

# If this is a wrapped function, unwrap it.
member = inspect.unwrap(member)
if not isinstance(member, type) and hasattr(member, '__wrapped__'):

@danielhollas danielhollas Feb 2, 2026

Copy link
Copy Markdown
Contributor Author

There was a problem hiding this comment.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This check was copied from the while loop in inspect.unwrap

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions
@danielhollas

danielhollas commented Feb 2, 2026

Copy link
Copy Markdown
Contributor Author

Deferring the call to inspect.signature to create __doc__ might also speed up the dataclasses creation a bit. e.g. here's what I see in a cProfile of the _colorize module which uses dataclasses heavily (on main branch)

$ ./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)
All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

StanFromIreland
StanFromIreland reviewed Feb 2, 2026
View reviewed changes
Comment thread Lib/dataclasses.py Resolved

Uh oh!

There was an error while loading. Please reload this page.

@danielhollas danielhollas mentioned this pull request Feb 2, 2026
_colorize module is slow to import, affecting traceback and logging modules #144384
Open
sergey-miryanov
sergey-miryanov reviewed Feb 3, 2026
View reviewed changes
Comment thread Lib/dataclasses.py Resolved

Uh oh!

There was an error while loading. Please reload this page.

danielhollas added 4 commits February 12, 2026 00:59
@danielhollas
Lazy import inspect in dataclasses module
4210688
@danielhollas
Simplify vendored unwrap function
b1578d5
@danielhollas
Unvendor unwrap, call it only when needed
43e2633
@danielhollas
Use PEP810!
6bc6199
@danielhollas
danielhollas force-pushed the dataclass-lazy-inspect branch from ca36c68 to 6bc6199 Compare February 12, 2026 01:19
danielhollas
danielhollas commented Feb 12, 2026
View reviewed changes
Comment thread Lib/dataclasses.py Resolved

Uh oh!

There was an error while loading. Please reload this page.

sergey-miryanov
sergey-miryanov reviewed Feb 12, 2026
View reviewed changes
Comment thread Lib/inspect.py Resolved

Uh oh!

There was an error while loading. Please reload this page.

@sergey-miryanov

sergey-miryanov commented Feb 12, 2026 •
edited
Loading

Uh oh!

There was an error while loading. Please reload this page.

Copy link
Copy Markdown
Contributor

I believe we should add a NEWS entry, because it is user-facing change (at least in the performance terms).

danielhollas reacted with thumbs up emoji
All reactions
  • 👍 1 reaction

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

danielhollas added 4 commits February 12, 2026 18:03
@danielhollas
Merge branch 'main' into dataclass-lazy-inspect
3901a4c
@danielhollas
Add blurb
9910cfd
@danielhollas
Add regression test
0d5efb7
@danielhollas
Remove unrelated change
ff59bd3
@danielhollas danielhollas mentioned this pull request Feb 13, 2026
gh-144764: Made dataclasses construct automatic docstrings lazily #144765
Closed
@danielhollas
move lazy import
6f0db76
@DavidCEllis

DavidCEllis commented Feb 16, 2026

Copy link
Copy Markdown
Contributor

As a side effect that may be worth noting, deferring __doc__ generation also 'fixes' the output for self referential dataclasses1.

@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 _AutoDocString instance, could they share the same instance?

Following on from this, the re and copy imports could also potentially be deferred, copy is only used in some cases for the serialization methods and re is only used for string annotations.

Deferring re only makes sense if inspect is also deferred as inspect currently eagerly imports it (I see you have a PR to change that too though).

Footnotes

  1. Arguably dataclasses should probably be doing something with Format.STRING here, other forward references are still ugly but that would be a separate issue. ↩

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@johnslavik

johnslavik commented Feb 16, 2026

Copy link
Copy Markdown
Member

could they share the same instance?

They could; that would be nice.

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

danielhollas added 4 commits February 16, 2026 21:36
@danielhollas
Lazy import copy module
7c9f75b
@danielhollas
Use the same _AutoDocstring instance
b577f3b
@danielhollas
Lazy import re module
1703153
@danielhollas
Merge branch 'main' into dataclass-lazy-inspect
b581777
@danielhollas

danielhollas commented Feb 16, 2026 •
edited
Loading

Uh oh!

There was an error while loading. Please reload this page.

Copy link
Copy Markdown
Contributor Author

Every dataclass is getting its own _AutoDocString instance, could they share the same instance?
Following on from this, the re and copy imports could also potentially be deferred, copy is only used in some cases for the serialization methods and re is only used for string annotations.

Done, thank you for the suggestions!
The lazy import or re is a bit more annoying since it is used to define a global compiled regex.
I am not sure I can judge whether the extra complexity is worth it --- the module will be imported as soon as you have e.g. from __future__ import annotations anywhere where dataclasses are defined.

Now the import time is dominated only by the "annotationlib" module

image

EDIT: Ugh, GitHub, you okay? The pushed new commits are still not appearing on the PR.

image
All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@danielhollas
danielhollas requested a review from StanFromIreland February 16, 2026 22:05
@danielhollas
danielhollas requested a review from sergey-miryanov February 16, 2026 22:05
@gpshead gpshead added the performance Performance or resource usage label Feb 16, 2026
@DavidCEllis

DavidCEllis commented Feb 16, 2026

Copy link
Copy Markdown
Contributor

I am not sure I can judge whether the extra complexity is worth it --- the module will be imported as soon as you have e.g. from __future__ import annotations anywhere where dataclasses are defined.

Ideally with the new annotations, uses of __future__ annotations will become less common and so they won't need the import. Modules like _colorize should already benefit as they don't use __future__ annotations.

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

danielhollas added 3 commits February 16, 2026 23:22
@danielhollas
Merge branch 'main' into dataclass-lazy-inspect
820e28c
@danielhollas
Add test for docstring of recursive dataclass
f0a4146
@danielhollas
Merge branch 'main' into dataclass-lazy-inspect
80dca62
@sergey-miryanov

sergey-miryanov commented Feb 17, 2026

Copy link
Copy Markdown
Contributor

I'm not sure we should mix changes for _MODULE_IDENTIFIER_RE and __doc__ in one PR. But I'm not an expert here.

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@hugovk

hugovk commented Apr 2, 2026

Copy link
Copy Markdown
Member

@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.

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@DavidCEllis

DavidCEllis commented Apr 2, 2026

Copy link
Copy Markdown
Contributor

@hugovk worth noting as it was one of the driving forces behind this that you lose a chunk of the benefit in _colorize without the autodoc stuff unless you write docstrings for the remaining dataclasses without them. The lack of docstring will trigger the inspect import.

One way of encouraging people to document their classes I guess 🙂 .

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@danielhollas

danielhollas commented Apr 2, 2026 •
edited
Loading

Uh oh!

There was an error while loading. Please reload this page.

Copy link
Copy Markdown
Contributor Author

@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.

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 _colorize since re is imported there as well). If you think splitting these two changes would help with the review I'll do it, but I think a more impactful thing here would be to tag somebody who you think should review this.

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@danielhollas danielhollas mentioned this pull request Apr 11, 2026
gh-137855: Improve import time of dataclasses by lazy importing re and copy modules #148379
Merged
@hugovk

hugovk commented Apr 11, 2026

Copy link
Copy Markdown
Member

This is a good improvement over main.

Here's importing _colorize on main vs. this PR (macOS optimised build):

main PR
image image
import
_colorize
13 ms 7 ms
import
dataclasses
6 ms 2 ms
Execute
_colorize
6 ms 4 ms
danielhollas reacted with heart emoji
All reactions
  • ❤️ 1 reaction

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@danielhollas

danielhollas commented Apr 11, 2026

Copy link
Copy Markdown
Contributor Author

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 _colorize since re is imported there as well).

I was wrong, I forgot that I made re lazy in inspect recently in #144756 😅 I've split the re and copy lazy imports in #148379. I will rebase this PR once that one is merged.

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@danielhollas
Merge branch 'main' into dataclass-lazy-inspect
152ae79
@danielhollas

danielhollas commented Apr 11, 2026

Copy link
Copy Markdown
Contributor Author

Alright, #148379 has been merged (thanks Hugo!) so this PR is now only about lazy importing inspect module.

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@danielhollas danielhollas mentioned this pull request Apr 12, 2026
gh-154675: Improve import performance of tkinter library with lazy imports #148409
Open
hugovk
hugovk approved these changes Apr 13, 2026
View reviewed changes
Comment thread Lib/dataclasses.py Resolved

Uh oh!

There was an error while loading. Please reload this page.

@bedevere-app bedevere-app Bot added awaiting merge and removed awaiting review labels Apr 13, 2026
danielhollas and others added 2 commits April 18, 2026 15:57
@danielhollas @hugovk
Update Lib/dataclasses.py
65d92cc
Co-authored-by: Hugo van Kemenade <[email protected]>
@danielhollas
Merge branch 'main' into dataclass-lazy-inspect
0299994
@danielhollas

danielhollas commented Apr 18, 2026

Copy link
Copy Markdown
Contributor Author

After merging main, _colorize module no longer imports re. Here's a comparison on this PR:

image

versus main

image
hugovk reacted with hooray emoji
All reactions
  • 🎉 1 reaction

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@danielhollas
Ensure inspect is not imported in test__colorize.py
ea805a1
@danielhollas

danielhollas commented Apr 25, 2026

Copy link
Copy Markdown
Contributor Author

Hi all, just a note that I will be away for two weeks starting 1st of May.
It would be great to get this merged before beta, since I don't think this would count as a "bugfix". While I am away, please feel free to push to this branch if any changes are needed.

hugovk reacted with rocket emoji
All reactions
  • 🚀 1 reaction

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

JelleZijlstra
JelleZijlstra approved these changes Apr 26, 2026
View reviewed changes
@JelleZijlstra
JelleZijlstra merged commit 0a39730 into python:main Apr 26, 2026
51 checks passed

Uh oh!

There was an error while loading. Please reload this page.

@bedevere-app bedevere-app Bot removed the awaiting merge label Apr 26, 2026
@danielhollas
danielhollas deleted the dataclass-lazy-inspect branch April 26, 2026 09:21
@DavidCEllis DavidCEllis mentioned this pull request May 7, 2026
Make dataclasses construct automatic docstrings lazily #144764
Closed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Reviewers

@johnslavik johnslavik johnslavik left review comments
@JelleZijlstra JelleZijlstra JelleZijlstra approved these changes
@hugovk hugovk hugovk approved these changes
@ericvsmith ericvsmith Awaiting requested review from ericvsmith ericvsmith is a code owner
@StanFromIreland StanFromIreland Awaiting requested review from StanFromIreland
@sergey-miryanov sergey-miryanov Awaiting requested review from sergey-miryanov

Uh oh!

There was an error while loading. Please reload this page.

Assignees

No one assigned

Labels

performance Performance or resource usage

Projects

None yet

Milestone

No milestone

Development

Successfully merging this pull request may close these issues.

Uh oh!

There was an error while loading. Please reload this page.

8 participants

@danielhollas @sergey-miryanov @DavidCEllis @johnslavik @hugovk @JelleZijlstra @StanFromIreland @gpshead
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
You are visitor 01402503 — GeezerVison does not store the pages you look up.
Best viewed with Netscape Navigator 4.7 or Internet Explorer 6 at 800×600. No cookies required.