Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

sum stub too permissive #7574

Closed
jpy-git opened this issue Apr 1, 2022 · 3 comments · Fixed by #8000
Closed

sum stub too permissive #7574

jpy-git opened this issue Apr 1, 2022 · 3 comments · Fixed by #8000
Labels
stubs: false negative Type checkers do not report an error, but should

Comments

@jpy-git
Copy link
Contributor

jpy-git commented Apr 1, 2022

Currently the stub for the sum built-in allows any iterable as an argument

typeshed/stdlib/builtins.pyi

Lines 1480 to 1489 in ae6ff79

@overload
def sum(__iterable: Iterable[_T]) -> _T | Literal[0]: ...
if sys.version_info >= (3, 8):
@overload
def sum(__iterable: Iterable[_T], start: _S) -> _T | _S: ...
else:
@overload
def sum(__iterable: Iterable[_T], __start: _S) -> _T | _S: ...

This means mypy has no issue with:

sum("hello")

Which results in this error at runtime:

TypeError: unsupported operand type(s) for +: 'int' and 'str'

context: I was introducing someone to mypy and used the example above as something that mypy should flag, but turns out it wasn't being flagged so logging here.

@AlexWaygood AlexWaygood added the stubs: false negative Type checkers do not report an error, but should label Apr 1, 2022
@Akuli
Copy link
Collaborator

Akuli commented Apr 1, 2022

It's easy to put a TypeVar bound on the first overload, but sum() doesn't work for strings even if you define start="". It intentionally works with everything except strings.

>>> sum("asd", start="")
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: sum() can't sum strings [use ''.join(seq) instead]

Fixing this properly would require python/typing#1043.

@AlexWaygood
Copy link
Member

AlexWaygood commented Apr 1, 2022

It's easy to put a TypeVar bound on the first overload

Even that's actually a little tricky, because you get into the question of what is a number.

For example, this works fine, even though there's not really anything that these classes all have in common either from a nominal or structural point of view:

>>> import numpy as np
>>> from fractions import Fraction
>>> sum((3, np.float64(), Fraction(7, 22), complex(8, 0), 9.83))
(21.14818181818182+0j)

This does too:

>>> from decimal import Decimal
>>> sum((3, Decimal('0.98')))
Decimal('3.98')

But these all break:

>>> from decimal import Decimal
>>> sum((2.34, Decimal('3.87')))
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: unsupported operand type(s) for +: 'float' and 'decimal.Decimal'
>>> from fractions import Fraction
>>> sum((Fraction(2, 3), Decimal('2.3')))
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: unsupported operand type(s) for +: 'Fraction' and 'decimal.Decimal'
>>> sum((complex(2, 0), Decimal('2.34')))
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: unsupported operand type(s) for +: 'complex' and 'decimal.Decimal'
>>> import numpy as np
>>> sum((np.float64(), Decimal('2.34')))
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: unsupported operand type(s) for +: 'float' and 'decimal.Decimal'

@JelleZijlstra
Copy link
Member

We could at least bound the TypeVar to a Protocol defining __add__. That wouldn't help with Alex's mixed numeric types, but it would help type checkers catch plausible mistakes like sum([1, None]).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
stubs: false negative Type checkers do not report an error, but should
Projects
None yet
4 participants