Skip to content

Commit

Permalink
Apply implicit int/float promotion to subclasses (#738)
Browse files Browse the repository at this point in the history
Fixes #736
  • Loading branch information
JelleZijlstra authored Mar 2, 2024
1 parent 988feac commit db5b7ef
Show file tree
Hide file tree
Showing 3 changed files with 10 additions and 8 deletions.
1 change: 1 addition & 0 deletions docs/changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

## Unreleased

- Treat subclasses of `int` as subclasses of `float` and `complex` too (#738)
- Fix crash on encountering certain decorators in stubs (#734)
- Fix inference of signature for certain secondary methods (#732)

Expand Down
13 changes: 7 additions & 6 deletions pyanalyze/test_type_object.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,9 @@ def test_float(self):
def take_float(x: float) -> None:
pass

class IntSubclass(int):
pass

def capybara(nt: NT, i: int, f: float) -> None:
take_float(nt)
take_float(i)
Expand All @@ -31,11 +34,9 @@ def capybara(nt: NT, i: int, f: float) -> None:
take_float(3)
take_float(1 + 1j) # E: incompatible_argument
take_float("string") # E: incompatible_argument
# Strictly speaking this should be allowed, but it doesn't
# seem useful and I don't know of a concrete use case that would
# require this to work. This can be revisited if we do find a use
# case.
take_float(True) # E: incompatible_argument
# bool is a subclass of int, which is treated as a subclass of float
take_float(True)
take_float(IntSubclass(3))

@assert_passes()
def test_complex(self):
Expand All @@ -57,7 +58,7 @@ def capybara(nti: NTI, ntf: NTF, i: int, f: float, c: complex) -> None:
take_complex(3)
take_complex(1 + 1j)
take_complex("string") # E: incompatible_argument
take_complex(True) # E: incompatible_argument
take_complex(True) # bool is an int, which is a float, which is a complex


class TestSyntheticType(TestNameCheckVisitorBase):
Expand Down
4 changes: 2 additions & 2 deletions pyanalyze/type_object.py
Original file line number Diff line number Diff line change
Expand Up @@ -68,10 +68,10 @@ def __post_init__(self) -> None:
self.base_classes |= set(get_mro(self.typ))
# As a special case, the Python type system treats int as
# a subtype of float, and both int and float as subtypes of complex.
if self.typ is int:
if self.typ is int or safe_in(int, self.base_classes):
self.artificial_bases.add(float)
self.artificial_bases.add(complex)
if self.typ is float:
if self.typ is float or safe_in(float, self.base_classes):
self.artificial_bases.add(complex)
if self.is_thrift_enum:
self.artificial_bases.add(int)
Expand Down

0 comments on commit db5b7ef

Please sign in to comment.