get_origin/get_args(TypeAliasType)

At runtime, pre-PEP-695 TypeAlias, and PEP-695 type statement are not equivalent.
While it surprised me :slight_smile:, then perhaps it’s not unsurprising that get_origin() and get_args() of a TypeAliasType instance return None and ().

However, is there a reason get_origin() and get_args() don’t resolve that by addition of below?

    if isinstance(tp, TypeAliasType):
        tp = tp.__value__

As I see it, nothing is lost (TypeAliasType never has any arguments) and it simplifies introspection for yet-another corner case.

Consider the following:

>>> from typing import get_origin as go, get_args as ga
>>> print(go(int))
None
>>> ga(int)
()
>>> type Foo = int
>>> print(go(Foo))
None
>>> ga(Foo)
()
>>> type Bar = str | int
>>> print(go(Bar))
None
>>> ga(Bar)
()
>>> go(Bar.__value__)
<class 'types.UnionType'>
>>> ga(str | int)
(<class 'str'>, <class 'int'>)
>>> type Baz[T] = T
>>> print(go(Baz[int]))
Baz
>>> ga(Baz[int])
(<class 'int'>,)

Unless I’m missing something, I think the current behaviour is justified. If you want .__value__'s origin and arguments, you can always reach for it yourself explicitly, can’t you?

When we have types with arguments, type statement and TypeAlias and ‘plain’ differ:

abc1: TypeAlias = Literal['a', 'b', 'c']  # pre PEP 695 - TypeAlias
type abc2 = Literal['a', 'b', 'c']        # equivalent with PEP 695 - type statement

print(get_origin(Literal['a', 'b', 'c']), get_args(Literal['a', 'b', 'c']))  # typing.Literal ('a', 'b', 'c')
print(get_origin(abc1), get_args(abc1))                                      # typing.Literal ('a', 'b', 'c')
print(get_origin(abc2), get_args(abc2))                                      # None ()
print(get_origin(abc2.__value__), get_args(abc2.__value__))                  # typing.Literal ('a', 'b', 'c')

So yes, explicitly I can resolve (of course), but only at the expense of yet another corner case in run-time interpretation of type expressions.
Type expression, TypeAlias and TypeAliasType.__value__ return the same and avoid this. TypeAliasType is the odd (?) one out.
I wondered why the explicit ‘delegation’ to .__value__ (in user code) isn’t done implicitly by get_args() and get_origin() (in library code). Having any introspection dealing both with type expressions / pre-PEP-695 aliases and with PEP-695 aliases will need to distinguish the cases. I’d rather have that done once (in library code) than repeatedly (in user code).