Skip to content

Commit 4285bc0

Browse files
committed
Merge branch 'master' into stubtest_context
2 parents ebe0bcb + 15b8ca9 commit 4285bc0

File tree

6 files changed

+105
-17
lines changed

6 files changed

+105
-17
lines changed

mypy/checkexpr.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -582,7 +582,10 @@ def visit_call_expr_inner(self, e: CallExpr, allow_none_return: bool = False) ->
582582
and not node.node.no_args
583583
and not (
584584
isinstance(union_target := get_proper_type(node.node.target), UnionType)
585-
and union_target.uses_pep604_syntax
585+
and (
586+
union_target.uses_pep604_syntax
587+
or self.chk.options.python_version >= (3, 10)
588+
)
586589
)
587590
):
588591
self.msg.type_arguments_not_allowed(e)

mypy/checkpattern.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -539,12 +539,12 @@ def visit_class_pattern(self, o: ClassPattern) -> PatternType:
539539
#
540540
type_info = o.class_ref.node
541541
if type_info is None:
542-
return PatternType(AnyType(TypeOfAny.from_error), AnyType(TypeOfAny.from_error), {})
543-
if isinstance(type_info, TypeAlias) and not type_info.no_args:
542+
typ: Type = AnyType(TypeOfAny.from_error)
543+
elif isinstance(type_info, TypeAlias) and not type_info.no_args:
544544
self.msg.fail(message_registry.CLASS_PATTERN_GENERIC_TYPE_ALIAS, o)
545545
return self.early_non_match()
546-
if isinstance(type_info, TypeInfo):
547-
typ: Type = fill_typevars_with_any(type_info)
546+
elif isinstance(type_info, TypeInfo):
547+
typ = fill_typevars_with_any(type_info)
548548
elif isinstance(type_info, TypeAlias):
549549
typ = type_info.target
550550
elif (

mypy/stubtest.py

Lines changed: 22 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -961,22 +961,36 @@ def from_overloadedfuncdef(stub: nodes.OverloadedFuncDef) -> Signature[nodes.Arg
961961
# For most dunder methods, just assume all args are positional-only
962962
assume_positional_only = is_dunder(stub.name, exclude_special=True)
963963

964-
all_args: dict[str, list[tuple[nodes.Argument, int]]] = {}
964+
is_arg_pos_only: defaultdict[str, set[bool]] = defaultdict(set)
965965
for func in map(_resolve_funcitem_from_decorator, stub.items):
966966
assert func is not None, "Failed to resolve decorated overload"
967967
args = maybe_strip_cls(stub.name, func.arguments)
968968
for index, arg in enumerate(args):
969-
# For positional-only args, we allow overloads to have different names for the same
970-
# argument. To accomplish this, we just make up a fake index-based name.
971-
name = (
972-
f"__{index}"
973-
if arg.variable.name.startswith("__")
969+
if (
970+
arg.variable.name.startswith("__")
974971
or arg.pos_only
975972
or assume_positional_only
976973
or arg.variable.name.strip("_") == "self"
977974
or (index == 0 and arg.variable.name.strip("_") == "cls")
978-
else arg.variable.name
979-
)
975+
):
976+
is_arg_pos_only[arg.variable.name].add(True)
977+
else:
978+
is_arg_pos_only[arg.variable.name].add(False)
979+
980+
all_args: dict[str, list[tuple[nodes.Argument, int]]] = {}
981+
for func in map(_resolve_funcitem_from_decorator, stub.items):
982+
assert func is not None, "Failed to resolve decorated overload"
983+
args = maybe_strip_cls(stub.name, func.arguments)
984+
for index, arg in enumerate(args):
985+
# For positional-only args, we allow overloads to have different names for the same
986+
# argument. To accomplish this, we just make up a fake index-based name.
987+
# We can only use the index-based name if the argument is always
988+
# positional only. Sometimes overloads have an arg as positional-only
989+
# in some but not all branches of the overload.
990+
name = arg.variable.name
991+
if is_arg_pos_only[name] == {True}:
992+
name = f"__{index}"
993+
980994
all_args.setdefault(name, []).append((arg, index))
981995

982996
def get_position(arg_name: str) -> int:

mypy/test/teststubtest.py

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,11 @@
1313
from typing import Any, Callable
1414

1515
import mypy.stubtest
16+
from mypy import build, nodes
17+
from mypy.modulefinder import BuildSource
18+
from mypy.options import Options
1619
from mypy.stubtest import parse_options, test_stubs
20+
from mypy.test.config import test_temp_dir
1721
from mypy.test.data import root_dir
1822

1923

@@ -158,6 +162,14 @@ def __invert__(self: _T) -> _T: pass
158162
"""
159163

160164

165+
def build_helper(source: str) -> build.BuildResult:
166+
return build.build(
167+
sources=[BuildSource("main.pyi", None, textwrap.dedent(source))],
168+
options=Options(),
169+
alt_lib_path=test_temp_dir,
170+
)
171+
172+
161173
def run_stubtest_with_stderr(
162174
stub: str, runtime: str, options: list[str], config_file: str | None = None
163175
) -> tuple[str, str]:
@@ -842,6 +854,18 @@ def f2(self, *a) -> int: ...
842854
""",
843855
error=None,
844856
)
857+
yield Case(
858+
stub="""
859+
@overload
860+
def f(a: int) -> int: ...
861+
@overload
862+
def f(a: int, b: str, /) -> str: ...
863+
""",
864+
runtime="""
865+
def f(a, *args): ...
866+
""",
867+
error=None,
868+
)
845869

846870
@collect_cases
847871
def test_property(self) -> Iterator[Case]:
@@ -2815,6 +2839,25 @@ def test_builtin_signature_with_unrepresentable_default(self) -> None:
28152839
== "def (self, sep = ..., bytes_per_sep = ...)"
28162840
)
28172841

