From 5206a52c2f96c17c14dd493eb27f4988fc5bb210 Mon Sep 17 00:00:00 2001 From: Daniel Hollas Date: Mon, 16 Feb 2026 21:36:23 +0000 Subject: [PATCH 1/7] Lazy import copy module --- Lib/dataclasses.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Lib/dataclasses.py b/Lib/dataclasses.py index 482a4c61039184..9a49bae53a9a8e 100644 --- a/Lib/dataclasses.py +++ b/Lib/dataclasses.py @@ -1,6 +1,6 @@ import re import sys -import copy +lazy import copy import types import inspect import keyword From dca8fc3f155717e3ad253791adada1808e8dfd0a Mon Sep 17 00:00:00 2001 From: Daniel Hollas Date: Mon, 16 Feb 2026 22:01:13 +0000 Subject: [PATCH 2/7] Lazy import re module --- Lib/dataclasses.py | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/Lib/dataclasses.py b/Lib/dataclasses.py index 9a49bae53a9a8e..50879815794ad7 100644 --- a/Lib/dataclasses.py +++ b/Lib/dataclasses.py @@ -1,4 +1,4 @@ -import re +lazy import re import sys lazy import copy import types @@ -217,9 +217,8 @@ def __repr__(self): _POST_INIT_NAME = '__post_init__' # String regex that string annotations for ClassVar or InitVar must match. -# Allows "identifier.identifier[" or "identifier[". -# https://bugs.python.org/issue33453 for details. -_MODULE_IDENTIFIER_RE = re.compile(r'^(?:\s*(\w+)\s*\.)?\s*(\w+)') +# This regular expression is compiled on demand so that 're' module can be imported lazily +_MODULE_IDENTIFIER_RE = None # Atomic immutable types which don't require any recursive handling and for which deepcopy # returns the same object. We can provide a fast-path for these types in asdict and astuple. @@ -804,6 +803,13 @@ def _is_type(annotation, cls, a_module, a_type, is_type_predicate): # a eval() penalty for every single field of every dataclass # that's defined. It was judged not worth it. + # String regex that string annotations for ClassVar or InitVar must match. + # Allows "identifier.identifier[" or "identifier[". + # https://bugs.python.org/issue33453 for details. + global _MODULE_IDENTIFIER_RE + if _MODULE_IDENTIFIER_RE is None: + _MODULE_IDENTIFIER_RE = re.compile(r'^(?:\s*(\w+)\s*\.)?\s*(\w+)') + match = _MODULE_IDENTIFIER_RE.match(annotation) if match: ns = None From 91473a5113782491acb5b43bdb87060637b9704f Mon Sep 17 00:00:00 2001 From: Daniel Hollas Date: Sat, 11 Apr 2026 12:32:56 +0100 Subject: [PATCH 3/7] Add blurb --- .../next/Library/2026-04-11-12-32-38.gh-issue-137855.tsVny_.rst | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 Misc/NEWS.d/next/Library/2026-04-11-12-32-38.gh-issue-137855.tsVny_.rst diff --git a/Misc/NEWS.d/next/Library/2026-04-11-12-32-38.gh-issue-137855.tsVny_.rst b/Misc/NEWS.d/next/Library/2026-04-11-12-32-38.gh-issue-137855.tsVny_.rst new file mode 100644 index 00000000000000..0ec08c2d2fd54a --- /dev/null +++ b/Misc/NEWS.d/next/Library/2026-04-11-12-32-38.gh-issue-137855.tsVny_.rst @@ -0,0 +1,2 @@ +Improve import time of :mod:`dataclasses` module by lazy importing :mod:`re` +and :mod:`copy` modules. From 8194309d5a8591eb62a7c40e4d7a14225ea19b35 Mon Sep 17 00:00:00 2001 From: Daniel Hollas Date: Sat, 11 Apr 2026 17:19:42 +0100 Subject: [PATCH 4/7] Update Lib/dataclasses.py Co-authored-by: Hugo van Kemenade <1324225+hugovk@users.noreply.github.com> --- Lib/dataclasses.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Lib/dataclasses.py b/Lib/dataclasses.py index 50879815794ad7..a14f95e4cd5440 100644 --- a/Lib/dataclasses.py +++ b/Lib/dataclasses.py @@ -805,7 +805,7 @@ def _is_type(annotation, cls, a_module, a_type, is_type_predicate): # String regex that string annotations for ClassVar or InitVar must match. # Allows "identifier.identifier[" or "identifier[". - # https://bugs.python.org/issue33453 for details. + # https://github.com/python/cpython/issues/77634 for details. global _MODULE_IDENTIFIER_RE if _MODULE_IDENTIFIER_RE is None: _MODULE_IDENTIFIER_RE = re.compile(r'^(?:\s*(\w+)\s*\.)?\s*(\w+)') From ab4bc95fe37ec435189206542945e3c00e427f28 Mon Sep 17 00:00:00 2001 From: Daniel Hollas Date: Sat, 11 Apr 2026 17:21:54 +0100 Subject: [PATCH 5/7] Reorder lazy imports --- Lib/dataclasses.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Lib/dataclasses.py b/Lib/dataclasses.py index a14f95e4cd5440..724b0dc2058663 100644 --- a/Lib/dataclasses.py +++ b/Lib/dataclasses.py @@ -1,6 +1,4 @@ -lazy import re import sys -lazy import copy import types import inspect import keyword @@ -8,6 +6,8 @@ import annotationlib import abc from reprlib import recursive_repr +lazy import copy +lazy import re __all__ = ['dataclass', From 933a9c56b06607ee5018b7ca1ed3f38c3f12f620 Mon Sep 17 00:00:00 2001 From: Daniel Hollas Date: Sat, 11 Apr 2026 17:25:50 +0100 Subject: [PATCH 6/7] Hugo's regex-fu --- Lib/dataclasses.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Lib/dataclasses.py b/Lib/dataclasses.py index 724b0dc2058663..42dd8cd4186838 100644 --- a/Lib/dataclasses.py +++ b/Lib/dataclasses.py @@ -808,12 +808,12 @@ def _is_type(annotation, cls, a_module, a_type, is_type_predicate): # https://github.com/python/cpython/issues/77634 for details. global _MODULE_IDENTIFIER_RE if _MODULE_IDENTIFIER_RE is None: - _MODULE_IDENTIFIER_RE = re.compile(r'^(?:\s*(\w+)\s*\.)?\s*(\w+)') + _MODULE_IDENTIFIER_RE = re.compile(r'(?:\s*(\w+)\s*\.)?\s*(\w+)') - match = _MODULE_IDENTIFIER_RE.match(annotation) + match = _MODULE_IDENTIFIER_RE.prefixmatch(annotation) if match: ns = None - module_name = match.group(1) + module_name = match[1] if not module_name: # No module name, assume the class's module did # "from dataclasses import InitVar". From 51fecc5c47fbc8d7da107e7fc409e8ad9ce3ab5a Mon Sep 17 00:00:00 2001 From: Daniel Hollas Date: Sat, 11 Apr 2026 18:09:15 +0100 Subject: [PATCH 7/7] One more match --- Lib/dataclasses.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Lib/dataclasses.py b/Lib/dataclasses.py index 42dd8cd4186838..7d046d02de35ba 100644 --- a/Lib/dataclasses.py +++ b/Lib/dataclasses.py @@ -823,7 +823,7 @@ def _is_type(annotation, cls, a_module, a_type, is_type_predicate): module = sys.modules.get(cls.__module__) if module and module.__dict__.get(module_name) is a_module: ns = sys.modules.get(a_type.__module__).__dict__ - if ns and is_type_predicate(ns.get(match.group(2)), a_module): + if ns and is_type_predicate(ns.get(match[2]), a_module): return True return False