diff --git a/tabulate/__init__.py b/tabulate/__init__.py index 12a2950..6fa0dca 100644 --- a/tabulate/__init__.py +++ b/tabulate/__init__.py @@ -1613,6 +1613,25 @@ def _normalize_tabular_data(tabular_data, headers, showindex="default"): return rows, headers, headers_pad +def _validate_maxcolwidth(width): + """Return a positive int column width, or None to leave the column unbound. + + ``bool`` is rejected (it is an ``int`` subclass): ``True`` previously wrapped + every character. Non-ints — including ``float('nan')`` — are rejected too; + ``nan`` bypassed ``width <= 0`` (comparisons with NaN are false) and hung + inside ``textwrap`` forever. + """ + if width is None: + return None + if isinstance(width, bool) or not isinstance(width, int): + raise TypeError( + f"maxcolwidths values must be positive ints or None, got {width!r}" + ) + if width <= 0: + raise ValueError(f"invalid width {width!r} (must be > 0)") + return width + + def _wrap_text_to_colwidths( list_of_lists, colwidths, @@ -1636,6 +1655,7 @@ def _wrap_text_to_colwidths( new_row.append(cell) continue + width = _validate_maxcolwidth(width) if width is not None: wrapper = _CustomTextWrap( width=width, diff --git a/test/test_regression.py b/test/test_regression.py index 9555676..c0911f4 100644 --- a/test/test_regression.py +++ b/test/test_regression.py @@ -2,7 +2,7 @@ from tabulate import DataRow, Line, TableFormat, tabulate -from common import assert_equal, skip +from common import assert_equal, raises, skip def test_ansi_color_in_table_cells(): @@ -598,3 +598,22 @@ def test_github_escape_pipe_character(): result = tabulate([["foo|bar"]], headers=("spam|eggs",), tablefmt="github") expected = "| spam\\|eggs |\n|:------------|\n| foo\\|bar |" assert_equal(expected, result) + +def test_maxcolwidths_rejects_bool_float_nan(): + "maxcolwidths: reject bool/float/nan (bool wrapped every char; nan hung)" + import math + + for bad in (True, [True], [0.5], [float("nan")], [math.inf]): + try: + tabulate([["hello"]], maxcolwidths=bad) + raise AssertionError(f"expected TypeError for {bad!r}") + except TypeError: + pass + try: + tabulate([["hello"]], maxcolwidths=[-1]) + raise AssertionError("expected ValueError for -1") + except ValueError: + pass + # None still means unbound + result = tabulate([["hello"]], maxcolwidths=[None], tablefmt="plain") + assert_equal("hello", result)