Repository navigation
Description
Summary
Most functions in humanize.number convert the input with float() before trying int(), purely to test for NaN/infinity. For an integer larger than the float range that conversion raises OverflowError, which escapes the except (TypeError, ValueError) clause and propagates to the caller — even though the value is a perfectly ordinary Python int.
Reproduction
>>> import humanize
>>> humanize.apnumber(10**400)
OverflowError: int too large to convert to float
>>> humanize.fractional(10**400)
OverflowError: int too large to convert to floatScope
Passing 10**400 + 123 on main:
| function | as int |
as str |
|---|---|---|
ordinal |
OverflowError |
+Inf |
intcomma |
correct | +Inf |
intword |
OverflowError |
+Inf |
apnumber |
OverflowError |
+Inf |
fractional |
OverflowError |
+Inf |
scientific |
OverflowError |
+Inf |
clamp |
OverflowError |
TypeError |
metric |
OverflowError |
TypeError |
The +Inf results are the same root cause seen from the string side: float("1" + "0"*400) returns inf rather than raising, so a finite integer is reported as infinite.
Why these are bugs rather than out-of-contract input
apnumber documents the fallback explicitly:
This always returns a string unless the value was not
int-able, thenstr(value)is returned.
10**400 is int-able — it is already an int — so by the documented contract it must return a string. ordinal carries the same wording. The others follow the same except ...: return str(value) shape, so graceful degradation is clearly the intent throughout; OverflowError simply isn't in the tuple.
clamp and metric annotate value: float, so arguably a large int is outside their contract — I've listed them for completeness rather than as a claim.
Related work already in flight
- Preserve integer inputs when formatting ordinals #414 fixes
ordinalby tryingint()beforefloat() - Preserve large integer strings in intcomma #404 fixes the
intcommastring case - Fix intword() rendering huge decillion counts between decillion and googol #415 / fix: handle decimal string input in intword() #332 touch
intword
So apnumber, fractional and scientific are the ones not currently covered by an open PR. I'll put up a PR for apnumber and fractional, where the correct result is unambiguous.
scientific is worth separating: falling back to str(value) there would return 401 digits from a function whose purpose is compact notation, so it probably wants Decimal-based formatting (1.00 x 10⁴⁰⁰) rather than a fallback. Happy to follow up with that if the approach sounds right.
Verified on main at 392aef7, Python 3.12.
Activity