From d08efaf902cae5e5f28afff7d6f8182e35a53f46 Mon Sep 17 00:00:00 2001 From: Zac Hatfield Dodds Date: Mon, 3 Apr 2017 10:47:44 +1000 Subject: [PATCH] Shorter repr for attributes (#1322) * 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. * Add test for attribute repr --- 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_formatting.py | 11 +++++++++++ xarray/tests/test_variable.py | 2 +- 6 files changed, 31 insertions(+), 6 deletions(-) diff --git a/doc/whats-new.rst b/doc/whats-new.rst index 086eb544163..438b4571171 100644 --- a/doc/whats-new.rst +++ b/doc/whats-new.rst @@ -43,6 +43,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 45193b4eb05..019aa06773f 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 9d1e28e1ca5..739dd930e3e 100644 --- a/xarray/tests/test_dataset.py +++ b/xarray/tests/test_dataset.py @@ -101,7 +101,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) @@ -187,7 +187,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_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() 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))