From baf5c52c0872a4adf96ae17ca4f57d5f8979e730 Mon Sep 17 00:00:00 2001 From: z3c0 Date: Fri, 11 Feb 2022 15:31:53 +0000 Subject: [PATCH 01/12] ENH: exclude html table border w/falsy value Per the HTML5 spec, border attributes on table elements are obsolete. Borders should be implemented via CSS instead. https://html.spec.whatwg.org/multipage/obsolete.html#attr-table-border This enhancement will retain the default behavior of the border keyword of to_html, but will exclude the attribute entirely upon receiving a falsy value. --- pandas/io/formats/html.py | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/pandas/io/formats/html.py b/pandas/io/formats/html.py index 0c927277e899a..8e2fa620e2106 100644 --- a/pandas/io/formats/html.py +++ b/pandas/io/formats/html.py @@ -59,6 +59,8 @@ def __init__( self.show_dimensions = self.fmt.show_dimensions if border is None: border = cast(int, get_option("display.html.border")) + elif not border: + border = None self.border = border self.table_id = table_id self.render_links = render_links @@ -237,8 +239,13 @@ def _write_table(self, indent: int = 0) -> None: else: id_section = f' id="{self.table_id}"' + if self.border is None: + border_attr = "" + else: + border_attr = f' border="{self.border}"' + self.write( - f'', + f'', indent, ) From e375c1d30afe23c24f3632dc73f4f444bbb36124 Mon Sep 17 00:00:00 2001 From: z3c0 Date: Fri, 11 Feb 2022 16:20:20 +0000 Subject: [PATCH 02/12] ENH: exclude html table border w/falsy value Per the HTML5 spec, border attributes on table elements are obsolete. Borders should be implemented via CSS instead. https://html.spec.whatwg.org/multipage/obsolete.html#attr-table-border This enhancement will retain the default behavior of the border keyword of to_html, but will exclude the attribute entirely upon receiving a falsy value. --- doc/source/whatsnew/v1.5.0.rst | 3 +-- pandas/core/frame.py | 2 +- pandas/io/formats/format.py | 2 +- pandas/io/formats/html.py | 5 ++++- pandas/tests/io/test_html.py | 17 +++++++++++++++++ 5 files changed, 24 insertions(+), 5 deletions(-) diff --git a/doc/source/whatsnew/v1.5.0.rst b/doc/source/whatsnew/v1.5.0.rst index 713863204f1a5..2926e1816bdac 100644 --- a/doc/source/whatsnew/v1.5.0.rst +++ b/doc/source/whatsnew/v1.5.0.rst @@ -39,8 +39,7 @@ Other enhancements - :meth:`.GroupBy.min` and :meth:`.GroupBy.max` now supports `Numba `_ execution with the ``engine`` keyword (:issue:`45428`) - Implemented a ``bool``-dtype :class:`Index`, passing a bool-dtype array-like to ``pd.Index`` will now retain ``bool`` dtype instead of casting to ``object`` (:issue:`45061`) - Implemented a complex-dtype :class:`Index`, passing a complex-dtype array-like to ``pd.Index`` will now retain complex dtype instead of casting to ``object`` (:issue:`45845`) - -- +- :meth:`to_html` now excludes the ``border`` attribute from ``
`` elements when a falsy value is passed to the ``border`` keyword. .. --------------------------------------------------------------------------- .. _whatsnew_150.notable_bug_fixes: diff --git a/pandas/core/frame.py b/pandas/core/frame.py index 082a5814c2fc7..0ec9adaf74880 100644 --- a/pandas/core/frame.py +++ b/pandas/core/frame.py @@ -2883,7 +2883,7 @@ def to_html( classes: str | list | tuple | None = None, escape: bool = True, notebook: bool = False, - border: int | None = None, + border: int | bool | None = None, table_id: str | None = None, render_links: bool = False, encoding: str | None = None, diff --git a/pandas/io/formats/format.py b/pandas/io/formats/format.py index 4bc45e290ce4a..38c366dfe8c49 100644 --- a/pandas/io/formats/format.py +++ b/pandas/io/formats/format.py @@ -1058,7 +1058,7 @@ def to_html( encoding: str | None = None, classes: str | list | tuple | None = None, notebook: bool = False, - border: int | None = None, + border: int | bool | None = None, table_id: str | None = None, render_links: bool = False, ) -> str | None: diff --git a/pandas/io/formats/html.py b/pandas/io/formats/html.py index 8e2fa620e2106..4895e74cf9f61 100644 --- a/pandas/io/formats/html.py +++ b/pandas/io/formats/html.py @@ -44,7 +44,7 @@ def __init__( self, formatter: DataFrameFormatter, classes: str | list[str] | tuple[str, ...] | None = None, - border: int | None = None, + border: int | bool | None = None, table_id: str | None = None, render_links: bool = False, ) -> None: @@ -61,6 +61,9 @@ def __init__( border = cast(int, get_option("display.html.border")) elif not border: border = None + elif isinstance(border, bool): # border=True + border = cast(int, get_option("display.html.border")) + self.border = border self.table_id = table_id self.render_links = render_links diff --git a/pandas/tests/io/test_html.py b/pandas/tests/io/test_html.py index eeebb9a638afb..9c3eaf31fef54 100644 --- a/pandas/tests/io/test_html.py +++ b/pandas/tests/io/test_html.py @@ -1132,6 +1132,23 @@ def test_to_html_timestamp(self): result = df.to_html() assert "2000-01-01" in result + def test_to_html_borderless(self): + df = DataFrame([{'A': 1, 'B': 2}]) + out_border_default = df.to_html() + out_border_true = df.to_html(border=True) + out_border_explicit_default = df.to_html(border=1) + out_border_nondefault = df.to_html(border=2) + out_border_false = df.to_html(border=False) + out_border_zero = df.to_html(border=0) + + assert ' border="1"' in out_border_default + assert out_border_true == out_border_default + assert out_border_default == out_border_explicit_default + assert out_border_default != out_border_nondefault + assert ' border="2"' in out_border_nondefault + assert " border" not in out_border_false + assert out_border_zero == out_border_false + @pytest.mark.parametrize( "displayed_only,exp0,exp1", [ From 5223ee2632274a30846d3072323c92e4697d73c8 Mon Sep 17 00:00:00 2001 From: z3c0 Date: Fri, 11 Feb 2022 16:36:57 +0000 Subject: [PATCH 03/12] Fixes from pre-commit [automated commit] --- pandas/tests/io/test_html.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pandas/tests/io/test_html.py b/pandas/tests/io/test_html.py index 9c3eaf31fef54..435f90c4e4473 100644 --- a/pandas/tests/io/test_html.py +++ b/pandas/tests/io/test_html.py @@ -1133,7 +1133,7 @@ def test_to_html_timestamp(self): assert "2000-01-01" in result def test_to_html_borderless(self): - df = DataFrame([{'A': 1, 'B': 2}]) + df = DataFrame([{"A": 1, "B": 2}]) out_border_default = df.to_html() out_border_true = df.to_html(border=True) out_border_explicit_default = df.to_html(border=1) From 48be71dc8637ba6a2d2d28b8c1f11e82ad1dc6e5 Mon Sep 17 00:00:00 2001 From: z3c0 Date: Fri, 11 Feb 2022 17:19:10 +0000 Subject: [PATCH 04/12] BUG: allow border="0", exclude border only on False --- pandas/io/formats/html.py | 6 ++---- pandas/tests/io/test_html.py | 6 ++++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/pandas/io/formats/html.py b/pandas/io/formats/html.py index 4895e74cf9f61..32fc0f8932f63 100644 --- a/pandas/io/formats/html.py +++ b/pandas/io/formats/html.py @@ -57,12 +57,10 @@ def __init__( self.bold_rows = self.fmt.bold_rows self.escape = self.fmt.escape self.show_dimensions = self.fmt.show_dimensions - if border is None: + if border is None or (border and isinstance(border, bool)): border = cast(int, get_option("display.html.border")) - elif not border: + elif not border and isinstance(border, bool): border = None - elif isinstance(border, bool): # border=True - border = cast(int, get_option("display.html.border")) self.border = border self.table_id = table_id diff --git a/pandas/tests/io/test_html.py b/pandas/tests/io/test_html.py index 435f90c4e4473..70c286b7e3e27 100644 --- a/pandas/tests/io/test_html.py +++ b/pandas/tests/io/test_html.py @@ -1138,16 +1138,18 @@ def test_to_html_borderless(self): out_border_true = df.to_html(border=True) out_border_explicit_default = df.to_html(border=1) out_border_nondefault = df.to_html(border=2) - out_border_false = df.to_html(border=False) out_border_zero = df.to_html(border=0) + out_border_false = df.to_html(border=False) + assert ' border="1"' in out_border_default assert out_border_true == out_border_default assert out_border_default == out_border_explicit_default assert out_border_default != out_border_nondefault assert ' border="2"' in out_border_nondefault + assert ' border="0"' in out_border_zero assert " border" not in out_border_false - assert out_border_zero == out_border_false + assert out_border_zero != out_border_false @pytest.mark.parametrize( "displayed_only,exp0,exp1", From 70a4cbaa42359b674fc4df595b79c906173c38d2 Mon Sep 17 00:00:00 2001 From: z3c0 Date: Fri, 11 Feb 2022 21:18:49 +0000 Subject: [PATCH 05/12] DOC: revise to_html enchancements to reflect border=0 fix --- doc/source/whatsnew/v1.5.0.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/source/whatsnew/v1.5.0.rst b/doc/source/whatsnew/v1.5.0.rst index 2926e1816bdac..aca21c1b4a549 100644 --- a/doc/source/whatsnew/v1.5.0.rst +++ b/doc/source/whatsnew/v1.5.0.rst @@ -39,7 +39,7 @@ Other enhancements - :meth:`.GroupBy.min` and :meth:`.GroupBy.max` now supports `Numba `_ execution with the ``engine`` keyword (:issue:`45428`) - Implemented a ``bool``-dtype :class:`Index`, passing a bool-dtype array-like to ``pd.Index`` will now retain ``bool`` dtype instead of casting to ``object`` (:issue:`45061`) - Implemented a complex-dtype :class:`Index`, passing a complex-dtype array-like to ``pd.Index`` will now retain complex dtype instead of casting to ``object`` (:issue:`45845`) -- :meth:`to_html` now excludes the ``border`` attribute from ``
`` elements when a falsy value is passed to the ``border`` keyword. +- :meth:`to_html` now excludes the ``border`` attribute from ``
`` elements when ``border`` keyword is set to ``False``. .. --------------------------------------------------------------------------- .. _whatsnew_150.notable_bug_fixes: From 16845e9aafd5f30ed9e10d9a7649c5d523f23217 Mon Sep 17 00:00:00 2001 From: z3c0 Date: Sun, 27 Feb 2022 16:50:33 -0500 Subject: [PATCH 06/12] move enhancement note to I/O section --- doc/source/whatsnew/v1.5.0.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/source/whatsnew/v1.5.0.rst b/doc/source/whatsnew/v1.5.0.rst index 12c280ddfba35..93af36516306a 100644 --- a/doc/source/whatsnew/v1.5.0.rst +++ b/doc/source/whatsnew/v1.5.0.rst @@ -39,7 +39,6 @@ Other enhancements - :meth:`.GroupBy.min` and :meth:`.GroupBy.max` now supports `Numba `_ execution with the ``engine`` keyword (:issue:`45428`) - Implemented a ``bool``-dtype :class:`Index`, passing a bool-dtype array-like to ``pd.Index`` will now retain ``bool`` dtype instead of casting to ``object`` (:issue:`45061`) - Implemented a complex-dtype :class:`Index`, passing a complex-dtype array-like to ``pd.Index`` will now retain complex dtype instead of casting to ``object`` (:issue:`45845`) -- :meth:`to_html` now excludes the ``border`` attribute from ``
`` elements when ``border`` keyword is set to ``False``. .. --------------------------------------------------------------------------- .. _whatsnew_150.notable_bug_fixes: @@ -353,6 +352,7 @@ I/O - Bug in :func:`read_csv` not recognizing line break for ``on_bad_lines="warn"`` for ``engine="c"`` (:issue:`41710`) - Bug in :func:`read_parquet` when ``engine="pyarrow"`` which caused partial write to disk when column of unsupported datatype was passed (:issue:`44914`) - Bug in :func:`DataFrame.to_excel` and :class:`ExcelWriter` would raise when writing an empty DataFrame to a ``.ods`` file (:issue:`45793`) +- :meth:`to_html` now excludes the ``border`` attribute from ``
`` elements when ``border`` keyword is set to ``False``. Period ^^^^^^ From 2cd301576f20981f3019a253e89b70f75c9c97b8 Mon Sep 17 00:00:00 2001 From: z3c0 Date: Thu, 3 Mar 2022 13:22:16 -0500 Subject: [PATCH 07/12] remove trailing whitespace --- doc/source/whatsnew/v1.5.0.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/source/whatsnew/v1.5.0.rst b/doc/source/whatsnew/v1.5.0.rst index b49646a4838d3..7b75e49e285b2 100644 --- a/doc/source/whatsnew/v1.5.0.rst +++ b/doc/source/whatsnew/v1.5.0.rst @@ -408,7 +408,7 @@ I/O - Bug in Parquet roundtrip for Interval dtype with ``datetime64[ns]`` subtype (:issue:`45881`) - Bug in :func:`read_excel` when reading a ``.ods`` file with newlines between xml elements(:issue:`45598`) - :meth:`to_html` now excludes the ``border`` attribute from ``
`` elements when ``border`` keyword is set to ``False``. -- +- Period ^^^^^^ From 51168dfe6ff6b38dc4419ecb2c425aeb51a7eee0 Mon Sep 17 00:00:00 2001 From: z3c0 Date: Sat, 9 Apr 2022 18:42:26 -0400 Subject: [PATCH 08/12] remove redundant line from merge resolution --- doc/source/whatsnew/v1.5.0.rst | 1 - 1 file changed, 1 deletion(-) diff --git a/doc/source/whatsnew/v1.5.0.rst b/doc/source/whatsnew/v1.5.0.rst index 3a42f44b7d7fd..902279f44299e 100644 --- a/doc/source/whatsnew/v1.5.0.rst +++ b/doc/source/whatsnew/v1.5.0.rst @@ -564,7 +564,6 @@ I/O - Bug in :func:`read_parquet` when ``engine="pyarrow"`` which caused partial write to disk when column of unsupported datatype was passed (:issue:`44914`) - Bug in :func:`DataFrame.to_excel` and :class:`ExcelWriter` would raise when writing an empty DataFrame to a ``.ods`` file (:issue:`45793`) - Bug in Parquet roundtrip for Interval dtype with ``datetime64[ns]`` subtype (:issue:`45881`) -- Bug in :func:`read_excel` when reading a ``.ods`` file with newlines between xml elements(:issue:`45598`) - Bug in :func:`read_excel` when reading a ``.ods`` file with newlines between xml elements (:issue:`45598`) - Bug in :func:`read_parquet` when ``engine="fastparquet"`` where the file was not closed on error (:issue:`46555`) - :meth:`to_html` now excludes the ``border`` attribute from ``
`` elements when ``border`` keyword is set to ``False``. From ea38d32cae1a19dab81bc4570c74cab566857eb6 Mon Sep 17 00:00:00 2001 From: z3c0 Date: Sun, 10 Apr 2022 14:13:59 -0400 Subject: [PATCH 09/12] adjust test cases to allow not testing instance --- pandas/io/formats/html.py | 4 ++-- pandas/tests/io/formats/test_to_html.py | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/pandas/io/formats/html.py b/pandas/io/formats/html.py index 32fc0f8932f63..1fcb77dab6b1c 100644 --- a/pandas/io/formats/html.py +++ b/pandas/io/formats/html.py @@ -57,9 +57,9 @@ def __init__( self.bold_rows = self.fmt.bold_rows self.escape = self.fmt.escape self.show_dimensions = self.fmt.show_dimensions - if border is None or (border and isinstance(border, bool)): + if border is None or border: border = cast(int, get_option("display.html.border")) - elif not border and isinstance(border, bool): + elif not border: border = None self.border = border diff --git a/pandas/tests/io/formats/test_to_html.py b/pandas/tests/io/formats/test_to_html.py index aa8508d8e8942..3059efef09095 100644 --- a/pandas/tests/io/formats/test_to_html.py +++ b/pandas/tests/io/formats/test_to_html.py @@ -342,9 +342,9 @@ def test_to_html_truncate_multi_index(sparsify, expected, datapath): "option,result,expected", [ (None, lambda df: df.to_html(), "1"), - (None, lambda df: df.to_html(border=0), "0"), - (0, lambda df: df.to_html(), "0"), - (0, lambda df: df._repr_html_(), "0"), + (None, lambda df: df.to_html(border=2), "2"), + (2, lambda df: df.to_html(), "2"), + (2, lambda df: df._repr_html_(), "2"), ], ) def test_to_html_border(option, result, expected): From 231982b9e65131ec0b652588f4103a3459d7b0f0 Mon Sep 17 00:00:00 2001 From: z3c0 Date: Sun, 10 Apr 2022 14:42:03 -0400 Subject: [PATCH 10/12] remove true check of border --- pandas/io/formats/html.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pandas/io/formats/html.py b/pandas/io/formats/html.py index 1fcb77dab6b1c..d57699cdb4ad7 100644 --- a/pandas/io/formats/html.py +++ b/pandas/io/formats/html.py @@ -57,7 +57,7 @@ def __init__( self.bold_rows = self.fmt.bold_rows self.escape = self.fmt.escape self.show_dimensions = self.fmt.show_dimensions - if border is None or border: + if border is None: border = cast(int, get_option("display.html.border")) elif not border: border = None From 16b9dbccd31b92c79d1555b687b82892e957ae02 Mon Sep 17 00:00:00 2001 From: z3c0 Date: Mon, 11 Apr 2022 09:18:33 -0400 Subject: [PATCH 11/12] check for border=True --- pandas/io/formats/html.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pandas/io/formats/html.py b/pandas/io/formats/html.py index d57699cdb4ad7..dfd95b96c68e8 100644 --- a/pandas/io/formats/html.py +++ b/pandas/io/formats/html.py @@ -57,7 +57,7 @@ def __init__( self.bold_rows = self.fmt.bold_rows self.escape = self.fmt.escape self.show_dimensions = self.fmt.show_dimensions - if border is None: + if border is None or border is True: border = cast(int, get_option("display.html.border")) elif not border: border = None From 46e03ba753fa69601fa4eeeb2e7ac911ed86a6e9 Mon Sep 17 00:00:00 2001 From: z3c0 Date: Mon, 11 Apr 2022 09:35:07 -0400 Subject: [PATCH 12/12] adjust tests to account for border=0 hiding border attr --- pandas/tests/io/test_html.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pandas/tests/io/test_html.py b/pandas/tests/io/test_html.py index 83a4bfcbbb7a9..ea50464155ad5 100644 --- a/pandas/tests/io/test_html.py +++ b/pandas/tests/io/test_html.py @@ -1163,9 +1163,9 @@ def test_to_html_borderless(self): assert out_border_default == out_border_explicit_default assert out_border_default != out_border_nondefault assert ' border="2"' in out_border_nondefault - assert ' border="0"' in out_border_zero + assert ' border="0"' not in out_border_zero assert " border" not in out_border_false - assert out_border_zero != out_border_false + assert out_border_zero == out_border_false @pytest.mark.parametrize( "displayed_only,exp0,exp1",