From 1e115fc508c44359de06ab98e4afee8295bbab19 Mon Sep 17 00:00:00 2001 From: Zac-HD Date: Fri, 24 Mar 2017 11:09:33 +1100 Subject: [PATCH 1/2] Shorter repr for attributes NetCDF files often have tens of attributes, including multi-paragraph summaries or the full modification history of the file. It's great to have this available in the .attrs, but we can truncate it substantially in the repr! Hopefully this will stop people writing `data.attrs = {}` and discarding metadata in interactive workflows for the sake of cleaner output. --- doc/whats-new.rst | 7 +++++++ xarray/core/formatting.py | 11 +++++++++-- xarray/tests/test_dataarray.py | 2 +- xarray/tests/test_dataset.py | 4 ++-- xarray/tests/test_variable.py | 2 +- 5 files changed, 20 insertions(+), 6 deletions(-) diff --git a/doc/whats-new.rst b/doc/whats-new.rst index bc7b0fd6c5c..ad9e0a2be20 100644 --- a/doc/whats-new.rst +++ b/doc/whats-new.rst @@ -41,6 +41,13 @@ By `Henry S. Harrison `_. Note, the default is ``autoclose=False``, which is consistent with previous xarray behavior. By `Phillip J. Wolfram `_. +- The ``repr()`` of ``Dataset`` and ``DataArray`` attributes uses a similar + format to coordinates and variables, with vertically aligned entries + truncated to fit on a single line. Hopefully this will stop people writing + ``data.attrs = {}`` and discarding metadata in notebooks for the sake of + cleaner output. The full metadata is still available as ``data.attrs``. + By `Zac Hatfield-Dodds `_. + Bug fixes ~~~~~~~~~ - ``rolling`` now keeps its original dimension order (:issue:`1125`). diff --git a/xarray/core/formatting.py b/xarray/core/formatting.py index df03c84fcc8..34bd033de51 100644 --- a/xarray/core/formatting.py +++ b/xarray/core/formatting.py @@ -253,8 +253,15 @@ def summarize_coord(name, var, col_width): def summarize_attr(key, value, col_width=None): - # ignore col_width for now to more clearly distinguish attributes - return u' %s: %s' % (key, maybe_truncate(value)) + """Summary for __repr__ - use ``X.attrs[key]`` for full value.""" + # Indent key and add ':', then right-pad if col_width is not None + k_str = u' %s:' % key + if col_width is not None: + k_str = pretty_print(k_str, col_width) + # Replace tabs and newlines, so we print on one line in known width + v_str = unicode_type(value).replace(u'\t', u'\\t').replace(u'\n', u'\\n') + # Finally, truncate to the desired display width + return maybe_truncate(u'%s %s' % (k_str, v_str), OPTIONS['display_width']) EMPTY_REPR = u' *empty*' diff --git a/xarray/tests/test_dataarray.py b/xarray/tests/test_dataarray.py index b73ad136438..b7d0d712e65 100644 --- a/xarray/tests/test_dataarray.py +++ b/xarray/tests/test_dataarray.py @@ -49,7 +49,7 @@ def test_repr(self): other int64 0 Dimensions without coordinates: time Attributes: - foo: bar""") + foo: bar""") self.assertEqual(expected, repr(data_array)) def test_repr_multiindex(self): diff --git a/xarray/tests/test_dataset.py b/xarray/tests/test_dataset.py index 1a9ad527103..002378cd528 100644 --- a/xarray/tests/test_dataset.py +++ b/xarray/tests/test_dataset.py @@ -97,7 +97,7 @@ def test_repr(self): var2 (dim1, dim2) float64 1.162 -1.097 -2.123 1.04 -0.4034 -0.126 ... var3 (dim3, dim1) float64 0.5565 -0.2121 0.4563 1.545 -0.2397 0.1433 ... Attributes: - foo: bar""") % data['dim3'].dtype + foo: bar""") % data['dim3'].dtype actual = '\n'.join(x.rstrip() for x in repr(data).split('\n')) print(actual) self.assertEqual(expected, actual) @@ -183,7 +183,7 @@ def test_unicode_data(self): Data variables: *empty* Attributes: - å: ∑""" % u'ba®') + å: ∑""" % u'ba®') actual = unicode_type(data) self.assertEqual(expected, actual) diff --git a/xarray/tests/test_variable.py b/xarray/tests/test_variable.py index 8cfa5681276..1c59d25c02e 100644 --- a/xarray/tests/test_variable.py +++ b/xarray/tests/test_variable.py @@ -644,7 +644,7 @@ def test_repr(self): array([[1, 2, 3], [4, 5, 6]]) Attributes: - foo: bar + foo: bar """).strip() self.assertEqual(expected, repr(v)) From b93eaab23b83f43c9aa96062b92fd7bd74df73d2 Mon Sep 17 00:00:00 2001 From: Zac-HD Date: Tue, 28 Mar 2017 18:02:19 +1100 Subject: [PATCH 2/2] Add test for attribute repr --- xarray/tests/test_formatting.py | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/xarray/tests/test_formatting.py b/xarray/tests/test_formatting.py index 8ff4ee7c41b..73c6bce1574 100644 --- a/xarray/tests/test_formatting.py +++ b/xarray/tests/test_formatting.py @@ -131,6 +131,17 @@ def test_format_timestamp_out_of_bounds(self): result = formatting.format_timestamp(date) self.assertEqual(result, expected) + def test_attribute_repr(self): + short = formatting.summarize_attr(u'key', u'Short string') + long = formatting.summarize_attr(u'key', 100 * u'Very long string ') + newlines = formatting.summarize_attr(u'key', u'\n\n\n') + tabs = formatting.summarize_attr(u'key', u'\t\t\t') + self.assertEqual(short, ' key: Short string') + self.assertLessEqual(len(long), 80) + self.assertTrue(long.endswith(u'...')) + self.assertNotIn(u'\n', newlines) + self.assertNotIn(u'\t', tabs) + def test_set_numpy_options(): original_options = np.get_printoptions()