2842+
def test_overload_signature(self) -> None:
2843+
# The same argument as both positional-only and pos-or-kw in
2844+
# different overloads previously produced incorrect signatures
2845+
source = """
2846+
from typing import overload
2847+
@overload
2848+
def myfunction(arg: int) -> None: ...
2849+
@overload
2850+
def myfunction(arg: str, /) -> None: ...
2851+
"""
2852+
result = build_helper(source)
2853+
stub = result.files["__main__"].names["myfunction"].node
2854+
assert isinstance(stub, nodes.OverloadedFuncDef)
2855+
sig = mypy.stubtest.Signature.from_overloadedfuncdef(stub)
2856+
if sys.version_info >= (3, 10):
2857+
assert str(sig) == "def (arg: builtins.int | builtins.str)"
2858+
else:
2859+
assert str(sig) == "def (arg: Union[builtins.int, builtins.str])"
2860+
28182861
def test_config_file(self) -> None:
28192862
runtime = "temp = 5\n"
28202863
stub = "from decimal import Decimal\ntemp: Decimal\n"

test-data/unit/check-python310.test

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -721,13 +721,14 @@ m: object
721721
match m:
722722
case xyz(y): # E: Name "xyz" is not defined
723723
reveal_type(m) # N: Revealed type is "Any"
724-
reveal_type(y) # E: Cannot determine type of "y" \
725-
# N: Revealed type is "Any"
724+
reveal_type(y) # N: Revealed type is "Any"
726725

727726
match m:
728727
case xyz(z=x): # E: Name "xyz" is not defined
729-
reveal_type(x) # E: Cannot determine type of "x" \
730-
# N: Revealed type is "Any"
728+
reveal_type(x) # N: Revealed type is "Any"
729+
case (xyz1() as n) | (xyz2(attr=n)): # E: Name "xyz1" is not defined \
730+
# E: Name "xyz2" is not defined
731+
reveal_type(n) # N: Revealed type is "Any"
731732

732733
[case testMatchClassPatternCaptureDataclass]
733734
from dataclasses import dataclass

test-data/unit/check-unions.test

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1345,3 +1345,30 @@ x: Union[C1, C2, C3, C4, C5, C6, C7, C8, C9, C10, C11]
13451345
y: Union[C1, C2, C3, C4, C5, C6, C7, C8, C9, C10, C11, None]
13461346
x = y # E: Incompatible types in assignment (expression has type "Union[C1, C2, C3, C4, C5, <6 more items>, None]", variable has type "Union[C1, C2, C3, C4, C5, <6 more items>]") \
13471347
# N: Item in the first union not in the second: "None"
1348+
1349+
[case testTypeAliasWithOldUnionIsInstance]
1350+
# flags: --python-version 3.10
1351+
from typing import Union
1352+
SimpleAlias = Union[int, str]
1353+
1354+
def foo(x: Union[int, str, tuple]):
1355+
# TODO: fix the typeshed stub for isinstance
1356+
if isinstance(x, SimpleAlias): # E: Argument 2 to "isinstance" has incompatible type "<typing special form>"; expected "type"
1357+
reveal_type(x) # N: Revealed type is "Union[builtins.int, builtins.str, builtins.tuple[Any, ...]]"
1358+
else:
1359+
reveal_type(x) # N: Revealed type is "Union[builtins.int, builtins.str, builtins.tuple[Any, ...]]"
1360+
[builtins fixtures/tuple.pyi]
1361+
1362+
1363+
[case testTypeAliasWithOldUnionIsInstancePython39]
1364+
# flags: --python-version 3.9
1365+
from typing import Union
1366+
SimpleAlias = Union[int, str]
1367+
1368+
def foo(x: Union[int, str, tuple]):
1369+
if isinstance(x, SimpleAlias): # E: Parameterized generics cannot be used with class or instance checks \
1370+
# E: Argument 2 to "isinstance" has incompatible type "<typing special form>"; expected "type"
1371+
reveal_type(x) # N: Revealed type is "Union[builtins.int, builtins.str, builtins.tuple[Any, ...]]"
1372+
else:
1373+
reveal_type(x) # N: Revealed type is "Union[builtins.int, builtins.str, builtins.tuple[Any, ...]]"
1374+
[builtins fixtures/tuple.pyi]

0 commit comments

Comments
 (0)