Repository: astanin/python-tabulate

Issue:
Float with comma formatting are mis-aligned
When using commas in `floatfmt`, like `,.2f`, alignment is off:
```
In [32]: print(tabulate([("a", 3456.321), ("b", 982.6513)], floatfmt=".2f"))
-  -------
a  3456.32
b   982.65
-  -------

In [33]: print(tabulate([("a", 3456.321), ("b", 982.6513)], floatfmt=",.2f"))
-  -----------
a  3,456.32
b       982.65
-  -----------
```

Comments:

Patch:
diff --git a/setup.py b/setup.py
index 1cb6a84..00be096 100644
--- a/setup.py
+++ b/setup.py
@@ -46,6 +46,7 @@ setup(
     author_email="s.astanin@gmail.com",
     url="https://github.com/astanin/python-tabulate",
     license="MIT",
+    python_requires=">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*",
     classifiers=[
         "Development Status :: 4 - Beta",
         "License :: OSI Approved :: MIT License",
@@ -58,6 +59,7 @@ setup(
         "Programming Language :: Python :: 3.7",
         "Programming Language :: Python :: 3.8",
         "Programming Language :: Python :: 3.9",
+        "Programming Language :: Python :: 3.10",
         "Topic :: Software Development :: Libraries",
     ],
     py_modules=["tabulate"],
diff --git a/tabulate.py b/tabulate.py
index 0579fb3..4afcd0d 100644
--- a/tabulate.py
+++ b/tabulate.py
@@ -572,6 +572,10 @@ _invisible_codes_link = re.compile(
 
 _ansi_color_reset_code = "\033[0m"
 
+_float_with_thousands_separators = re.compile(
+    r"^(([+-]?[0-9]{1,3})(?:,([0-9]{3}))*)?(?(1)\.[0-9]*|\.[0-9]+)?$"
+)
+
 
 def simple_separated_format(separator):
     """Construct a simple TableFormat with columns separated by a separator.
@@ -593,6 +597,39 @@ def simple_separated_format(separator):
     )
 
 
+def _isnumber_with_thousands_separator(string):
+    """
+    >>> _isnumber_with_thousands_separator(".")
+    False
+    >>> _isnumber_with_thousands_separator("1")
+    True
+    >>> _isnumber_with_thousands_separator("1.")
+    True
+    >>> _isnumber_with_thousands_separator(".1")
+    True
+    >>> _isnumber_with_thousands_separator("1000")
+    False
+    >>> _isnumber_with_thousands_separator("1,000")
+    True
+    >>> _isnumber_with_thousands_separator("1,0000")
+    False
+    >>> _isnumber_with_thousands_separator("1,000.1234")
+    True
+    >>> _isnumber_with_thousands_separator(b"1,000.1234")
+    True
+    >>> _isnumber_with_thousands_separator("+1,000.1234")
+    True
+    >>> _isnumber_with_thousands_separator("-1,000.1234")
+    True
+    """
+    try:
+        string = string.decode()
+    except (UnicodeDecodeError, AttributeError):
+        pass
+
+    return bool(re.match(_float_with_thousands_separators, string))
+
+
 def _isconvertible(conv, string):
     try:
         conv(string)
@@ -701,9 +738,11 @@ def _afterpoint(string):
     -1
     >>> _afterpoint("123e45")
     2
+    >>> _afterpoint("123,456.78")
+    2
 
     """
-    if _isnumber(string):
+    if _isnumber(string) or _isnumber_with_thousands_separator(string):
         if _isint(string):
             return -1
         else:


Test patch:
diff --git a/test/test_internal.py b/test/test_internal.py
index 44765cf..681ee7b 100644
--- a/test/test_internal.py
+++ b/test/test_internal.py
@@ -31,6 +31,36 @@ def test_align_column_decimal():
     assert_equal(output, expected)
 
 
+def test_align_column_decimal_with_thousand_separators():
+    "Internal: _align_column(..., 'decimal')"
+    column = ["12.345", "-1234.5", "1.23", "1,234.5", "1e+234", "1.0e234"]
+    output = T._align_column(column, "decimal")
+    expected = [
+        "   12.345  ",
+        "-1234.5    ",
+        "    1.23   ",
+        "1,234.5    ",
+        "    1e+234 ",
+        "    1.0e234",
+    ]
+    assert_equal(output, expected)
+
+
+def test_align_column_decimal_with_incorrect_thousand_separators():
+    "Internal: _align_column(..., 'decimal')"
+    column = ["12.345", "-1234.5", "1.23", "12,34.5", "1e+234", "1.0e234"]
+    output = T._align_column(column, "decimal")
+    expected = [
+        "     12.345  ",
+        "  -1234.5    ",
+        "      1.23   ",
+        "12,34.5      ",
+        "      1e+234 ",
+        "      1.0e234",
+    ]
+    assert_equal(output, expected)
+
+
 def test_align_column_none():
     "Internal: _align_column(..., None)"
     column = ["123.4", "56.7890"]