From 03d6747f8ff1bd62c12c6fffef5444f97977f2bb Mon Sep 17 00:00:00 2001 From: Erik De Bonte Date: Thu, 31 Mar 2022 08:15:58 -0700 Subject: [PATCH] PEP 681: Move descriptor-field support to Rejected Ideas (#2477) Co-authored-by: CAM Gerlach Co-authored-by: Jelle Zijlstra --- pep-0681.rst | 67 ++++++++++++++++------------------------------------ 1 file changed, 21 insertions(+), 46 deletions(-) diff --git a/pep-0681.rst b/pep-0681.rst index e9871fb667f..1d3dc0a32fc 100644 --- a/pep-0681.rst +++ b/pep-0681.rst @@ -213,7 +213,6 @@ customization of default behaviors: eq_default: bool = True, order_default: bool = False, kw_only_default: bool = False, - transform_descriptor_types: bool = False, field_descriptors: tuple[type | Callable[..., Any], ...] = (), ) -> Callable[[_T], _T]: ... @@ -229,14 +228,6 @@ customization of default behaviors: 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). * ``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 @@ -342,37 +333,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 ----------------- @@ -506,7 +466,6 @@ For example: "eq_default": True, "order_default": False, "kw_only_default": False, - "transform_descriptor_types": False, "field_descriptors": (), } @@ -597,11 +556,6 @@ If multiple ``dataclass_transform`` decorators are found, either on a single function/class or within a class hierarchy, the resulting 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 -undefined. - Reference Implementation ======================== @@ -711,6 +665,26 @@ We chose not to support this feature, since it is specific to SQLAlchemy. Users can manually set ``default=None`` on these fields instead. +Descriptor-typed field support +------------------------------ + +We considered adding a boolean parameter on ``dataclass_transform`` +to enable better support for fields with descriptor types, which is +common in SQLAlchemy. When enabled, the type of each parameter on the +synthesized ``__init__`` method corresponding to a descriptor-typed +field would be the type of the value parameter to the descriptor's +``__set__`` method rather than the descriptor type itself. Similarly, +when setting the field, the ``__set__`` value type would be expected. +And when getting the value of the field, its type would be expected to +match the return type of ``__get__``. + +This idea was based on the belief that ``dataclass`` did not properly +support descriptor-typed fields. In fact it does, but type checkers +(at least mypy and pyright) did not reflect the runtime behavior which +led to our misunderstanding. For more details, see the +`Pyright bug <#pyright-descriptor-bug_>`__. + + ``converter`` field descriptor parameter ---------------------------------------- @@ -748,6 +722,7 @@ References .. _#kw-only-docs: https://docs.python.org/3/library/dataclasses.html#dataclasses.KW_ONLY .. _#kw-only-issue: https://bugs.python.org/issue43532 .. _#class-var: https://docs.python.org/3/library/dataclasses.html#class-variables +.. _#pyright-descriptor-bug: https://github.com/microsoft/pyright/issues/3245 Copyright =========