Repository: pyxem/kikuchipy (language: python)

Issue:
Failing tests due to change of binned attribute in HyperSpy
https://github.com/hyperspy/hyperspy/pull/2652 moved the metadata `binned` attribute to the axis property `is_binned`. Some of the `kikuchipy`-tests are now failing as the axis-dictionary of loaded files now includes `as-binned: False`, which is missing in the tests.

See https://github.com/hyperspy/hyperspy-extensions-list/runs/2214805581?check_suite_focus=true
Failing tests due to change of binned attribute in HyperSpy
https://github.com/hyperspy/hyperspy/pull/2652 moved the metadata `binned` attribute to the axis property `is_binned`. Some of the `kikuchipy`-tests are now failing as the axis-dictionary of loaded files now includes `as-binned: False`, which is missing in the tests.

See https://github.com/hyperspy/hyperspy-extensions-list/runs/2214805581?check_suite_focus=true


Discussion / hints:
Thanks a lot for letting us know, @jlaehne! I think I'll make a patch release next week with a couple of fixes and this in.
Sorry to bother you with this @jlaehne, but could you tell me which version of HyperSpy the change in hyperspy/hyperspy#2652 is/will be part of? I was wondering because our tests on master apparently pass with HyperSpy 1.6.2 (https://github.com/pyxem/kikuchipy/pull/331).
It will come in in the next minor release (ie `1.7.0`) ~ https://github.com/hyperspy/hyperspy/blob/RELEASE_next_minor/CHANGES.rst
Ah, of course, thanks @pc494! I blanked out... For some minutes now I was thinking of HyperSpy v.1.6.2 as a minor.
Thanks a lot for letting us know, @jlaehne! I think I'll make a patch release next week with a couple of fixes and this in.
Sorry to bother you with this @jlaehne, but could you tell me which version of HyperSpy the change in hyperspy/hyperspy#2652 is/will be part of? I was wondering because our tests on master apparently pass with HyperSpy 1.6.2 (https://github.com/pyxem/kikuchipy/pull/331).
It will come in in the next minor release (ie `1.7.0`) ~ https://github.com/hyperspy/hyperspy/blob/RELEASE_next_minor/CHANGES.rst
Ah, of course, thanks @pc494! I blanked out... For some minutes now I was thinking of HyperSpy v.1.6.2 as a minor.

Fix patch:
diff --git a/doc/changelog.rst b/doc/changelog.rst
index 4e376d73..e83ac5b8 100644
--- a/doc/changelog.rst
+++ b/doc/changelog.rst
@@ -34,7 +34,7 @@ Fixed
 -----
 - Passing a static background pattern to EBSD.remove_static_background() for a
   non-square detector dataset works.
-  (`#? <https://github.com/pyxem/kikuchipy/pull/?>`_)
+  (`#331 <https://github.com/pyxem/kikuchipy/pull/331>`_)
 
 0.3.2 (2021-02-01)
 ==================


Test patch:
diff --git a/kikuchipy/conftest.py b/kikuchipy/conftest.py
index dd1b76ad..e42e40c3 100644
--- a/kikuchipy/conftest.py
+++ b/kikuchipy/conftest.py
@@ -17,12 +17,16 @@
 # along with kikuchipy. If not, see <http://www.gnu.org/licenses/>.
 
 import gc
+from numbers import Number
 import os
+from packaging import version
 import tempfile
 from typing import Tuple
 
 from diffpy.structure import Atom, Lattice, Structure
 from diffsims.crystallography import ReciprocalLatticePoint
+from hyperspy import __version__ as hs_version
+from hyperspy.misc.utils import DictionaryTreeBrowser
 import numpy as np
 from orix.crystal_map import CrystalMap, Phase, PhaseList
 from orix.quaternion.rotation import Rotation
@@ -39,6 +43,33 @@
 from kikuchipy.simulations.features import KikuchiBand, ZoneAxis
 
 
