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
31 changes: 31 additions & 0 deletions doc/internals.rst
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,37 @@ 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 variables of ``Dataset``), xarray
will display the repr of a `duck array`_ in a single line, limiting it to a
certain number of characters. If that would drop too much information, the
`duck array`_ may define a ``_repr_inline_`` method:

.. code:: python

class MyDuckArray:
...

def _repr_inline_(self, max_width):
""" display the array in a single line with max_width characters """

...

...

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


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

Expand Down
3 changes: 3 additions & 0 deletions doc/whats-new.rst
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,9 @@ 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. (: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