Skip to content

Commit b50471f

Browse files
authored
Merge branch 'master' into refactor-november
2 parents 21db9fd + 45aa599 commit b50471f

4 files changed

Lines changed: 80 additions & 4 deletions

File tree

docs/source/duck_type_compatibility.rst

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@ supported for a small set of built-in types:
99
* ``int`` is duck type compatible with ``float`` and ``complex``.
1010
* ``float`` is duck type compatible with ``complex``.
1111
* ``bytearray`` and ``memoryview`` are duck type compatible with ``bytes``.
12+
(this will be disabled by default in **mypy 2.0**, and currently can be
13+
disabled with :option:`--strict-bytes <mypy --strict-bytes>`.)
1214

1315
For example, mypy considers an ``int`` object to be valid whenever a
1416
``float`` object is expected. Thus code like this is nice and clean

mypy/semanal.py

Lines changed: 53 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5637,13 +5637,20 @@ def visit_type_alias_stmt(self, s: TypeAliasStmt) -> None:
56375637
else:
56385638
incomplete_target = has_placeholder(res)
56395639

5640-
incomplete_tv = any(has_placeholder(tv) for tv in alias_tvars)
5641-
if self.found_incomplete_ref(tag) or incomplete_target or incomplete_tv:
5640+
if self.found_incomplete_ref(tag) or incomplete_target:
56425641
# Since we have got here, we know this must be a type alias (incomplete refs
56435642
# may appear in nested positions), therefore use becomes_typeinfo=True.
56445643
self.mark_incomplete(s.name.name, s.value, becomes_typeinfo=True)
56455644
return
56465645

5646+
# Now go through all new variables and temporary replace all tvars that still
5647+
# refer to some placeholders. We defer the whole alias and will revisit it again,
5648+
# as well as all its dependents.
5649+
for i, tv in enumerate(alias_tvars):
5650+
if has_placeholder(tv):
5651+
self.mark_incomplete(s.name.name, s.value, becomes_typeinfo=True)
5652+
alias_tvars[i] = self._trivial_typevarlike_like(tv)
5653+
56475654
self.add_type_alias_deps(depends_on)
56485655
check_for_explicit_any(
56495656
res, self.options, self.is_typeshed_stub_file, self.msg, context=s
@@ -5677,7 +5684,10 @@ def visit_type_alias_stmt(self, s: TypeAliasStmt) -> None:
56775684
):
56785685
updated = False
56795686
if isinstance(existing.node, TypeAlias):
5680-
if existing.node.target != res:
5687+
if (
5688+
existing.node.target != res
5689+
or existing.node.alias_tvars != alias_node.alias_tvars
5690+
):
56815691
# Copy expansion to the existing alias, this matches how we update base classes
56825692
# for a TypeInfo _in place_ if there are nested placeholders.
56835693
existing.node.target = res
@@ -5707,6 +5717,46 @@ def visit_type_alias_stmt(self, s: TypeAliasStmt) -> None:
57075717
finally:
57085718
self.pop_type_args(s.type_args)
57095719

5720+
def _trivial_typevarlike_like(self, tv: TypeVarLikeType) -> TypeVarLikeType:
5721+
object_type = self.named_type("builtins.object")
5722+
if isinstance(tv, TypeVarType):
5723+
return TypeVarType(
5724+
tv.name,
5725+
tv.fullname,
5726+
tv.id,
5727+
values=[],
5728+
upper_bound=object_type,
5729+
default=AnyType(TypeOfAny.from_omitted_generics),
5730+
variance=tv.variance,
5731+
line=tv.line,
5732+
column=tv.column,
5733+
)
5734+
elif isinstance(tv, TypeVarTupleType):
5735+
tuple_type = self.named_type("builtins.tuple", [object_type])
5736+
return TypeVarTupleType(
5737+
tv.name,
5738+
tv.fullname,
5739+
tv.id,
5740+
upper_bound=tuple_type,
5741+
tuple_fallback=tuple_type,
5742+
default=AnyType(TypeOfAny.from_omitted_generics),
5743+
line=tv.line,
5744+
column=tv.column,
5745+
)
5746+
elif isinstance(tv, ParamSpecType):
5747+
return ParamSpecType(
5748+
tv.name,
5749+
tv.fullname,
5750+
tv.id,
5751+
flavor=tv.flavor,
5752+
upper_bound=object_type,
5753+
default=AnyType(TypeOfAny.from_omitted_generics),
5754+
line=tv.line,
5755+
column=tv.column,
5756+
)
5757+
else:
5758+
assert False, f"Unknown TypeVarLike: {tv!r}"
5759+
57105760
#
57115761
# Expressions
57125762
#

mypy/semanal_typeargs.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -151,7 +151,7 @@ def validate_args(
151151

152152
is_error = False
153153
is_invalid = False
154-
for (i, arg), tvar in zip(enumerate(args), type_vars):
154+
for arg, tvar in zip(args, type_vars):
155155
context = ctx if arg.line < 0 else arg
156156
if isinstance(tvar, TypeVarType):
157157
if isinstance(arg, ParamSpecType):

test-data/unit/check-python312.test

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2057,6 +2057,7 @@ reveal_type(AA.XX) # N: Revealed type is "def () -> __main__.XX"
20572057
y: B.Y
20582058
reveal_type(y) # N: Revealed type is "__main__.Y"
20592059
[builtins fixtures/tuple.pyi]
2060+
[typing fixtures/typing-full.pyi]
20602061

20612062
[case testPEP695TypeAliasRecursiveInvalid]
20622063
type X = X # E: Cannot resolve name "X" (possible cyclic definition)
@@ -2168,3 +2169,26 @@ x: MyTuple[int, str]
21682169
reveal_type(x[0]) # N: Revealed type is "Any"
21692170
[builtins fixtures/tuple.pyi]
21702171
[typing fixtures/typing-full.pyi]
2172+
2173+
[case testPEP695TypeAliasRecursiveInParameterBound]
2174+
from typing import Any
2175+
2176+
type A1[T: B1] = list[int]
2177+
type B1 = None | A1[B1]
2178+
x1: A1[B1]
2179+
y1: A1[int] # E: Type argument "int" of "A1" must be a subtype of "B1"
2180+
z1: A1[None]
2181+
2182+
type A2[T: B2] = list[T]
2183+
type B2 = None | A2[Any]
2184+
x2: A2[B2]
2185+
y2: A2[int] # E: Type argument "int" of "A2" must be a subtype of "B2"
2186+
z2: A2[None]
2187+
2188+
type A3[T: B3] = list[T]
2189+
type B3 = None | A3[B3]
2190+
x3: A3[B3]
2191+
y3: A3[int] # E: Type argument "int" of "A3" must be a subtype of "B3"
2192+
z3: A3[None]
2193+
[builtins fixtures/tuple.pyi]
2194+
[typing fixtures/typing-full.pyi]

0 commit comments

Comments
 (0)