From 3c6e4185f7f0d033074d1ed53f598c9cfc8abff1 Mon Sep 17 00:00:00 2001 From: George Schizas Date: Tue, 27 Feb 2024 13:28:48 +0200 Subject: [PATCH 1/4] Allow columns to wrap even if they have None values within them. --- tabulate/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tabulate/__init__.py b/tabulate/__init__.py index 11bb865..f4bb2de 100644 --- a/tabulate/__init__.py +++ b/tabulate/__init__.py @@ -1529,7 +1529,7 @@ def _wrap_text_to_colwidths(list_of_lists, colwidths, numparses=True): # Any future custom formatting of types (such as datetimes) # may need to be more explicit than just `str` of the object casted_cell = ( - str(cell) if _isnumber(cell) else _type(cell, numparse)(cell) + '' if cell is None else str(cell) if _isnumber(cell) else _type(cell, numparse)(cell) ) wrapped = [ "\n".join(wrapper.wrap(line)) From 3b2b69505b3962ace99ac69ee96da1224b477f1a Mon Sep 17 00:00:00 2001 From: George Schizas Date: Tue, 27 Feb 2024 13:42:58 +0200 Subject: [PATCH 2/4] Added a simple test for wrapping None values in cells --- test/test_textwrapper.py | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/test/test_textwrapper.py b/test/test_textwrapper.py index f3070b1..9fd2660 100644 --- a/test/test_textwrapper.py +++ b/test/test_textwrapper.py @@ -184,3 +184,27 @@ def test_wrap_datetime(): ] expected = "\n".join(expected) assert_equal(expected, result) + + +def test_wrap_none_value(): + """TextWrapper: Show that None can be wrapped without crashing""" + data = [ + ["First Entry", None], + ["Second Entry", None] + ] + headers = ["Title", "Value"] + result = tabulate(data, headers=headers, tablefmt="grid", maxcolwidths=[7, 5]) + + expected = [ + "+---------+---------+", + "| Title | Value |", + "+=========+=========+", + "| First | |", + "| Entry | |", + "+---------+---------+", + "| Second | |", + "| Entry | |", + "+---------+---------+", + ] + expected = "\n".join(expected) + assert_equal(expected, result) From 787851ffe4350a779ba8ecd1e58c570f20d28c34 Mon Sep 17 00:00:00 2001 From: George Schizas Date: Tue, 22 Oct 2024 23:31:51 +0300 Subject: [PATCH 3/4] Use missingval instead of empty string for None values --- tabulate/__init__.py | 8 ++++---- test/test_textwrapper.py | 28 ++++++++++++++++++++++++---- 2 files changed, 28 insertions(+), 8 deletions(-) diff --git a/tabulate/__init__.py b/tabulate/__init__.py index 520b60e..a782683 100644 --- a/tabulate/__init__.py +++ b/tabulate/__init__.py @@ -1623,7 +1623,7 @@ def _normalize_tabular_data(tabular_data, headers, showindex="default"): return rows, headers, headers_pad -def _wrap_text_to_colwidths(list_of_lists, colwidths, numparses=True): +def _wrap_text_to_colwidths(list_of_lists, colwidths, numparses=True, missingval=_DEFAULT_MISSINGVAL): if len(list_of_lists): num_cols = len(list_of_lists[0]) else: @@ -1646,7 +1646,7 @@ def _wrap_text_to_colwidths(list_of_lists, colwidths, numparses=True): # explicit than just `str` of the object. Also doesn't work for # custom floatfmt/intfmt, nor with any missing/blank cells. casted_cell = ( - '' if cell is None else str(cell) if _isnumber(cell) else _type(cell, numparse)(cell) + missingval if cell is None else str(cell) if _isnumber(cell) else _type(cell, numparse)(cell) ) wrapped = [ "\n".join(wrapper.wrap(line)) @@ -2247,7 +2247,7 @@ def tabulate( numparses = _expand_numparse(disable_numparse, num_cols) list_of_lists = _wrap_text_to_colwidths( - list_of_lists, maxcolwidths, numparses=numparses + list_of_lists, maxcolwidths, numparses=numparses, missingval=missingval ) if maxheadercolwidths is not None: @@ -2261,7 +2261,7 @@ def tabulate( numparses = _expand_numparse(disable_numparse, num_cols) headers = _wrap_text_to_colwidths( - [headers], maxheadercolwidths, numparses=numparses + [headers], maxheadercolwidths, numparses=numparses, missingval=missingval )[0] # empty values in the first column of RST tables should be escaped (issue #82) diff --git a/test/test_textwrapper.py b/test/test_textwrapper.py index b47ea3e..02dcc41 100644 --- a/test/test_textwrapper.py +++ b/test/test_textwrapper.py @@ -224,10 +224,7 @@ def test_wrap_datetime(): def test_wrap_none_value(): """TextWrapper: Show that None can be wrapped without crashing""" - data = [ - ["First Entry", None], - ["Second Entry", None] - ] + data = [["First Entry", None], ["Second Entry", None]] headers = ["Title", "Value"] result = tabulate(data, headers=headers, tablefmt="grid", maxcolwidths=[7, 5]) @@ -244,3 +241,26 @@ def test_wrap_none_value(): ] expected = "\n".join(expected) assert_equal(expected, result) + + +def test_wrap_none_value_with_missingval(): + """TextWrapper: Show that None can be wrapped without crashing and with a missing value""" + data = [["First Entry", None], ["Second Entry", None]] + headers = ["Title", "Value"] + result = tabulate( + data, headers=headers, tablefmt="grid", maxcolwidths=[7, 5], missingval="???" + ) + + expected = [ + "+---------+---------+", + "| Title | Value |", + "+=========+=========+", + "| First | ??? |", + "| Entry | |", + "+---------+---------+", + "| Second | ??? |", + "| Entry | |", + "+---------+---------+", + ] + expected = "\n".join(expected) + assert_equal(expected, result) From bdc0c76f7165b7273dbce8fad96ae2c5ee312a09 Mon Sep 17 00:00:00 2001 From: Perry Kundert Date: Wed, 20 Nov 2024 07:41:50 -0700 Subject: [PATCH 4/4] Automate release to PyPI, remove Python 3.8 testing --- .github/workflows/release.yml | 29 +++++++++++++++++++++++++++++ HOWTOPUBLISH | 6 +++--- README.md | 14 ++++++++++---- pyproject.toml | 2 +- tabulate/__init__.py | 10 ++++++++-- tox.ini | 8 +------- 6 files changed, 52 insertions(+), 17 deletions(-) create mode 100644 .github/workflows/release.yml diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..0187705 --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,29 @@ +name: Release + +on: + push: + tags: + - '*' + +jobs: + pypi-publish: + if: startsWith(github.ref, 'refs/tags') + name: Upload release to PyPI + runs-on: ubuntu-latest + environment: + name: pypi + url: https://pypi.org/p/tabulate-slip39 + permissions: + id-token: write # IMPORTANT: this permission is mandatory for trusted publishing + steps: + - name: Checkout + uses: actions/checkout@v3 + - name: Set up Python + uses: actions/setup-python@v4 + - name: Build + run: | + python -m pip install --upgrade pip + python -m pip install setuptools build + python -m build -s + - name: Publish package distributions to PyPI + uses: pypa/gh-action-pypi-publish@release/v1 diff --git a/HOWTOPUBLISH b/HOWTOPUBLISH index 29c4545..bd78db2 100644 --- a/HOWTOPUBLISH +++ b/HOWTOPUBLISH @@ -1,4 +1,5 @@ # update contributors and CHANGELOG in README +python -m pip install pre-commit python -m pre_commit run -a # and then commit changes tox -e py39-extra,py310-extra,py311-extra,py312-extra,py313-extra # tag version release @@ -6,10 +7,9 @@ python -m build -s # this will update tabulate/version.py python -m pip install . # install tabulate in the current venv python -m pip install -r benchmark/requirements.txt python benchmark/benchmark.py # then update README -# move tag to the last commit +# move tag to the last commit: eg. +git tag v0.10.4 python -m build -s # update tabulate/version.py python -m build -nswx . git push # wait for all CI builds to succeed git push --tags # if CI builds succeed -twine upload --repository-url https://test.pypi.org/legacy/ dist/* -twine upload dist/* diff --git a/README.md b/README.md index f3d0fa9..41f2a94 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,12 @@ python-tabulate =============== +> This is a temporary upgrade shim for https://github.com/astanin/python-tabulate + +> Install `tabulate` via `python -m pip install tabulate-slip39`, +> until the upstream https://pypi.org/project/tabulate is upgraded + + Pretty-print tabular data in Python, a library and a command-line utility. @@ -503,10 +509,10 @@ format: >>> print(tabulate(table, headers, tablefmt="asciidoc")) [cols="8<,7>",options="header"] |==== -| item | qty -| spam | 42 -| eggs | 451 -| bacon | 0 +| item | qty +| spam | 42 +| eggs | 451 +| bacon | 0 |==== ``` diff --git a/pyproject.toml b/pyproject.toml index 4144f9b..67c5429 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -3,7 +3,7 @@ requires = ["setuptools>=61.2.0", "setuptools_scm[toml]>=3.4.3"] build-backend = "setuptools.build_meta" [project] -name = "tabulate" +name = "tabulate-slip39" authors = [{name = "Sergey Astanin", email = "s.astanin@gmail.com"}] license = {text = "MIT"} description = "Pretty-print tabular data" diff --git a/tabulate/__init__.py b/tabulate/__init__.py index a782683..f491325 100644 --- a/tabulate/__init__.py +++ b/tabulate/__init__.py @@ -1623,7 +1623,9 @@ def _normalize_tabular_data(tabular_data, headers, showindex="default"): return rows, headers, headers_pad -def _wrap_text_to_colwidths(list_of_lists, colwidths, numparses=True, missingval=_DEFAULT_MISSINGVAL): +def _wrap_text_to_colwidths( + list_of_lists, colwidths, numparses=True, missingval=_DEFAULT_MISSINGVAL +): if len(list_of_lists): num_cols = len(list_of_lists[0]) else: @@ -1646,7 +1648,11 @@ def _wrap_text_to_colwidths(list_of_lists, colwidths, numparses=True, missingval # explicit than just `str` of the object. Also doesn't work for # custom floatfmt/intfmt, nor with any missing/blank cells. casted_cell = ( - missingval if cell is None else str(cell) if _isnumber(cell) else _type(cell, numparse)(cell) + missingval + if cell is None + else str(cell) + if _isnumber(cell) + else _type(cell, numparse)(cell) ) wrapped = [ "\n".join(wrapper.wrap(line)) diff --git a/tox.ini b/tox.ini index 9605e79..51f0100 100644 --- a/tox.ini +++ b/tox.ini @@ -8,7 +8,7 @@ # for testing and it is disabled by default. [tox] -envlist = lint, py{38, 39, 310, 311, 312, 313} +envlist = lint, py{39, 310, 311, 312, 313} isolated_build = True [gh] @@ -33,12 +33,6 @@ commands = python -m pre_commit run -a deps = pre-commit -[testenv:py38] -basepython = python3.8 -commands = pytest -v --doctest-modules --ignore benchmark {posargs} -deps = - pytest - [testenv:py38-extra] basepython = python3.8 commands = pytest -v --doctest-modules --ignore benchmark {posargs}