Skip to content

typing._eval_type is not preserving GenericAlias subclasses #130870

Open
@Viicos

Description

@Viicos

Bug report

Bug description:

In typing._eval_type, generic aliases are reconstructed this way:

cpython/Lib/typing.py

Lines 488 to 489 in e53d105

if isinstance(t, GenericAlias):
return GenericAlias(t.__origin__, ev_args)

As GenericAlias is subclassable, we can loose the actual subclass in some cases:

from typing import get_type_hints

from collections.abc import Callable

C = Callable[[str, 'int'], int]

C.__class__
#> <class 'collections.abc._CallableGenericAlias'>

C.__class__.__bases__
#> (<class 'types.GenericAlias'>,)

class A:
    c: C

hints = get_type_hints(A)
hints['c'].__class__
#> <class 'types.GenericAlias'>

I couldn't find a way to get actual bugs from it, but the repr is different:

hints['c']
#> collections.abc.Callable[str, int, int]
C
#> collections.abc.Callable[[str, 'int'], int]

The issue is also relevant for typing._strip_annotations().

Proposed fix

diff --git a/Lib/typing.py b/Lib/typing.py
index 4b3c63b25ae..25e0576839f 100644
--- a/Lib/typing.py
+++ b/Lib/typing.py
@@ -486,7 +486,9 @@ def _eval_type(t, globalns, localns, type_params=_sentinel, *, recursive_guard=f
         if ev_args == t.__args__:
             return t
         if isinstance(t, GenericAlias):
-            return GenericAlias(t.__origin__, ev_args)
+            if _should_unflatten_callable_args(t, ev_args):
+                return t.__class__(t.__origin__, (ev_args[:-1], ev_args[-1]))
+            return t.__class__(t.__origin__, ev_args)
         if isinstance(t, Union):
             return functools.reduce(operator.or_, ev_args)
         else:

CPython versions tested on:

3.13

Operating systems tested on:

Linux

Linked PRs

Metadata

Metadata

Assignees

No one assigned

    Labels

    stdlibPython modules in the Lib dirtopic-typingtype-bugAn unexpected behavior, bug, or error

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions