From 596683bb90cd7bbb4905383b69d0b4c600fba492 Mon Sep 17 00:00:00 2001 From: Ali Hamdan Date: Sat, 28 Dec 2024 16:38:17 +0100 Subject: [PATCH 1/4] stubtest: Fix crash with numpy array default values --- mypy/stubtest.py | 36 ++++++++++++++++++++++-------------- mypy/test/teststubtest.py | 12 ++++++++++++ 2 files changed, 34 insertions(+), 14 deletions(-) diff --git a/mypy/stubtest.py b/mypy/stubtest.py index 6c8d033198934..e68727af5be7a 100644 --- a/mypy/stubtest.py +++ b/mypy/stubtest.py @@ -670,7 +670,7 @@ def _verify_arg_default_value( stub_arg: nodes.Argument, runtime_arg: inspect.Parameter ) -> Iterator[str]: """Checks whether argument default values are compatible.""" - if runtime_arg.default != inspect.Parameter.empty: + if runtime_arg.default is not inspect.Parameter.empty: if stub_arg.kind.is_required(): yield ( f'runtime argument "{runtime_arg.name}" ' @@ -705,18 +705,26 @@ def _verify_arg_default_value( stub_default is not UNKNOWN and stub_default is not ... and runtime_arg.default is not UNREPRESENTABLE - and ( - stub_default != runtime_arg.default - # We want the types to match exactly, e.g. in case the stub has - # True and the runtime has 1 (or vice versa). - or type(stub_default) is not type(runtime_arg.default) - ) ): - yield ( - f'runtime argument "{runtime_arg.name}" ' - f"has a default value of {runtime_arg.default!r}, " - f"which is different from stub argument default {stub_default!r}" - ) + no_match = False + # We want the types to match exactly, e.g. in case the stub has + # True and the runtime has 1 (or vice versa). + if type(stub_default) is not type(runtime_arg.default): + no_match = True + else: + try: + no_match = bool(stub_default != runtime_arg.default) + except Exception: + # Exception can be raised in eq/ne dunder methods (e.g. numpy arrays) + # At this point, consider the default to be different, it is probably + # too complex to put in a stub anyway. + no_match = True + if no_match: + yield ( + f'runtime argument "{runtime_arg.name}" ' + f"has a default value of {runtime_arg.default!r}, " + f"which is different from stub argument default {stub_default!r}" + ) else: if stub_arg.kind.is_optional(): yield ( @@ -758,7 +766,7 @@ def get_type(arg: Any) -> str | None: def has_default(arg: Any) -> bool: if isinstance(arg, inspect.Parameter): - return bool(arg.default != inspect.Parameter.empty) + return arg.default is not inspect.Parameter.empty if isinstance(arg, nodes.Argument): return arg.kind.is_optional() raise AssertionError @@ -1628,7 +1636,7 @@ def anytype() -> mypy.types.AnyType: arg_names.append( None if arg.kind == inspect.Parameter.POSITIONAL_ONLY else arg.name ) - has_default = arg.default == inspect.Parameter.empty + has_default = arg.default is inspect.Parameter.empty if arg.kind == inspect.Parameter.POSITIONAL_ONLY: arg_kinds.append(nodes.ARG_POS if has_default else nodes.ARG_OPT) elif arg.kind == inspect.Parameter.POSITIONAL_OR_KEYWORD: diff --git a/mypy/test/teststubtest.py b/mypy/test/teststubtest.py index 6dc1feb670894..f099ebdc55a56 100644 --- a/mypy/test/teststubtest.py +++ b/mypy/test/teststubtest.py @@ -529,6 +529,18 @@ def f11(text=None) -> None: pass error="f11", ) + # Simulate numpy ndarray.__bool__ that raises an error + yield Case( + stub="def f12(x=1): ...", + runtime=""" + class _ndarray: + def __eq__(self, obj): return self + def __bool__(self): raise ValueError + def f12(x=_ndarray()) -> None: pass + """, + error="f12", + ) + @collect_cases def test_static_class_method(self) -> Iterator[Case]: yield Case( From bce69d100c383eb6564d4a4019e634aa424ec38d Mon Sep 17 00:00:00 2001 From: Ali Hamdan Date: Sat, 28 Dec 2024 17:19:11 +0100 Subject: [PATCH 2/4] Avoid double negative --- mypy/stubtest.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/mypy/stubtest.py b/mypy/stubtest.py index e68727af5be7a..ed32d61963f6d 100644 --- a/mypy/stubtest.py +++ b/mypy/stubtest.py @@ -706,20 +706,20 @@ def _verify_arg_default_value( and stub_default is not ... and runtime_arg.default is not UNREPRESENTABLE ): - no_match = False + defaults_match = True # We want the types to match exactly, e.g. in case the stub has # True and the runtime has 1 (or vice versa). if type(stub_default) is not type(runtime_arg.default): - no_match = True + defaults_match = False else: try: - no_match = bool(stub_default != runtime_arg.default) + defaults_match = bool(stub_default == runtime_arg.default) except Exception: # Exception can be raised in eq/ne dunder methods (e.g. numpy arrays) # At this point, consider the default to be different, it is probably # too complex to put in a stub anyway. - no_match = True - if no_match: + defaults_match = False + if not defaults_match: yield ( f'runtime argument "{runtime_arg.name}" ' f"has a default value of {runtime_arg.default!r}, " From 6b32557e0c99a82d2dc6b423d863af67e0edc4dd Mon Sep 17 00:00:00 2001 From: Ali Hamdan Date: Sat, 28 Dec 2024 17:20:41 +0100 Subject: [PATCH 3/4] Rename has_default to no_default --- mypy/stubtest.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/mypy/stubtest.py b/mypy/stubtest.py index ed32d61963f6d..0b34e8f91b903 100644 --- a/mypy/stubtest.py +++ b/mypy/stubtest.py @@ -1636,13 +1636,13 @@ def anytype() -> mypy.types.AnyType: arg_names.append( None if arg.kind == inspect.Parameter.POSITIONAL_ONLY else arg.name ) - has_default = arg.default is inspect.Parameter.empty + no_default = arg.default is inspect.Parameter.empty if arg.kind == inspect.Parameter.POSITIONAL_ONLY: - arg_kinds.append(nodes.ARG_POS if has_default else nodes.ARG_OPT) + arg_kinds.append(nodes.ARG_POS if no_default else nodes.ARG_OPT) elif arg.kind == inspect.Parameter.POSITIONAL_OR_KEYWORD: - arg_kinds.append(nodes.ARG_POS if has_default else nodes.ARG_OPT) + arg_kinds.append(nodes.ARG_POS if no_default else nodes.ARG_OPT) elif arg.kind == inspect.Parameter.KEYWORD_ONLY: - arg_kinds.append(nodes.ARG_NAMED if has_default else nodes.ARG_NAMED_OPT) + arg_kinds.append(nodes.ARG_NAMED if no_default else nodes.ARG_NAMED_OPT) elif arg.kind == inspect.Parameter.VAR_POSITIONAL: arg_kinds.append(nodes.ARG_STAR) elif arg.kind == inspect.Parameter.VAR_KEYWORD: From b226188f806e3d8829840c358697b486b4ffb5b4 Mon Sep 17 00:00:00 2001 From: Ali Hamdan Date: Sat, 28 Dec 2024 17:22:19 +0100 Subject: [PATCH 4/4] Fix comment --- mypy/stubtest.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mypy/stubtest.py b/mypy/stubtest.py index 0b34e8f91b903..6b5ea0d5af619 100644 --- a/mypy/stubtest.py +++ b/mypy/stubtest.py @@ -715,7 +715,7 @@ def _verify_arg_default_value( try: defaults_match = bool(stub_default == runtime_arg.default) except Exception: - # Exception can be raised in eq/ne dunder methods (e.g. numpy arrays) + # Exception can be raised in bool dunder method (e.g. numpy arrays) # At this point, consider the default to be different, it is probably # too complex to put in a stub anyway. defaults_match = False