From 3c621b9e9b68aa3b0cebf16d6b841ddc3e83002e Mon Sep 17 00:00:00 2001 From: Jukka Lehtosalo Date: Tue, 25 Apr 2023 16:18:35 -0600 Subject: [PATCH 1/5] Add test case --- test-data/unit/check-classes.test | 28 ++++++++++++++++++++++++++++ test-data/unit/lib-stub/numbers.pyi | 10 ++++++++++ 2 files changed, 38 insertions(+) create mode 100644 test-data/unit/lib-stub/numbers.pyi diff --git a/test-data/unit/check-classes.test b/test-data/unit/check-classes.test index a33f8b0e6eb9d..70e9f921ab247 100644 --- a/test-data/unit/check-classes.test +++ b/test-data/unit/check-classes.test @@ -7826,3 +7826,31 @@ class D: # and that's what matters. a, b = self.f() # E: "C" has no attribute "__iter__" (not iterable) [builtins fixtures/tuple.pyi] + +[case testUsingNumbersType] +from numbers import Number, Complex, Real, Rational, Integral + +def f1(x: Number) -> None: pass +f1(1) # E: Argument 1 to "f1" has incompatible type "int"; expected "Number" \ + # N: Types from "numbers" aren't supported for static type checking \ + # N: See https://peps.python.org/pep-0484/#the-numeric-tower + +def f2(x: Complex) -> None: pass +f2(1) # E: Argument 1 to "f2" has incompatible type "int"; expected "Complex" \ + # N: Types from "numbers" aren't supported for static type checking \ + # N: See https://peps.python.org/pep-0484/#the-numeric-tower + +def f3(x: Real) -> None: pass +f3(1) # E: Argument 1 to "f3" has incompatible type "int"; expected "Real" \ + # N: Types from "numbers" aren't supported for static type checking \ + # N: See https://peps.python.org/pep-0484/#the-numeric-tower + +def f4(x: Rational) -> None: pass +f4(1) # E: Argument 1 to "f4" has incompatible type "int"; expected "Rational" \ + # N: Types from "numbers" aren't supported for static type checking \ + # N: See https://peps.python.org/pep-0484/#the-numeric-tower + +def f5(x: Integral) -> None: pass +f5(1) # E: Argument 1 to "f5" has incompatible type "int"; expected "Integral" \ + # N: Types from "numbers" aren't supported for static type checking \ + # N: See https://peps.python.org/pep-0484/#the-numeric-tower diff --git a/test-data/unit/lib-stub/numbers.pyi b/test-data/unit/lib-stub/numbers.pyi new file mode 100644 index 0000000000000..fad173c9a8b66 --- /dev/null +++ b/test-data/unit/lib-stub/numbers.pyi @@ -0,0 +1,10 @@ +# Test fixture for numbers +# +# The numbers module isn't properly supported, but we want to test that mypy +# can tell that it doesn't work as expected. + +class Number: pass +class Complex: pass +class Real: pass +class Rational: pass +class Integral: pass From 8643c566398b6a10bba740b309ad4786bf83e5a6 Mon Sep 17 00:00:00 2001 From: Jukka Lehtosalo Date: Tue, 25 Apr 2023 16:32:51 -0600 Subject: [PATCH 2/5] Add note when using types from numbers --- mypy/messages.py | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/mypy/messages.py b/mypy/messages.py index 030e95f2e88a6..2d3c55083e1a6 100644 --- a/mypy/messages.py +++ b/mypy/messages.py @@ -137,6 +137,14 @@ "typing._SpecialForm": "typing-medium.pyi", } +UNSUPPORTED_NUMBERS_TYPES: Final = { + "numbers.Number", + "numbers.Complex", + "numbers.Real", + "numbers.Rational", + "numbers.Integral", +} + class MessageBuilder: """Helper class for reporting type checker error messages with parameters. @@ -792,6 +800,7 @@ def incompatible_argument( for type in get_proper_types(expected_types): if isinstance(arg_type, Instance) and isinstance(type, Instance): notes = append_invariance_notes(notes, arg_type, type) + notes = append_numbers_notes(notes, arg_type, type) object_type = get_proper_type(object_type) if isinstance(object_type, TypedDictType): code = codes.TYPEDDICT_ITEM @@ -2992,6 +3001,16 @@ def append_invariance_notes( return notes +def append_numbers_notes( + notes: list[str], arg_type: Instance, expected_type: Instance +) -> list[str]: + """Explain if an unsupported type from "numbres" is used in a subtype check.""" + if expected_type.type.fullname in UNSUPPORTED_NUMBERS_TYPES: + notes.append('Types from "numbers" aren\'t supported for static type checking') + notes.append("See https://peps.python.org/pep-0484/#the-numeric-tower") + return notes + + def make_inferred_type_note( context: Context, subtype: Type, supertype: Type, supertype_str: str ) -> str: From 029edf5c84f5f0598c4383f44f2d42687862757b Mon Sep 17 00:00:00 2001 From: Jukka Lehtosalo Date: Tue, 25 Apr 2023 17:19:46 -0600 Subject: [PATCH 3/5] Update mypy/messages.py Co-authored-by: Alex Waygood --- mypy/messages.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mypy/messages.py b/mypy/messages.py index 2d3c55083e1a6..bd5f8981895ef 100644 --- a/mypy/messages.py +++ b/mypy/messages.py @@ -3004,7 +3004,7 @@ def append_invariance_notes( def append_numbers_notes( notes: list[str], arg_type: Instance, expected_type: Instance ) -> list[str]: - """Explain if an unsupported type from "numbres" is used in a subtype check.""" + """Explain if an unsupported type from "numbers" is used in a subtype check.""" if expected_type.type.fullname in UNSUPPORTED_NUMBERS_TYPES: notes.append('Types from "numbers" aren\'t supported for static type checking') notes.append("See https://peps.python.org/pep-0484/#the-numeric-tower") From 6e14bbd019977b0e3c6f6bd6a8909822b4a29d62 Mon Sep 17 00:00:00 2001 From: Jukka Lehtosalo Date: Tue, 25 Apr 2023 17:20:56 -0600 Subject: [PATCH 4/5] Update mypy/messages.py Co-authored-by: Alex Waygood --- mypy/messages.py | 1 + 1 file changed, 1 insertion(+) diff --git a/mypy/messages.py b/mypy/messages.py index bd5f8981895ef..3b112dff44bfc 100644 --- a/mypy/messages.py +++ b/mypy/messages.py @@ -3008,6 +3008,7 @@ def append_numbers_notes( if expected_type.type.fullname in UNSUPPORTED_NUMBERS_TYPES: notes.append('Types from "numbers" aren\'t supported for static type checking') notes.append("See https://peps.python.org/pep-0484/#the-numeric-tower") + notes.append("Consider using a protocol instead, such as typing.SupportsFloat") return notes From 90733e5108868b358cec45272adf6334699af4d3 Mon Sep 17 00:00:00 2001 From: Jukka Lehtosalo Date: Tue, 25 Apr 2023 17:22:17 -0600 Subject: [PATCH 5/5] Update tests --- test-data/unit/check-classes.test | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/test-data/unit/check-classes.test b/test-data/unit/check-classes.test index 70e9f921ab247..6724b7398d2b5 100644 --- a/test-data/unit/check-classes.test +++ b/test-data/unit/check-classes.test @@ -7833,24 +7833,29 @@ from numbers import Number, Complex, Real, Rational, Integral def f1(x: Number) -> None: pass f1(1) # E: Argument 1 to "f1" has incompatible type "int"; expected "Number" \ # N: Types from "numbers" aren't supported for static type checking \ - # N: See https://peps.python.org/pep-0484/#the-numeric-tower + # N: See https://peps.python.org/pep-0484/#the-numeric-tower \ + # N: Consider using a protocol instead, such as typing.SupportsFloat def f2(x: Complex) -> None: pass f2(1) # E: Argument 1 to "f2" has incompatible type "int"; expected "Complex" \ # N: Types from "numbers" aren't supported for static type checking \ - # N: See https://peps.python.org/pep-0484/#the-numeric-tower + # N: See https://peps.python.org/pep-0484/#the-numeric-tower \ + # N: Consider using a protocol instead, such as typing.SupportsFloat def f3(x: Real) -> None: pass f3(1) # E: Argument 1 to "f3" has incompatible type "int"; expected "Real" \ # N: Types from "numbers" aren't supported for static type checking \ - # N: See https://peps.python.org/pep-0484/#the-numeric-tower + # N: See https://peps.python.org/pep-0484/#the-numeric-tower \ + # N: Consider using a protocol instead, such as typing.SupportsFloat def f4(x: Rational) -> None: pass f4(1) # E: Argument 1 to "f4" has incompatible type "int"; expected "Rational" \ # N: Types from "numbers" aren't supported for static type checking \ - # N: See https://peps.python.org/pep-0484/#the-numeric-tower + # N: See https://peps.python.org/pep-0484/#the-numeric-tower \ + # N: Consider using a protocol instead, such as typing.SupportsFloat def f5(x: Integral) -> None: pass f5(1) # E: Argument 1 to "f5" has incompatible type "int"; expected "Integral" \ # N: Types from "numbers" aren't supported for static type checking \ - # N: See https://peps.python.org/pep-0484/#the-numeric-tower + # N: See https://peps.python.org/pep-0484/#the-numeric-tower \ + # N: Consider using a protocol instead, such as typing.SupportsFloat