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 749: Move dataclass field types out of open issues #3994

Merged
merged 1 commit into from
Sep 24, 2024
Merged
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
92 changes: 35 additions & 57 deletions peps/pep-0749.rst
Original file line number Diff line number Diff line change
Expand Up @@ -661,6 +661,41 @@ Usually, users would use these attributes in combinations with
in SOURCE format, one could write
``annotationlib.call_evaluate_function(T.evaluate_bound, annotationlib.Format.SOURCE)``.

Behavior of dataclass field types
=================================

One consequence of the deferred evaluation of annotations is that
dataclasses can use forward references in their annotations:

.. code:: pycon

>>> from dataclasses import dataclass
>>> @dataclass
... class D:
... x: undefined
...

However, the ``FORWARDREF`` format leaks into the field types of the dataclass:

.. code:: pycon

>>> fields(D)[0].type
ForwardRef('undefined')

We considered a change where the ``.type`` attribute of a field object would
trigger evaluation of annotations, so that the field type could contain actual
values in the case of forward references that were defined after the dataclass
itself was created, but before the field type is accessed.
However, this would also mean that accessing ``.type`` could now run arbitrary
code in the annotation, and potentially throws errors such as :py:exc:`NameError`.

Therefore, we consider it more user-friendly to keep the ``ForwardRef`` object
in the type, and document that users who want to resolve forward references
can use the ``ForwardRef.evaluate`` method.

If use cases come up in the future, we could add additional functionality,
such as a new method that re-evaluates the annotation from scratch.

Miscellaneous implementation details
====================================

Expand Down Expand Up @@ -793,63 +828,6 @@ as we make progress on implementing it. Readers are encouraged to follow the
implementation of the PEP and try out the draft implementation. Any feedback may
be incorporated into future versions of this PEP.

Should dataclass field types use deferred evaluation?
-----------------------------------------------------

The current draft implementation already supports deferred evaluation in dataclasses,
so this works:

.. code:: pycon

>>> from dataclasses import dataclass
>>> @dataclass
... class D:
... x: undefined
...

However, the ``FORWARDREF`` format leaks into the field types of the dataclass:

.. code:: pycon

>>> fields(D)[0].type
ForwardRef('undefined')

We could instead add deferred evaluation for the field type, similar to that outlined
above for type alias values.

Accessing ``.type`` might throw an error:

.. code:: pycon

>>> @dataclass
... class D:
... x: undefined
...
>>> field = fields(D)[0]
>>> field.type
Traceback (most recent call last):
File "<python-input-4>", line 1, in <module>
field.type
File ".../dataclasses.py", line 308, in type
annos = self._annotate(annotationlib.Format.VALUE)
File "<python-input-2>", line 3, in __annotate__
x: undefined
^^^^^^^^^
NameError: name 'undefined' is not defined

But users could use ``annotationlib.call_evaluate_function`` to get the type in other formats:

.. code:: pycon

>>> annotationlib.call_evaluate_function(field.evaluate_type, annotationlib.Format.SOURCE)
'undefined'
>>> annotationlib.call_evaluate_function(field.evaluate_type, annotationlib.Format.FORWARDREF)
ForwardRef('undefined')

Other variations are possible. For example, we could leave the ``type`` attribute unchanged,
and only add the ``evaluate_type`` method. This avoids unpleasant surprises where accessing
``.type`` may throw an exception.

Acknowledgments
===============

Expand Down
Loading