-
-
Notifications
You must be signed in to change notification settings - Fork 3k
Description
Bug Report
builtins.float and builtins.int shouldn't be assignable to a Protocol implementing hex: () -> str, because that would be type-unsafe:
>>> int().hex()
Traceback (most recent call last):
File "<python-input-0>", line 1, in <module>
int().hex()
^^^^^^^^^
AttributeError: 'int' object has no attribute 'hex'
>>> bool().hex()
Traceback (most recent call last):
File "<python-input-1>", line 1, in <module>
bool().hex()
^^^^^^^^^^
AttributeError: 'bool' object has no attribute 'hex'Mypy accepts this regardless.
To Reproduce
from typing import Protocol
class CanHex(Protocol):
def hex(self, /) -> str: ...True negative:
float_has_hex: CanHex = 3.14 # acceptedTrue positives:
object_doesnt_have_hex: CanHex = object() # rejected: [assignment]
complex_doesnt_have_hex: CanHex = 1j # rejected: [assignment]False negatives:
int_doesnt_have_hex: CanHex = 666 # accepted?
bool_clearly_also_doesnt_have_hex: CanHex = False # accepted??https://mypy-play.net/?mypy=latest&python=3.12&gist=8ac210b6e647983b5c165af053ccd7d1
This isn't limited to .hex(), but applies to all methods that are implemented by builtins.float but not by builtins.int.
I consider this a high-priority issue, because of the following reasons:
- It's type-unsafe, even in the most explicit of situations:
def tohex(x: CanHex): return x.hex() - This issue, in combination with
floatandcomplexforcibly change invariant types to covariant #18257, makes it impossible to distinguish betweenintandfloatinput using static typing.
The second reason in particular is a big problem for NumPy, because it forces us to annotate e.g. np.dtype(complex) as np.dtype[np.complex128 | np.float64 | np.int_ | np.bool], and np.array(3.14) as np.ndarray[tuple[()], np.dtype[np.float64 | np.int_ | np.bool]].
So I'm sure you can see how this is confusing for users, especially if you consider that usually tends to propagate to other dtype- and array-types.
We can't use the "Any trick" either, because that'd be problematic for functions that overload on dtype, of which there are many.