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
Note that this is already an issue on Python < 3.14. However, the impact is somewhat limited as afaik this only happens with functions. The reason is that there is a really old cache invalidation mechanism seemingly introduced to avoid a leak issue in ForwardRef._evaluate. This logic would force the evaluation of the forward reference (even if _evaluate was already called) if the provided localns is different from the globalns (on L920 the evaluation logic goes on):
Using typing.get_type_hints, this condition is false when getting annotations of functions, because functions don't have locals and locals are set to globals if this is the case:
And I believe this is going to cause some issues with existing code bases, especially the ones using runtime typing libraries. In Pydantic, we already had issues like this one, as we recently changed our global/local namespace logic (and as such, we had a report of the above issue with classes: cloudflare/cloudflare-python#116 (comment)).
While we might argue that string annotations are no longer needed in 3.14, it is still relevant for libraries which need to keep support for older Python versions.
One possible solution would be to have _tp_cache() skip string arguments, so that we don't end up reusing the same ForwardRef instances. Not sure how big the impact will be.
CPython versions tested on:
3.14, CPython main branch
Operating systems tested on:
No response
The text was updated successfully, but these errors were encountered:
Bug report
Bug description:
Any typing construct using
typing._GenericAlias
(and nottypes.GenericAlias
) will convert the arguments toForwardRef
instances at runtime:Because
_GenericAlias.__getitem__
calls are cached with the_tp_cache()
decorator, we end up with the sameForwardRef
instance used:And this becomes an issue as
ForwardRef._evaluate()
calls are also cached per instance. Consider the following setup:Note that this is already an issue on Python < 3.14. However, the impact is somewhat limited as afaik this only happens with functions. The reason is that there is a really old cache invalidation mechanism seemingly introduced to avoid a leak issue in
ForwardRef._evaluate
. This logic would force the evaluation of the forward reference (even if_evaluate
was already called) if the providedlocalns
is different from theglobalns
(onL920
the evaluation logic goes on):cpython/Lib/typing.py
Lines 916 to 920 in dae5b16
Using
typing.get_type_hints
, this condition is false when getting annotations of functions, because functions don't have locals and locals are set to globals if this is the case:cpython/Lib/typing.py
Lines 2441 to 2442 in 8f93dd8
However, the implementation of PEP 649 removed this check and the cached evaluated value is unconditionally used:
cpython/Lib/annotationlib.py
Lines 95 to 101 in 8f93dd8
While the described bug above is pretty uncommon as it only occurs with functions, it also happens in classes with 3.14:
And I believe this is going to cause some issues with existing code bases, especially the ones using runtime typing libraries. In Pydantic, we already had issues like this one, as we recently changed our global/local namespace logic (and as such, we had a report of the above issue with classes: cloudflare/cloudflare-python#116 (comment)).
While we might argue that string annotations are no longer needed in 3.14, it is still relevant for libraries which need to keep support for older Python versions.
One possible solution would be to have
_tp_cache()
skip string arguments, so that we don't end up reusing the sameForwardRef
instances. Not sure how big the impact will be.CPython versions tested on:
3.14, CPython main branch
Operating systems tested on:
No response
The text was updated successfully, but these errors were encountered: