diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index 5abda71b..03e93c1d 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -110,7 +110,7 @@ jobs:
- name: Run tests
run: |
- pytest --pyargs orix --runslow --reruns 2 -n 2 --cov=orix
+ pytest --pyargs orix --slow --reruns 2 -n 2 --cov=orix
- name: Generate line coverage
if: ${{ matrix.os == 'ubuntu-latest' }}
diff --git a/doc/conf.py b/doc/conf.py
index 2b81c2d3..9dafd02c 100644
--- a/doc/conf.py
+++ b/doc/conf.py
@@ -1,3 +1,22 @@
+#
+# Copyright 2019-2025 the orix developers
+#
+# This file is part of orix.
+#
+# orix is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# orix is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with orix. If not, see .
+#
+
# Configuration file for the Sphinx documentation app.
# See the documentation for a full list of configuration options:
# https://www.sphinx-doc.org/en/master/usage/configuration.html
@@ -64,9 +83,10 @@
"numpydoc": ("https://numpydoc.readthedocs.io/en/latest", None),
"pooch": ("https://www.fatiando.org/pooch/latest", None),
"pytest": ("https://docs.pytest.org/en/stable", None),
+ "pytest-xdist": ("https://pytest-xdist.readthedocs.io/en/stable/", None),
"python": ("https://docs.python.org/3", None),
"pyxem": ("https://pyxem.readthedocs.io/en/latest", None),
- "readthedocs": ("https://docs.readthedocs.io/en/stable", None),
+ "readthedocs": ("https://docs.readthedocs.com/platform/stable/", None),
"scipy": ("https://docs.scipy.org/doc/scipy", None),
"sklearn": ("https://scikit-learn.org/stable", None),
"sphinx": ("https://www.sphinx-doc.org/en/master", None),
diff --git a/doc/dev/running_writing_tests.rst b/doc/dev/running_writing_tests.rst
index ab205cf4..da832cb8 100644
--- a/doc/dev/running_writing_tests.rst
+++ b/doc/dev/running_writing_tests.rst
@@ -3,11 +3,12 @@ Run and write tests
All functionality in orix is tested with :doc:`pytest `.
The tests reside in a ``tests`` module.
-Tests are short methods that call functions in ``orix`` and compare resulting output
-values with known answers.
+Tests are short methods that call functions in orix and compare resulting output values
+with known answers.
+
Install necessary dependencies to run the tests::
- pip install --editable ".[tests]"
+ pip install -e ".[tests]"
Some useful :doc:`fixtures ` are available in the
``conftest.py`` file.
@@ -22,29 +23,28 @@ Some useful :doc:`fixtures ` are available in the
To run the tests::
- pytest --cov --pyargs orix -n auto
+ pytest --cov --pyargs orix -n auto
-The ``-n auto`` is an optional flag to enable parallelized testing.
-The ``--cov`` flag makes :doc:`coverage.py ` print a nice report.
-For an even nicer presentation, you can use ``coverage.py`` directly::
+The ``-n auto`` is an optional flag to enable parallelized testing with
+:doc:`pytest-xdist `.
+We aim to cover all lines when all :ref:`dependencies` are installed.
+The ``--cov`` flag makes :doc:`coverage.py ` print a nice coverage
+report.
+For an even nicer presentation, you can use coverage.py directly::
- coverage html
+ coverage html
Coverage can then be inspected in the browser by opening ``htmlcov/index.html``.
-We strive for 100% test coverage of lines when all dependencies are installed.
-
-If you have a test that takes a long time to run, you can mark it to skip it from running by default
-
-.. code-block:: Python
+If a test takes a long time to run, you can mark it to skip it from running by default::
@pytest.mark.slow
def test_slow_function():
- pass
+ ...
-Then you can run the tests with the ``--runslow`` option to skip slow tests::
+To run tests marked as slow, add the flag when running pytest::
- pytest --runslow
+ pytest --slow
Docstring examples are tested with :doc:`pytest ` as well.
:mod:`numpy` and :mod:`matplotlib.pyplot` should not be imported in examples as they are
diff --git a/orix/crystal_map/phase_list.py b/orix/crystal_map/phase_list.py
index ff99ee89..ce7e3b91 100644
--- a/orix/crystal_map/phase_list.py
+++ b/orix/crystal_map/phase_list.py
@@ -1,4 +1,5 @@
-# Copyright 2018-2024 the orix developers
+#
+# Copyright 2019-2025 the orix developers
#
# This file is part of orix.
#
@@ -9,11 +10,12 @@
#
# orix is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
-# along with orix. If not, see .
+# along with orix. If not, see .
+#
from __future__ import annotations
@@ -37,7 +39,8 @@
_groups,
get_point_group,
)
-from orix.vector import Miller, Vector3d
+from orix.vector.miller import Miller
+from orix.vector.vector3d import Vector3d
# All named Matplotlib colors (tableau and xkcd already lower case hex)
ALL_COLORS = mcolors.TABLEAU_COLORS
@@ -366,18 +369,26 @@ def deepcopy(self) -> Phase:
return copy.deepcopy(self)
def expand_asymmetric_unit(self) -> Phase:
- """Return new instance with all symmetrically equivalent atoms.
+ """Return a new phase with all symmetrically equivalent atoms.
+
+ Returns
+ -------
+ expanded_phase
+ New phase with the a :attr:`structure` with the unit cell
+ filled with symmetrically equivalent atoms.
Examples
--------
+ >>> from diffpy.structure import Atom, Lattice, Structure
+ >>> import orix.crystal_map as ocm
>>> atoms = [Atom("Si", xyz=(0, 0, 1))]
>>> lattice = Lattice(4.04, 4.04, 4.04, 90, 90, 90)
>>> structure = Structure(atoms = atoms,lattice=lattice)
- >>> phase = Phase(structure=structure, space_group=227)
+ >>> phase = ocm.Phase(structure=structure, space_group=227)
>>> phase.structure
[Si 0.000000 0.000000 1.000000 1.0000]
- >>> expanded = phase.expand_asymmetric_unit()
- >>> expanded.structure
+ >>> expanded_phase = phase.expand_asymmetric_unit()
+ >>> expanded_phase.structure
[Si 0.000000 0.000000 0.000000 1.0000,
Si 0.000000 0.500000 0.500000 1.0000,
Si 0.500000 0.500000 0.000000 1.0000,
@@ -411,9 +422,10 @@ def expand_asymmetric_unit(self) -> Phase:
diffpy_structure.append(new_atom)
# This handles conversion back to correct alignment
- out = Phase(self)
- out.structure = diffpy_structure
- return out
+ expanded_phase = self.__class__(self)
+ expanded_phase.structure = diffpy_structure
+
+ return expanded_phase
class PhaseList:
diff --git a/orix/quaternion/quaternion.py b/orix/quaternion/quaternion.py
index 9e0b260d..b0b67730 100644
--- a/orix/quaternion/quaternion.py
+++ b/orix/quaternion/quaternion.py
@@ -1,4 +1,5 @@
-# Copyright 2018-2024 the orix developers
+#
+# Copyright 2019-2025 the orix developers
#
# This file is part of orix.
#
@@ -9,15 +10,16 @@
#
# orix is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
-# along with orix. If not, see .
+# along with orix. If not, see .
+#
from __future__ import annotations
-from typing import Any, Optional, Tuple, Union
+from typing import Any
import warnings
import dask.array as da
@@ -29,7 +31,9 @@
from orix._base import Object3d
from orix.constants import installed
from orix.quaternion import _conversions
-from orix.vector import AxAngle, Homochoric, Miller, Rodrigues, Vector3d
+from orix.vector.miller import Miller
+from orix.vector.neo_euler import AxAngle, Homochoric, Rodrigues
+from orix.vector.vector3d import Vector3d
class Quaternion(Object3d):
@@ -193,9 +197,7 @@ def conj(self) -> Quaternion:
def __invert__(self) -> Quaternion:
return self.__class__(self.conj.data / (self.norm**2)[..., np.newaxis])
- def __mul__(
- self, other: Union[Quaternion, Vector3d]
- ) -> Union[Quaternion, Vector3d]:
+ def __mul__(self, other: Quaternion | Vector3d) -> Quaternion | Vector3d:
if isinstance(other, Quaternion):
if installed["numpy-quaternion"]:
import quaternion
@@ -231,7 +233,7 @@ def __mul__(
def __neg__(self) -> Quaternion:
return self.__class__(-self.data)
- def __eq__(self, other: Union[Any, Quaternion]) -> bool:
+ def __eq__(self, other: Any | Quaternion) -> bool:
"""Check if quaternions have equal shapes and components."""
if (
isinstance(other, Quaternion)
@@ -247,8 +249,8 @@ def __eq__(self, other: Union[Any, Quaternion]) -> bool:
@classmethod
def from_axes_angles(
cls,
- axes: Union[np.ndarray, Vector3d, tuple, list],
- angles: Union[np.ndarray, tuple, list, float],
+ axes: np.ndarray | Vector3d | tuple | list,
+ angles: np.ndarray | tuple | list | float,
degrees: bool = False,
) -> Quaternion:
r"""Create unit quaternions from axis-angle pairs
@@ -299,8 +301,7 @@ def from_axes_angles(
@classmethod
def from_homochoric(
- cls,
- ho: Union[Vector3d, Homochoric, np.ndarray, tuple, list],
+ cls, ho: Vector3d | Homochoric | np.ndarray | tuple | list
) -> Quaternion:
r"""Create unit quaternions from homochoric vectors
:math:`\mathbf{h}` :cite:`rowenhorst2015consistent`.
@@ -346,8 +347,8 @@ def from_homochoric(
@classmethod
def from_rodrigues(
cls,
- ro: Union[np.ndarray, Vector3d, tuple, list],
- angles: Union[np.ndarray, tuple, list, float, None] = None,
+ ro: np.ndarray | Vector3d | tuple | list,
+ angles: np.ndarray | tuple | list | float | None = None,
) -> Quaternion:
r"""Create unit quaternions from three-component Rodrigues
vectors :math:`\hat{\mathbf{n}}` or four-component
@@ -448,7 +449,7 @@ def from_rodrigues(
@classmethod
def from_euler(
cls,
- euler: Union[np.ndarray, tuple, list],
+ euler: np.ndarray | tuple | list,
direction: str = "lab2crystal",
degrees: bool = False,
) -> Quaternion:
@@ -505,7 +506,7 @@ def from_euler(
return Q
@classmethod
- def from_matrix(cls, matrix: Union[np.ndarray, tuple, list]) -> Quaternion:
+ def from_matrix(cls, matrix: np.ndarray | tuple | list) -> Quaternion:
"""Create unit quaternions from orientation matrices
:cite:`rowenhorst2015consistent`.
@@ -552,9 +553,13 @@ def from_scipy_rotation(cls, rotation: SciPyRotation) -> Quaternion:
Returns
-------
- quaternion
+ Q
Quaternions.
+ See Also
+ --------
+ to_scipy_rotation
+
Notes
-----
The SciPy rotation is inverted to be consistent with the orix
@@ -600,17 +605,17 @@ def from_scipy_rotation(cls, rotation: SciPyRotation) -> Quaternion:
@classmethod
def from_align_vectors(
cls,
- other: Union[Vector3d, tuple, list],
- initial: Union[Vector3d, tuple, list],
- weights: Optional[np.ndarray] = None,
+ other: Vector3d | tuple | list,
+ initial: Vector3d | tuple | list,
+ weights: np.ndarray | None = None,
return_rmsd: bool = False,
return_sensitivity: bool = False,
- ) -> Union[
- Quaternion,
- Tuple[Quaternion, float],
- Tuple[Quaternion, np.ndarray],
- Tuple[Quaternion, float, np.ndarray],
- ]:
+ ) -> (
+ Quaternion
+ | tuple[Quaternion, float]
+ | tuple[Quaternion, np.ndarray]
+ | tuple[Quaternion, float, np.ndarray]
+ ):
"""Estimate a quaternion to optimally align two sets of vectors.
This method wraps
@@ -739,7 +744,7 @@ def triple_cross(cls, q1: Quaternion, q2: Quaternion, q3: Quaternion) -> Quatern
return Q
@classmethod
- def identity(cls, shape: Union[int, tuple] = (1,)) -> Quaternion:
+ def identity(cls, shape: int | tuple = (1,)) -> Quaternion:
"""Create identity quaternions.
Parameters
@@ -836,7 +841,7 @@ def to_axes_angles(self) -> AxAngle:
ax = AxAngle(axes * angles)
return ax
- def to_rodrigues(self, frank: bool = False) -> Union[Rodrigues, np.ndarray]:
+ def to_rodrigues(self, frank: bool = False) -> Rodrigues | np.ndarray:
r"""Return the unit quaternions as Rodrigues or Rodrigues-Frank
vectors :cite:`rowenhorst2015consistent`.
@@ -946,45 +951,44 @@ def to_homochoric(self) -> Homochoric:
return ho
def to_scipy_rotation(self) -> SciPyRotation:
- r"""Return the unit quaternions as
- :class:`scipy.spatial.transform.Rotation` objects used in scipy's
- spatial module.
+ r"""Return unit quaternions as a SciPy rotation.
Returns
-------
- SciPy_Rotation
- a Rotation object generated from the unit quaternion data
- (i.e, unaffected by symmetry, phase, or length).
+ scipy_rotation
+ A SciPy rotation (flattened) given by the unit quaternions
+ without considering any symmetry.
+
+ See Also
+ --------
+ from_scipy_rotation
Notes
-----
- SciPy by default uses the Active rotation convention along with the
- vector-scalar quaternion definition, as opposed to ORIX's passive,
- scalar-vector convention. Thus, the following quaternion in orix:
- :math: `q_{orix} = [q_0, q_1, q_2, q_3]`
- represents the same operations as the following quaternion in scipy:
- :math: `q_{SciPy} = [-q_1, -q_2, -q_3, q_0]`
-
- See the function description for Quaternion.from_scipy_rotation
- for an example of how these differing parameterizations still produce
- identical rotation operations.
-
- Additionally, note that Orix enforces :math: `q_0 >= 0` whereas
- SciPy does not. Thus, the operation
-
- >>> Quaternion.from_scipy_rotation(r).to_scipy_rotation.as_quat()
-
- will produce an identical rotation operation, but not
- necessarily an idential quaternion. Look up "quaternion double cover"
- for more information on why this occurs.
-
- Finally, ORIX supports N-dimensional arrays, whereas SciPy
- currently supports only 1-dimensional vectors. Thus, this function
- will also flatten arrays when converting to SciPy Rotations.
+ SciPy by default uses the active rotation interpretation along
+ with the vector-scalar quaternion definition, as opposed to
+ orix's passive one, scalar-vector interpretation. Thus, the
+ following quaternion in orix,
+ :math:`Q_{orix} = [q_0, q_1, q_2, q_3]` represents the same
+ transformation as the following quaternion in SciPy:
+ :math:`Q_{SciPy} = [-q_1, -q_2, -q_3, q_0]`
+
+ See the function description for :meth:`from_scipy_rotation` for
+ an example of how these differing parameterizations still
+ produce identical transformations.
+
+ Additionally, note that orix enforces :math:`Q_0 \geq 0` whereas
+ SciPy does not. Thus, the operation::
+
+ Quaternion.from_scipy_rotation(r).to_scipy_rotation.as_quat()
+
+ will produce an identical transformation, but not necessarily an
+ idential quaternion. Look up "quaternion double cover" for more
+ information on why this occurs.
"""
if self.ndim > 1:
warnings.warn(
- "\n {} dimension greater than 1. ".format(self.__class__.__name__)
+ f"\n {self.__class__.__name__} dimension greater than 1. "
+ "Flattening into a 1-dimensional vector"
)
self = self.flatten()
@@ -1075,11 +1079,11 @@ def mean(self) -> Quaternion:
def outer(
self,
- other: Union[Quaternion, Vector3d],
+ other: Quaternion | Vector3d,
lazy: bool = False,
chunk_size: int = 20,
progressbar: bool = True,
- ) -> Union[Quaternion, Vector3d]:
+ ) -> Quaternion | Vector3d:
"""Return the outer products of the quaternions and the other
quaternions or vectors.
@@ -1171,7 +1175,7 @@ def inv(self) -> Quaternion:
# -------------------- Other private methods --------------------- #
def _outer_dask(
- self, other: Union[Quaternion, Vector3d], chunk_size: int = 20
+ self, other: Quaternion | Vector3d, chunk_size: int = 20
) -> da.Array:
"""Compute the product of every quaternion in this instance to
every quaternion or vector in another instance, returned as a
diff --git a/orix/quaternion/rotation.py b/orix/quaternion/rotation.py
index 64ce43e8..811f79a0 100644
--- a/orix/quaternion/rotation.py
+++ b/orix/quaternion/rotation.py
@@ -1,4 +1,5 @@
-# Copyright 2018-2024 the orix developers
+#
+# Copyright 2019-2025 the orix developers
#
# This file is part of orix.
#
@@ -9,23 +10,24 @@
#
# orix is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
-# along with orix. If not, see .
+# along with orix. If not, see .
+#
from __future__ import annotations
-from typing import Any, Tuple, Union
+from typing import Any
import dask.array as da
from dask.diagnostics import ProgressBar
import numpy as np
from scipy.special import hyp0f1
-from orix.quaternion import Quaternion
-from orix.vector import Vector3d
+from orix.quaternion.quaternion import Quaternion
+from orix.vector.vector3d import Vector3d
class Rotation(Quaternion):
@@ -73,7 +75,7 @@ class Rotation(Quaternion):
True
"""
- def __init__(self, data: Union[np.ndarray, Rotation, list, tuple]):
+ def __init__(self, data: np.ndarray | Rotation | list | tuple) -> None:
super().__init__(data)
self._data = np.concatenate((self.data, np.zeros(self.shape + (1,))), axis=-1)
if isinstance(data, Rotation):
@@ -104,8 +106,8 @@ def antipodal(self) -> Rotation:
# ------------------------ Dunder methods ------------------------ #
def __mul__(
- self, other: Union[Rotation, Quaternion, Vector3d, np.ndarray, int, list]
- ):
+ self, other: Rotation | Quaternion | Vector3d | np.ndarray | int | list
+ ) -> Rotation | Quaternion | Vector3d:
# Combine rotations self * other as first other, then self
if isinstance(other, Rotation):
Q = Quaternion(self) * Quaternion(other)
@@ -146,7 +148,7 @@ def __invert__(self) -> Rotation:
R.improper = self.improper
return R
- def __eq__(self, other: Union[Any, Rotation]) -> bool:
+ def __eq__(self, other: Any | Rotation) -> bool:
"""Check if the rotations have equal shapes and values."""
if (
isinstance(other, Rotation)
@@ -163,9 +165,9 @@ def __eq__(self, other: Union[Any, Rotation]) -> bool:
@classmethod
def random_vonmises(
cls,
- shape: Union[int, tuple] = (1,),
+ shape: int | tuple = (1,),
alpha: float = 1.0,
- reference: Union[list, tuple, Rotation] = (1, 0, 0, 0),
+ reference: Rotation | list | tuple = (1, 0, 0, 0),
) -> Rotation:
"""Return random rotations with a simplified Von Mises-Fisher
distribution.
@@ -206,11 +208,9 @@ def unique(
return_index: bool = False,
return_inverse: bool = False,
antipodal: bool = True,
- ) -> Union[
- Rotation,
- Tuple[Rotation, np.ndarray],
- Tuple[Rotation, np.ndarray, np.ndarray],
- ]:
+ ) -> (
+ Rotation | tuple[Rotation, np.ndarray] | tuple[Rotation, np.ndarray, np.ndarray]
+ ):
"""Return the unique rotations from these rotations.
Two rotations are not unique if they have the same propriety
@@ -343,11 +343,11 @@ def angle_with_outer(self, other: Rotation, degrees: bool = False) -> np.ndarray
def outer(
self,
- other: Union[Rotation, Vector3d],
+ other: Rotation | Vector3d,
lazy: bool = False,
chunk_size: int = 20,
progressbar: bool = True,
- ) -> Union[Rotation, Vector3d]:
+ ) -> Rotation | Vector3d:
"""Return the outer rotation products of the rotations and the
other rotations or vectors.
diff --git a/orix/quaternion/symmetry.py b/orix/quaternion/symmetry.py
index ec3061ef..417f04ad 100644
--- a/orix/quaternion/symmetry.py
+++ b/orix/quaternion/symmetry.py
@@ -1,4 +1,5 @@
-# Copyright 2018-2024 the orix developers
+#
+# Copyright 2019-2025 the orix developers
#
# This file is part of orix.
#
@@ -9,11 +10,12 @@
#
# orix is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
-# along with orix. If not, see .
+# along with orix. If not, see .
+#
from __future__ import annotations
@@ -24,11 +26,11 @@
import numpy as np
from orix.quaternion.rotation import Rotation
-from orix.vector import Vector3d
+from orix.vector.vector3d import Vector3d
if TYPE_CHECKING: # pragma: no cover
- from orix.quaternion import Orientation
- from orix.vector import FundamentalSector
+ from orix.quaternion.orientation import Orientation
+ from orix.vector.fundamental_sector import FundamentalSector
class Symmetry(Rotation):
diff --git a/orix/tests/conftest.py b/orix/tests/conftest.py
index 7684e6d6..6852fd3d 100644
--- a/orix/tests/conftest.py
+++ b/orix/tests/conftest.py
@@ -16,80 +16,64 @@
# You should have received a copy of the GNU General Public License
# along with orix. If not, see .
#
-
-
-import os
-from tempfile import TemporaryDirectory
-
from diffpy.structure import Atom, Lattice, Structure
from h5py import File
import matplotlib.pyplot as plt
import numpy as np
import pytest
-from orix import constants
-from orix.crystal_map import CrystalMap, PhaseList, create_coordinate_arrays
-from orix.quaternion import Rotation
+from orix.constants import installed
+from orix.crystal_map.crystal_map import CrystalMap, create_coordinate_arrays
+from orix.crystal_map.phase_list import PhaseList
+from orix.quaternion.rotation import Rotation
# --------------------------- pytest hooks --------------------------- #
-def pytest_sessionstart(session): # pragma: no cover
+def pytest_sessionstart(session):
plt.rcParams["backend"] = "agg"
# -------------------- Control of test selection --------------------- #
skipif_numpy_quaternion_present = pytest.mark.skipif(
- constants.installed["numpy-quaternion"], reason="numpy-quaternion installed"
+ installed["numpy-quaternion"], reason="numpy-quaternion installed"
)
skipif_numpy_quaternion_missing = pytest.mark.skipif(
- not constants.installed["numpy-quaternion"], reason="numpy-quaternion not installed"
+ not installed["numpy-quaternion"], reason="numpy-quaternion not installed"
)
-# ---------------------------- IO fixtures --------------------------- #
-
-# ----------------------------- .ang file ---------------------------- #
-
def pytest_addoption(parser):
parser.addoption(
- "--runslow", action="store_true", default=False, help="run slow tests"
+ "--slow", action="store_true", default=False, help="Run slow tests"
)
-def pytest_configure(config):
- config.addinivalue_line("markers", "slow: mark test as slow to run")
+# Markers are defined in package configuration
+MARKERS = ["slow"]
-def pytest_collection_modifyitems(config, items):
- if config.getoption("--runslow"):
- # --runslow given in cli: do not skip slow tests
- return
- else: # pragma: no cover
- skip_slow = pytest.mark.skip(reason="need --runslow option to run")
- for item in items:
- if "slow" in item.keywords:
- item.add_marker(skip_slow)
+def pytest_runtest_setup(item):
+ # Skip certain tests when flag is missing:
+ # https://docs.pytest.org/en/stable/reference/reference.html#pytest.hookspec.pytest_runtest_setup
+ for marker in MARKERS:
+ marker_str = f"--{marker}"
+ if marker in item.keywords and not item.config.getoption(marker_str):
+ pytest.skip(f"Needs {marker_str} flag to run")
-@pytest.fixture()
-def temp_ang_file():
- with TemporaryDirectory() as tempdir:
- f = open(os.path.join(tempdir, "temp_ang_file.ang"), mode="w+")
- yield f
+# ---------------------------- IO fixtures --------------------------- #
+# ----------------------------- .ang file ---------------------------- #
-@pytest.fixture(params=["h5"])
-def temp_file_path(request):
- """Temporary file in a temporary directory for use when tests need
- to write, and sometimes read again, data to, and from, a file.
- """
- ext = request.param
- with TemporaryDirectory() as tmp:
- file_path = os.path.join(tmp, "data_temp." + ext)
- yield file_path
+
+@pytest.fixture()
+def temp_ang_file(tmpdir):
+ fname = tmpdir.join("temp_ang_file.ang")
+ with open(fname, mode="w+") as f:
+ yield f
ANGFILE_TSL_HEADER = r"""# TEM_PIXperUM 1.000000
@@ -392,7 +376,7 @@ def angfile_emsoft(tmpdir, request):
# Variable map shape and step sizes
CTF_OXFORD_HEADER = r"""Channel Text File
Prj standard steel sample
-Author
+Author
JobMode Grid
XCells %i
YCells %i
@@ -754,7 +738,7 @@ def ctf_emsoft(tmpdir, request):
AcqE1 0.0000
AcqE2 0.0000
AcqE3 0.0000
-Euler angles refer to Sample Coordinate system (CS0)! Mag 0.0000 Coverage 0 Device 0 KV 0.0000 TiltAngle 0.0000 TiltAxis 0 DetectorOrientationE1 0.0000 DetectorOrientationE2 0.0000 DetectorOrientationE3 0.0000 WorkingDistance 0.0000 InsertionDistance 0.0000
+Euler angles refer to Sample Coordinate system (CS0)! Mag 0.0000 Coverage 0 Device 0 KV 0.0000 TiltAngle 0.0000 TiltAxis 0 DetectorOrientationE1 0.0000 DetectorOrientationE2 0.0000 DetectorOrientationE3 0.0000 WorkingDistance 0.0000 InsertionDistance 0.0000
Phases 1
4.079;4.079;4.079 90.000;90.000;90.000 Gold 11 0 Created from mtex
Phase X Y Bands Error Euler1 Euler2 Euler3 MAD BC BS"""
diff --git a/orix/tests/io/test_ang.py b/orix/tests/io/test_ang.py
index b224bfda..f1f57e78 100644
--- a/orix/tests/io/test_ang.py
+++ b/orix/tests/io/test_ang.py
@@ -1,4 +1,5 @@
-# Copyright 2018-2024 the orix developers
+#
+# Copyright 2019-2025 the orix developers
#
# This file is part of orix.
#
@@ -9,11 +10,12 @@
#
# orix is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
-# along with orix. If not, see .
+# along with orix. If not, see .
+#
import numpy as np
import pytest
@@ -371,23 +373,24 @@ def test_load_ang_emsoft(
def test_get_header(self, temp_ang_file):
temp_ang_file.write(ANGFILE_ASTAR_HEADER)
temp_ang_file.close()
- assert _get_header(open(temp_ang_file.name)) == [
- "# File created from ACOM RES results",
- "# ni-dislocations.res",
- "# ".rstrip(),
- "# ".rstrip(),
- "# MaterialName Nickel",
- "# Formula",
- "# Symmetry 43",
- "# LatticeConstants 3.520 3.520 3.520 90.000 90.000 90.000",
- "# NumberFamilies 4",
- "# hklFamilies 1 1 1 1 0.000000",
- "# hklFamilies 2 0 0 1 0.000000",
- "# hklFamilies 2 2 0 1 0.000000",
- "# hklFamilies 3 1 1 1 0.000000",
- "#",
- "# GRID: SqrGrid#",
- ]
+ with open(temp_ang_file.name) as f:
+ assert _get_header(f) == [
+ "# File created from ACOM RES results",
+ "# ni-dislocations.res",
+ "# ".rstrip(),
+ "# ".rstrip(),
+ "# MaterialName Nickel",
+ "# Formula",
+ "# Symmetry 43",
+ "# LatticeConstants 3.520 3.520 3.520 90.000 90.000 90.000",
+ "# NumberFamilies 4",
+ "# hklFamilies 1 1 1 1 0.000000",
+ "# hklFamilies 2 0 0 1 0.000000",
+ "# hklFamilies 2 2 0 1 0.000000",
+ "# hklFamilies 3 1 1 1 0.000000",
+ "#",
+ "# GRID: SqrGrid#",
+ ]
@pytest.mark.parametrize(
"expected_vendor, expected_columns, vendor_header",
@@ -419,7 +422,8 @@ def test_get_vendor_columns(
temp_ang_file.write(vendor_header)
temp_ang_file.close()
- header = _get_header(open(temp_ang_file.name))
+ with open(temp_ang_file.name) as f:
+ header = _get_header(f)
vendor, column_names = _get_vendor_columns(header, n_cols_file)
assert vendor == expected_vendor
@@ -429,7 +433,8 @@ def test_get_vendor_columns(
def test_get_vendor_columns_unknown(self, temp_ang_file, n_cols_file):
temp_ang_file.write("Look at me!\nI'm Mr. .ang file!\n")
temp_ang_file.close()
- header = _get_header(open(temp_ang_file.name))
+ with open(temp_ang_file.name) as f:
+ header = _get_header(f)
with pytest.warns(UserWarning, match=f"Number of columns, {n_cols_file}, "):
vendor, column_names = _get_vendor_columns(header, n_cols_file)
assert vendor == "unknown"
diff --git a/orix/tests/io/test_h5ebsd.py b/orix/tests/io/test_h5ebsd.py
index c364e71a..b309bbec 100644
--- a/orix/tests/io/test_h5ebsd.py
+++ b/orix/tests/io/test_h5ebsd.py
@@ -1,4 +1,5 @@
-# Copyright 2018-2024 the orix developers
+#
+# Copyright 2019-2025 the orix developers
#
# This file is part of orix.
#
@@ -9,11 +10,12 @@
#
# orix is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
-# along with orix. If not, see .
+# along with orix. If not, see .
+#
from h5py import File
@@ -22,12 +24,13 @@
class TestH5ebsd:
- def test_hdf5group2dict_update_dict(self, temp_file_path, crystal_map):
+ def test_hdf5group2dict_update_dict(self, tmp_path, crystal_map):
"""Can read datasets from an HDF5 file into an existing
dictionary.
"""
- save(temp_file_path, crystal_map)
- with File(temp_file_path, mode="r") as f:
+ fname = tmp_path / "test.h5"
+ save(fname, crystal_map)
+ with File(fname, mode="r") as f:
this_dict = {"hello": "there"}
this_dict = hdf5group2dict(f["crystal_map"], dictionary=this_dict)
assert this_dict["hello"] == "there"
diff --git a/orix/tests/io/test_io.py b/orix/tests/io/test_io.py
index fe276927..46258910 100644
--- a/orix/tests/io/test_io.py
+++ b/orix/tests/io/test_io.py
@@ -1,4 +1,5 @@
-# Copyright 2018-2024 the orix developers
+#
+# Copyright 2019-2025 the orix developers
#
# This file is part of orix.
#
@@ -9,11 +10,12 @@
#
# orix is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
-# along with orix. If not, see .
+# along with orix. If not, see .
+#
from collections import OrderedDict
from contextlib import contextmanager
@@ -73,11 +75,11 @@ def test_load_no_filename_match(self):
with pytest.raises(IOError, match=f"No filename matches '{fname}'."):
_ = load(fname)
- @pytest.mark.parametrize("temp_file_path", ["ktf"], indirect=["temp_file_path"])
- def test_load_unsupported_format(self, temp_file_path):
- np.savetxt(temp_file_path, X=np.random.rand(100, 8))
- with pytest.raises(IOError, match=f"Could not read "):
- _ = load(temp_file_path)
+ def test_load_unsupported_format(self, tmp_path):
+ fname = tmp_path / "unsupported_file.ktf"
+ np.savetxt(fname, X=np.random.rand(100, 8))
+ with pytest.raises(IOError, match="Could not read "):
+ _ = load(fname)
@pytest.mark.parametrize(
"manufacturer, expected_plugin",
@@ -88,61 +90,62 @@ def test_load_unsupported_format(self, temp_file_path):
("Oxford", None),
],
)
- def test_plugin_from_manufacturer(
- self, temp_file_path, manufacturer, expected_plugin
- ):
+ def test_plugin_from_manufacturer(self, manufacturer, expected_plugin, tmp_path):
h5ebsd_plugin_list = [bruker_h5ebsd, emsoft_h5ebsd, orix_hdf5]
- with File(temp_file_path, mode="w") as f:
+ fname = tmp_path / "test.h5"
+ with File(fname, mode="w") as f:
f.create_dataset(name="Manufacturer", data=manufacturer)
assert (
- _plugin_from_manufacturer(temp_file_path, plugins=h5ebsd_plugin_list)
+ _plugin_from_manufacturer(fname, plugins=h5ebsd_plugin_list)
is expected_plugin
)
- def test_overwrite_or_not(self, crystal_map, temp_file_path):
- save(temp_file_path, crystal_map)
+ def test_overwrite_or_not(self, crystal_map, tmp_path):
+ fname = tmp_path / "test.h5"
+ save(fname, crystal_map)
with pytest.warns(UserWarning, match="Not overwriting, since your terminal "):
- _overwrite_or_not(temp_file_path)
+ _overwrite_or_not(fname)
@pytest.mark.parametrize(
"answer, expected", [("y", True), ("n", False), ("m", None)]
)
- def test_overwrite_or_not_input(
- self, crystal_map, temp_file_path, answer, expected
- ):
- save(temp_file_path, crystal_map)
+ def test_overwrite_or_not_input(self, crystal_map, answer, expected, tmp_path):
+ fname = tmp_path / "test.h5"
+ save(fname, crystal_map)
if answer == "m":
with replace_stdin(StringIO(answer)):
with pytest.raises(EOFError):
- _overwrite_or_not(temp_file_path)
+ _overwrite_or_not(fname)
else:
with replace_stdin(StringIO(answer)):
- assert _overwrite_or_not(temp_file_path) is expected
+ assert _overwrite_or_not(fname) is expected
- @pytest.mark.parametrize("temp_file_path", ["angs", "hdf4", "h6"])
- def test_save_unsupported_raises(self, temp_file_path, crystal_map):
- _, ext = os.path.splitext(temp_file_path)
+ @pytest.mark.parametrize("ext", ["angs", "hdf4", "h6"])
+ def test_save_unsupported_raises(self, ext, crystal_map, tmp_path):
+ fname = tmp_path / f"test.{ext}"
with pytest.raises(IOError, match=f"'{ext}' does not correspond to any "):
- save(temp_file_path, crystal_map)
+ save(fname, crystal_map)
- def test_save_overwrite_raises(self, temp_file_path, crystal_map):
+ def test_save_overwrite_raises(self, crystal_map, tmp_path):
with pytest.raises(ValueError, match="`overwrite` parameter can only be "):
- save(temp_file_path, crystal_map, overwrite=1)
+ save(tmp_path / "test.h5", crystal_map, overwrite=1)
@pytest.mark.parametrize(
"overwrite, expected_phase_name", [(True, "hepp"), (False, "")]
)
def test_save_overwrite(
- self, temp_file_path, crystal_map, overwrite, expected_phase_name
+ self, crystal_map, overwrite, expected_phase_name, tmp_path
):
+ fname = tmp_path / "test.h5"
+
assert crystal_map.phases[0].name == ""
- save(temp_file_path, crystal_map)
- assert os.path.isfile(temp_file_path) is True
+ save(fname, crystal_map)
+ assert os.path.isfile(fname) is True
crystal_map.phases[0].name = "hepp"
- save(temp_file_path, crystal_map, overwrite=overwrite)
+ save(fname, crystal_map, overwrite=overwrite)
- crystal_map2 = load(temp_file_path)
+ crystal_map2 = load(fname)
assert crystal_map2.phases[0].name == expected_phase_name
diff --git a/orix/tests/io/test_orix_hdf5.py b/orix/tests/io/test_orix_hdf5.py
index 11796600..8640788c 100644
--- a/orix/tests/io/test_orix_hdf5.py
+++ b/orix/tests/io/test_orix_hdf5.py
@@ -1,4 +1,5 @@
-# Copyright 2018-2024 the orix developers
+#
+# Copyright 2019-2025 the orix developers
#
# This file is part of orix.
#
@@ -9,11 +10,12 @@
#
# orix is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
-# along with orix. If not, see .
+# along with orix. If not, see .
+#
from diffpy.structure.spacegroups import GetSpaceGroup
from h5py import File
@@ -22,7 +24,7 @@
from orix import __version__ as orix_version
from orix.crystal_map import CrystalMap, Phase
-from orix.io import load, save
+import orix.io as oio
from orix.io.plugins.orix_hdf5 import (
atom2dict,
crystalmap2dict,
@@ -42,10 +44,12 @@
class TestOrixHDF5Plugin:
- def test_file_writer(self, crystal_map, temp_file_path):
- save(filename=temp_file_path, object2write=crystal_map)
+ def test_file_writer(self, crystal_map, tmp_path):
+ fname = tmp_path / "test.h5"
+
+ oio.save(filename=fname, object2write=crystal_map)
- with File(temp_file_path) as f:
+ with File(fname) as f:
assert f["manufacturer"][()][0].decode() == "orix"
assert f["version"][()][0].decode() == orix_version
@@ -57,10 +61,12 @@ def test_file_writer(self, crystal_map, temp_file_path):
],
indirect=["crystal_map_input"],
)
- def test_write_read_masked(self, crystal_map_input, temp_file_path):
+ def test_write_read_masked(self, crystal_map_input, tmp_path):
+ fname = tmp_path / "test.h5"
+
xmap = CrystalMap(**crystal_map_input)
- save(filename=temp_file_path, object2write=xmap[xmap.x > 2])
- xmap2 = load(temp_file_path)
+ oio.save(filename=fname, object2write=xmap[xmap.x > 2])
+ xmap2 = oio.load(fname)
assert xmap2.size != xmap.size
with pytest.raises(ValueError, match="operands could not be broadcast"):
@@ -70,13 +76,14 @@ def test_write_read_masked(self, crystal_map_input, temp_file_path):
assert xmap2.size == xmap.size
assert np.allclose(xmap2.x, xmap.x)
- def test_file_writer_raises(self, temp_file_path, crystal_map):
+ def test_file_writer_raises(self, crystal_map, tmp_path):
+ fname = tmp_path / "test.h5"
with pytest.raises(OSError, match="Cannot write to the already open file "):
- with File(temp_file_path, mode="w") as _:
- save(temp_file_path, crystal_map, overwrite=True)
+ with File(fname, mode="w") as _:
+ oio.save(fname, crystal_map, overwrite=True)
- def test_dict2hdf5group(self, temp_file_path):
- with File(temp_file_path, mode="w") as f:
+ def test_dict2hdf5group(self, tmp_path):
+ with File(tmp_path / "test.h5", mode="w") as f:
group = f.create_group(name="a_group")
with pytest.warns(UserWarning, match="The orix HDF5 writer could not"):
dict2hdf5group(
@@ -141,9 +148,11 @@ def test_structure2dict(self, phase_list):
assert np.allclose(lattice1["baserot"], lattice2["baserot"])
assert_dictionaries_are_equal(structure_dict["atoms"], this_dict["atoms"])
- def test_file_reader(self, crystal_map, temp_file_path):
- save(filename=temp_file_path, object2write=crystal_map)
- xmap2 = load(filename=temp_file_path)
+ def test_file_reader(self, crystal_map, tmp_path):
+ fname = tmp_path / "test.h5"
+
+ oio.save(filename=fname, object2write=crystal_map)
+ xmap2 = oio.load(filename=fname)
assert_dictionaries_are_equal(crystal_map.__dict__, xmap2.__dict__)
def test_dict2crystalmap(self, crystal_map):
@@ -207,7 +216,7 @@ def test_dict2atom(self, phase_list):
assert str(atom.element) == str(atom2.element)
assert np.allclose(atom.xyz, atom2.xyz)
- def test_write_read_nd_crystalmap_properties(self, temp_file_path, crystal_map):
+ def test_write_read_nd_crystalmap_properties(self, crystal_map, tmp_path):
"""Crystal map properties with more than one value in each point
(e.g. top matching scores from dictionary indexing) can be written
and read from file correctly.
@@ -225,8 +234,9 @@ def test_write_read_nd_crystalmap_properties(self, temp_file_path, crystal_map):
prop3d = np.arange(map_size * 4).reshape(prop3d_shape)
xmap.prop[prop3d_name] = prop3d
- save(filename=temp_file_path, object2write=xmap)
- xmap2 = load(temp_file_path)
+ fname = tmp_path / "test.h5"
+ oio.save(filename=fname, object2write=xmap)
+ xmap2 = oio.load(fname)
assert np.allclose(xmap2.prop[prop2d_name], xmap.prop[prop2d_name])
assert np.allclose(xmap2.prop[prop3d_name], xmap.prop[prop3d_name])
diff --git a/orix/vector/miller.py b/orix/vector/miller.py
index 897898ce..2511a95a 100644
--- a/orix/vector/miller.py
+++ b/orix/vector/miller.py
@@ -1,4 +1,5 @@
-# Copyright 2018-2024 the orix developers
+#
+# Copyright 2019-2025 the orix developers
#
# This file is part of orix.
#
@@ -9,11 +10,12 @@
#
# orix is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
-# along with orix. If not, see .
+# along with orix. If not, see .
+#
from __future__ import annotations
@@ -21,12 +23,6 @@
from itertools import product
from typing import TYPE_CHECKING, Optional, Tuple, Union
-try:
- # New in Python 3.11
- from typing import Self
-except ImportError: # pragma: no cover
- from typing_extensions import Self
-
from diffpy.structure import Lattice
import numpy as np
@@ -344,7 +340,7 @@ def is_hexagonal(self) -> bool:
return self.phase.is_hexagonal
@property
- def unit(self) -> Self:
+ def unit(self) -> Miller:
"""Return unit vectors."""
m = self.__class__(xyz=super().unit.data, phase=self.phase)
m.coordinate_format = self.coordinate_format
@@ -363,7 +359,7 @@ def __repr__(self) -> str:
f"{name} {shape}, point group {symmetry}, {coordinate_format}\n" f"{data}"
)
- def __getitem__(self, key) -> Self:
+ def __getitem__(self, key) -> Miller:
"""NumPy fancy indexing of vectors."""
m = self.__class__(xyz=self.data[key], phase=self.phase).deepcopy()
m.coordinate_format = self.coordinate_format
@@ -378,7 +374,7 @@ def from_highest_indices(
uvw: Union[np.ndarray, list, tuple, None] = None,
hkl: Union[np.ndarray, list, tuple, None] = None,
include_zero_vector: bool = False,
- ) -> Self:
+ ) -> Miller:
"""Create a set of unique direct or reciprocal lattice vectors
from three highest indices and a phase (crystal lattice and
symmetry).
@@ -405,7 +401,7 @@ def from_highest_indices(
return cls(**init_kw).unique()
@classmethod
- def from_min_dspacing(cls, phase: "Phase", min_dspacing: float = 0.05) -> Self:
+ def from_min_dspacing(cls, phase: "Phase", min_dspacing: float = 0.05) -> Miller:
"""Create a set of unique reciprocal lattice vectors with a
a direct space interplanar spacing greater than a lower
threshold.
@@ -432,7 +428,7 @@ def random(
phase: "Phase",
shape: Union[int, tuple] = 1,
coordinate_format: str = "xyz",
- ) -> Self:
+ ) -> Miller:
"""Create random Miller indices.
Parameters
@@ -466,11 +462,11 @@ def random(
# --------------------- Other public methods --------------------- #
- def deepcopy(self) -> Self:
+ def deepcopy(self) -> Miller:
"""Return a deepcopy of the instance."""
return deepcopy(self)
- def round(self, max_index: int = 20) -> Self:
+ def round(self, max_index: int = 20) -> Miller:
"""Round a set of index triplet (Miller) or quartet
(Miller-Bravais/Weber) to the *closest* smallest integers.
@@ -498,7 +494,9 @@ def symmetrise(
unique: bool = False,
return_multiplicity: bool = False,
return_index: bool = False,
- ) -> Union[Self, Tuple[Self, np.ndarray], Tuple[Self, np.ndarray, np.ndarray]]:
+ ) -> Union[
+ Miller, Tuple[Miller, np.ndarray], Tuple[Miller, np.ndarray, np.ndarray]
+ ]:
"""Return vectors symmetrically equivalent to the vectors.
Parameters
@@ -585,7 +583,7 @@ def symmetrise(
def angle_with(
self,
- other: Self,
+ other: Miller,
use_symmetry: bool = False,
degrees: bool = False,
) -> np.ndarray:
@@ -630,7 +628,7 @@ def angle_with(
return angles
- def cross(self, other: Self) -> Self:
+ def cross(self, other: Miller) -> Miller:
"""Return the cross products of the vectors with the other
vectors, which is considered the zone axes between the vectors.
@@ -652,7 +650,7 @@ def cross(self, other: Self) -> Self:
m.coordinate_format = new_fmt[self.coordinate_format]
return m
- def dot(self, other: Self) -> np.ndarray:
+ def dot(self, other: Miller) -> np.ndarray:
"""Return the dot products of the vectors and the other vectors.
Parameters
@@ -669,7 +667,7 @@ def dot(self, other: Self) -> np.ndarray:
self._compatible_with(other, raise_error=True)
return super().dot(other)
- def dot_outer(self, other: Self) -> np.ndarray:
+ def dot_outer(self, other: Miller) -> np.ndarray:
"""Return the outer dot products of the vectors and the other
vectors.
@@ -687,7 +685,7 @@ def dot_outer(self, other: Self) -> np.ndarray:
self._compatible_with(other, raise_error=True)
return super().dot_outer(other)
- def flatten(self) -> Self:
+ def flatten(self) -> Miller:
"""Return the flattened vectors.
Returns
@@ -699,7 +697,7 @@ def flatten(self) -> Self:
m.coordinate_format = self.coordinate_format
return m
- def transpose(self, *axes: Optional[int]) -> Self:
+ def transpose(self, *axes: Optional[int]) -> Miller:
"""Return a new instance with the data transposed.
The order may be undefined if :attr:`ndim` is originally 2. In
@@ -725,7 +723,7 @@ def get_nearest(self, *args) -> NotImplemented:
"""NotImplemented."""
return NotImplemented
- def mean(self, use_symmetry: bool = False) -> Self:
+ def mean(self, use_symmetry: bool = False) -> Miller:
"""Return the mean vector of the set of vectors.
Parameters
@@ -745,7 +743,7 @@ def mean(self, use_symmetry: bool = False) -> Self:
m.coordinate_format = self.coordinate_format
return m
- def reshape(self, *shape: Union[int, tuple]) -> Self:
+ def reshape(self, *shape: Union[int, tuple]) -> Miller:
"""Return a new instance with the vectors reshaped.
Parameters
@@ -764,7 +762,7 @@ def reshape(self, *shape: Union[int, tuple]) -> Self:
def unique(
self, use_symmetry: bool = False, return_index: bool = False
- ) -> Union[Self, Tuple[Self, np.ndarray]]:
+ ) -> Union[Miller, Tuple[Miller, np.ndarray]]:
"""Unique vectors in ``self``.
Parameters
@@ -809,7 +807,7 @@ def unique(
else:
return m
- def in_fundamental_sector(self, symmetry: Optional["Symmetry"] = None) -> Self:
+ def in_fundamental_sector(self, symmetry: Optional["Symmetry"] = None) -> Miller:
"""Project Miller indices to a symmetry's fundamental sector
(inverse pole figure).
@@ -856,7 +854,7 @@ def in_fundamental_sector(self, symmetry: Optional["Symmetry"] = None) -> Self:
# -------------------- Other private methods --------------------- #
- def _compatible_with(self, other: Self, raise_error: bool = False) -> bool:
+ def _compatible_with(self, other: Miller, raise_error: bool = False) -> bool:
"""Whether ``self`` and ``other`` are the same (the same crystal
lattice and symmetry) with vectors in the same space.
diff --git a/pyproject.toml b/pyproject.toml
index ec28e9d9..314d5025 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -36,8 +36,6 @@ dependencies = [
"pycifrw",
"scipy",
"tqdm",
- # TODO: Remove once Python >= 3.11
- "typing_extensions",
]
[project.optional-dependencies]
@@ -49,7 +47,8 @@ doc = [
"memory_profiler",
"nbconvert >= 7.16.4",
"nbsphinx >= 0.7",
- "numpydoc",
+ # Restriction due to https://github.com/pyxem/orix/issues/570
+ "numpydoc != 1.9.0",
"pydata-sphinx-theme",
"scikit-image",
"scikit-learn",
@@ -100,12 +99,16 @@ addopts = [
"-ra",
"--ignore=doc/_static/img/colormap_banners/create_colormap_banners.py",
"--ignore=examples/*/*.py",
+ "--strict-markers",
]
doctest_optionflags = "NORMALIZE_WHITESPACE"
filterwarnings = [
"ignore:Deprecated call to `pkg_resources:DeprecationWarning",
"ignore:pkg_resources is deprecated as an API:DeprecationWarning",
]
+markers = [
+ "slow: mark test as slow",
+]
[tool.isort]
profile = "black"