From 0d213b158a23d86f15ea74a8880bd13846945ed9 Mon Sep 17 00:00:00 2001 From: Jelle Zijlstra Date: Tue, 24 Sep 2024 14:00:01 -0700 Subject: [PATCH] PEP 749: Move dataclass field types out of open issues --- peps/pep-0749.rst | 92 ++++++++++++++++++----------------------------- 1 file changed, 35 insertions(+), 57 deletions(-) diff --git a/peps/pep-0749.rst b/peps/pep-0749.rst index 6503ef832f1..601dc063d64 100644 --- a/peps/pep-0749.rst +++ b/peps/pep-0749.rst @@ -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 ==================================== @@ -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 "", line 1, in - field.type - File ".../dataclasses.py", line 308, in type - annos = self._annotate(annotationlib.Format.VALUE) - File "", 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 ===============