Skip to content

Commit 232ed74

Browse files
committed
Begin trying to simplify _isnumber
1 parent c37de75 commit 232ed74

File tree

2 files changed

+53
-9
lines changed

2 files changed

+53
-9
lines changed

README.md

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -813,6 +813,20 @@ data in the column is used to infer the type:
813813

814814
```
815815

816+
The deduced type (eg. str, float) influences the rendering of any types
817+
that have alternative representations. For example, since `Fraction` has
818+
methods `__str__` and `__float__` defined (and hence is convertible to a
819+
`float` and also has a `str` representation), the appropriate
820+
representation is selected for the column's deduced type. In order to not
821+
lose precision accidentally, types having both an `__int__` and
822+
`__float__` represention will be considered a `float`.
823+
824+
Therefore, if your table contains types convertible to int/float but you'd
825+
*prefer* they be represented as strings, or your strings *might* all look
826+
like numbers such as "1e23": either convert them to the desired
827+
representation before you `tabulate`, or ensure that the column always
828+
contains at least one other `str`.
829+
816830
### Text formatting
817831

818832
By default, `tabulate` removes leading and trailing whitespace from text

tabulate/__init__.py

Lines changed: 39 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -876,25 +876,55 @@ def _isconvertible(conv, string):
876876

877877

878878
def _isnumber(string):
879-
"""
879+
"""Detects if something *could* be considered a numeric value, vs. just a string.
880+
881+
This promotes types convertible to both int and float to be considered
882+
a float. Note that, iff *all* values appear to be some form of numeric
883+
value such as eg. "1e2", they would be considered numbers!
884+
885+
The exception is things that appear to be numbers but overflow to
886+
+/-inf, eg. "1e23456"; we'll have to exclude them explicitly.
887+
888+
>>> _isnumber(123)
889+
True
890+
>>> _isnumber(123.45)
891+
True
880892
>>> _isnumber("123.45")
881893
True
882894
>>> _isnumber("123")
883895
True
884896
>>> _isnumber("spam")
885897
False
886-
>>> _isnumber("123e45678")
898+
>>> _isnumber("123e45")
899+
True
900+
>>> _isnumber("123e45678") # evaluates equal to 'inf', but ... isn't
887901
False
888902
>>> _isnumber("inf")
889903
True
904+
>>> from fractions import Fraction
905+
>>> _isnumber(Fraction(1,3))
906+
True
907+
890908
"""
891-
if not _isconvertible(float, string):
892-
return False
893-
elif isinstance(string, (str, bytes)) and (
894-
math.isinf(float(string)) or math.isnan(float(string))
895-
):
896-
return string.lower() in ["inf", "-inf", "nan"]
897-
return True
909+
return (
910+
# fast path
911+
type(string) in (float, int)
912+
# covers 'NaN', +/- 'inf', and eg. '1e2', as well as any type
913+
# convertible to int/float.
914+
or (
915+
_isconvertible(float, string)
916+
and (
917+
# some other type convertible to float
918+
not isinstance(string, (str, bytes))
919+
# or, a numeric string eg. "1e1...", "NaN", ..., but isn't
920+
# just an over/underflow
921+
or (
922+
not (math.isinf(float(string)) or math.isnan(float(string)))
923+
or string.lower() in ["inf", "-inf", "nan"]
924+
)
925+
)
926+
)
927+
)
898928

899929

900930
def _isint(string, inttype=int):

0 commit comments

Comments
 (0)