Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

PEP 681: Rename transform_descriptor_types to delete_class_attributes #2455

Closed
wants to merge 6 commits into from
Closed
Changes from 5 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
145 changes: 77 additions & 68 deletions pep-0681.rst
Original file line number Diff line number Diff line change
Expand Up @@ -214,30 +214,31 @@ customization of default behaviors:
eq_default: bool = True,
order_default: bool = False,
kw_only_default: bool = False,
transform_descriptor_types: bool = False,
delete_class_attributes: bool = True,
field_descriptors: tuple[type | Callable[..., Any], ...] = (),
) -> Callable[[_T], _T]: ...

* ``eq_default`` indicates whether the ``eq`` parameter is assumed to
be True or False if it is omitted by the caller. If not specified,
``eq_default`` will default to True (the default assumption for
dataclass).
* ``order_default`` indicates whether the ``order`` parameter is
assumed to be True or False if it is omitted by the caller. If not
specified, ``order_default`` will default to False (the default
be ``True`` or ``False`` if it is omitted by the caller. If not
specified, ``eq_default`` will default to ``True`` (the default
assumption for dataclass).
* ``order_default`` indicates whether the ``order`` parameter is
assumed to be ``True`` or ``False`` if it is omitted by the caller.
If not specified, ``order_default`` will default to ``False`` (the
default assumption for dataclass).
* ``kw_only_default`` indicates whether the ``kw_only`` parameter is
assumed to be True or False if it is omitted by the caller. If not
specified, ``kw_only_default`` will default to False (the default
assumption for dataclass).
* ``transform_descriptor_types`` affects fields annotated with
descriptor types that define a ``__set__`` method. If True, the type
of each parameter on the synthesized ``__init__`` method
corresponding to such a field will be the type of the value
parameter to the descriptor's ``__set__`` method. If False, the
descriptor type will be used. If not specified,
``transform_descriptor_types`` will default to False (the default
behavior of dataclass).
assumed to be ``True`` or ``False`` if it is omitted by the caller.
If not specified, ``kw_only_default`` will default to ``False`` (the
default assumption for dataclass).
* ``delete_class_attributes`` indicates whether the class attributes
for fields should be deleted following the rules defined for
dataclass or retained. If ``True``, class attributes for fields
without default values will be deleted. This is
:pep:`dataclass's behavior <557#specification>`. If ``False``, class
attributes for all fields will be left intact. If not specified,
``delete_class_attributes`` will default to ``True``. For more
information, see the `delete_class_attributes parameter`_ section
below.
* ``field_descriptors`` specifies a static list of supported classes
that describe fields. Some libraries also supply functions to
allocate instances of field descriptors, and those functions may
Expand All @@ -249,8 +250,49 @@ customization of default behaviors:
describing the stdlib dataclass behavior, we would provide the
tuple argument ``(dataclasses.Field, dataclasses.field)``.

The following sections provide additional examples showing how these
parameters are used.
delete_class_attributes parameter
'''''''''''''''''''''''''''''''''

When ``delete_class_attributes`` is set to ``False``, class attributes
for fields are left intact. The APIs of some libraries, such as
SQLAlchemy, allow the use of the class attributes of fields. If a type
checker believes that those class attributes have been deleted, it
will flag their usage as errors, making usage of such features
awkward.

Retaining the class attributes of fields also enables fields to have
descriptor types. In addition to the standard behavior expected of
descriptors, the parameter corresponding to a descriptor field on the
synthesized ``__init__`` method will have the type of the value
parameter of the descriptor's ``__set__`` method (if present), rather
than the descriptor's type.

We anticipate that most descriptor classes used with
``delete_class_attributes`` will be generic with ``__set__`` functions
whose value parameters are based on the descriptor's type vars.
However, this is not required.

In the following example, because ``delete_class_attributes`` is set
to ``False``, the ``target`` parameter on the synthesized ``__init__``
method will be of type ``float`` (the type of ``__set__``\ 's
``value`` parameter) instead of ``Descriptor``.

.. code-block:: python

@typing.dataclass_transform(delete_class_attributes=False)
def create_model() -> Callable[[Type[_T]], Type[_T]]: ...

class Descriptor:
def __get__(self, instance: object, owner: Any) -> int:
...

def __set__(self, instance: object, value: float):
debonte marked this conversation as resolved.
Show resolved Hide resolved
...

@create_model
class CustomerModel:
target: Descriptor


Decorator function example
''''''''''''''''''''''''''
Expand Down Expand Up @@ -343,37 +385,6 @@ Metaclass example
id: int
name: str

``transform_descriptor_types`` example
``````````````````````````````````````

Because ``transform_descriptor_types`` is set to ``True``, the
``target`` parameter on the synthesized ``__init__`` method will be of
type ``float`` (the type of ``__set__``\ 's ``value`` parameter)
instead of ``Descriptor``.

.. code-block:: python

@typing.dataclass_transform(transform_descriptor_types=True)
def create_model() -> Callable[[Type[_T]], Type[_T]]: ...

# We anticipate that most descriptor classes used with
# transform_descriptor_types will be generic with __set__ functions
# whose value parameters are based on the generic's type vars.
# However, this is not required.
class Descriptor:
def __get__(self, instance: object, owner: Any) -> int:
...

# The setter and getter can have different types (asymmetric).
# The setter's value type is used for the __init__ parameter.
# The getter's return type is ignored.
def __set__(self, instance: object, value: float):
...

@create_model
class CustomerModel:
target: Descriptor


Field descriptors
-----------------
Expand Down Expand Up @@ -425,9 +436,9 @@ positional and may use other names.

* ``init`` is an optional bool parameter that indicates whether the
field should be included in the synthesized ``__init__`` method. If
unspecified, ``init`` defaults to True. Field descriptor functions
can use overloads that implicitly specify the value of ``init``
using a literal bool value type
unspecified, ``init`` defaults to ``True``. Field descriptor
functions can use overloads that implicitly specify the value of
``init`` using a literal bool value type
(``Literal[False]`` or ``Literal[True]``).
* ``default`` is an optional parameter that provides the default value
for the field.
Expand All @@ -440,11 +451,12 @@ positional and may use other names.
use the name ``default_factory``, but attrs uses the name ``factory``
in many scenarios, so this alias is necessary for supporting attrs.
* ``kw_only`` is an optional bool parameter that indicates whether the
field should be marked as keyword-only. If true, the field will be
keyword-only. If false, it will not be keyword-only. If unspecified,
the value of the ``kw_only`` parameter on the object decorated with
``dataclass_transform`` will be used, or if that is unspecified, the
value of ``kw_only_default`` on ``dataclass_transform`` will be used.
field should be marked as keyword-only. If ``True``, the field will
be keyword-only. If ``False``, it will not be keyword-only. If
unspecified, the value of the ``kw_only`` parameter on the object
decorated with ``dataclass_transform`` will be used, or if that is
unspecified, the value of ``kw_only_default`` on
``dataclass_transform`` will be used.
* ``alias`` is an optional str parameter that provides an alternative
name for the field. This alternative name is used in the synthesized
``__init__`` method.
Expand Down Expand Up @@ -507,7 +519,7 @@ For example:
"eq_default": True,
"order_default": False,
"kw_only_default": False,
"transform_descriptor_types": False,
"delete_class_attributes": True,
"field_descriptors": (),
}

Expand Down Expand Up @@ -600,7 +612,7 @@ behavior is undefined. Library authors should avoid these scenarios.

The ``__set__`` method on descriptors is not expected to be
overloaded. If such overloads are found when
``transform_descriptor_types`` is ``True``, the resulting behavior is
``delete_class_attributes`` is ``False``, the resulting behavior is
undefined.


Expand Down Expand Up @@ -642,10 +654,10 @@ that they should always supply assigned values.
-----------------

The attrs library supports a bool parameter ``cmp`` that is equivalent
to setting both ``eq`` and ``order`` to True. We chose not to support
a ``cmp`` parameter, since it only applies to attrs. Attrs users
should use the dataclass-standard ``eq`` and ``order`` parameter names
instead.
to setting both ``eq`` and ``order`` to ``True``. We chose not to
support a ``cmp`` parameter, since it only applies to attrs. Attrs
users should use the dataclass-standard ``eq`` and ``order`` parameter
names instead.

Automatic field name aliasing
-----------------------------
Expand Down Expand Up @@ -700,9 +712,6 @@ As this is not broadly applicable to dataclass libraries, this
additional logic is not accommodated with this proposal, so
users of Django would need to explicitly declare the ``id`` field.

This limitation may make it impractical to use the
``dataclass_transform`` mechanism with Django.

Class-wide default values
-------------------------

Expand Down