From 73804f33f554590a5826303c86028ff4d61645b0 Mon Sep 17 00:00:00 2001 From: Stephen Morton Date: Thu, 19 Dec 2024 13:41:05 -0800 Subject: [PATCH 1/3] stubtest: distinguish metaclass attributes from class attributes If the runtime attribute of a class is actually from the metaclass, consider it to be MISSING at runtime. --- mypy/stubtest.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/mypy/stubtest.py b/mypy/stubtest.py index 36cd0a213d4d..a3e9c82567b7 100644 --- a/mypy/stubtest.py +++ b/mypy/stubtest.py @@ -568,6 +568,13 @@ def verify_typeinfo( # Catch all exceptions in case the runtime raises an unexpected exception # from __getattr__ or similar. continue + + # If it came from the metaclass, consider the runtime_attr to be MISSING + # for a more accurate message + if runtime_attr is not MISSING and type(runtime) != runtime: + if getattr(runtime_attr, "__objclass__", None) is type(runtime): + runtime_attr = MISSING + # Do not error for an object missing from the stub # If the runtime object is a types.WrapperDescriptorType object # and has a non-special dunder name. From 891fc5ddbc23ce46670be1f08d40eb7e99b13c24 Mon Sep 17 00:00:00 2001 From: Stephen Morton Date: Thu, 19 Dec 2024 17:56:00 -0800 Subject: [PATCH 2/3] add a test --- mypy/stubtest.py | 3 ++- mypy/test/teststubtest.py | 10 ++++++++++ 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/mypy/stubtest.py b/mypy/stubtest.py index a3e9c82567b7..cdd8f5d37fba 100644 --- a/mypy/stubtest.py +++ b/mypy/stubtest.py @@ -571,7 +571,7 @@ def verify_typeinfo( # If it came from the metaclass, consider the runtime_attr to be MISSING # for a more accurate message - if runtime_attr is not MISSING and type(runtime) != runtime: + if runtime_attr is not MISSING and type(runtime) is not runtime: if getattr(runtime_attr, "__objclass__", None) is type(runtime): runtime_attr = MISSING @@ -1526,6 +1526,7 @@ def is_probably_a_function(runtime: Any) -> bool: isinstance(runtime, (types.FunctionType, types.BuiltinFunctionType)) or isinstance(runtime, (types.MethodType, types.BuiltinMethodType)) or (inspect.ismethoddescriptor(runtime) and callable(runtime)) + or (inspect.ismethodwrapper(runtime) and callable(runtime)) ) diff --git a/mypy/test/teststubtest.py b/mypy/test/teststubtest.py index fcbf07b4d371..b16cb18ace21 100644 --- a/mypy/test/teststubtest.py +++ b/mypy/test/teststubtest.py @@ -1460,6 +1460,16 @@ def h(x: str): ... runtime="__all__ += ['Z']\nclass Z:\n def __reduce__(self): return (Z,)", error=None, ) + # __call__ exists on type, so it appears to exist on the class. + # This checks that we identify it as missing at runtime anyway. + yield Case( + stub=""" + class ClassWithMetaclassOverride: + def __call__(*args, **kwds): ... + """, + runtime="class ClassWithMetaclassOverride: ...", + error="ClassWithMetaclassOverride.__call__", + ) @collect_cases def test_missing_no_runtime_all(self) -> Iterator[Case]: From aa327b967c186d3de3739922c2b29c5bad7022eb Mon Sep 17 00:00:00 2001 From: Stephen Morton Date: Thu, 19 Dec 2024 18:00:00 -0800 Subject: [PATCH 3/3] don't use inspect.ismethodwrapper --- mypy/stubtest.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mypy/stubtest.py b/mypy/stubtest.py index cdd8f5d37fba..6c8d03319893 100644 --- a/mypy/stubtest.py +++ b/mypy/stubtest.py @@ -1526,7 +1526,7 @@ def is_probably_a_function(runtime: Any) -> bool: isinstance(runtime, (types.FunctionType, types.BuiltinFunctionType)) or isinstance(runtime, (types.MethodType, types.BuiltinMethodType)) or (inspect.ismethoddescriptor(runtime) and callable(runtime)) - or (inspect.ismethodwrapper(runtime) and callable(runtime)) + or (isinstance(runtime, types.MethodWrapperType) and callable(runtime)) )