Repository: pydata/xarray

Issue:
New .broadcast_like() incorrect
Unfortunately, more robust tests show there is some logic problem in the @dcherian solution to #2885.  I was merging my work in #3127 with his more-principled refactoring in #3086, and my tests showed an error in the refactoring.

Discussion:
pep8speaks: Hello @DavidMertz! Thanks for opening this PR. We checked the lines you've touched for [PEP 8](https://www.python.org/dev/peps/pep-0008) issues, and found:

* In the file [`xarray/core/dataarray.py`](https://github.com/pydata/xarray/blob/f90388ab8a167c2e01db2ef9e14b6e526b6640fb/xarray/core/dataarray.py):

> [Line 999:24](https://github.com/pydata/xarray/blob/f90388ab8a167c2e01db2ef9e14b6e526b6640fb/xarray/core/dataarray.py#L999): [E128](https://duckduckgo.com/?q=pep8%20E128) continuation line under-indented for visual indent
> [Line 1000:24](https://github.com/pydata/xarray/blob/f90388ab8a167c2e01db2ef9e14b6e526b6640fb/xarray/core/dataarray.py#L1000): [E128](https://duckduckgo.com/?q=pep8%20E128) continuation line under-indented for visual indent

* In the file [`xarray/tests/test_dataarray.py`](https://github.com/pydata/xarray/blob/f90388ab8a167c2e01db2ef9e14b6e526b6640fb/xarray/tests/test_dataarray.py):

> [Line 1266:1](https://github.com/pydata/xarray/blob/f90388ab8a167c2e01db2ef9e14b6e526b6640fb/xarray/tests/test_dataarray.py#L1266): [W293](https://duckduckgo.com/?q=pep8%20W293) blank line contains whitespace
> [Line 1278:1](https://github.com/pydata/xarray/blob/f90388ab8a167c2e01db2ef9e14b6e526b6640fb/xarray/tests/test_dataarray.py#L1278): [W293](https://duckduckgo.com/?q=pep8%20W293) blank line contains whitespace


codecov[bot]: # [Codecov](https://codecov.io/gh/pydata/xarray/pull/3130?src=pr&el=h1) Report
> Merging [#3130](https://codecov.io/gh/pydata/xarray/pull/3130?src=pr&el=desc) into [master](https://codecov.io/gh/pydata/xarray/commit/9438390da9ecafc2601a7f06c9e4f87637b456df?src=pr&el=desc) will **decrease** coverage by `0.64%`.
> The diff coverage is `100%`.


```diff
@@            Coverage Diff             @@
##           master    #3130      +/-   ##
==========================================
- Coverage   95.99%   95.35%   -0.65%     
==========================================
  Files          63       63              
  Lines       12799    12801       +2     
==========================================
- Hits        12287    12206      -81     
- Misses        512      595      +83
```

dcherian: Thanks @DavidMertz 

Line 2042 of dataset.py should be 
`return _broadcast_helper(args[1], exclude, dims_map, common_coords)` i.e. `args[1]` instead of `self`.

Can you make that change (and the same change in dataarray.py, Line 1019)?
DavidMertz: Made changes suggested by @dcherian. This resolves the detected bug in the test.  If CI passes, this should be ready to merge.
dcherian: Thanks for catching that @DavidMertz. Can you add back the original, simpler test too? This one is really just testing align. You could use `@pytest.mark.parametrize` to provide two different sets of `arr1, arr2` arguments.

Patch:
diff --git a/doc/whats-new.rst b/doc/whats-new.rst
index 03898ae1..a8c53423 100644
--- a/doc/whats-new.rst
+++ b/doc/whats-new.rst
@@ -25,7 +25,8 @@ New functions/methods
 ~~~~~~~~~~~~~~~~~~~~~
 
 - Added :py:meth:`DataArray.broadcast_like` and :py:meth:`Dataset.broadcast_like`.
-  By `Deepak Cherian <https://github.com/dcherian>`_.
+  By `Deepak Cherian <https://github.com/dcherian>`_ and `David Mertz 
+  <http://github.com/DavidMertz>`_.
 
 Enhancements
 ~~~~~~~~~~~~
@@ -35,6 +36,11 @@ Bug fixes
 
 - Improved error handling and documentation for `.expand_dims()` 
   read-only view.
+- Fix tests for big-endian systems (:issue:`3125`).
+  By `Graham Inggs <https://github.com/ginggs>`_.
+- XFAIL several tests which are expected to fail on ARM systems
+  due to a ``datetime`` issue in NumPy (:issue:`2334`).
+  By `Graham Inggs <https://github.com/ginggs>`_.
 
 .. _whats-new.0.12.3:
 
diff --git a/xarray/core/dataarray.py b/xarray/core/dataarray.py
index aa16a04e..99014e9e 100644
--- a/xarray/core/dataarray.py
+++ b/xarray/core/dataarray.py
@@ -998,17 +998,58 @@ class DataArray(AbstractArray, DataWithCoords):
     def broadcast_like(self,
                        other: Union['DataArray', Dataset],
                        exclude=None) -> 'DataArray':
-        """Broadcast this DataArray against another Dataset or DataArray.
+        """Broadcast a DataArray to the shape of another DataArray or Dataset
+
         This is equivalent to xr.broadcast(other, self)[1]
 
+        xarray objects are broadcast against each other in arithmetic
+        operations, so this method is not be necessary for most uses.
+
+        If no change is needed, the input data is returned to the output
+        without being copied.
+
+        If new coords are added by the broadcast, their values are
+        NaN filled.
+
         Parameters
         ----------
         other : Dataset or DataArray
             Object against which to broadcast this array.
+
         exclude : sequence of str, optional
             Dimensions that must not be broadcasted
-        """
 
+        Returns
+        -------
+        new_da: xr.DataArray
+
+        Examples
+        --------
+
+        >>> arr1
+        <xarray.DataArray (x: 2, y: 3)>
+        array([[0.840235, 0.215216, 0.77917 ],
+               [0.726351, 0.543824, 0.875115]])
+        Coordinates:
+          * x        (x) <U1 'a' 'b'
+          * y        (y) <U1 'a' 'b' 'c'
+        >>> arr2
+        <xarray.DataArray (x: 3, y: 2)>
+        array([[0.612611, 0.125753],
+               [0.853181, 0.948818],
+               [0.180885, 0.33363 ]])
+        Coordinates:
+          * x        (x) <U1 'a' 'b' 'c'
+          * y        (y) <U1 'a' 'b'
+        >>> arr1.broadcast_like(arr2)
+        <xarray.DataArray (x: 3, y: 3)>
+        array([[0.840235, 0.215216, 0.77917 ],
+               [0.726351, 0.543824, 0.875115],
+               [     nan,      nan,      nan]])
+        Coordinates:
+          * x        (x) object 'a' 'b' 'c'
+          * y        (y) object 'a' 'b' 'c'
+        """
         if exclude is None:
             exclude = set()
         args = align(other, self, join='outer', copy=False, exclude=exclude)
@@ -1016,7 +1057,7 @@ class DataArray(AbstractArray, DataWithCoords):
         dims_map, common_coords = _get_broadcast_dims_map_common_coords(
             args, exclude)
 
-        return _broadcast_helper(self, exclude, dims_map, common_coords)
+        return _broadcast_helper(args[1], exclude, dims_map, common_coords)
 
     def reindex_like(self, other: Union['DataArray', Dataset],
                      method: Optional[str] = None, tolerance=None,
diff --git a/xarray/core/dataset.py b/xarray/core/dataset.py
index 775fdd49..d00ea1e4 100644
--- a/xarray/core/dataset.py
+++ b/xarray/core/dataset.py
@@ -2039,7 +2039,7 @@ class Dataset(Mapping, ImplementsDatasetReduce, DataWithCoords):
         dims_map, common_coords = _get_broadcast_dims_map_common_coords(
             args, exclude)
 
-        return _broadcast_helper(self, exclude, dims_map, common_coords)
+        return _broadcast_helper(args[1], exclude, dims_map, common_coords)
 
     def reindex_like(self, other, method=None, tolerance=None, copy=True,
                      fill_value=dtypes.NA):


Test patch:
diff --git a/xarray/tests/__init__.py b/xarray/tests/__init__.py
index 81bb1a1e..4e50a8bc 100644
--- a/xarray/tests/__init__.py
+++ b/xarray/tests/__init__.py
@@ -31,6 +31,11 @@ try:
 except ImportError:
     pass
 
+import platform
+arm_xfail = pytest.mark.xfail(platform.machine() == 'aarch64' or
+                              'arm' in platform.machine(),
+                              reason='expected failure on ARM')
+
 
 def _importorskip(modname, minversion=None):
     try:
diff --git a/xarray/tests/test_backends.py b/xarray/tests/test_backends.py
index f5c27f2f..2235be4f 100644
--- a/xarray/tests/test_backends.py
+++ b/xarray/tests/test_backends.py
@@ -35,7 +35,7 @@ from . import (
     requires_cftime, requires_dask, requires_h5fileobj, requires_h5netcdf,
     requires_netCDF4, requires_pathlib, requires_pseudonetcdf, requires_pydap,
     requires_pynio, requires_rasterio, requires_scipy,
-    requires_scipy_or_netCDF4, requires_zarr)
+    requires_scipy_or_netCDF4, requires_zarr, arm_xfail)
 from .test_coding_times import (
     _ALL_CALENDARS, _NON_STANDARD_CALENDARS, _STANDARD_CALENDARS)
 from .test_dataset import create_test_data, create_append_test_data
@@ -400,6 +400,7 @@ class DatasetIOBase:
             assert_identical(expected, actual)
             assert actual['x'].encoding['_Encoding'] == 'ascii'
 
+    @arm_xfail
     def test_roundtrip_numpy_datetime_data(self):
         times = pd.to_datetime(['2000-01-01', '2000-01-02', 'NaT'])
         expected = Dataset({'t': ('t', times), 't0': times[0]})
diff --git a/xarray/tests/test_coding_times.py b/xarray/tests/test_coding_times.py
index 4de0d7f6..dacf68f6 100644
--- a/xarray/tests/test_coding_times.py
+++ b/xarray/tests/test_coding_times.py
@@ -16,7 +16,7 @@ from xarray.testing import assert_equal
 
 from . import (
     assert_array_equal, has_cftime, has_cftime_or_netCDF4, has_dask,
-    requires_cftime, requires_cftime_or_netCDF4)
+    requires_cftime, requires_cftime_or_netCDF4, arm_xfail)
 
 try:
     from pandas.errors import OutOfBoundsDatetime
@@ -470,6 +470,7 @@ def test_decode_360_day_calendar():
         assert_array_equal(actual, expected)
 
 
+@arm_xfail
 @pytest.mark.skipif(not has_cftime_or_netCDF4, reason='cftime not installed')
 @pytest.mark.parametrize(
     ['num_dates', 'units', 'expected_list'],
diff --git a/xarray/tests/test_dataarray.py b/xarray/tests/test_dataarray.py
index 63317519..5697704b 100644
--- a/xarray/tests/test_dataarray.py
+++ b/xarray/tests/test_dataarray.py
@@ -1256,19 +1256,23 @@ class TestDataArray:
         assert_identical(actual, expected)
 
     def test_broadcast_like(self):
-        original1 = DataArray(np.random.randn(5),
-                              [('x', range(5))])
+        arr1 = DataArray(np.ones((2, 3)), dims=['x', 'y'],
+                         coords={'x': ['a', 'b'], 'y': ['a', 'b', 'c']})
+        arr2 = DataArray(np.ones((3, 2)), dims=['x', 'y'],
+                         coords={'x': ['a', 'b', 'c'], 'y': ['a', 'b']})
+        orig1, orig2 = broadcast(arr1, arr2)
+        new1 = arr1.broadcast_like(arr2)
+        new2 = arr2.broadcast_like(arr1)
 
-        original2 = DataArray(np.random.randn(6),
-                              [('y', range(6))])
+        assert orig1.identical(new1)
+        assert orig2.identical(new2)
 
-        expected1, expected2 = broadcast(original1, original2)
+        orig3 = DataArray(np.random.randn(5), [('x', range(5))])
+        orig4 = DataArray(np.random.randn(6), [('y', range(6))])
+        new3, new4 = broadcast(orig3, orig4)
 
-        assert_identical(original1.broadcast_like(original2),
-                         expected1.transpose('y', 'x'))
-
-        assert_identical(original2.broadcast_like(original1),
-                         expected2)
+        assert_identical(orig3.broadcast_like(orig4), new3.transpose('y', 'x'))
+        assert_identical(orig4.broadcast_like(orig3), new4)
 
     def test_reindex_like(self):
         foo = DataArray(np.random.randn(5, 6),
@@ -3140,7 +3144,9 @@ class TestDataArray:
         expected_no_data = expected.copy()
         del expected_no_data['data']
         del expected_no_data['coords']['x']['data']
-        expected_no_data['coords']['x'].update({'dtype': '<U1', 'shape': (2,)})
+        endiantype = '<U1' if sys.byteorder == 'little' else '>U1'
+        expected_no_data['coords']['x'].update({'dtype': endiantype,
+                                                'shape': (2,)})
         expected_no_data.update({'dtype': 'float64', 'shape': (2, 3)})
         actual_no_data = array.to_dict(data=False)
         assert expected_no_data == actual_no_data
diff --git a/xarray/tests/test_dataset.py b/xarray/tests/test_dataset.py
index db7c5a35..fc6f7f36 100644
--- a/xarray/tests/test_dataset.py
+++ b/xarray/tests/test_dataset.py
@@ -3460,7 +3460,8 @@ class TestDataset:
         del expected_no_data['coords']['t']['data']
         del expected_no_data['data_vars']['a']['data']
         del expected_no_data['data_vars']['b']['data']
-        expected_no_data['coords']['t'].update({'dtype': '<U1',
+        endiantype = '<U1' if sys.byteorder == 'little' else '>U1'
+        expected_no_data['coords']['t'].update({'dtype': endiantype,
                                                 'shape': (10,)})
         expected_no_data['data_vars']['a'].update({'dtype': 'float64',
                                                    'shape': (10,)})
diff --git a/xarray/tests/test_duck_array_ops.py b/xarray/tests/test_duck_array_ops.py
index 87a7a286..b0b7cfbd 100644
--- a/xarray/tests/test_duck_array_ops.py
+++ b/xarray/tests/test_duck_array_ops.py
@@ -17,7 +17,7 @@ from xarray.testing import assert_allclose, assert_equal
 
 from . import (
     assert_array_equal, has_dask, has_np113, raises_regex, requires_cftime,
-    requires_dask)
+    requires_dask, arm_xfail)
 
 
 class TestOps:
@@ -252,6 +252,7 @@ def assert_dask_array(da, dask):
         assert isinstance(da.data, dask_array_type)
 
 
+@arm_xfail
 @pytest.mark.parametrize('dask', [False, True])
 def test_datetime_reduce(dask):
     time = np.array(pd.date_range('15/12/1999', periods=11))
diff --git a/xarray/tests/test_formatting.py b/xarray/tests/test_formatting.py
index 8a3c95a4..be3e368e 100644
--- a/xarray/tests/test_formatting.py
+++ b/xarray/tests/test_formatting.py
@@ -1,5 +1,6 @@
 # -*- coding: utf-8 -*-
 from textwrap import dedent
+import sys
 
 import numpy as np
 import pandas as pd
@@ -204,6 +205,7 @@ class TestFormatting:
                     'label': ('x', np.array([1, 2], dtype='int64'))},
             attrs={'units': 'kg'})
 
+        byteorder = '<' if sys.byteorder == 'little' else '>'
         expected = dedent("""\
         Left and right DataArray objects are not identical
         Differing dimensions:
@@ -215,8 +217,8 @@ class TestFormatting:
         R
             array([1, 2], dtype=int64)
         Differing coordinates:
-        L * x        (x) <U1 'a' 'b'
-        R * x        (x) <U1 'a' 'c'
+        L * x        (x) %cU1 'a' 'b'
+        R * x        (x) %cU1 'a' 'c'
         Coordinates only on the left object:
           * y        (y) int64 1 2 3
         Coordinates only on the right object:
@@ -225,7 +227,7 @@ class TestFormatting:
         L   units: m
         R   units: kg
         Attributes only on the left object:
-            description: desc""")
+            description: desc""" % (byteorder, byteorder))
 
         actual = formatting.diff_array_repr(da_a, da_b, 'identical')
         try:
@@ -277,13 +279,14 @@ class TestFormatting:
             attrs={'units': 'kg'}
         )
 
+        byteorder = '<' if sys.byteorder == 'little' else '>'
         expected = dedent("""\
         Left and right Dataset objects are not identical
         Differing dimensions:
             (x: 2, y: 3) != (x: 2)
         Differing coordinates:
-        L * x        (x) <U1 'a' 'b'
-        R * x        (x) <U1 'a' 'c'
+        L * x        (x) %cU1 'a' 'b'
+        R * x        (x) %cU1 'a' 'c'
             source: 0
         Coordinates only on the left object:
           * y        (y) int64 1 2 3
@@ -298,7 +301,7 @@ class TestFormatting:
         L   units: m
         R   units: kg
         Attributes only on the left object:
-            description: desc""")
+            description: desc""" % (byteorder, byteorder))
 
         actual = formatting.diff_dataset_repr(ds_a, ds_b, 'identical')
         assert actual == expected