Skip to content

Commit 0c6e18a

Browse files
authored
Add explanation if argument type is incompatible because of a "numbers" type (#15137)
Types from `numbers` aren't really supported in any useful way. Make it more explicit, since this is surprising. Work on #3186.
1 parent 0dafc47 commit 0c6e18a

File tree

3 files changed

+63
-0
lines changed

3 files changed

+63
-0
lines changed

mypy/messages.py

+20
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,14 @@
137137
"typing._SpecialForm": "typing-medium.pyi",
138138
}
139139

140+
UNSUPPORTED_NUMBERS_TYPES: Final = {
141+
"numbers.Number",
142+
"numbers.Complex",
143+
"numbers.Real",
144+
"numbers.Rational",
145+
"numbers.Integral",
146+
}
147+
140148

141149
class MessageBuilder:
142150
"""Helper class for reporting type checker error messages with parameters.
@@ -792,6 +800,7 @@ def incompatible_argument(
792800
for type in get_proper_types(expected_types):
793801
if isinstance(arg_type, Instance) and isinstance(type, Instance):
794802
notes = append_invariance_notes(notes, arg_type, type)
803+
notes = append_numbers_notes(notes, arg_type, type)
795804
object_type = get_proper_type(object_type)
796805
if isinstance(object_type, TypedDictType):
797806
code = codes.TYPEDDICT_ITEM
@@ -2992,6 +3001,17 @@ def append_invariance_notes(
29923001
return notes
29933002

29943003

3004+
def append_numbers_notes(
3005+
notes: list[str], arg_type: Instance, expected_type: Instance
3006+
) -> list[str]:
3007+
"""Explain if an unsupported type from "numbers" is used in a subtype check."""
3008+
if expected_type.type.fullname in UNSUPPORTED_NUMBERS_TYPES:
3009+
notes.append('Types from "numbers" aren\'t supported for static type checking')
3010+
notes.append("See https://peps.python.org/pep-0484/#the-numeric-tower")
3011+
notes.append("Consider using a protocol instead, such as typing.SupportsFloat")
3012+
return notes
3013+
3014+
29953015
def make_inferred_type_note(
29963016
context: Context, subtype: Type, supertype: Type, supertype_str: str
29973017
) -> str:

test-data/unit/check-classes.test

+33
Original file line numberDiff line numberDiff line change
@@ -7826,3 +7826,36 @@ class D:
78267826
# and that's what matters.
78277827
a, b = self.f() # E: "C" has no attribute "__iter__" (not iterable)
78287828
[builtins fixtures/tuple.pyi]
7829+
7830+
[case testUsingNumbersType]
7831+
from numbers import Number, Complex, Real, Rational, Integral
7832+
7833+
def f1(x: Number) -> None: pass
7834+
f1(1) # E: Argument 1 to "f1" has incompatible type "int"; expected "Number" \
7835+
# N: Types from "numbers" aren't supported for static type checking \
7836+
# N: See https://peps.python.org/pep-0484/#the-numeric-tower \
7837+
# N: Consider using a protocol instead, such as typing.SupportsFloat
7838+
7839+
def f2(x: Complex) -> None: pass
7840+
f2(1) # E: Argument 1 to "f2" has incompatible type "int"; expected "Complex" \
7841+
# N: Types from "numbers" aren't supported for static type checking \
7842+
# N: See https://peps.python.org/pep-0484/#the-numeric-tower \
7843+
# N: Consider using a protocol instead, such as typing.SupportsFloat
7844+
7845+
def f3(x: Real) -> None: pass
7846+
f3(1) # E: Argument 1 to "f3" has incompatible type "int"; expected "Real" \
7847+
# N: Types from "numbers" aren't supported for static type checking \
7848+
# N: See https://peps.python.org/pep-0484/#the-numeric-tower \
7849+
# N: Consider using a protocol instead, such as typing.SupportsFloat
7850+
7851+
def f4(x: Rational) -> None: pass
7852+
f4(1) # E: Argument 1 to "f4" has incompatible type "int"; expected "Rational" \
7853+
# N: Types from "numbers" aren't supported for static type checking \
7854+
# N: See https://peps.python.org/pep-0484/#the-numeric-tower \
7855+
# N: Consider using a protocol instead, such as typing.SupportsFloat
7856+
7857+
def f5(x: Integral) -> None: pass
7858+
f5(1) # E: Argument 1 to "f5" has incompatible type "int"; expected "Integral" \
7859+
# N: Types from "numbers" aren't supported for static type checking \
7860+
# N: See https://peps.python.org/pep-0484/#the-numeric-tower \
7861+
# N: Consider using a protocol instead, such as typing.SupportsFloat

test-data/unit/lib-stub/numbers.pyi

+10
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
# Test fixture for numbers
2+
#
3+
# The numbers module isn't properly supported, but we want to test that mypy
4+
# can tell that it doesn't work as expected.
5+
6+
class Number: pass
7+
class Complex: pass
8+
class Real: pass
9+
class Rational: pass
10+
class Integral: pass

0 commit comments

Comments
 (0)