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
48 changes: 32 additions & 16 deletions Lib/dataclasses.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
import sys
import copy
import types
import inspect
import keyword
import itertools
import annotationlib
Expand Down Expand Up @@ -982,6 +981,29 @@ def _hash_exception(cls, fields, func_builder):
# See https://bugs.python.org/issue32929#msg312829 for an if-statement
# version of this table.

class AutoDocstring:
"""A non-data descriptor to autogenerate class docstring
from the signature of its __init__ method.
"""

def __get__(self, _obj, cls):
# TODO: Make this top-level lazy import once PEP810 lands
import inspect

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 +1231,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 +1389,12 @@ 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

# TODO: Make this top-level lazy import once PEP810 lands
Copy link
Member

Choose a reason for hiding this comment

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

PEP 810 is landing in 3.15, there is no point in adding an intermediate stage here, we can wait a few weeks.

import inspect
member = inspect.unwrap(member)

if isinstance(member, types.FunctionType):
if _update_func_cell_for__class__(member, cls, newcls):
Expand Down
2 changes: 1 addition & 1 deletion Lib/inspect.py
Original file line number Diff line number Diff line change
Expand Up @@ -165,7 +165,7 @@
from collections import namedtuple, OrderedDict
from weakref import ref as make_weakref

# Create constants for the compiler flags in Include/code.h
# Create constants for the compiler flags in Include/cpython/code.h
# We try to get them from dis to avoid duplication
mod_dict = globals()
for k, v in dis.COMPILER_FLAG_NAMES.items():
Expand Down
7 changes: 7 additions & 0 deletions Lib/test/test_dataclasses/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -2295,6 +2295,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
Loading