You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Calling help() on a dataclass shows dataclass internal names instead of type names in 3.14 or later #137530
Help on class Example in module __main__:
class Example(builtins.object)
| Example(x: int) -> None
|
| docstring
|
| Methods defined here:
|
| __eq__(self, other)
| Return self==value.
|
| __init__(self, x: int) -> None
| Initialize self. See help(type(self)) for accurate signature.
...
Now, on 3.14 or later
Help on class Example in module __main__:
class Example(builtins.object)
| Example(x: __dataclass_type_x__) -> __dataclass___init___return_type__
|
| docstring
|
| Methods defined here:
|
| __eq__(self, other)
| Return self==value.
|
| __init__(self, x: __dataclass_type_x__) -> __dataclass___init___return_type__
| Initialize self. See help(type(self)) for accurate signature.
...
This looks a bit messy and reveals inner details that users of help probably don't need to see.
It looks like this comes from inspect.signature(cls, annotation_format=Format.STRING) being used in pydoc, but I think it's a dataclasses issue.
attrs doesn't suffer the same fate as it attaches annotations as __annotations__ instead of defining them in the source code string to be evaluated.
Inspired by that, for my own dataclass-like implementation for 3.14+ I'm collecting the annotations and generating an __annotate__ function which appears to solve this issue: see here
dataclasses could potentially do something similar.
This is a 3.14 regression, so I've marked it as a blocker for now. This doesn't look like something that can easily be fixed, but we should come to a decision before 3.14.0.
I think we should fix this but it doesn't seem critical for 3.14.0 since it's not runtime behavior; it can wait for 3.14.1.
I don't think @sobolevn's translation table is a great solution; ideally the internal names shouldn't bleed to users at all. Maybe we can attach a custom __annotate__ function that outputs nicer STRING format outputs?
The issue is caused by get_annotations with Format.STRING attempting to get the text from the source code and in this case succeeding, but the output is not what you actually want users to see.
These values are specifically from the generated __init__ function.
It may be worth noting that if you have a forward reference in your dataclass annotations, this will also appear in __annotations__ (and get_annotations with format.VALUE).
Other than probably needing some clean up/review, the problem I have with the state of that PR is VALUE annotations remain somewhat broken. An init=False field with a forward reference will break them even though that field isn't necessary for the __init__ annotations.
Unless dataclasses is going to start messing with annotationlib internals I think you either have to live with that or allow ForwardRef values to potentially leak into the VALUE annotations by using those as the source instead.
There is the possibility of just generating string annotations and attaching them to __annotations__ as if this were generated under __future__ annotations. Those would at least be consistent even if it would also defeat the point of the new deferred annotations.
I proposed this idea on discuss which would provide the tools needed to solve this cleanly (and for third party dataclass-like implementations) but there seemed to be absolutely no interest. Even so I imagine something like that might only be usable in a future release so we may need either a compromise or some internal work for 3.14.
An init=False field with a forward reference will break them
That doesn't seem like the worst thing to me; it's generally true in 3.14 that VALUE format annotations are sometimes not available, and you have to use FORWARDREF/STRING in those cases. Not ideal that VALUE here doesn't work when in theory it could work, but I think your PR is a great improvement already.
It's an improvement for STRING and FORWARDREF annotations. Technically VALUE can now fail in places it would have "succeeded" before (by leaking forward references).
I don't like that it has to replicate the logic of the field gathering in order to do this. This isn't too bad for dataclasses, but other implementations will have extra things (such as attrs converters) which need to be taken into account when generating annotations that are going to make their annotate functions more complicated.
Activity
ZeroIntensity commentedon Aug 8, 2025
This is a 3.14 regression, so I've marked it as a blocker for now. This doesn't look like something that can easily be fixed, but we should come to a decision before 3.14.0.
sobolevn commentedon Aug 8, 2025
Idea: we can at least define a table with translation. Something like:
What do you think?
CC @JelleZijlstra
JelleZijlstra commentedon Aug 8, 2025
That's unfortunate, thanks for noticing!
I think we should fix this but it doesn't seem critical for 3.14.0 since it's not runtime behavior; it can wait for 3.14.1.
I don't think @sobolevn's translation table is a great solution; ideally the internal names shouldn't bleed to users at all. Maybe we can attach a custom
__annotate__function that outputs nicer STRING format outputs?sobolevn commentedon Aug 8, 2025
Ok, I will try this out: custom
__annotate__seems like an interesting idea.DavidCEllis commentedon Aug 8, 2025
The issue is caused by
get_annotationswithFormat.STRINGattempting to get the text from the source code and in this case succeeding, but the output is not what you actually want users to see.These values are specifically from the generated
__init__function.It may be worth noting that if you have a forward reference in your dataclass annotations, this will also appear in
__annotations__(andget_annotationswithformat.VALUE).As linked earlier, this is what I currently have for my own implementation in ducktools-classbuilder
Where
annoswould be{f.name: f.type for f in fields}with the resulting function attached to the__init__method.Edit: Along with
"return": Noneincluded in the annotation dictEdit 2: And possibly with some extra filtering for
f.init.hugovk commentedon Aug 12, 2025
Agreed, let's wait for 3.14.1 and move to
deferred-blocker.hugovk commentedon Oct 14, 2025
3.14.0 is out, it's time to consider this for 3.14.1.
DavidCEllis commentedon Oct 14, 2025
I've kind of been sitting on this PR: #137711
Other than probably needing some clean up/review, the problem I have with the state of that PR is
VALUEannotations remain somewhat broken. Aninit=Falsefield with a forward reference will break them even though that field isn't necessary for the__init__annotations.Unless
dataclassesis going to start messing withannotationlibinternals I think you either have to live with that or allowForwardRefvalues to potentially leak into theVALUEannotations by using those as the source instead.There is the possibility of just generating string annotations and attaching them to
__annotations__as if this were generated under__future__annotations. Those would at least be consistent even if it would also defeat the point of the new deferred annotations.I proposed this idea on discuss which would provide the tools needed to solve this cleanly (and for third party dataclass-like implementations) but there seemed to be absolutely no interest. Even so I imagine something like that might only be usable in a future release so we may need either a compromise or some internal work for 3.14.
JelleZijlstra commentedon Oct 20, 2025
That doesn't seem like the worst thing to me; it's generally true in 3.14 that VALUE format annotations are sometimes not available, and you have to use FORWARDREF/STRING in those cases. Not ideal that VALUE here doesn't work when in theory it could work, but I think your PR is a great improvement already.
DavidCEllis commentedon Oct 21, 2025
It's an improvement for
STRINGandFORWARDREFannotations. TechnicallyVALUEcan now fail in places it would have "succeeded" before (by leaking forward references).I don't like that it has to replicate the logic of the field gathering in order to do this. This isn't too bad for dataclasses, but other implementations will have extra things (such as
attrsconverters) which need to be taken into account when generating annotations that are going to make their annotate functions more complicated.gh-137530: generate an __annotate__ function for dataclasses __init__ (…
[3.14] pythongh-137530: generate an __annotate__ function for datacla…
[3.14] gh-137530: generate an __annotate__ function for dataclasses _…
encukou commentedon Nov 11, 2025
Thank you @DavidCEllis for the report and fix, and @JelleZijlstra for reviews!
pythongh-137530: generate an __annotate__ function for dataclasses __…