@@ -506,28 +506,57 @@ def _is_disjoint_base(typ: type[object]) -> bool:
506506def _verify_disjoint_base (
507507 stub : nodes .TypeInfo , runtime : type [object ], object_path : list [str ]
508508) -> Iterator [Error ]:
509- # If it's final, doesn't matter whether it's a disjoint base or not
510- if stub .is_final :
511- return
512509 is_disjoint_runtime = _is_disjoint_base (runtime )
513510 # Don't complain about missing @disjoint_base if there are __slots__, because
514511 # in that case we can infer that it's a disjoint base.
515- if is_disjoint_runtime and not stub .is_disjoint_base and not runtime .__dict__ .get ("__slots__" ):
512+ if (
513+ is_disjoint_runtime
514+ and not stub .is_disjoint_base
515+ and not runtime .__dict__ .get ("__slots__" )
516+ and not stub .is_final
517+ and not (stub .is_enum and stub .enum_members )
518+ ):
516519 yield Error (
517520 object_path ,
518521 "is a disjoint base at runtime, but isn't marked with @disjoint_base in the stub" ,
519522 stub ,
520523 runtime ,
521524 stub_desc = repr (stub ),
522525 )
523- elif not is_disjoint_runtime and stub .is_disjoint_base :
524- yield Error (
525- object_path ,
526- "is marked with @disjoint_base in the stub, but isn't a disjoint base at runtime" ,
527- stub ,
528- runtime ,
529- stub_desc = repr (stub ),
530- )
526+ elif stub .is_disjoint_base :
527+ if not is_disjoint_runtime :
528+ yield Error (
529+ object_path ,
530+ "is marked with @disjoint_base in the stub, but isn't a disjoint base at runtime" ,
531+ stub ,
532+ runtime ,
533+ stub_desc = repr (stub ),
534+ )
535+ if runtime .__dict__ .get ("__slots__" ):
536+ yield Error (
537+ object_path ,
538+ "is marked as @disjoint_base, but also has slots; add __slots__ instead" ,
539+ stub ,
540+ runtime ,
541+ stub_desc = repr (stub ),
542+ )
543+ elif stub .is_final :
544+ yield Error (
545+ object_path ,
546+ "is marked as @disjoint_base, but also marked as @final; remove @disjoint_base" ,
547+ stub ,
548+ runtime ,
549+ stub_desc = repr (stub ),
550+ )
551+ elif stub .is_enum and stub .enum_members :
552+ yield Error (
553+ object_path ,
554+ "is marked as @disjoint_base, but is an enum with members, which is implicitly final; "
555+ "remove @disjoint_base" ,
556+ stub ,
557+ runtime ,
558+ stub_desc = repr (stub ),
559+ )
531560
532561
533562def _verify_metaclass (
@@ -747,8 +776,8 @@ def names_approx_match(a: str, b: str) -> bool:
747776 if stub_arg .variable .name == "_self" :
748777 return
749778 yield (
750- f'stub argument "{ stub_arg .variable .name } " '
751- f'differs from runtime argument "{ runtime_arg .name } "'
779+ f'stub parameter "{ stub_arg .variable .name } " '
780+ f'differs from runtime parameter "{ runtime_arg .name } "'
752781 )
753782
754783
@@ -759,8 +788,8 @@ def _verify_arg_default_value(
759788 if runtime_arg .default is not inspect .Parameter .empty :
760789 if stub_arg .kind .is_required ():
761790 yield (
762- f'runtime argument "{ runtime_arg .name } " '
763- "has a default value but stub argument does not"
791+ f'runtime parameter "{ runtime_arg .name } " '
792+ "has a default value but stub parameter does not"
764793 )
765794 else :
766795 runtime_type = get_mypy_type_of_runtime_value (runtime_arg .default )
@@ -781,9 +810,9 @@ def _verify_arg_default_value(
781810 and not is_subtype_helper (runtime_type , stub_type )
782811 ):
783812 yield (
784- f'runtime argument "{ runtime_arg .name } " '
813+ f'runtime parameter "{ runtime_arg .name } " '
785814 f"has a default value of type { runtime_type } , "
786- f"which is incompatible with stub argument type { stub_type } "
815+ f"which is incompatible with stub parameter type { stub_type } "
787816 )
788817 if stub_arg .initializer is not None :
789818 stub_default = evaluate_expression (stub_arg .initializer )
@@ -807,15 +836,15 @@ def _verify_arg_default_value(
807836 defaults_match = False
808837 if not defaults_match :
809838 yield (
810- f'runtime argument "{ runtime_arg .name } " '
839+ f'runtime parameter "{ runtime_arg .name } " '
811840 f"has a default value of { runtime_arg .default !r} , "
812- f"which is different from stub argument default { stub_default !r} "
841+ f"which is different from stub parameter default { stub_default !r} "
813842 )
814843 else :
815844 if stub_arg .kind .is_optional ():
816845 yield (
817- f'stub argument "{ stub_arg .variable .name } " has a default value '
818- f"but runtime argument does not"
846+ f'stub parameter "{ stub_arg .variable .name } " has a default value '
847+ f"but runtime parameter does not"
819848 )
820849
821850
@@ -925,22 +954,36 @@ def from_overloadedfuncdef(stub: nodes.OverloadedFuncDef) -> Signature[nodes.Arg
925954 # For most dunder methods, just assume all args are positional-only
926955 assume_positional_only = is_dunder (stub .name , exclude_special = True )
927956
928- all_args : dict [str , list [ tuple [ nodes . Argument , int ]]] = {}
957+ is_arg_pos_only : defaultdict [str , set [ bool ]] = defaultdict ( set )
929958 for func in map (_resolve_funcitem_from_decorator , stub .items ):
930959 assert func is not None , "Failed to resolve decorated overload"
931960 args = maybe_strip_cls (stub .name , func .arguments )
932961 for index , arg in enumerate (args ):
933- # For positional-only args, we allow overloads to have different names for the same
934- # argument. To accomplish this, we just make up a fake index-based name.
935- name = (
936- f"__{ index } "
937- if arg .variable .name .startswith ("__" )
962+ if (
963+ arg .variable .name .startswith ("__" )
938964 or arg .pos_only
939965 or assume_positional_only
940966 or arg .variable .name .strip ("_" ) == "self"
941967 or (index == 0 and arg .variable .name .strip ("_" ) == "cls" )
942- else arg .variable .name
943- )
968+ ):
969+ is_arg_pos_only [arg .variable .name ].add (True )
970+ else :
971+ is_arg_pos_only [arg .variable .name ].add (False )
972+
973+ all_args : dict [str , list [tuple [nodes .Argument , int ]]] = {}
974+ for func in map (_resolve_funcitem_from_decorator , stub .items ):
975+ assert func is not None , "Failed to resolve decorated overload"
976+ args = maybe_strip_cls (stub .name , func .arguments )
977+ for index , arg in enumerate (args ):
978+ # For positional-only args, we allow overloads to have different names for the same
979+ # argument. To accomplish this, we just make up a fake index-based name.
980+ # We can only use the index-based name if the argument is always
981+ # positional only. Sometimes overloads have an arg as positional-only
982+ # in some but not all branches of the overload.
983+ name = arg .variable .name
984+ if is_arg_pos_only [name ] == {True }:
985+ name = f"__{ index } "
986+
944987 all_args .setdefault (name , []).append ((arg , index ))
945988
946989 def get_position (arg_name : str ) -> int :
@@ -1013,7 +1056,7 @@ def _verify_signature(
10131056 and not is_dunder (function_name , exclude_special = True ) # noisy for dunder methods
10141057 ):
10151058 yield (
1016- f'stub argument "{ stub_arg .variable .name } " should be positional-only '
1059+ f'stub parameter "{ stub_arg .variable .name } " should be positional-only '
10171060 f'(add "/", e.g. "{ runtime_arg .name } , /")'
10181061 )
10191062 if (
@@ -1025,7 +1068,7 @@ def _verify_signature(
10251068 and not is_dunder (function_name , exclude_special = True ) # noisy for dunder methods
10261069 ):
10271070 yield (
1028- f'stub argument "{ stub_arg .variable .name } " should be positional or keyword '
1071+ f'stub parameter "{ stub_arg .variable .name } " should be positional or keyword '
10291072 '(remove "/")'
10301073 )
10311074
@@ -1040,28 +1083,28 @@ def _verify_signature(
10401083 # If the variable is in runtime.kwonly, it's just mislabelled as not a
10411084 # keyword-only argument
10421085 if stub_arg .variable .name not in runtime .kwonly :
1043- msg = f'runtime does not have argument "{ stub_arg .variable .name } "'
1086+ msg = f'runtime does not have parameter "{ stub_arg .variable .name } "'
10441087 if runtime .varkw is not None :
10451088 msg += ". Maybe you forgot to make it keyword-only in the stub?"
10461089 yield msg
10471090 else :
1048- yield f'stub argument "{ stub_arg .variable .name } " is not keyword-only'
1091+ yield f'stub parameter "{ stub_arg .variable .name } " is not keyword-only'
10491092 if stub .varpos is not None :
1050- yield f'runtime does not have *args argument "{ stub .varpos .variable .name } "'
1093+ yield f'runtime does not have *args parameter "{ stub .varpos .variable .name } "'
10511094 elif len (stub .pos ) < len (runtime .pos ):
10521095 for runtime_arg in runtime .pos [len (stub .pos ) :]:
10531096 if runtime_arg .name not in stub .kwonly :
10541097 if not _is_private_parameter (runtime_arg ):
1055- yield f'stub does not have argument "{ runtime_arg .name } "'
1098+ yield f'stub does not have parameter "{ runtime_arg .name } "'
10561099 else :
1057- yield f'runtime argument "{ runtime_arg .name } " is not keyword-only'
1100+ yield f'runtime parameter "{ runtime_arg .name } " is not keyword-only'
10581101
10591102 # Checks involving *args
10601103 if len (stub .pos ) <= len (runtime .pos ) or runtime .varpos is None :
10611104 if stub .varpos is None and runtime .varpos is not None :
1062- yield f'stub does not have *args argument "{ runtime .varpos .name } "'
1105+ yield f'stub does not have *args parameter "{ runtime .varpos .name } "'
10631106 if stub .varpos is not None and runtime .varpos is None :
1064- yield f'runtime does not have *args argument "{ stub .varpos .variable .name } "'
1107+ yield f'runtime does not have *args parameter "{ stub .varpos .variable .name } "'
10651108
10661109 # Check keyword-only args
10671110 for arg in sorted (set (stub .kwonly ) & set (runtime .kwonly )):
@@ -1080,20 +1123,20 @@ def _verify_signature(
10801123 if arg in {runtime_arg .name for runtime_arg in runtime .pos }:
10811124 # Don't report this if we've reported it before
10821125 if arg not in {runtime_arg .name for runtime_arg in runtime .pos [len (stub .pos ) :]}:
1083- yield f'runtime argument "{ arg } " is not keyword-only'
1126+ yield f'runtime parameter "{ arg } " is not keyword-only'
10841127 else :
1085- yield f'runtime does not have argument "{ arg } "'
1128+ yield f'runtime does not have parameter "{ arg } "'
10861129 for arg in sorted (set (runtime .kwonly ) - set (stub .kwonly )):
10871130 if arg in {stub_arg .variable .name for stub_arg in stub .pos }:
10881131 # Don't report this if we've reported it before
10891132 if not (
10901133 runtime .varpos is None
10911134 and arg in {stub_arg .variable .name for stub_arg in stub .pos [len (runtime .pos ) :]}
10921135 ):
1093- yield f'stub argument "{ arg } " is not keyword-only'
1136+ yield f'stub parameter "{ arg } " is not keyword-only'
10941137 else :
10951138 if not _is_private_parameter (runtime .kwonly [arg ]):
1096- yield f'stub does not have argument "{ arg } "'
1139+ yield f'stub does not have parameter "{ arg } "'
10971140
10981141 # Checks involving **kwargs
10991142 if stub .varkw is None and runtime .varkw is not None :
@@ -1103,9 +1146,9 @@ def _verify_signature(
11031146 stub_pos_names = {stub_arg .variable .name for stub_arg in stub .pos }
11041147 # Ideally we'd do a strict subset check, but in practice the errors from that aren't useful
11051148 if not set (runtime .kwonly ).issubset (set (stub .kwonly ) | stub_pos_names ):
1106- yield f'stub does not have **kwargs argument "{ runtime .varkw .name } "'
1149+ yield f'stub does not have **kwargs parameter "{ runtime .varkw .name } "'
11071150 if stub .varkw is not None and runtime .varkw is None :
1108- yield f'runtime does not have **kwargs argument "{ stub .varkw .variable .name } "'
1151+ yield f'runtime does not have **kwargs parameter "{ stub .varkw .variable .name } "'
11091152
11101153
11111154def _is_private_parameter (arg : inspect .Parameter ) -> bool :
@@ -1425,7 +1468,7 @@ def apply_decorator_to_funcitem(
14251468 if decorator .fullname == "builtins.classmethod" :
14261469 if func .arguments [0 ].variable .name not in ("cls" , "mcs" , "metacls" ):
14271470 raise StubtestFailure (
1428- f"unexpected class argument name { func .arguments [0 ].variable .name !r} "
1471+ f"unexpected class parameter name { func .arguments [0 ].variable .name !r} "
14291472 f"in { dec .fullname } "
14301473 )
14311474 # FuncItem is written so that copy.copy() actually works, even when compiled
0 commit comments