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))