PEP 681: Move descriptor-field support to Rejected Ideas (#2477)

Co-authored-by: CAM Gerlach <CAM.Gerlach@Gerlach.CAM>
Co-authored-by: Jelle Zijlstra <jelle.zijlstra@gmail.com>
This commit is contained in:
Erik De Bonte 2022-03-31 08:15:58 -07:00 committed by GitHub
parent d0a55d763b
commit 03d6747f8f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
1 changed files with 21 additions and 46 deletions

View File

@ -213,7 +213,6 @@ customization of default behaviors:
eq_default: bool = True, eq_default: bool = True,
order_default: bool = False, order_default: bool = False,
kw_only_default: bool = False, kw_only_default: bool = False,
transform_descriptor_types: bool = False,
field_descriptors: tuple[type | Callable[..., Any], ...] = (), field_descriptors: tuple[type | Callable[..., Any], ...] = (),
) -> Callable[[_T], _T]: ... ) -> 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 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 specified, ``kw_only_default`` will default to False (the default
assumption for dataclass). 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 * ``field_descriptors`` specifies a static list of supported classes
that describe fields. Some libraries also supply functions to that describe fields. Some libraries also supply functions to
allocate instances of field descriptors, and those functions may allocate instances of field descriptors, and those functions may
@ -342,37 +333,6 @@ Metaclass example
id: int id: int
name: str 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 Field descriptors
----------------- -----------------
@ -506,7 +466,6 @@ For example:
"eq_default": True, "eq_default": True,
"order_default": False, "order_default": False,
"kw_only_default": False, "kw_only_default": False,
"transform_descriptor_types": False,
"field_descriptors": (), "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 single function/class or within a class hierarchy, the resulting
behavior is undefined. Library authors should avoid these scenarios. 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 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 SQLAlchemy. Users can manually set ``default=None`` on these fields
instead. 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 ``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-docs: https://docs.python.org/3/library/dataclasses.html#dataclasses.KW_ONLY
.. _#kw-only-issue: https://bugs.python.org/issue43532 .. _#kw-only-issue: https://bugs.python.org/issue43532
.. _#class-var: https://docs.python.org/3/library/dataclasses.html#class-variables .. _#class-var: https://docs.python.org/3/library/dataclasses.html#class-variables
.. _#pyright-descriptor-bug: https://github.com/microsoft/pyright/issues/3245
Copyright Copyright
========= =========