diff --git a/mypy/semanal.py b/mypy/semanal.py index 6714e8c56de9f..3e3056a9adf7b 100644 --- a/mypy/semanal.py +++ b/mypy/semanal.py @@ -775,7 +775,7 @@ def file_context( self.globals = file_node.names self.tvar_scope = TypeVarLikeScope() - self.named_tuple_analyzer = NamedTupleAnalyzer(options, self) + self.named_tuple_analyzer = NamedTupleAnalyzer(options, self, self.msg) self.typed_dict_analyzer = TypedDictAnalyzer(options, self, self.msg) self.enum_call_analyzer = EnumCallAnalyzer(options, self) self.newtype_analyzer = NewTypeAnalyzer(options, self, self.msg) diff --git a/mypy/semanal_namedtuple.py b/mypy/semanal_namedtuple.py index 80cf1c4e184a5..bc3c5dd61894a 100644 --- a/mypy/semanal_namedtuple.py +++ b/mypy/semanal_namedtuple.py @@ -9,6 +9,7 @@ from typing import Final, Iterator, List, Mapping, cast from mypy.exprtotype import TypeTranslationError, expr_to_unanalyzed_type +from mypy.messages import MessageBuilder from mypy.nodes import ( ARG_NAMED_OPT, ARG_OPT, @@ -91,9 +92,12 @@ class NamedTupleAnalyzer: - def __init__(self, options: Options, api: SemanticAnalyzerInterface) -> None: + def __init__( + self, options: Options, api: SemanticAnalyzerInterface, msg: MessageBuilder + ) -> None: self.options = options self.api = api + self.msg = msg def analyze_namedtuple_classdef( self, defn: ClassDef, is_stub_file: bool, is_func_scope: bool @@ -204,6 +208,10 @@ def check_namedtuple_classdef( ) else: default_items[name] = stmt.rvalue + if defn.keywords: + for_function = ' for "__init_subclass__" of "NamedTuple"' + for key in defn.keywords: + self.msg.unexpected_keyword_argument_for_function(for_function, key, defn) return items, types, default_items, statements def check_namedtuple( diff --git a/mypy/semanal_typeddict.py b/mypy/semanal_typeddict.py index f399d8872a327..13aab4de65e47 100644 --- a/mypy/semanal_typeddict.py +++ b/mypy/semanal_typeddict.py @@ -325,7 +325,7 @@ def analyze_typeddict_classdef_fields( total = require_bool_literal_argument(self.api, defn.keywords["total"], "total", True) if defn.keywords and defn.keywords.keys() != {"total"}: for_function = ' for "__init_subclass__" of "TypedDict"' - for key in defn.keywords.keys(): + for key in defn.keywords: if key == "total": continue self.msg.unexpected_keyword_argument_for_function(for_function, key, defn) diff --git a/test-data/unit/check-namedtuple.test b/test-data/unit/check-namedtuple.test index 14e0753395727..51b02b500bd12 100644 --- a/test-data/unit/check-namedtuple.test +++ b/test-data/unit/check-namedtuple.test @@ -1354,3 +1354,20 @@ class Test: self.item: self.Item # E: Name "self.Item" is not defined [builtins fixtures/tuple.pyi] [typing fixtures/typing-namedtuple.pyi] + +[case testNoClassKeywordsForNamedTuple] +from typing import NamedTuple +class Test1(NamedTuple, x=1, y=2): # E: Unexpected keyword argument "x" for "__init_subclass__" of "NamedTuple" \ + # E: Unexpected keyword argument "y" for "__init_subclass__" of "NamedTuple" + ... + +class Meta(type): ... + +class Test2(NamedTuple, metaclass=Meta): # E: Unexpected keyword argument "metaclass" for "__init_subclass__" of "NamedTuple" + ... + +# Technically this would work, but it is just easier for the implementation: +class Test3(NamedTuple, metaclass=type): # E: Unexpected keyword argument "metaclass" for "__init_subclass__" of "NamedTuple" + ... +[builtins fixtures/tuple.pyi] +[typing fixtures/typing-namedtuple.pyi]