Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

allow customizing the inline repr of a duck array #4248

Merged
merged 13 commits into from
Aug 6, 2020
32 changes: 32 additions & 0 deletions doc/internals.rst
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,38 @@ xarray objects via the (readonly) :py:attr:`Dataset.variables
<xarray.Dataset.variables>` and
:py:attr:`DataArray.variable <xarray.DataArray.variable>` attributes.

Duck arrays
-----------

.. warning::

This is a experimental feature.

xarray can wrap custom `duck array`_ objects as long as they define numpy's
``shape``, ``dtype`` and ``ndim`` properties and the ``__array__``,
``__array_ufunc__`` and ``__array_function__`` methods.

In certain situations (e.g. when printing the collapsed preview of
variables of a ``Dataset``), xarray will display the repr of a `duck array`_
in a single line, truncating it to a certain number of characters. If that
would drop too much information, the `duck array`_ may define a
``_repr_inline_`` method that takes ``max_width`` (number of characters) as an
argument:
Comment on lines +56 to +61
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It would be nice to add examples of what should and should not be included in _repr_inline_.

In particular, shape and dtype should not be included because xarray already adds those in a standard way.

For examples of what good _inline_repr_ look like, we could show examples of the output for dask and sparse.


.. code:: python

class MyDuckArray:
...

def _repr_inline_(self, max_width):
""" format to a single line with at most max_width characters """
...

...

.. _duck array: https://numpy.org/neps/nep-0022-ndarray-duck-typing-overview.html


Extending xarray
----------------

Expand Down
4 changes: 4 additions & 0 deletions doc/whats-new.rst
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,10 @@ New Features
property for :py:class:`CFTimeIndex` and show ``calendar`` and ``length`` in
:py:meth:`CFTimeIndex.__repr__` (:issue:`2416`, :pull:`4092`)
`Aaron Spring <https://github.com/aaronspring>`_.
- Use a wrapped array's ``_repr_inline_`` method to construct the collapsed ``repr``
of :py:class:`DataArray` and :py:class:`Dataset` objects and
document the new method in :doc:`internals`. (:pull:`4248`).
By `Justus Magin <https://github.com/keewis>`_.


Bug fixes
Expand Down
2 changes: 2 additions & 0 deletions xarray/core/formatting.py
Original file line number Diff line number Diff line change
Expand Up @@ -261,6 +261,8 @@ def inline_variable_array_repr(var, max_width):
return inline_dask_repr(var.data)
elif isinstance(var._data, sparse_array_type):
return inline_sparse_repr(var.data)
elif hasattr(var._data, "_repr_inline_"):
return var._data._repr_inline_(max_width)
elif hasattr(var._data, "__array_function__"):
return maybe_truncate(repr(var._data).replace("\n", " "), max_width)
else:
Expand Down
39 changes: 39 additions & 0 deletions xarray/tests/test_formatting.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@

import xarray as xr
from xarray.core import formatting
from xarray.core.npcompat import IS_NEP18_ACTIVE

from . import raises_regex

Expand Down Expand Up @@ -391,6 +392,44 @@ def test_array_repr(self):
assert actual == expected


@pytest.mark.skipif(not IS_NEP18_ACTIVE, reason="requires __array_function__")
def test_inline_variable_array_repr_custom_repr():
class CustomArray:
def __init__(self, value, attr):
self.value = value
self.attr = attr

def _repr_inline_(self, width):
formatted = f"({self.attr}) {self.value}"
if len(formatted) > width:
formatted = f"({self.attr}) ..."

return formatted

def __array_function__(self, *args, **kwargs):
return NotImplemented

@property
def shape(self):
return self.value.shape

@property
def dtype(self):
return self.value.dtype

@property
def ndim(self):
return self.value.ndim

value = CustomArray(np.array([20, 40]), "m")
variable = xr.Variable("x", value)

max_width = 10
actual = formatting.inline_variable_array_repr(variable, max_width=10)

assert actual == value._repr_inline_(max_width)


def test_set_numpy_options():
original_options = np.get_printoptions()
with formatting.set_numpy_options(threshold=10):
Expand Down