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:
parent
d0a55d763b
commit
03d6747f8f
67
pep-0681.rst
67
pep-0681.rst
|
@ -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
|
||||||
=========
|
=========
|
||||||
|
|
Loading…
Reference in New Issue