Skip to content

Commit 45d661a

Browse files
Merge branch 'master' into pos_only-in-stubgen
2 parents a2a6e1c + db67888 commit 45d661a

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

75 files changed

+2322
-1590
lines changed

.github/workflows/mypy_primer.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ jobs:
2828
runs-on: ubuntu-latest
2929
strategy:
3030
matrix:
31-
shard-index: [0, 1, 2, 3, 4]
31+
shard-index: [0, 1, 2, 3, 4, 5]
3232
fail-fast: false
3333
timeout-minutes: 60
3434
steps:
@@ -63,7 +63,7 @@ jobs:
6363
mypy_primer \
6464
--repo mypy_to_test \
6565
--new $GITHUB_SHA --old base_commit \
66-
--num-shards 5 --shard-index ${{ matrix.shard-index }} \
66+
--num-shards 6 --shard-index ${{ matrix.shard-index }} \
6767
--debug \
6868
--additional-flags="--debug-serialize" \
6969
--output concise \

mypy/checker.py

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3138,7 +3138,7 @@ def check_assignment(
31383138
else:
31393139
self.check_getattr_method(signature, lvalue, name)
31403140

3141-
if name == "__slots__":
3141+
if name == "__slots__" and self.scope.active_class() is not None:
31423142
typ = lvalue_type or self.expr_checker.accept(rvalue)
31433143
self.check_slots_definition(typ, lvalue)
31443144
if name == "__match_args__" and inferred is not None:
@@ -3317,6 +3317,12 @@ def get_variable_type_context(self, inferred: Var, rvalue: Expression) -> Type |
33173317
type_contexts.append(base_type)
33183318
# Use most derived supertype as type context if available.
33193319
if not type_contexts:
3320+
if inferred.name == "__slots__" and self.scope.active_class() is not None:
3321+
str_type = self.named_type("builtins.str")
3322+
return self.named_generic_type("typing.Iterable", [str_type])
3323+
if inferred.name == "__all__" and self.scope.is_top_level():
3324+
str_type = self.named_type("builtins.str")
3325+
return self.named_generic_type("typing.Sequence", [str_type])
33203326
return None
33213327
candidate = type_contexts[0]
33223328
for other in type_contexts:
@@ -7273,7 +7279,7 @@ def named_type(self, name: str) -> Instance:
72737279
if isinstance(node, TypeAlias):
72747280
assert isinstance(node.target, Instance) # type: ignore[misc]
72757281
node = node.target.type
7276-
assert isinstance(node, TypeInfo)
7282+
assert isinstance(node, TypeInfo), node
72777283
any_type = AnyType(TypeOfAny.from_omitted_generics)
72787284
return Instance(node, [any_type] * len(node.defn.type_vars))
72797285

@@ -7292,7 +7298,7 @@ def lookup_typeinfo(self, fullname: str) -> TypeInfo:
72927298
# Assume that the name refers to a class.
72937299
sym = self.lookup_qualified(fullname)
72947300
node = sym.node
7295-
assert isinstance(node, TypeInfo)
7301+
assert isinstance(node, TypeInfo), node
72967302
return node
72977303

72987304
def type_type(self) -> Instance:
@@ -7883,6 +7889,9 @@ def has_valid_attribute(self, typ: Type, name: str) -> bool:
78837889
def get_expression_type(self, node: Expression, type_context: Type | None = None) -> Type:
78847890
return self.expr_checker.accept(node, type_context=type_context)
78857891

7892+
def is_defined_in_stub(self, typ: Instance, /) -> bool:
7893+
return self.modules[typ.type.module_name].is_stub
7894+
78867895
def check_deprecated(self, node: Node | None, context: Context) -> None:
78877896
"""Warn if deprecated and not directly imported with a `from` statement."""
78887897
if isinstance(node, Decorator):

mypy/checker_shared.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -277,6 +277,10 @@ def checking_await_set(self) -> Iterator[None]:
277277
def get_precise_awaitable_type(self, typ: Type, local_errors: ErrorWatcher) -> Type | None:
278278
raise NotImplementedError
279279

280+
@abstractmethod
281+
def is_defined_in_stub(self, typ: Instance, /) -> bool:
282+
raise NotImplementedError
283+
280284

281285
class CheckerScope:
282286
# We keep two stacks combined, to maintain the relative order
@@ -334,6 +338,10 @@ def current_self_type(self) -> Instance | TupleType | None:
334338
return fill_typevars(item)
335339
return None
336340

341+
def is_top_level(self) -> bool:
342+
"""Is current scope top-level (no classes or functions)?"""
343+
return len(self.stack) == 1
344+
337345
@contextmanager
338346
def push_function(self, item: FuncItem) -> Iterator[None]:
339347
self.stack.append(item)

mypy/checkpattern.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -796,9 +796,9 @@ def get_var(expr: Expression) -> Var:
796796
Warning: this in only true for expressions captured by a match statement.
797797
Don't call it from anywhere else
798798
"""
799-
assert isinstance(expr, NameExpr)
799+
assert isinstance(expr, NameExpr), expr
800800
node = expr.node
801-
assert isinstance(node, Var)
801+
assert isinstance(node, Var), node
802802
return node
803803

804804

mypy/fastparse.py

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -631,7 +631,7 @@ def fix_function_overloads(self, stmts: list[Statement]) -> list[Statement]:
631631
ret: list[Statement] = []
632632
current_overload: list[OverloadPart] = []
633633
current_overload_name: str | None = None
634-
seen_unconditional_func_def = False
634+
last_unconditional_func_def: str | None = None
635635
last_if_stmt: IfStmt | None = None
636636
last_if_overload: Decorator | FuncDef | OverloadedFuncDef | None = None
637637
last_if_stmt_overload_name: str | None = None
@@ -641,7 +641,7 @@ def fix_function_overloads(self, stmts: list[Statement]) -> list[Statement]:
641641
if_overload_name: str | None = None
642642
if_block_with_overload: Block | None = None
643643
if_unknown_truth_value: IfStmt | None = None
644-
if isinstance(stmt, IfStmt) and seen_unconditional_func_def is False:
644+
if isinstance(stmt, IfStmt):
645645
# Check IfStmt block to determine if function overloads can be merged
646646
if_overload_name = self._check_ifstmt_for_overloads(stmt, current_overload_name)
647647
if if_overload_name is not None:
@@ -669,11 +669,18 @@ def fix_function_overloads(self, stmts: list[Statement]) -> list[Statement]:
669669
last_if_unknown_truth_value = None
670670
current_overload.append(stmt)
671671
if isinstance(stmt, FuncDef):
672-
seen_unconditional_func_def = True
672+
# This is, strictly speaking, wrong: there might be a decorated
673+
# implementation. However, it only affects the error message we show:
674+
# ideally it's "already defined", but "implementation must come last"
675+
# is also reasonable.
676+
# TODO: can we get rid of this completely and just always emit
677+
# "implementation must come last" instead?
678+
last_unconditional_func_def = stmt.name
673679
elif (
674680
current_overload_name is not None
675681
and isinstance(stmt, IfStmt)
676682
and if_overload_name == current_overload_name
683+
and last_unconditional_func_def != current_overload_name
677684
):
678685
# IfStmt only contains stmts relevant to current_overload.
679686
# Check if stmts are reachable and add them to current_overload,
@@ -729,7 +736,7 @@ def fix_function_overloads(self, stmts: list[Statement]) -> list[Statement]:
729736
# most of mypy/mypyc assumes that all the functions in an OverloadedFuncDef are
730737
# related, but multiple underscore functions next to each other aren't necessarily
731738
# related
732-
seen_unconditional_func_def = False
739+
last_unconditional_func_def = None
733740
if isinstance(stmt, Decorator) and not unnamed_function(stmt.name):
734741
current_overload = [stmt]
735742
current_overload_name = stmt.name

mypy/plugins/attrs.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -458,7 +458,7 @@ def _analyze_class(
458458
if isinstance(node, PlaceholderNode):
459459
# This node is not ready yet.
460460
continue
461-
assert isinstance(node, Var)
461+
assert isinstance(node, Var), node
462462
node.is_initialized_in_class = False
463463

464464
# Traverse the MRO and collect attributes from the parents.

mypy/plugins/dataclasses.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -610,7 +610,7 @@ def collect_attributes(self) -> list[DataclassAttribute] | None:
610610
# We will issue an error later.
611611
continue
612612

613-
assert isinstance(node, Var)
613+
assert isinstance(node, Var), node
614614

615615
# x: ClassVar[int] is ignored by dataclasses.
616616
if node.is_classvar:

mypy/plugins/enums.py

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,10 +17,12 @@
1717
from typing import TypeVar, cast
1818

1919
import mypy.plugin # To avoid circular imports.
20-
from mypy.nodes import TypeInfo
20+
from mypy.checker_shared import TypeCheckerSharedApi
21+
from mypy.nodes import TypeInfo, Var
2122
from mypy.subtypes import is_equivalent
2223
from mypy.typeops import fixup_partial_type, make_simplified_union
2324
from mypy.types import (
25+
ELLIPSIS_TYPE_NAMES,
2426
CallableType,
2527
Instance,
2628
LiteralType,
@@ -79,6 +81,19 @@ def _infer_value_type_with_auto_fallback(
7981
if proper_type is None:
8082
return None
8183
proper_type = get_proper_type(fixup_partial_type(proper_type))
84+
# Enums in stubs may have ... instead of actual values. If `_value_` is annotated
85+
# (manually or inherited from IntEnum, for example), it is a more reasonable guess
86+
# than literal ellipsis type.
87+
if (
88+
_is_defined_in_stub(ctx)
89+
and isinstance(proper_type, Instance)
90+
and proper_type.type.fullname in ELLIPSIS_TYPE_NAMES
91+
and isinstance(ctx.type, Instance)
92+
):
93+
value_type = ctx.type.type.get("_value_")
94+
if value_type is not None and isinstance(var := value_type.node, Var):
95+
return var.type
96+
return proper_type
8297
if not (isinstance(proper_type, Instance) and proper_type.type.fullname == "enum.auto"):
8398
if is_named_instance(proper_type, "enum.member") and proper_type.args:
8499
return proper_type.args[0]
@@ -106,6 +121,11 @@ def _infer_value_type_with_auto_fallback(
106121
return ctx.default_attr_type
107122

108123

124+
def _is_defined_in_stub(ctx: mypy.plugin.AttributeContext) -> bool:
125+
assert isinstance(ctx.api, TypeCheckerSharedApi)
126+
return isinstance(ctx.type, Instance) and ctx.api.is_defined_in_stub(ctx.type)
127+
128+
109129
def _implements_new(info: TypeInfo) -> bool:
110130
"""Check whether __new__ comes from enum.Enum or was implemented in a
111131
subclass. In the latter case, we must infer Any as long as mypy can't infer

mypy/semanal.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6630,7 +6630,7 @@ def named_type(self, fullname: str, args: list[Type] | None = None) -> Instance:
66306630
sym = self.lookup_fully_qualified(fullname)
66316631
assert sym, "Internal error: attempted to construct unknown type"
66326632
node = sym.node
6633-
assert isinstance(node, TypeInfo)
6633+
assert isinstance(node, TypeInfo), node
66346634
if args:
66356635
# TODO: assert len(args) == len(node.defn.type_vars)
66366636
return Instance(node, args)

mypy/semanal_main.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -290,7 +290,7 @@ def process_functions(graph: Graph, scc: list[str], patches: Patches) -> None:
290290

291291
for module, target, node, active_type in order_by_subclassing(all_targets):
292292
analyzer = graph[module].manager.semantic_analyzer
293-
assert isinstance(node, (FuncDef, OverloadedFuncDef, Decorator))
293+
assert isinstance(node, (FuncDef, OverloadedFuncDef, Decorator)), node
294294
process_top_level_function(
295295
analyzer, graph[module], module, target, node, active_type, patches
296296
)

0 commit comments

Comments
 (0)