Repository navigation
Regression of #305 in v0.10.0: disable_numparse=True is ignored when maxcolwidths is set #428
Description
Disclaimer: The following was generated with AI. Bug was originally discovered on BBOT via our CI dependabot: blacklanternsecurity/bbot#3045
This is a regression. The same bug was previously reported as #305 ("Can not tabulate row with a field that looks like a Python Bool using maxcolwidths"), fixed by PR #362 ("Fix handling 'True'/'False' bool str and None"), merged on 2025-07-23. The buggy code came back in a later merge and is now present on master and in the v0.10.0 release.
Symptom
When disable_numparse=True is combined with maxcolwidths, cells that look number-ish but cannot actually be parsed (e.g. '80,443', a comma-separated port list) crash with ValueError: invalid literal for int() with base 10. The same input works fine without maxcolwidths — disable_numparse=True is honored on the non-wrap code path, but ignored on the wrap path.
Repro (v0.10.0, also present on master)
from tabulate import tabulate
tabulate(
[['ports', 'str', 'comma-separated port list', '80,443']],
['name', 'type', 'desc', 'default'],
tablefmt='grid',
disable_numparse=True,
maxcolwidths=40,
)Traceback:
File ".../tabulate/__init__.py", line 1705, in _wrap_text_to_colwidths
else str(_type(cell, numparse)(cell))
ValueError: invalid literal for int() with base 10: '80,443'
Omit maxcolwidths and disable_numparse=True is honored and the call succeeds.
Root cause
In _wrap_text_to_colwidths, the per-cell numparse flag is correctly computed (the traceback's locals show numparses = [False, False, False, False] for this call), and it is correctly honored in the first branch:
if _isnumber(cell) and numparse:
new_row.append(cell)
continueBut a few lines later it is passed positionally into _type:
else str(_type(cell, numparse)(cell))_type's signature is:
def _type(string, has_invisible=True, numparse=True):so the False lands in the has_invisible slot and numparse stays at its default True. Result: _type('80,443') returns int, then int('80,443') raises.
This is what PR #362's commit d29909b already called out:
"calling
_typeis incorrect here; the subsequent lines need astr. … This regressed in e8e3091…"
How it came back
886e2ed(Sep 2022) — introduced the buggy_type(cell, numparse)(cell)line.d29909b/ PR Fix handling "True"/"False" bool str and None #362 (Mar 2025) — replaced the whole conditional withcasted_cell = str(cell). Issue Can not tabulate row with a field that looks like a Python Bool using maxcolwidths #305 closed as completed on 2025-07-23.- A subsequent merge brought the old buggy version back (now wrapped in
str(...)and reformatted by ruff/black, but functionally the same_type(cell, numparse)call). Master and v0.10.0 both ship the regressed code.
Impact
Anyone passing disable_numparse=True together with maxcolwidths and a string cell that doesn't satisfy _isnumber but is recognized as numeric by _type's default numparse=True (e.g. '80,443', 'True', 'False') will crash. We hit this in BBOT's CLI, which renders module-options tables; '80,443' is a default value for several modules.
Environment
- tabulate 0.10.0 (also reproduced building from current
master) - Python 3.10 – 3.14 (reproduced on all)
Activity