From 070282479de1c5a820670cec7402cf2d5aceea44 Mon Sep 17 00:00:00 2001 From: Ben Date: Wed, 14 Nov 2018 07:28:44 -0800 Subject: [PATCH 01/13] ENH: Add render_links option to DataFrame to_html() --- pandas/core/frame.py | 13 ++++-- pandas/io/formats/format.py | 6 ++- pandas/io/formats/html.py | 20 +++++++-- pandas/tests/io/formats/test_to_html.py | 54 +++++++++++++++++++++++++ 4 files changed, 85 insertions(+), 8 deletions(-) diff --git a/pandas/core/frame.py b/pandas/core/frame.py index 032d68e2d3e8c..e0bc5d176f22a 100644 --- a/pandas/core/frame.py +++ b/pandas/core/frame.py @@ -2093,8 +2093,8 @@ def to_html(self, buf=None, columns=None, col_space=None, header=True, index=True, na_rep='NaN', formatters=None, float_format=None, sparsify=None, index_names=True, justify=None, max_rows=None, max_cols=None, show_dimensions=False, decimal='.', - bold_rows=True, classes=None, escape=True, - notebook=False, border=None, table_id=None): + bold_rows=True, classes=None, escape=True, notebook=False, + border=None, table_id=None, render_links=False): """ Render a DataFrame as an HTML table. %(shared_params)s @@ -2116,6 +2116,12 @@ def to_html(self, buf=None, columns=None, col_space=None, header=True, A css id is included in the opening `` tag if specified. .. versionadded:: 0.23.0 + + render_links : boolean, default False + Convert URLs to HTML links. + + .. versionadded:: 0.24.0 + %(returns)s See Also -------- @@ -2137,7 +2143,8 @@ def to_html(self, buf=None, columns=None, col_space=None, header=True, max_rows=max_rows, max_cols=max_cols, show_dimensions=show_dimensions, - decimal=decimal, table_id=table_id) + decimal=decimal, table_id=table_id, + render_links=render_links) # TODO: a generic formatter wld b in DataFrameFormatter formatter.to_html(classes=classes, notebook=notebook, border=border) diff --git a/pandas/io/formats/format.py b/pandas/io/formats/format.py index 4c08ee89c33df..41e8068981382 100644 --- a/pandas/io/formats/format.py +++ b/pandas/io/formats/format.py @@ -383,7 +383,7 @@ def __init__(self, frame, buf=None, columns=None, col_space=None, justify=None, float_format=None, sparsify=None, index_names=True, line_width=None, max_rows=None, max_cols=None, show_dimensions=False, decimal='.', - table_id=None, **kwds): + table_id=None, render_links=False, **kwds): self.frame = frame if buf is not None: self.buf = _expand_user(_stringify_path(buf)) @@ -410,6 +410,7 @@ def __init__(self, frame, buf=None, columns=None, col_space=None, len(self.frame)) self.show_dimensions = show_dimensions self.table_id = table_id + self.render_links = render_links if justify is None: self.justify = get_option("display.colheader_justify") @@ -735,7 +736,8 @@ def to_html(self, classes=None, notebook=False, border=None): max_cols=self.max_cols, notebook=notebook, border=border, - table_id=self.table_id) + table_id=self.table_id, + render_links=self.render_links) if hasattr(self.buf, 'write'): html_renderer.write_result(self.buf) elif isinstance(self.buf, compat.string_types): diff --git a/pandas/io/formats/html.py b/pandas/io/formats/html.py index bc2de210df3f4..8f372aec210f5 100644 --- a/pandas/io/formats/html.py +++ b/pandas/io/formats/html.py @@ -19,6 +19,7 @@ from pandas.io.formats.format import ( TableFormatter, buffer_put_lines, get_level_lengths) from pandas.io.formats.printing import pprint_thing +from pandas.io.common import _is_url class HTMLFormatter(TableFormatter): @@ -26,7 +27,8 @@ class HTMLFormatter(TableFormatter): indent_delta = 2 def __init__(self, formatter, classes=None, max_rows=None, max_cols=None, - notebook=False, border=None, table_id=None): + notebook=False, border=None, table_id=None, + render_links=False): self.fmt = formatter self.classes = classes @@ -46,6 +48,7 @@ def __init__(self, formatter, classes=None, max_rows=None, max_cols=None, border = get_option('display.html.border') self.border = border self.table_id = table_id + self.render_links = render_links def write(self, s, indent=0): rs = pprint_thing(s) @@ -74,9 +77,20 @@ def _write_cell(self, s, kind='td', indent=0, tags=None): ('>', r'>')]) else: esc = {} + rs = pprint_thing(s, escape_chars=esc).strip() - self.write(u'{start}{rs}' - .format(start=start_tag, rs=rs, kind=kind), indent) + + if self.render_links and _is_url(rs): + rs_unescaped = pprint_thing(s, escape_chars={}).strip() + start_tag += ''\ + .format(url=rs_unescaped) + end_a = '' + else: + end_a = '' + + self.write('{start}{rs}{end_a}' + .format(start=start_tag, rs=rs, end_a=end_a, kind=kind), + indent) def write_tr(self, line, indent=0, indent_delta=0, header=False, align=None, tags=None, nindex_levels=0): diff --git a/pandas/tests/io/formats/test_to_html.py b/pandas/tests/io/formats/test_to_html.py index 882a629b5b706..bddaa2937bbc9 100644 --- a/pandas/tests/io/formats/test_to_html.py +++ b/pandas/tests/io/formats/test_to_html.py @@ -477,3 +477,57 @@ def test_to_html_float_format_no_fixed_width(self, datapath): df = DataFrame({'x': [100.0]}) expected = expected_html(datapath, 'gh22270_expected_output') assert df.to_html(float_format='%.0f') == expected + + @pytest.mark.parametrize("render_links", [True, False]) + def test_to_html_render_links(self, render_links): + # GH 2679 + + data = [ + { + 'foo': 0, + 'bar': 'http://pandas.pydata.org/?q1=a&q2=b', + None: 'pydata.org', + }, + { + 'foo': 0, + 'bar': 'www.pydata.org', + None: 'pydata.org', + }, + ] + df = DataFrame(data, columns=['foo', 'bar', None], + index=range(len(data))) + + result = df.to_html(render_links=render_links) + + open_tag = not render_links and '' or '' + + expected = dedent("""\ +
' or\ + ('') + close_tag = not render_links and '
+ + + + + + + + + + + + + {open_tag}http://pandas.pydata.org/?q1=a&q2=b{close_tag} + + + + + + + + + +
foobarNone
00pydata.org
10www.pydata.orgpydata.org
""").format(open_tag=open_tag, close_tag=close_tag) + + assert result == expected From 61c22a373dcab56fe724f1d423300adbb6118adb Mon Sep 17 00:00:00 2001 From: Ben Date: Tue, 27 Nov 2018 20:50:04 -0800 Subject: [PATCH 02/13] Remove usage of dedent --- pandas/tests/io/formats/test_to_html.py | 50 ++++++++++++------------- 1 file changed, 25 insertions(+), 25 deletions(-) diff --git a/pandas/tests/io/formats/test_to_html.py b/pandas/tests/io/formats/test_to_html.py index bddaa2937bbc9..1eeb2716a2c9b 100644 --- a/pandas/tests/io/formats/test_to_html.py +++ b/pandas/tests/io/formats/test_to_html.py @@ -504,30 +504,30 @@ def test_to_html_render_links(self, render_links): ' target="_blank">') close_tag = not render_links and '' or '' - expected = dedent("""\ - - - - - - - - - - - - - - {open_tag}http://pandas.pydata.org/?q1=a&q2=b{close_tag} - - - - - - - - - -
foobarNone
00pydata.org
10www.pydata.orgpydata.org
""").format(open_tag=open_tag, close_tag=close_tag) + expected = """\ + + + + + + + + + + + + + + {open_tag}http://pandas.pydata.org/?q1=a&q2=b{close_tag} + + + + + + + + + +
foobarNone
00pydata.org
10www.pydata.orgpydata.org
""".format(open_tag=open_tag, close_tag=close_tag) assert result == expected From 793006d4f39fd59c62b12908a85c4c4156240dbe Mon Sep 17 00:00:00 2001 From: Ben Date: Wed, 28 Nov 2018 12:50:44 -0800 Subject: [PATCH 03/13] HTML writer to write unicode --- 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 2df0dce8bfbe1..de00c5534e6f2 100644 --- a/pandas/io/formats/html.py +++ b/pandas/io/formats/html.py @@ -90,7 +90,7 @@ def _write_cell(self, s, kind='td', indent=0, tags=None): else: end_a = '' - self.write('{start}{rs}{end_a}' + self.write(u'{start}{rs}{end_a}' .format(start=start_tag, rs=rs, end_a=end_a, kind=kind), indent) From 13e20ea9ad65414436f4532887aedcc1fa4f50ae Mon Sep 17 00:00:00 2001 From: Ben Date: Wed, 28 Nov 2018 22:29:24 -0800 Subject: [PATCH 04/13] Move _is_url import to correct position --- 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 de00c5534e6f2..367811777b24f 100644 --- a/pandas/io/formats/html.py +++ b/pandas/io/formats/html.py @@ -16,10 +16,10 @@ import pandas.core.common as com from pandas.core.config import get_option +from pandas.io.common import _is_url from pandas.io.formats.format import ( TableFormatter, buffer_put_lines, get_level_lengths) from pandas.io.formats.printing import pprint_thing -from pandas.io.common import _is_url class HTMLFormatter(TableFormatter): From db31c1d766576d69d980c0d6c68157cfdb56e232 Mon Sep 17 00:00:00 2001 From: Ben Date: Sun, 2 Dec 2018 13:10:24 -0800 Subject: [PATCH 05/13] A few style fixes --- pandas/io/formats/html.py | 9 ++++----- pandas/tests/io/formats/test_to_html.py | 10 ++++------ 2 files changed, 8 insertions(+), 11 deletions(-) diff --git a/pandas/io/formats/html.py b/pandas/io/formats/html.py index 367811777b24f..966b229be6b94 100644 --- a/pandas/io/formats/html.py +++ b/pandas/io/formats/html.py @@ -84,15 +84,14 @@ def _write_cell(self, s, kind='td', indent=0, tags=None): if self.render_links and _is_url(rs): rs_unescaped = pprint_thing(s, escape_chars={}).strip() - start_tag += ''\ - .format(url=rs_unescaped) + start_tag += ''.format( + url=rs_unescaped) end_a = '' else: end_a = '' - self.write(u'{start}{rs}{end_a}' - .format(start=start_tag, rs=rs, end_a=end_a, kind=kind), - indent) + self.write(u'{start}{rs}{end_a}'.format( + start=start_tag, rs=rs, end_a=end_a, kind=kind), indent) def write_tr(self, line, indent=0, indent_delta=0, header=False, align=None, tags=None, nindex_levels=0): diff --git a/pandas/tests/io/formats/test_to_html.py b/pandas/tests/io/formats/test_to_html.py index 1eeb2716a2c9b..8926f94773c16 100644 --- a/pandas/tests/io/formats/test_to_html.py +++ b/pandas/tests/io/formats/test_to_html.py @@ -499,11 +499,6 @@ def test_to_html_render_links(self, render_links): result = df.to_html(render_links=render_links) - open_tag = not render_links and '' or\ - ('') - close_tag = not render_links and '' or '' - expected = """\ @@ -528,6 +523,9 @@ def test_to_html_render_links(self, render_links): -
pydata.org
""".format(open_tag=open_tag, close_tag=close_tag) +""".format(open_tag=not render_links and '' or ( + ''), + close_tag=not render_links and '' or '') assert result == expected From a59bc4f415657555f65d2d79e182a0e1ebce8d26 Mon Sep 17 00:00:00 2001 From: Ben Date: Sun, 2 Dec 2018 22:44:24 -0800 Subject: [PATCH 06/13] Pass expected start and end tags as parameters --- pandas/tests/io/formats/test_to_html.py | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/pandas/tests/io/formats/test_to_html.py b/pandas/tests/io/formats/test_to_html.py index 23fae120eb85c..077ca34882e42 100644 --- a/pandas/tests/io/formats/test_to_html.py +++ b/pandas/tests/io/formats/test_to_html.py @@ -478,8 +478,12 @@ def test_to_html_float_format_no_fixed_width(self, datapath): expected = expected_html(datapath, 'gh22270_expected_output') assert df.to_html(float_format='%.0f') == expected - @pytest.mark.parametrize("render_links", [True, False]) - def test_to_html_render_links(self, render_links): + @pytest.mark.parametrize("render_links, open_tag, close_tag", [ + (True, '', ''), + (False, '', ''), + ]) + def test_to_html_render_links(self, render_links, open_tag, close_tag): # GH 2679 data = [ @@ -498,7 +502,6 @@ def test_to_html_render_links(self, render_links): index=range(len(data))) result = df.to_html(render_links=render_links) - expected = """\ @@ -523,9 +526,7 @@ def test_to_html_render_links(self, render_links): -
pydata.org
""".format(open_tag=not render_links and '' or ( - ''), - close_tag=not render_links and '' or '') +""".format(open_tag=open_tag, + close_tag=close_tag) assert result == expected From 14b79e1e98966e6c77fa502a3ee6a9836b753c69 Mon Sep 17 00:00:00 2001 From: Ben Date: Mon, 3 Dec 2018 18:51:00 -0800 Subject: [PATCH 07/13] Move render_links test expectations to separate files --- .../io/formats/data/render_links_false.html | 24 ++++++++++++ .../io/formats/data/render_links_true.html | 24 ++++++++++++ pandas/tests/io/formats/test_to_html.py | 38 +++---------------- 3 files changed, 53 insertions(+), 33 deletions(-) create mode 100644 pandas/tests/io/formats/data/render_links_false.html create mode 100644 pandas/tests/io/formats/data/render_links_true.html diff --git a/pandas/tests/io/formats/data/render_links_false.html b/pandas/tests/io/formats/data/render_links_false.html new file mode 100644 index 0000000000000..a856c9ca0f4f4 --- /dev/null +++ b/pandas/tests/io/formats/data/render_links_false.html @@ -0,0 +1,24 @@ + + + + + + + + + + + + + + + + + + + + + + + +
foobarNone
00http://pandas.pydata.org/?q1=a&q2=bpydata.org
10www.pydata.orgpydata.org
\ No newline at end of file diff --git a/pandas/tests/io/formats/data/render_links_true.html b/pandas/tests/io/formats/data/render_links_true.html new file mode 100644 index 0000000000000..19afc58765f40 --- /dev/null +++ b/pandas/tests/io/formats/data/render_links_true.html @@ -0,0 +1,24 @@ + + + + + + + + + + + + + + + + + + + + + + + +
foobarNone
00http://pandas.pydata.org/?q1=a&q2=bpydata.org
10www.pydata.orgpydata.org
\ No newline at end of file diff --git a/pandas/tests/io/formats/test_to_html.py b/pandas/tests/io/formats/test_to_html.py index 077ca34882e42..ba13e1a1305bc 100644 --- a/pandas/tests/io/formats/test_to_html.py +++ b/pandas/tests/io/formats/test_to_html.py @@ -478,14 +478,12 @@ def test_to_html_float_format_no_fixed_width(self, datapath): expected = expected_html(datapath, 'gh22270_expected_output') assert df.to_html(float_format='%.0f') == expected - @pytest.mark.parametrize("render_links, open_tag, close_tag", [ - (True, '', ''), - (False, '', ''), + @pytest.mark.parametrize("render_links, file_name", [ + (True, 'render_links_true'), + (False, 'render_links_false'), ]) - def test_to_html_render_links(self, render_links, open_tag, close_tag): + def test_to_html_render_links(self, render_links, file_name, datapath): # GH 2679 - data = [ { 'foo': 0, @@ -502,31 +500,5 @@ def test_to_html_render_links(self, render_links, open_tag, close_tag): index=range(len(data))) result = df.to_html(render_links=render_links) - expected = """\ - - - - - - - - - - - - - - {open_tag}http://pandas.pydata.org/?q1=a&q2=b{close_tag} - - - - - - - - - -
foobarNone
00pydata.org
10www.pydata.orgpydata.org
""".format(open_tag=open_tag, - close_tag=close_tag) - + expected = expected_html(datapath, file_name) assert result == expected From 2f02e287f032931caaf90e894367d14d53e08ba7 Mon Sep 17 00:00:00 2001 From: Ben Date: Tue, 4 Dec 2018 21:41:42 -0800 Subject: [PATCH 08/13] Add render_links description and example to whatsnew and IO docs --- doc/source/io.rst | 21 +++++++++++++++++++++ doc/source/whatsnew/v0.24.0.rst | 1 + 2 files changed, 22 insertions(+) diff --git a/doc/source/io.rst b/doc/source/io.rst index fbd238586c776..563b240979c44 100644 --- a/doc/source/io.rst +++ b/doc/source/io.rst @@ -2605,6 +2605,27 @@ table CSS classes. Note that these classes are *appended* to the existing print(df.to_html(classes=['awesome_table_class', 'even_more_awesome_class'])) +The ``render_links`` argument provides the ability to add hyperlinks to cells +that contain URLs. + +.. ipython:: python + + url_df = pd.DataFrame({ + 'name': ['Python', 'Pandas'], + 'url': ['https://www.python.org/', 'http://pandas.pydata.org'] + }) + print(url_df.to_html(render_links=True)) + +.. ipython:: python + :suppress: + + write_html(url_df, 'render_links', render_links=True) + +HTML: + +.. raw:: html + :file: _static/render_links.html + Finally, the ``escape`` argument allows you to control whether the "<", ">" and "&" characters escaped in the resulting HTML (by default it is ``True``). So to get the HTML without escaped characters pass ``escape=False`` diff --git a/doc/source/whatsnew/v0.24.0.rst b/doc/source/whatsnew/v0.24.0.rst index 93ac9caa42e3e..189ed8d75ab8a 100644 --- a/doc/source/whatsnew/v0.24.0.rst +++ b/doc/source/whatsnew/v0.24.0.rst @@ -25,6 +25,7 @@ New features dataframe's indexes from the resulting Parquet file. (:issue:`20768`) - :meth:`DataFrame.corr` and :meth:`Series.corr` now accept a callable for generic calculation methods of correlation, e.g. histogram intersection (:issue:`22684`) - :func:`DataFrame.to_string` now accepts ``decimal`` as an argument, allowing the user to specify which decimal separator should be used in the output. (:issue:`23614`) +- :func:`DataFrame.to_html` now accepts ``render_links`` as an argument, allowing the user to generate HTML with links to any URLs that appear in the DataFrame. (:issue:`2679`) .. _whatsnew_0240.values_api: From 7e0de0842caac3ea210dc32c293642d7512be36f Mon Sep 17 00:00:00 2001 From: Ben Date: Sat, 8 Dec 2018 19:24:10 -0800 Subject: [PATCH 09/13] Minor style fix in io.rst --- doc/source/io.rst | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/doc/source/io.rst b/doc/source/io.rst index 563b240979c44..8ac787c2b7bac 100644 --- a/doc/source/io.rst +++ b/doc/source/io.rst @@ -2612,8 +2612,7 @@ that contain URLs. url_df = pd.DataFrame({ 'name': ['Python', 'Pandas'], - 'url': ['https://www.python.org/', 'http://pandas.pydata.org'] - }) + 'url': ['https://www.python.org/', 'http://pandas.pydata.org']}) print(url_df.to_html(render_links=True)) .. ipython:: python From 7b5b323152cca58e4f99452fa476ae4fdf5b783a Mon Sep 17 00:00:00 2001 From: Ben Date: Sun, 9 Dec 2018 08:13:48 -0800 Subject: [PATCH 10/13] Style fix on render_links test --- pandas/tests/io/formats/test_to_html.py | 15 +++------------ 1 file changed, 3 insertions(+), 12 deletions(-) diff --git a/pandas/tests/io/formats/test_to_html.py b/pandas/tests/io/formats/test_to_html.py index 706e6c54106cf..9662b3d514cb8 100644 --- a/pandas/tests/io/formats/test_to_html.py +++ b/pandas/tests/io/formats/test_to_html.py @@ -485,19 +485,10 @@ def test_to_html_float_format_no_fixed_width(self, datapath): def test_to_html_render_links(self, render_links, file_name, datapath): # GH 2679 data = [ - { - 'foo': 0, - 'bar': 'http://pandas.pydata.org/?q1=a&q2=b', - None: 'pydata.org', - }, - { - 'foo': 0, - 'bar': 'www.pydata.org', - None: 'pydata.org', - }, + [0, 'http://pandas.pydata.org/?q1=a&q2=b', 'pydata.org'], + [0, 'www.pydata.org', 'pydata.org'] ] - df = DataFrame(data, columns=['foo', 'bar', None], - index=range(len(data))) + df = DataFrame(data, columns=['foo', 'bar', None]) result = df.to_html(render_links=render_links) expected = expected_html(datapath, file_name) From 05f17f2802d32c2156847442f7cbe4b3db62d9aa Mon Sep 17 00:00:00 2001 From: Ben Date: Sun, 9 Dec 2018 08:31:51 -0800 Subject: [PATCH 11/13] Add versionadded in io.rst --- doc/source/io.rst | 2 ++ 1 file changed, 2 insertions(+) diff --git a/doc/source/io.rst b/doc/source/io.rst index 17d10d8559078..8dec7eb2fbfe2 100644 --- a/doc/source/io.rst +++ b/doc/source/io.rst @@ -2607,6 +2607,8 @@ table CSS classes. Note that these classes are *appended* to the existing The ``render_links`` argument provides the ability to add hyperlinks to cells that contain URLs. +.. versionadded:: 0.24 + .. ipython:: python url_df = pd.DataFrame({ From bb637a895b92a1cdff0f4ffe03838245b816c1d8 Mon Sep 17 00:00:00 2001 From: Ben Date: Wed, 12 Dec 2018 12:38:56 -0800 Subject: [PATCH 12/13] boolean -> bool and newlines for render_links_*.html --- pandas/core/frame.py | 2 +- pandas/tests/io/formats/data/render_links_false.html | 2 +- pandas/tests/io/formats/data/render_links_true.html | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/pandas/core/frame.py b/pandas/core/frame.py index 93742b0b9004b..6b74fd7e06de9 100644 --- a/pandas/core/frame.py +++ b/pandas/core/frame.py @@ -2068,7 +2068,7 @@ def to_html(self, buf=None, columns=None, col_space=None, header=True, .. versionadded:: 0.23.0 - render_links : boolean, default False + render_links : bool, default False Convert URLs to HTML links. .. versionadded:: 0.24.0 diff --git a/pandas/tests/io/formats/data/render_links_false.html b/pandas/tests/io/formats/data/render_links_false.html index a856c9ca0f4f4..6509a0e985597 100644 --- a/pandas/tests/io/formats/data/render_links_false.html +++ b/pandas/tests/io/formats/data/render_links_false.html @@ -21,4 +21,4 @@ pydata.org - \ No newline at end of file + diff --git a/pandas/tests/io/formats/data/render_links_true.html b/pandas/tests/io/formats/data/render_links_true.html index 19afc58765f40..e9cb5632aad1d 100644 --- a/pandas/tests/io/formats/data/render_links_true.html +++ b/pandas/tests/io/formats/data/render_links_true.html @@ -21,4 +21,4 @@ pydata.org - \ No newline at end of file + From 78ed4b9792c420622235d1364b219df6facf501e Mon Sep 17 00:00:00 2001 From: Ben Date: Wed, 12 Dec 2018 21:32:17 -0800 Subject: [PATCH 13/13] Add link to IO docs from render_links whatsnew entry --- doc/source/whatsnew/v0.24.0.rst | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/doc/source/whatsnew/v0.24.0.rst b/doc/source/whatsnew/v0.24.0.rst index 2c6eabe71b65f..be09a1e22372c 100644 --- a/doc/source/whatsnew/v0.24.0.rst +++ b/doc/source/whatsnew/v0.24.0.rst @@ -27,7 +27,8 @@ New features - :meth:`DataFrame.corr` and :meth:`Series.corr` now accept a callable for generic calculation methods of correlation, e.g. histogram intersection (:issue:`22684`) - :func:`DataFrame.to_string` now accepts ``decimal`` as an argument, allowing the user to specify which decimal separator should be used in the output. (:issue:`23614`) - :func:`DataFrame.read_feather` now accepts ``columns`` as an argument, allowing the user to specify which columns should be read. (:issue:`24025`) -- :func:`DataFrame.to_html` now accepts ``render_links`` as an argument, allowing the user to generate HTML with links to any URLs that appear in the DataFrame. (:issue:`2679`) +- :func:`DataFrame.to_html` now accepts ``render_links`` as an argument, allowing the user to generate HTML with links to any URLs that appear in the DataFrame. + See the :ref:`section on writing HTML ` in the IO docs for example usage. (:issue:`2679`) .. _whatsnew_0240.values_api: