import sys
import types
-import inspect
import keyword
import itertools
import annotationlib
import abc
from reprlib import recursive_repr
lazy import copy
+lazy import inspect
lazy import re
# See https://bugs.python.org/issue32929#msg312829 for an if-statement
# version of this table.
+# A non-data descriptor to autogenerate class docstring
+# from the signature of its __init__ method on demand.
+# The primary reason is to be able to lazy import `inspect` module.
+class _AutoDocstring:
+
+ def __get__(self, _obj, cls):
+ try:
+ # In some cases fetching a signature is not possible.
+ # But, we surely should not fail in this case.
+ text_sig = str(inspect.signature(
+ cls,
+ annotation_format=annotationlib.Format.FORWARDREF,
+ )).replace(' -> None', '')
+ except TypeError, ValueError:
+ text_sig = ''
+
+ doc = cls.__name__ + text_sig
+ setattr(cls, '__doc__', doc)
+ return doc
+
+_auto_docstring = _AutoDocstring()
+
def _process_class(cls, init, repr, eq, order, unsafe_hash, frozen,
match_args, kw_only, slots, weakref_slot):
if hash_action:
cls.__hash__ = hash_action(cls, field_list, func_builder)
- # Generate the methods and add them to the class. This needs to be done
- # before the __doc__ logic below, since inspect will look at the __init__
- # signature.
+ # Generate the methods and add them to the class.
func_builder.add_fns_to_class(cls)
if not getattr(cls, '__doc__'):
- # Create a class doc-string.
- try:
- # In some cases fetching a signature is not possible.
- # But, we surely should not fail in this case.
- text_sig = str(inspect.signature(
- cls,
- annotation_format=annotationlib.Format.FORWARDREF,
- )).replace(' -> None', '')
- except (TypeError, ValueError):
- text_sig = ''
- cls.__doc__ = (cls.__name__ + text_sig)
+ # Create a class doc-string lazily via descriptor protocol
+ # to avoid importing `inspect` module.
+ cls.__doc__ = _auto_docstring
if match_args:
# I could probably compute this once.
# make an update, since all closures for a class will share a
# given cell.
for member in newcls.__dict__.values():
+
# If this is a wrapped function, unwrap it.
- member = inspect.unwrap(member)
+ if not isinstance(member, type) and hasattr(member, '__wrapped__'):
+ member = inspect.unwrap(member)
if isinstance(member, types.FunctionType):
if _update_func_cell_for__class__(member, cls, newcls):
import dataclasses # Needed for the string "dataclasses.InitVar[int]" to work as an annotation.
from test import support
-from test.support import import_helper
+from test.support import cpython_only, import_helper
# Just any custom exception we can catch.
class CustomError(Exception): pass
+
+class TestImportTime(unittest.TestCase):
+
+ @cpython_only
+ def test_lazy_import(self):
+ import_helper.ensure_lazy_imports(
+ "dataclasses", {"inspect", "re", "copy"}
+ )
+
+
class TestCase(unittest.TestCase):
def test_no_fields(self):
@dataclass
self.assertDocStrEqual(C.__doc__, "C()")
+ def test_docstring_slotted(self):
+ @dataclass(slots=True)
+ class C:
+ x: int
+
+ self.assertDocStrEqual(C.__doc__, "C(x:int)")
+
+ def test_docstring_recursive(self):
+ @dataclass()
+ class C:
+ x: list[C]
+
+ self.assertDocStrEqual(C.__doc__, "C(x:list[test.test_dataclasses.TestDocString.test_docstring_recursive.<locals>.C])")
+
def test_docstring_one_field(self):
@dataclass
class C: