Skip to content

Commit f094905

Browse files
committed
Split up _determine_typevar_substitution
1 parent 23b393b commit f094905

File tree

1 file changed

+106
-76
lines changed

1 file changed

+106
-76
lines changed

Lib/typing.py

Lines changed: 106 additions & 76 deletions
Original file line numberDiff line numberDiff line change
@@ -1157,10 +1157,112 @@ def _is_unpacked_arbitrary_length_tuple(x: Any) -> bool:
11571157
_TypeVarOrTypeVarTuple = TypeVar | TypeVarTuple
11581158

11591159

1160+
def _replace_degenerate_unpacked_tuples(
1161+
args: tuple[type, ...]
1162+
) -> tuple[type, ...]:
1163+
"""Replaces e.g. `*tuple[int]` with just `int` in `args`."""
1164+
new_args = []
1165+
for arg in args:
1166+
if (_is_unpacked_tuple(arg)
1167+
and not _is_unpacked_arbitrary_length_tuple(arg)):
1168+
arg_tuple = arg.__args__[0] # The actual tuple[int]
1169+
new_args.extend(arg_tuple.__args__)
1170+
else:
1171+
new_args.append(arg)
1172+
return tuple(new_args)
1173+
1174+
1175+
def _determine_typevar_substition_no_typevartuples(
1176+
typevars: tuple[TypeVar, ...],
1177+
args: tuple[type, ...]
1178+
) -> dict[TypeVar, type]:
1179+
if any(_is_unpacked_arbitrary_length_tuple(arg) for arg in args):
1180+
raise TypeError(f"Cannot assign type arguments '{args}' to type "
1181+
f"variables '{typevars}': cannot assign "
1182+
"unpacked arbitrary-length tuple to a TypeVar")
1183+
if len(typevars) != len(args):
1184+
raise TypeError(f"Number of type variables ({len(typevars)}) "
1185+
f"doesn't match number of type "
1186+
f"arguments ({len(args)})")
1187+
return dict(zip(typevars, args))
1188+
1189+
1190+
def _count_num_items_before_and_after(
1191+
items: tuple[Any, ...],
1192+
item_of_interest: Any,
1193+
) -> tuple[int, int]:
1194+
item_of_interest_idxs = [i for i, x in enumerate(items)
1195+
if x == item_of_interest]
1196+
if not item_of_interest_idxs:
1197+
raise ValueError("Item of interest not found")
1198+
if len(item_of_interest_idxs) > 1:
1199+
raise ValueError("Item of interest occurs more than once")
1200+
[item_of_interest_idx] = item_of_interest_idxs
1201+
num_start = item_of_interest_idx
1202+
# Assuming len(items) == 3:
1203+
# * If item_of_interest_idx == 0, there are 2 items after item_of_interest.
1204+
# * If item_of_interest_idx == 2, there are 0 items after item_of_interest.
1205+
num_end = len(items) - item_of_interest_idx - 1
1206+
return num_start, num_end
1207+
1208+
1209+
def _determine_typevar_substitution_single_typevartuple(
1210+
typevars: tuple[_TypeVarOrTypeVarTuple, ...],
1211+
args: tuple[type, ...]
1212+
) -> dict[_TypeVarOrTypeVarTuple, type | tuple[type, ...]]:
1213+
[typevartuple_idx] = [i for i, x in enumerate(typevars)
1214+
if isinstance(x, TypeVarTuple)]
1215+
typevartuple = typevars[typevartuple_idx]
1216+
num_start_typevars, num_end_typevars = _count_num_items_before_and_after(
1217+
typevars, typevartuple
1218+
)
1219+
1220+
# Even if one of the args is an unpacked arbitrary-length tuple, we still
1221+
# need at least as many args as normal TypeVars.
1222+
# Couldn't we split the arbitrary-length tuples across multiple TypeVars?
1223+
# No, because an arbitrary-length tuple could have length zero!
1224+
if len(args) < num_start_typevars + num_end_typevars:
1225+
raise TypeError(
1226+
"Expected at least {} type parameters, but only got {}".format(
1227+
num_start_typevars + num_end_typevars, len(args)
1228+
)
1229+
)
1230+
1231+
if num_start_typevars == num_end_typevars == 0:
1232+
# It's just a single TypeVarTuple.
1233+
return {typevars[0]: args}
1234+
elif num_end_typevars == 0:
1235+
args_for_typevartuple = args[num_start_typevars:]
1236+
args_for_typevars = args[:num_start_typevars]
1237+
if any(_is_unpacked_arbitrary_length_tuple(arg)
1238+
for arg in args_for_typevars):
1239+
raise TypeError(f"Cannot assign type arguments '{args}' to type "
1240+
f"variables '{typevars}': cannot assign "
1241+
"arbitrary-length tuple to a TypeVar")
1242+
return {
1243+
**dict(zip(typevars[:num_start_typevars], args_for_typevars)),
1244+
typevartuple: args_for_typevartuple,
1245+
}
1246+
else:
1247+
args_for_left_typevars = args[:num_start_typevars]
1248+
args_for_typevartuple = args[num_start_typevars:-num_end_typevars]
1249+
args_for_right_typevars = args[-num_end_typevars:]
1250+
if any(_is_unpacked_arbitrary_length_tuple(arg)
1251+
for arg in args_for_left_typevars + args_for_right_typevars):
1252+
raise TypeError(f"Cannot assign type arguments '{args}' to type "
1253+
f"variables '{typevars}': cannot assign "
1254+
"arbitrary-length tuple to a TypeVar")
1255+
return {
1256+
**dict(zip(typevars[:num_start_typevars], args_for_left_typevars)),
1257+
typevartuple: args_for_typevartuple,
1258+
**dict(zip(typevars[-num_end_typevars:], args_for_right_typevars)),
1259+
}
1260+
1261+
11601262
def _determine_typevar_substitution(
11611263
typevars: tuple[_TypeVarOrTypeVarTuple, ...],
11621264
args: tuple[type, ...]
1163-
) -> dict[_TypeVarOrTypeVarTuple, type]:
1265+
) -> dict[_TypeVarOrTypeVarTuple, type | tuple[type, ...]]:
11641266
"""Determines how to assign type arguments to type variables.
11651267
11661268
Args:
@@ -1256,83 +1358,11 @@ def _determine_typevar_substitution(
12561358
raise TypeError("At most 1 TypeVarTuple may be used in a type "
12571359
f"parameter list, but saw {num_typevartuples}")
12581360

1259-
# First, we should go through `args` to replace e.g. *tuple[int] with int.
1260-
new_args = []
1261-
for arg in args:
1262-
if (_is_unpacked_tuple(arg)
1263-
and not _is_unpacked_arbitrary_length_tuple(arg)):
1264-
arg_tuple = arg.__args__[0] # The actual tuple[int]
1265-
new_args.extend(arg_tuple.__args__)
1266-
else:
1267-
new_args.append(arg)
1268-
args = tuple(new_args)
1269-
1270-
# Case 1: typevars does not contain any TypeVarTuples
1361+
args = _replace_degenerate_unpacked_tuples(args)
12711362

12721363
if num_typevartuples == 0:
1273-
if any(_is_unpacked_arbitrary_length_tuple(arg) for arg in args):
1274-
raise TypeError(f"Cannot assign type arguments '{args}' to type "
1275-
f"variables '{typevars}': cannot assign "
1276-
"unpacked arbitrary-length tuple to a TypeVar")
1277-
if len(typevars) != len(args):
1278-
raise TypeError(f"Number of type variables ({len(typevars)}) "
1279-
f"doesn't match number of type "
1280-
f"arguments ({len(args)})")
1281-
return dict(zip(typevars, args))
1282-
1283-
# Case 2: typevars contains a single TypeVarTuple
1284-
1285-
[typevartuple_idx] = [i for i, x in enumerate(typevars)
1286-
if isinstance(x, TypeVarTuple)]
1287-
typevartuple = typevars[typevartuple_idx]
1288-
num_start_typevars = typevartuple_idx
1289-
# Assuming len(typevars) == 3:
1290-
# * If typevartuple_idx == 0, there are 2 TypeVars at
1291-
# the end of typevars.
1292-
# * If typevartuple_idx == 2 there are 0 TypeVars at
1293-
# the end of typevars.
1294-
num_end_typevars = len(typevars) - typevartuple_idx - 1
1295-
1296-
# Even if one of the args is an unpacked arbitrary-length tuple, we still
1297-
# need at least as many args as normal TypeVars.
1298-
# Couldn't we split the arbitrary-length tuples across multiple TypeVars?
1299-
# No, because an arbitrary-length tuple could have length zero!
1300-
if len(args) < num_start_typevars + num_end_typevars:
1301-
raise TypeError(
1302-
"Expected at least {} type parameters, but only got {}".format(
1303-
num_start_typevars + num_end_typevars, len(args)
1304-
)
1305-
)
1306-
1307-
if num_start_typevars == num_end_typevars == 0:
1308-
# It's just a single TypeVarTuple.
1309-
return {typevars[0]: args}
1310-
elif num_end_typevars == 0:
1311-
args_for_typevartuple = args[num_start_typevars:]
1312-
args_for_typevars = args[:num_start_typevars]
1313-
if any(_is_unpacked_arbitrary_length_tuple(arg)
1314-
for arg in args_for_typevars):
1315-
raise TypeError(f"Cannot assign type arguments '{args}' to type "
1316-
f"variables '{typevars}': cannot assign "
1317-
"arbitrary-length tuple to a TypeVar")
1318-
return {
1319-
**dict(zip(typevars[:num_start_typevars], args_for_typevars)),
1320-
typevartuple: args_for_typevartuple,
1321-
}
1322-
else:
1323-
args_for_left_typevars = args[:num_start_typevars]
1324-
args_for_typevartuple = args[num_start_typevars:-num_end_typevars]
1325-
args_for_right_typevars = args[-num_end_typevars:]
1326-
if any(_is_unpacked_arbitrary_length_tuple(arg)
1327-
for arg in args_for_left_typevars + args_for_right_typevars):
1328-
raise TypeError(f"Cannot assign type arguments '{args}' to type "
1329-
f"variables '{typevars}': cannot assign "
1330-
"arbitrary-length tuple to a TypeVar")
1331-
return {
1332-
**dict(zip(typevars[:num_start_typevars], args_for_left_typevars)),
1333-
typevartuple: args_for_typevartuple,
1334-
**dict(zip(typevars[-num_end_typevars:], args_for_right_typevars)),
1335-
}
1364+
return _determine_typevar_substition_no_typevartuples(typevars, args)
1365+
return _determine_typevar_substitution_single_typevartuple(typevars, args)
13361366

13371367

13381368
# Special typing constructs Union, Optional, Generic, Callable and Tuple

0 commit comments

Comments
 (0)