Skip to content

Commit

Permalink
Merge remote-tracking branch 'upstream/main' into ParamSpec/Generic-s…
Browse files Browse the repository at this point in the history
…imple
  • Loading branch information
Daraan committed Oct 28, 2024
2 parents a280a44 + 67c16e1 commit edf7d19
Show file tree
Hide file tree
Showing 4 changed files with 102 additions and 2 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/third_party.yml
Original file line number Diff line number Diff line change
Expand Up @@ -412,5 +412,5 @@ jobs:
owner: "python",
repo: "typing_extensions",
title: `Third-party tests failed on ${new Date().toDateString()}`,
body: "Runs listed here: https://github.com/python/typing_extensions/actions/workflows/third_party.yml",
body: "Full history of runs listed here: https://github.com/python/typing_extensions/actions/workflows/third_party.yml",
})
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,9 @@ aliases that have a `Concatenate` special form as their argument.
`Ellipsis` as an argument. Patch by [Daraan](https://github.com/Daraan).
- Fix error in subscription of `Unpack` aliases causing nested Unpacks
to not be resolved correctly. Patch by [Daraan](https://github.com/Daraan).
- Backport CPython PR [#124795](https://github.com/python/cpython/pull/124795):
fix `TypeAliasType` not raising an error on non-tuple inputs for `type_params`.
Patch by [Daraan](https://github.com/Daraan).

# Release 4.12.2 (June 7, 2024)

Expand Down
78 changes: 78 additions & 0 deletions src/test_typing_extensions.py
Original file line number Diff line number Diff line change
Expand Up @@ -6294,6 +6294,10 @@ def test_typing_extensions_defers_when_possible(self):
'AsyncGenerator', 'ContextManager', 'AsyncContextManager',
'ParamSpec', 'TypeVar', 'TypeVarTuple', 'get_type_hints',
}
if sys.version_info < (3, 14):
exclude |= {
'TypeAliasType'
}
if not typing_extensions._PEP_728_IMPLEMENTED:
exclude |= {'TypedDict', 'is_typeddict'}
for item in typing_extensions.__all__:
Expand Down Expand Up @@ -7504,6 +7508,80 @@ def test_no_instance_subclassing(self):
class MyAlias(TypeAliasType):
pass

def test_type_var_compatibility(self):
# Regression test to assure compatibility with typing variants
typingT = typing.TypeVar('typingT')
T1 = TypeAliasType("TypingTypeVar", ..., type_params=(typingT,))
self.assertEqual(T1.__type_params__, (typingT,))

# Test typing_extensions backports
textT = TypeVar('textT')
T2 = TypeAliasType("TypingExtTypeVar", ..., type_params=(textT,))
self.assertEqual(T2.__type_params__, (textT,))

textP = ParamSpec("textP")
T3 = TypeAliasType("TypingExtParamSpec", ..., type_params=(textP,))
self.assertEqual(T3.__type_params__, (textP,))

textTs = TypeVarTuple("textTs")
T4 = TypeAliasType("TypingExtTypeVarTuple", ..., type_params=(textTs,))
self.assertEqual(T4.__type_params__, (textTs,))

@skipUnless(TYPING_3_10_0, "typing.ParamSpec is not available before 3.10")
def test_param_spec_compatibility(self):
# Regression test to assure compatibility with typing variant
typingP = typing.ParamSpec("typingP")
T5 = TypeAliasType("TypingParamSpec", ..., type_params=(typingP,))
self.assertEqual(T5.__type_params__, (typingP,))

@skipUnless(TYPING_3_12_0, "typing.TypeVarTuple is not available before 3.12")
def test_type_var_tuple_compatibility(self):
# Regression test to assure compatibility with typing variant
typingTs = typing.TypeVarTuple("typingTs")
T6 = TypeAliasType("TypingTypeVarTuple", ..., type_params=(typingTs,))
self.assertEqual(T6.__type_params__, (typingTs,))

def test_type_params_possibilities(self):
T = TypeVar('T')
# Test not a tuple
with self.assertRaisesRegex(TypeError, "type_params must be a tuple"):
TypeAliasType("InvalidTypeParams", List[T], type_params=[T])

# Test default order and other invalid inputs
T_default = TypeVar('T_default', default=int)
Ts = TypeVarTuple('Ts')
Ts_default = TypeVarTuple('Ts_default', default=Unpack[Tuple[str, int]])
P = ParamSpec('P')
P_default = ParamSpec('P_default', default=[str, int])

# NOTE: PEP 696 states: "TypeVars with defaults cannot immediately follow TypeVarTuples"
# this is currently not enforced for the type statement and is not tested.
# PEP 695: Double usage of the same name is also not enforced and not tested.
valid_cases = [
(T, P, Ts),
(T, Ts_default),
(P_default, T_default),
(P, T_default, Ts_default),
(T_default, P_default, Ts_default),
]
invalid_cases = [
((T_default, T), f"non-default type parameter '{T!r}' follows default"),
((P_default, P), f"non-default type parameter '{P!r}' follows default"),
((Ts_default, T), f"non-default type parameter '{T!r}' follows default"),
# Only type params are accepted
((1,), "Expected a type param, got 1"),
((str,), f"Expected a type param, got {str!r}"),
# Unpack is not a TypeVar but isinstance(Unpack[Ts], TypeVar) is True in Python < 3.12
((Unpack[Ts],), f"Expected a type param, got {re.escape(repr(Unpack[Ts]))}"),
]

for case in valid_cases:
with self.subTest(type_params=case):
TypeAliasType("OkCase", List[T], type_params=case)
for case, msg in invalid_cases:
with self.subTest(type_params=case):
with self.assertRaisesRegex(TypeError, msg):
TypeAliasType("InvalidCase", List[T], type_params=case)

class DocTests(BaseTestCase):
def test_annotation(self):
Expand Down
21 changes: 20 additions & 1 deletion src/typing_extensions.py
Original file line number Diff line number Diff line change
Expand Up @@ -3543,8 +3543,9 @@ def __ror__(self, other):
return typing.Union[other, self]


if hasattr(typing, "TypeAliasType"):
if sys.version_info >= (3, 14):
TypeAliasType = typing.TypeAliasType
# 3.8-3.13
else:
def _is_unionable(obj):
"""Corresponds to is_unionable() in unionobject.c in CPython."""
Expand Down Expand Up @@ -3617,11 +3618,29 @@ class TypeAliasType:
def __init__(self, name: str, value, *, type_params=()):
if not isinstance(name, str):
raise TypeError("TypeAliasType name must be a string")
if not isinstance(type_params, tuple):
raise TypeError("type_params must be a tuple")
self.__value__ = value
self.__type_params__ = type_params

default_value_encountered = False
parameters = []
for type_param in type_params:
if (
not isinstance(type_param, (TypeVar, TypeVarTuple, ParamSpec))
# 3.8-3.11
# Unpack Backport passes isinstance(type_param, TypeVar)
or _is_unpack(type_param)
):
raise TypeError(f"Expected a type param, got {type_param!r}")
has_default = (
getattr(type_param, '__default__', NoDefault) is not NoDefault
)
if default_value_encountered and not has_default:
raise TypeError(f"non-default type parameter '{type_param!r}'"
" follows default type parameter")
if has_default:
default_value_encountered = True
if isinstance(type_param, TypeVarTuple):
parameters.extend(type_param)
else:
Expand Down

0 comments on commit edf7d19

Please sign in to comment.