Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
44 changes: 28 additions & 16 deletions Lib/dataclasses.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
import sys
import copy
import types
import inspect
lazy import inspect
import keyword
import itertools
import annotationlib
Expand Down Expand Up @@ -982,6 +982,26 @@ def _hash_exception(cls, fields, func_builder):
# 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


def _process_class(cls, init, repr, eq, order, unsafe_hash, frozen,
match_args, kw_only, slots, weakref_slot):
Expand Down Expand Up @@ -1209,23 +1229,13 @@ def _process_class(cls, init, repr, eq, order, unsafe_hash, frozen,
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__ = _AutoDocstring()

if match_args:
# I could probably compute this once.
Expand Down Expand Up @@ -1377,8 +1387,10 @@ def _add_slots(cls, is_frozen, weakref_slot, defined_fields):
# 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__'):
Copy link
Contributor Author

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

member = inspect.unwrap(member)

if isinstance(member, types.FunctionType):
if _update_func_cell_for__class__(member, cls, newcls):
Expand Down
19 changes: 18 additions & 1 deletion Lib/test/test_dataclasses/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,11 +27,21 @@
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"}
)


class TestCase(unittest.TestCase):
def test_no_fields(self):
@dataclass
Expand Down Expand Up @@ -2295,6 +2305,13 @@ class C:

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_one_field(self):
@dataclass
class C:
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Reduce the import time of :mod:`dataclasses` module by ~20%.
Loading