@@ -244,11 +244,13 @@ def verify_typeinfo(
244244 yield Error (object_path , "is not a type" , stub , runtime , stub_desc = repr (stub ))
245245 return
246246
247+ # Check everything already defined in the stub
247248 to_check = set (stub .names )
248- dunders_to_check = ( "__init__" , "__new__" , "__call__" , "__class_getitem__" )
249- # cast to workaround mypyc complaints
249+ # There's a reasonable case to be made that we should always check all dunders, but it's
250+ # currently quite noisy. We could turn this into a denylist instead of an allowlist.
250251 to_check .update (
251- m for m in cast (Any , vars )(runtime ) if m in dunders_to_check or not m .startswith ("_" )
252+ # cast to workaround mypyc complaints
253+ m for m in cast (Any , vars )(runtime ) if not m .startswith ("_" ) or m in SPECIAL_DUNDERS
252254 )
253255
254256 for entry in sorted (to_check ):
@@ -265,8 +267,8 @@ def verify_typeinfo(
265267def _verify_static_class_methods (
266268 stub : nodes .FuncItem , runtime : types .FunctionType , object_path : List [str ]
267269) -> Iterator [str ]:
268- if stub .name == "__new__" :
269- # Special cased by Python, so never declared as staticmethod
270+ if stub .name in ( "__new__" , "__init_subclass__" , "__class_getitem__" ) :
271+ # Special cased by Python, so don't bother checking
270272 return
271273 if inspect .isbuiltin (runtime ):
272274 # The isinstance checks don't work reliably for builtins, e.g. datetime.datetime.now, so do
@@ -303,8 +305,8 @@ def _verify_arg_name(
303305 stub_arg : nodes .Argument , runtime_arg : inspect .Parameter , function_name : str
304306) -> Iterator [str ]:
305307 """Checks whether argument names match."""
306- # Ignore exact names for all dunder methods other than __init__
307- if is_dunder (function_name , exclude_init = True ):
308+ # Ignore exact names for most dunder methods
309+ if is_dunder (function_name , exclude_special = True ):
308310 return
309311
310312 def strip_prefix (s : str , prefix : str ) -> str :
@@ -468,8 +470,8 @@ def from_overloadedfuncdef(stub: nodes.OverloadedFuncDef) -> "Signature[nodes.Ar
468470 lies it might try to tell.
469471
470472 """
471- # For all dunder methods other than __init__ , just assume all args are positional-only
472- assume_positional_only = is_dunder (stub .name , exclude_init = True )
473+ # For most dunder methods, just assume all args are positional-only
474+ assume_positional_only = is_dunder (stub .name , exclude_special = True )
473475
474476 all_args = {} # type: Dict[str, List[Tuple[nodes.Argument, int]]]
475477 for func in map (_resolve_funcitem_from_decorator , stub .items ):
@@ -548,7 +550,7 @@ def _verify_signature(
548550 runtime_arg .kind == inspect .Parameter .POSITIONAL_ONLY
549551 and not stub_arg .variable .name .startswith ("__" )
550552 and not stub_arg .variable .name .strip ("_" ) == "self"
551- and not is_dunder (function_name ) # noisy for dunder methods
553+ and not is_dunder (function_name , exclude_special = True ) # noisy for dunder methods
552554 ):
553555 yield (
554556 'stub argument "{}" should be positional-only '
@@ -656,6 +658,13 @@ def verify_funcitem(
656658 # catch RuntimeError because of https://bugs.python.org/issue39504
657659 return
658660
661+ if stub .name in ("__init_subclass__" , "__class_getitem__" ):
662+ # These are implicitly classmethods. If the stub chooses not to have @classmethod, we
663+ # should remove the cls argument
664+ if stub .arguments [0 ].variable .name == "cls" :
665+ stub = copy .copy (stub )
666+ stub .arguments = stub .arguments [1 :]
667+
659668 stub_sig = Signature .from_funcitem (stub )
660669 runtime_sig = Signature .from_inspect_signature (signature )
661670
@@ -846,13 +855,16 @@ def verify_typealias(
846855 yield None
847856
848857
849- def is_dunder (name : str , exclude_init : bool = False ) -> bool :
858+ SPECIAL_DUNDERS = ("__init__" , "__new__" , "__call__" , "__init_subclass__" , "__class_getitem__" )
859+
860+
861+ def is_dunder (name : str , exclude_special : bool = False ) -> bool :
850862 """Returns whether name is a dunder name.
851863
852- :param exclude_init : Whether to return False for __init__
864+ :param exclude_special : Whether to return False for a couple special dunder methods.
853865
854866 """
855- if exclude_init and name == "__init__" :
867+ if exclude_special and name in SPECIAL_DUNDERS :
856868 return False
857869 return name .startswith ("__" ) and name .endswith ("__" )
858870
0 commit comments