diff --git a/mypy/checker.py b/mypy/checker.py index 225a50c7e646..163ed9722158 100644 --- a/mypy/checker.py +++ b/mypy/checker.py @@ -3138,7 +3138,7 @@ def check_assignment( else: self.check_getattr_method(signature, lvalue, name) - if name == "__slots__": + if name == "__slots__" and self.scope.active_class() is not None: typ = lvalue_type or self.expr_checker.accept(rvalue) self.check_slots_definition(typ, lvalue) if name == "__match_args__" and inferred is not None: @@ -3317,6 +3317,12 @@ def get_variable_type_context(self, inferred: Var, rvalue: Expression) -> Type | type_contexts.append(base_type) # Use most derived supertype as type context if available. if not type_contexts: + if inferred.name == "__slots__" and self.scope.active_class() is not None: + str_type = self.named_type("builtins.str") + return self.named_generic_type("typing.Iterable", [str_type]) + if inferred.name == "__all__" and self.scope.is_top_level(): + str_type = self.named_type("builtins.str") + return self.named_generic_type("typing.Sequence", [str_type]) return None candidate = type_contexts[0] for other in type_contexts: diff --git a/mypy/checker_shared.py b/mypy/checker_shared.py index 2ab4548edfaf..a9cbae643dca 100644 --- a/mypy/checker_shared.py +++ b/mypy/checker_shared.py @@ -334,6 +334,10 @@ def current_self_type(self) -> Instance | TupleType | None: return fill_typevars(item) return None + def is_top_level(self) -> bool: + """Is current scope top-level (no classes or functions)?""" + return len(self.stack) == 1 + @contextmanager def push_function(self, item: FuncItem) -> Iterator[None]: self.stack.append(item) diff --git a/test-data/unit/check-modules.test b/test-data/unit/check-modules.test index 858024e7daf2..862cd8ea3905 100644 --- a/test-data/unit/check-modules.test +++ b/test-data/unit/check-modules.test @@ -423,7 +423,35 @@ import typing __all__ = [1, 2, 3] [builtins fixtures/module_all.pyi] [out] -main:2: error: Type of __all__ must be "Sequence[str]", not "list[int]" +main:2: error: List item 0 has incompatible type "int"; expected "str" +main:2: error: List item 1 has incompatible type "int"; expected "str" +main:2: error: List item 2 has incompatible type "int"; expected "str" + +[case testAllMustBeSequenceStr2] +import typing +__all__ = 1 # E: Type of __all__ must be "Sequence[str]", not "int" +reveal_type(__all__) # N: Revealed type is "builtins.int" +[builtins fixtures/module_all.pyi] + +[case testAllMustBeSequenceStr3] +import typing +__all__ = set() # E: Need type annotation for "__all__" (hint: "__all__: set[] = ...") \ + # E: Type of __all__ must be "Sequence[str]", not "set[Any]" +reveal_type(__all__) # N: Revealed type is "builtins.set[Any]" +[builtins fixtures/set.pyi] + +[case testModuleAllEmptyList] +__all__ = [] +reveal_type(__all__) # N: Revealed type is "builtins.list[builtins.str]" +[builtins fixtures/module_all.pyi] + +[case testDunderAllNotGlobal] +class A: + __all__ = 1 + +def foo() -> None: + __all__ = 1 +[builtins fixtures/module_all.pyi] [case testUnderscoreExportedValuesInImportAll] import typing diff --git a/test-data/unit/check-slots.test b/test-data/unit/check-slots.test index b7ce5e596101..e924ac9e5f57 100644 --- a/test-data/unit/check-slots.test +++ b/test-data/unit/check-slots.test @@ -496,6 +496,29 @@ class A: self.missing = 3 [builtins fixtures/dict.pyi] +[case testSlotsNotInClass] +# Shouldn't be triggered +__slots__ = [1, 2] +reveal_type(__slots__) # N: Revealed type is "builtins.list[builtins.int]" + +def foo() -> None: + __slots__ = 1 + reveal_type(__slots__) # N: Revealed type is "builtins.int" + +[case testSlotsEmptyList] +class A: + __slots__ = [] + reveal_type(__slots__) # N: Revealed type is "builtins.list[builtins.str]" + +reveal_type(A.__slots__) # N: Revealed type is "builtins.list[builtins.str]" + +[case testSlotsEmptySet] +class A: + __slots__ = set() + reveal_type(__slots__) # N: Revealed type is "builtins.set[builtins.str]" + +reveal_type(A.__slots__) # N: Revealed type is "builtins.set[builtins.str]" +[builtins fixtures/set.pyi] [case testSlotsWithAny] from typing import Any