+def assert_dictionary(dict1, dict2):
+    """Assert that two dictionaries are (almost) equal.
+
+    Used to compare signal's axes managers or metadata in tests.
+    """
+    if isinstance(dict1, DictionaryTreeBrowser):
+        dict1 = dict1.as_dictionary()
+        dict2 = dict2.as_dictionary()
+    for key in dict2.keys():
+        if key in ["is_binned", "binned"] and version.parse(
+            hs_version
+        ) > version.parse(
+            "1.6.2"
+        ):  # pragma: no cover
+            continue
+        if isinstance(dict2[key], dict):
+            assert_dictionary(dict1[key], dict2[key])
+        else:
+            if isinstance(dict2[key], list) or isinstance(dict1[key], list):
+                dict2[key] = np.array(dict2[key])
+                dict1[key] = np.array(dict1[key])
+            if isinstance(dict2[key], (np.ndarray, Number)):
+                assert np.allclose(dict1[key], dict2[key])
+            else:
+                assert dict1[key] == dict2[key]
+
+
 @pytest.fixture
 def dummy_signal():
     """Dummy signal of shape <(3, 3)|(3, 3)>. If this is changed, all
diff --git a/kikuchipy/io/plugins/tests/test_emsoft_ebsd_masterpattern.py b/kikuchipy/io/plugins/tests/test_emsoft_ebsd_masterpattern.py
index c822e68e..24d2e2e2 100644
--- a/kikuchipy/io/plugins/tests/test_emsoft_ebsd_masterpattern.py
+++ b/kikuchipy/io/plugins/tests/test_emsoft_ebsd_masterpattern.py
@@ -23,6 +23,7 @@
 import pytest
 
 from kikuchipy import load
+from kikuchipy.conftest import assert_dictionary
 from kikuchipy.io.plugins.emsoft_ebsd_master_pattern import (
     _check_file_format,
     _get_data_shape_slices,
@@ -32,7 +33,6 @@
     EBSDMasterPattern,
     LazyEBSDMasterPattern,
 )
-from kikuchipy.signals.tests.test_ebsd import assert_dictionary
 
 
 DIR_PATH = os.path.dirname(__file__)
@@ -101,7 +101,7 @@ def test_file_reader(self):
         axes_manager = setup_axes_manager(["energy", "height", "width"])
 
         assert s.data.shape == (11, 13, 13)
-        assert s.axes_manager.as_dictionary() == axes_manager
+        assert_dictionary(s.axes_manager.as_dictionary(), axes_manager)
         assert_dictionary(s.metadata.as_dictionary(), METADATA)
 
         signal_indx = s.axes_manager.signal_indices_in_array
@@ -112,10 +112,8 @@ def test_file_reader(self):
     def test_projection_lambert(self):
         s = load(EMSOFT_FILE, projection="lambert", hemisphere="both")
 
-        axes_manager = setup_axes_manager()
-
         assert s.data.shape == (2, 11, 13, 13)
-        assert s.axes_manager.as_dictionary() == axes_manager
+        assert_dictionary(s.axes_manager.as_dictionary(), setup_axes_manager())
 
     def test_check_file_format(self, save_path_hdf5):
         with File(save_path_hdf5, mode="w") as f:
diff --git a/kikuchipy/io/plugins/tests/test_h5ebsd.py b/kikuchipy/io/plugins/tests/test_h5ebsd.py
index 219a9bde..b70ab7d8 100644
--- a/kikuchipy/io/plugins/tests/test_h5ebsd.py
+++ b/kikuchipy/io/plugins/tests/test_h5ebsd.py
@@ -27,6 +27,7 @@
 import pytest
 
 from kikuchipy.data import nickel_ebsd_small
+from kikuchipy.conftest import assert_dictionary
 from kikuchipy.io._io import load
 from kikuchipy.io.plugins.h5ebsd import (
     check_h5ebsd,
@@ -91,7 +92,7 @@ def test_load_kikuchipy(self):
         s = load(KIKUCHIPY_FILE)
 
         assert s.data.shape == (3, 3, 60, 60)
-        assert s.axes_manager.as_dictionary() == AXES_MANAGER
+        assert_dictionary(s.axes_manager.as_dictionary(), AXES_MANAGER)
 
     def test_load_edax(self):
         with File(EDAX_FILE, mode="r+") as f:
@@ -105,7 +106,7 @@ def test_load_edax(self):
 
         s = load(EDAX_FILE)
         assert s.data.shape == (3, 3, 60, 60)
-        assert s.axes_manager.as_dictionary() == AXES_MANAGER
+        assert_dictionary(s.axes_manager.as_dictionary(), AXES_MANAGER)
 
     def test_load_bruker(self):
         with File(BRUKER_FILE, mode="r+") as f:
@@ -119,7 +120,7 @@ def test_load_bruker(self):
 
         s = load(BRUKER_FILE)
         assert s.data.shape == (3, 3, 60, 60)
-        assert s.axes_manager.as_dictionary() == AXES_MANAGER
+        assert_dictionary(s.axes_manager.as_dictionary(), AXES_MANAGER)
 
     def test_load_manufacturer(self, save_path_hdf5):
         s = EBSD((255 * np.random.rand(10, 3, 5, 5)).astype(np.uint8))
@@ -177,7 +178,7 @@ def test_load_with_padding(self, save_path_hdf5, lazy):
         with pytest.warns(UserWarning, match="Will attempt to load by zero"):
             s_reload = load(save_path_hdf5, lazy=lazy)
         AXES_MANAGER["axis-1"]["size"] = new_n_columns
-        assert s_reload.axes_manager.as_dictionary() == AXES_MANAGER
+        assert_dictionary(s_reload.axes_manager.as_dictionary(), AXES_MANAGER)
 
     @pytest.mark.parametrize("remove_phases", (True, False))
     def test_load_save_cycle(self, save_path_hdf5, remove_phases):
diff --git a/kikuchipy/io/plugins/tests/test_nordif.py b/kikuchipy/io/plugins/tests/test_nordif.py
index 1ee19e7a..0a11a178 100644
--- a/kikuchipy/io/plugins/tests/test_nordif.py
+++ b/kikuchipy/io/plugins/tests/test_nordif.py
@@ -31,6 +31,7 @@
 import numpy as np
 import pytest
 
+from kikuchipy.conftest import assert_dictionary
 from kikuchipy.io._io import load
 from kikuchipy.io.plugins.nordif import get_settings_from_file, get_string
 from kikuchipy.signals.ebsd import EBSD
@@ -235,6 +236,7 @@
         "navigate": False,
     },
 }
+# AXES_MANAGER = make_dicts_compatible_with_hyperspy_v1_7(AXES_MANAGER)
 
 
 @pytest.fixture()
@@ -276,7 +278,7 @@ def test_load(self, setting_file):
         s = load(PATTERN_FILE, setting_file=SETTING_FILE)
 
         assert s.data.shape == (3, 3, 60, 60)
-        assert s.axes_manager.as_dictionary() == AXES_MANAGER
+        assert_dictionary(s.axes_manager.as_dictionary(), AXES_MANAGER)
 
         static_bg = imread(BG_FILE)
         assert np.allclose(
@@ -386,7 +388,7 @@ def test_load_inplace(self, lazy):
                 _ = load(PATTERN_FILE, lazy=lazy, mmap_mode="r+")
         else:
             s = load(PATTERN_FILE, lazy=lazy, mmap_mode="r+")
-            assert s.axes_manager.as_dictionary() == AXES_MANAGER
+            assert_dictionary(s.axes_manager.as_dictionary(), AXES_MANAGER)
 
     def test_save_fresh(self, save_path_nordif):
         scan_size = (10, 3)
diff --git a/kikuchipy/signals/tests/test_ebsd.py b/kikuchipy/signals/tests/test_ebsd.py
index dd939775..c47ed85a 100644
--- a/kikuchipy/signals/tests/test_ebsd.py
+++ b/kikuchipy/signals/tests/test_ebsd.py
@@ -17,11 +17,9 @@
 # along with kikuchipy. If not, see <http://www.gnu.org/licenses/>.
 
 import os
-from numbers import Number
 
 import dask.array as da
 from hyperspy.utils.roi import RectangularROI
-from hyperspy.misc.utils import DictionaryTreeBrowser
 import matplotlib
 from matplotlib.pyplot import close
 import numpy as np
@@ -33,6 +31,7 @@
 
 from kikuchipy import load
 from kikuchipy.data import nickel_ebsd_small, nickel_ebsd_large
+from kikuchipy.conftest import assert_dictionary
 from kikuchipy.filters.window import Window
 from kikuchipy.pattern._pattern import fft_spectrum
 from kikuchipy.signals.ebsd import EBSD, LazyEBSD
@@ -45,25 +44,6 @@
 EMSOFT_FILE = os.path.join(DIR_PATH, "../../data/emsoft_ebsd/simulated_ebsd.h5")
 
 
-def assert_dictionary(input_dict, output_dict):
-    if isinstance(input_dict, DictionaryTreeBrowser):
-        input_dict = input_dict.as_dictionary()
-        output_dict = output_dict.as_dictionary()
-    for key in output_dict.keys():
-        if isinstance(output_dict[key], dict):
-            assert_dictionary(input_dict[key], output_dict[key])
-        else:
-            if isinstance(output_dict[key], list) or isinstance(
-                input_dict[key], list
-            ):
-                output_dict[key] = np.array(output_dict[key])
-                input_dict[key] = np.array(input_dict[key])
-            if isinstance(output_dict[key], (np.ndarray, Number)):
-                assert np.allclose(input_dict[key], output_dict[key])
-            else:
-                assert input_dict[key] == output_dict[key]
-
-
 class TestEBSD:
     def test_init(self):
 
diff --git a/kikuchipy/signals/tests/test_ebsd_masterpattern.py b/kikuchipy/signals/tests/test_ebsd_masterpattern.py
index e8f68d16..58ce2b8a 100644
--- a/kikuchipy/signals/tests/test_ebsd_masterpattern.py
+++ b/kikuchipy/signals/tests/test_ebsd_masterpattern.py
@@ -28,6 +28,7 @@
 import pytest
 
 from kikuchipy import load
+from kikuchipy.conftest import assert_dictionary
 from kikuchipy.data import nickel_ebsd_master_pattern_small
 from kikuchipy.detectors import EBSDDetector
 from kikuchipy.io.plugins.tests.test_emsoft_ebsd_masterpattern import (
@@ -89,21 +90,21 @@ def test_save_load_hspy(self, save_path_hdf5):
         axes_manager = setup_axes_manager(["energy", "height", "width"])
 
         assert isinstance(s, EBSDMasterPattern)
-        assert s.axes_manager.as_dictionary() == axes_manager
+        assert_dictionary(s.axes_manager.as_dictionary(), axes_manager)
         assert_dictionary(s.metadata.as_dictionary(), METADATA)
 
         s.save(save_path_hdf5)
 
         s2 = hs_load(save_path_hdf5, signal_type="EBSDMasterPattern")
         assert isinstance(s2, EBSDMasterPattern)
-        assert s2.axes_manager.as_dictionary() == axes_manager
+        assert_dictionary(s2.axes_manager.as_dictionary(), axes_manager)
         assert_dictionary(s2.metadata.as_dictionary(), METADATA)
 
         s3 = hs_load(save_path_hdf5)
         assert isinstance(s3, Signal2D)
         s3.set_signal_type("EBSDMasterPattern")
         assert isinstance(s3, EBSDMasterPattern)
-        assert s3.axes_manager.as_dictionary() == axes_manager
+        assert_dictionary(s3.axes_manager.as_dictionary(), axes_manager)
         assert_dictionary(s.metadata.as_dictionary(), METADATA)
 
     @pytest.mark.parametrize(