[Git][debian-gis-team/pint-xarray][upstream] New upstream version 0.5.0

Antonio Valentino (@antonio.valentino) gitlab at salsa.debian.org
Sun Aug 10 17:25:44 BST 2025



Antonio Valentino pushed to branch upstream at Debian GIS Project / pint-xarray


Commits:
fbb67a3e by Antonio Valentino at 2025-08-09T08:14:42+00:00
New upstream version 0.5.0
- - - - -


21 changed files:

- − .flake8
- + .github/release.yml
- .github/workflows/ci-additional.yml
- .github/workflows/ci.yml
- .github/workflows/nightly.yml
- .github/workflows/pypi.yaml
- − .pep8speaks.yml
- .pre-commit-config.yaml
- README.md
- docs/examples/plotting.ipynb
- docs/whats-new.rst
- pint_xarray/__init__.py
- pint_xarray/accessors.py
- pint_xarray/conversion.py
- + pint_xarray/index.py
- pint_xarray/testing.py
- pint_xarray/tests/test_accessors.py
- pint_xarray/tests/test_conversion.py
- + pint_xarray/tests/test_index.py
- pint_xarray/tests/utils.py
- pyproject.toml


Changes:

=====================================
.flake8 deleted
=====================================
@@ -1,13 +0,0 @@
-[flake8]
-ignore =
-    # E203: whitespace before ':' - doesn't work well with black
-    # E402: module level import not at top of file
-    # E501: line too long - let black worry about that
-    # E731: do not assign a lambda expression, use a def
-    # W503: line break before binary operator
-    E203, E402, E501, E731, W503
-exclude =
-    .eggs
-    doc
-builtins =
-    ellipsis


=====================================
.github/release.yml
=====================================
@@ -0,0 +1,5 @@
+changelog:
+  exclude:
+    authors:
+      - dependabot
+      - pre-commit-ci


=====================================
.github/workflows/ci-additional.yml
=====================================
@@ -23,32 +23,35 @@ jobs:
       matrix:
         python-version: ["3.12"]
 
+    env:
+      FORCE_COLOR: 3
+
     steps:
-    - name: checkout
-      uses: actions/checkout at v4
-    - name: setup python
-      uses: actions/setup-python at v5
-      with:
-        python-version: ${{ matrix.python-version }}
-    - name: initialize cache
-      uses: actions/cache at v4
-      with:
-        path: ~/.cache/pip
-        key: ${{ runner.os }}-pip-py${{ matrix.python-version }}-${{ hashFiles('ci/requirements/**.txt') }}
-        restore-keys: |
-          ${{ runner.os }}-pip-py${{ matrix.python-version }}-
-    - name: upgrade pip
-      run: |
-        python -m pip install --upgrade pip setuptools wheel
-    - name: install dependencies
-      run: |
-        python -m pip install -r ci/requirements.txt
-    - name: install pint-xarray
-      run: |
-        python -m pip install .
-    - name: show versions
-      run: |
-        python -c 'import pint_xarray'
-    - name: run doctests
-      run: |
-        python -m pytest --doctest-modules pint_xarray --ignore pint_xarray/tests
+      - name: checkout
+        uses: actions/checkout at v4
+      - name: setup python
+        uses: actions/setup-python at v5
+        with:
+          python-version: ${{ matrix.python-version }}
+      - name: initialize cache
+        uses: actions/cache at v4
+        with:
+          path: ~/.cache/pip
+          key: ${{ runner.os }}-pip-py${{ matrix.python-version }}-${{ hashFiles('ci/requirements/**.txt') }}
+          restore-keys: |
+            ${{ runner.os }}-pip-py${{ matrix.python-version }}-
+      - name: upgrade pip
+        run: |
+          python -m pip install --upgrade pip setuptools wheel
+      - name: install dependencies
+        run: |
+          python -m pip install -r ci/requirements.txt
+      - name: install pint-xarray
+        run: |
+          python -m pip install .
+      - name: show versions
+        run: |
+          python -c 'import pint_xarray'
+      - name: run doctests
+        run: |
+          python -m pytest --doctest-modules pint_xarray --ignore pint_xarray/tests


=====================================
.github/workflows/ci.yml
=====================================
@@ -3,9 +3,9 @@ name: CI
 
 on:
   push:
-    branches: [ main ]
+    branches: [main]
   pull_request:
-    branches: [ main ]
+    branches: [main]
   workflow_dispatch:
 
 concurrency:
@@ -20,13 +20,13 @@ jobs:
     outputs:
       triggered: ${{ steps.detect-trigger.outputs.trigger-found }}
     steps:
-    - uses: actions/checkout at v4
-      with:
-        fetch-depth: 2
-    - uses: xarray-contrib/ci-trigger at v1
-      id: detect-trigger
-      with:
-        keyword: "[skip-ci]"
+      - uses: actions/checkout at v4
+        with:
+          fetch-depth: 2
+      - uses: xarray-contrib/ci-trigger at v1
+        id: detect-trigger
+        with:
+          keyword: "[skip-ci]"
 
   ci:
     name: ${{ matrix.os }} py${{ matrix.python-version }}
@@ -36,6 +36,9 @@ jobs:
       run:
         shell: bash -l {0}
 
+    env:
+      FORCE_COLOR: 3
+
     if: |
       always()
       && github.repository == 'xarray-contrib/pint-xarray'
@@ -47,61 +50,57 @@ jobs:
     strategy:
       fail-fast: false
       matrix:
-        python-version: ["3.9", "3.10", "3.11", "3.12"]
+        python-version: ["3.10", "3.11", "3.12"]
         os: ["ubuntu-latest", "macos-latest", "windows-latest"]
 
     steps:
-    - name: checkout the repository
-      uses: actions/checkout at v4
-      with:
-        # need to fetch all tags to get a correct version
-        fetch-depth: 0  # fetch all branches and tags
-
-    - name: cache pip
-      uses: actions/cache at v4
-      with:
-        path: ~/.cache/pip
-        key: pip-py${{ matrix.python-version }}
-        restore-keys: |
-          pip-
-
-    - name: setup python
-      uses: actions/setup-python at v5
-      with:
-        python-version: ${{ matrix.python-version }}
-
-    - name: upgrade pip
-      run: python -m pip install --upgrade pip setuptools wheel
-
-    - name: install dependencies
-      run: |
-        python -m pip install -r ci/requirements.txt
-        if [[ "${{matrix.python-version}}" == "3.9" ]]; then
-            # remove after the release
-            python -m pip install 'numpy<2.0'
-        fi
-
-    - name: install pint-xarray
-      run: python -m pip install --no-deps .
-
-    - name: show versions
-      run: python -m pip list
-
-    - name: import pint-xarray
-      run: |
-        python -c 'import pint_xarray'
-
-    - name: run tests
-      if: success()
-      id: status
-      run: |
-        python -m pytest --cov=pint_xarray --cov-report=xml
-
-    - name: Upload code coverage to Codecov
-      uses: codecov/codecov-action at v4.5.0
-      with:
-        file: ./coverage.xml
-        flags: unittests
-        env_vars: RUNNER_OS,PYTHON_VERSION
-        name: codecov-umbrella
-        fail_ci_if_error: false
+      - name: checkout the repository
+        uses: actions/checkout at v4
+        with:
+          # need to fetch all tags to get a correct version
+          fetch-depth: 0 # fetch all branches and tags
+
+      - name: cache pip
+        uses: actions/cache at v4
+        with:
+          path: ~/.cache/pip
+          key: pip-py${{ matrix.python-version }}
+          restore-keys: |
+            pip-
+
+      - name: setup python
+        uses: actions/setup-python at v5
+        with:
+          python-version: ${{ matrix.python-version }}
+
+      - name: upgrade pip
+        run: python -m pip install --upgrade pip setuptools wheel
+
+      - name: install dependencies
+        run: |
+          python -m pip install -r ci/requirements.txt
+
+      - name: install pint-xarray
+        run: python -m pip install --no-deps .
+
+      - name: show versions
+        run: python -m pip list
+
+      - name: import pint-xarray
+        run: |
+          python -c 'import pint_xarray'
+
+      - name: run tests
+        if: success()
+        id: status
+        run: |
+          python -m pytest --cov=pint_xarray --cov-report=xml
+
+      - name: Upload code coverage to Codecov
+        uses: codecov/codecov-action at v5.4.3
+        with:
+          file: ./coverage.xml
+          flags: unittests
+          env_vars: RUNNER_OS,PYTHON_VERSION
+          name: codecov-umbrella
+          fail_ci_if_error: false


=====================================
.github/workflows/nightly.yml
=====================================
@@ -3,11 +3,11 @@ name: Nightly CI
 
 on:
   push:
-    branches: [ main ]
+    branches: [main]
   pull_request:
-    branches: [ main ]
+    branches: [main]
   schedule:
-    - cron: "0 0 * * *"  # Daily "At 00:00" UTC
+    - cron: "0 0 * * *" # Daily "At 00:00" UTC
 
   workflow_dispatch:
 
@@ -25,13 +25,13 @@ jobs:
     outputs:
       triggered: ${{ steps.detect-trigger.outputs.trigger-found }}
     steps:
-    - uses: actions/checkout at v4
-      with:
-        fetch-depth: 2
-    - uses: xarray-contrib/ci-trigger at v1.2
-      id: detect-trigger
-      with:
-        keyword: "[test-upstream]"
+      - uses: actions/checkout at v4
+        with:
+          fetch-depth: 2
+      - uses: xarray-contrib/ci-trigger at v1.2
+        id: detect-trigger
+        with:
+          keyword: "[test-upstream]"
 
   upstream-dev:
     name: upstream-dev
@@ -58,46 +58,49 @@ jobs:
     outputs:
       artifacts_availability: ${{ steps.status.outputs.ARTIFACTS_AVAILABLE }}
 
+    env:
+      FORCE_COLOR: 3
+
     steps:
-    - name: checkout the repository
-      uses: actions/checkout at v4
-      with:
-        # need to fetch all tags to get a correct version
-        fetch-depth: 0  # fetch all branches and tags
-
-    - name: setup python
-      uses: actions/setup-python at v5
-      with:
-        python-version: ${{ matrix.python-version }}
-
-    - name: upgrade pip
-      run: python -m pip install --upgrade pip
-
-    - name: install dependencies
-      run: |
-        python -m pip install -r ci/requirements.txt
-        python -m pip install pytest-reportlog
-
-    - name: install upstream-dev dependencies
-      run: bash ci/install-upstream-dev.sh
-
-    - name: install pint-xarray
-      run: python -m pip install .
-
-    - name: show versions
-      run: python -m pip list
-
-    - name: run tests
-      if: success()
-      id: status
-      run: |
-        python -m pytest -rf --report-log=pytest-log.jsonl
-
-    - name: report failures
-      if: |
-        failure()
-        && steps.tests.outcome == 'failure'
-        && github.event_name == 'schedule'
-      uses: xarray-contrib/issue-from-pytest-log at v1
-      with:
-        log-path: pytest-log.jsonl
+      - name: checkout the repository
+        uses: actions/checkout at v4
+        with:
+          # need to fetch all tags to get a correct version
+          fetch-depth: 0 # fetch all branches and tags
+
+      - name: setup python
+        uses: actions/setup-python at v5
+        with:
+          python-version: ${{ matrix.python-version }}
+
+      - name: upgrade pip
+        run: python -m pip install --upgrade pip
+
+      - name: install dependencies
+        run: |
+          python -m pip install -r ci/requirements.txt
+          python -m pip install pytest-reportlog
+
+      - name: install upstream-dev dependencies
+        run: bash ci/install-upstream-dev.sh
+
+      - name: install pint-xarray
+        run: python -m pip install .
+
+      - name: show versions
+        run: python -m pip list
+
+      - name: run tests
+        if: success()
+        id: status
+        run: |
+          python -m pytest -rf --report-log=pytest-log.jsonl
+
+      - name: report failures
+        if: |
+          failure()
+          && steps.tests.outcome == 'failure'
+          && github.event_name == 'schedule'
+        uses: xarray-contrib/issue-from-pytest-log at v1
+        with:
+          log-path: pytest-log.jsonl


=====================================
.github/workflows/pypi.yaml
=====================================
@@ -62,6 +62,6 @@ jobs:
           name: releases
           path: dist
       - name: Publish package to PyPI
-        uses: pypa/gh-action-pypi-publish at ec4db0b4ddc65acdf4bff5fa45ac92d78b56bdf0
+        uses: pypa/gh-action-pypi-publish at 76f52bc884231f62b9a034ebfe128415bbaabdfc
         with:
           verbose: true


=====================================
.pep8speaks.yml deleted
=====================================
@@ -1,3 +0,0 @@
-scanner:
-    diff_only: False
-    linter: flake8


=====================================
.pre-commit-config.yaml
=====================================
@@ -4,34 +4,45 @@ ci:
 # https://pre-commit.com/
 repos:
   - repo: https://github.com/pre-commit/pre-commit-hooks
-    rev: v4.6.0
+    rev: v5.0.0
     hooks:
       - id: trailing-whitespace
       - id: end-of-file-fixer
       - id: check-docstring-first
-      - id: check-yaml
-  # isort should run before black as black sometimes tweaks the isort output
-  - repo: https://github.com/pycqa/isort
-    rev: 5.13.2
+  - repo: https://github.com/rbubley/mirrors-prettier
+    rev: v3.5.3
     hooks:
-      - id: isort
+      - id: prettier
+        args: ["--cache-location=.prettier_cache/cache"]
+  - repo: https://github.com/ComPWA/taplo-pre-commit
+    rev: v0.9.3
+    hooks:
+      - id: taplo-format
+        args: [--option, array_auto_collapse=false]
+      - id: taplo-lint
+        args: [--no-schema]
+  - repo: https://github.com/abravalheri/validate-pyproject
+    rev: v0.24.1
+    hooks:
+      - id: validate-pyproject
   # https://github.com/python/black#version-control-integration
-  - repo: https://github.com/psf/black
-    rev: 24.4.2
+  - repo: https://github.com/psf/black-pre-commit-mirror
+    rev: 25.1.0
     hooks:
       - id: black-jupyter
   - repo: https://github.com/keewis/blackdoc
     rev: v0.3.9
     hooks:
       - id: blackdoc
-        additional_dependencies: ["black==24.4.2"]
+        additional_dependencies: ["black==25.1.0"]
       - id: blackdoc-autoupdate-black
-  - repo: https://github.com/pycqa/flake8
-    rev: 7.1.0
+  - repo: https://github.com/astral-sh/ruff-pre-commit
+    rev: v0.11.13
     hooks:
-      - id: flake8
+      - id: ruff
+        args: [--fix]
   - repo: https://github.com/kynan/nbstripout
-    rev: 0.7.1
+    rev: 0.8.1
     hooks:
       - id: nbstripout
         args: [--extra-keys=metadata.kernelspec metadata.language_info.version]


=====================================
README.md
=====================================
@@ -13,6 +13,7 @@ A convenience wrapper for using [pint](https://pint.readthedocs.io) with
 ## Usage
 
 To convert the variables of a `Dataset` to quantities:
+
 ```python
 In [1]: import pint_xarray
    ...: import xarray as xr
@@ -37,7 +38,9 @@ Data variables:
     a        (x) int64 [s] 0 1 2
     b        (y) int64 [m] -3 5 1
 ```
+
 to convert to different units:
+
 ```python
 In [4]: c = q.pint.to({"a": "ms", "b": "km"})
    ...: c
@@ -49,7 +52,9 @@ Data variables:
     a        (x) float64 [ms] 0.0 1e+03 2e+03
     b        (y) float64 [km] -0.003 0.005 0.001
 ```
+
 to convert back to non-quantities:
+
 ```python
 In [5]: d = c.pint.dequantify()
    ...: d


=====================================
docs/examples/plotting.ipynb
=====================================
@@ -15,11 +15,11 @@
    "metadata": {},
    "outputs": [],
    "source": [
+    "# to be able to read unit attributes following the CF conventions\n",
+    "import cf_xarray.units  # noqa: F401  # must be imported before pint_xarray\n",
     "import xarray as xr\n",
     "\n",
-    "# to be able to read unit attributes following the CF conventions\n",
-    "import cf_xarray.units  # must be imported before pint_xarray\n",
-    "import pint_xarray\n",
+    "import pint_xarray  # noqa: F401\n",
     "from pint_xarray import unit_registry as ureg\n",
     "\n",
     "xr.set_options(display_expand_data=False)"
@@ -108,7 +108,7 @@
    "metadata": {},
    "outputs": [],
    "source": [
-    "monthly_means.pint.sel(\n",
+    "monthly_means.sel(\n",
     "    lat=ureg.Quantity(4350, \"angular_minute\"),\n",
     "    lon=ureg.Quantity(12000, \"angular_minute\"),\n",
     ")"


=====================================
docs/whats-new.rst
=====================================
@@ -2,6 +2,16 @@
 
 What's new
 ==========
+0.5 (09 Jun 2025)
+------------------
+- drop support for python 3.9 (:pull:`266`)
+  By `Justus Magin <https://github.com/keewis>`_.
+- create a `PintIndex` to allow units on indexed coordinates (:pull:`163`, :issue:`162`)
+  By `Justus Magin <https://github.com/keewis>`_ and `Benoit Bovy <https://github.com/benbovy>`_.
+- fix :py:meth:`Dataset.pint.interp` and :py:meth:`DataArray.pint.interp` bug
+  failing to pass through arguments (:pull:`270`, :issue:`267`)
+  By `Martijn van der Marel <https://github.com/martijnvandermarel>`_
+
 0.4 (23 Jun 2024)
 -----------------
 - adopt `SPEC0 <https://scientific-python.org/specs/spec-0000>`_ (:pull:`228`)


=====================================
pint_xarray/__init__.py
=====================================
@@ -2,9 +2,10 @@ from importlib.metadata import version
 
 import pint
 
-from . import accessors, formatting, testing  # noqa: F401
-from .accessors import default_registry as unit_registry
-from .accessors import setup_registry
+from pint_xarray import accessors, formatting, testing  # noqa: F401
+from pint_xarray.accessors import default_registry as unit_registry
+from pint_xarray.accessors import setup_registry
+from pint_xarray.index import PintIndex
 
 try:
     __version__ = version("pint-xarray")
@@ -21,4 +22,5 @@ __all__ = [
     "testing",
     "unit_registry",
     "setup_registry",
+    "PintIndex",
 ]


=====================================
pint_xarray/accessors.py
=====================================
@@ -6,9 +6,9 @@ from pint import Unit
 from xarray import register_dataarray_accessor, register_dataset_accessor
 from xarray.core.dtypes import NA
 
-from . import conversion
-from .conversion import no_unit_values
-from .errors import format_error_message
+from pint_xarray import conversion
+from pint_xarray.conversion import no_unit_values
+from pint_xarray.errors import format_error_message
 
 _default = object()
 
@@ -152,10 +152,9 @@ class DatasetLocIndexer:
             raise NotImplementedError("pandas-style indexing is not supported, yet")
 
         dims = self.ds.dims
+        indexer_units = conversion.extract_indexer_units(indexers)
         indexer_units = {
-            name: conversion.extract_indexer_units(indexer)
-            for name, indexer in indexers.items()
-            if name in dims
+            name: indexer for name, indexer in indexer_units.items() if name in dims
         }
 
         # convert the indexes to the indexer's units
@@ -165,11 +164,13 @@ class DatasetLocIndexer:
             raise KeyError(*e.args) from e
 
         # index
-        stripped_indexers = {
-            name: conversion.strip_indexer_units(indexer)
-            for name, indexer in indexers.items()
-        }
-        return converted.loc[stripped_indexers]
+        stripped_indexers = conversion.strip_indexer_units(indexers)
+
+        stripped = conversion.strip_units(converted)
+        converted_units = conversion.extract_units(converted)
+        indexed = stripped.loc[stripped_indexers]
+
+        return conversion.attach_units(indexed, converted_units)
 
 
 class DataArrayLocIndexer:
@@ -183,10 +184,9 @@ class DataArrayLocIndexer:
             raise NotImplementedError("pandas-style indexing is not supported, yet")
 
         dims = self.da.dims
+        indexer_units = conversion.extract_indexer_units(indexers)
         indexer_units = {
-            name: conversion.extract_indexer_units(indexer)
-            for name, indexer in indexers.items()
-            if name in dims
+            name: indexer for name, indexer in indexer_units.items() if name in dims
         }
 
         # convert the indexes to the indexer's units
@@ -196,11 +196,13 @@ class DataArrayLocIndexer:
             raise KeyError(*e.args) from e
 
         # index
-        stripped_indexers = {
-            name: conversion.strip_indexer_units(indexer)
-            for name, indexer in indexers.items()
-        }
-        return converted.loc[stripped_indexers]
+        stripped_indexers = conversion.strip_indexer_units(indexers)
+
+        stripped = conversion.strip_units(converted)
+        converted_units = conversion.extract_units(converted)
+        indexed = stripped.loc[stripped_indexers]
+
+        return conversion.attach_units(indexed, converted_units)
 
     def __setitem__(self, indexers, values):
         if not is_dict_like(indexers):
@@ -219,10 +221,7 @@ class DataArrayLocIndexer:
             raise KeyError(*e.args) from e
 
         # index
-        stripped_indexers = {
-            name: conversion.strip_indexer_units(indexer)
-            for name, indexer in converted.items()
-        }
+        stripped_indexers = conversion.strip_indexer_units(converted)
         self.da.loc[stripped_indexers] = values
 
 
@@ -252,12 +251,6 @@ class PintDataArrayAccessor:
             the data into memory. To avoid that, consider converting
             to ``dask`` first (e.g. using ``chunk``).
 
-        .. warning::
-
-            As units in dimension coordinates are not supported until
-            ``xarray`` changes the way it implements indexes, these
-            units will be set as attributes.
-
         .. note::
             Also note that datetime units (i.e. ones that match
             ``{units} since {date}``) in unit attributes will be
@@ -648,10 +641,9 @@ class PintDataArrayAccessor:
         indexers = either_dict_or_kwargs(indexers, indexers_kwargs, "reindex")
 
         dims = self.da.dims
+        indexer_units = conversion.extract_indexer_units(indexers)
         indexer_units = {
-            name: conversion.extract_indexer_units(indexer)
-            for name, indexer in indexers.items()
-            if name in dims
+            name: indexer for name, indexer in indexer_units.items() if name in dims
         }
 
         # TODO: handle tolerance
@@ -659,20 +651,19 @@ class PintDataArrayAccessor:
 
         # convert the indexes to the indexer's units
         converted = conversion.convert_units(self.da, indexer_units)
+        converted_units = conversion.extract_units(converted)
+        stripped = conversion.strip_units(converted)
 
         # index
-        stripped_indexers = {
-            name: conversion.strip_indexer_units(indexer)
-            for name, indexer in indexers.items()
-        }
-        indexed = converted.reindex(
+        stripped_indexers = conversion.strip_indexer_units(indexers)
+        indexed = stripped.reindex(
             stripped_indexers,
             method=method,
             tolerance=tolerance,
             copy=copy,
             fill_value=fill_value,
         )
-        return indexed
+        return conversion.attach_units(indexed, converted_units)
 
     def reindex_like(
         self, other, method=None, tolerance=None, copy=True, fill_value=NA
@@ -692,19 +683,24 @@ class PintDataArrayAccessor:
         xarray.DataArray.pint.reindex
         xarray.DataArray.reindex_like
         """
-        indexer_units = conversion.extract_unit_attributes(other)
+        indexer_units = conversion.extract_units(other)
+
+        converted = conversion.convert_units(self.da, indexer_units)
+        units = conversion.extract_units(converted)
+        stripped = conversion.strip_units(converted)
+        stripped_other = conversion.strip_units(other)
 
         # TODO: handle tolerance
         # TODO: handle fill_value
 
-        converted = conversion.convert_units(self.da, indexer_units)
-        return converted.reindex_like(
-            other,
+        reindexed = stripped.reindex_like(
+            stripped_other,
             method=method,
             tolerance=tolerance,
             copy=copy,
             fill_value=fill_value,
         )
+        return conversion.attach_units(reindexed, units)
 
     def interp(
         self,
@@ -720,7 +716,8 @@ class PintDataArrayAccessor:
         converted to the units of the indexers first.
 
         .. note::
-            ``kwargs`` is passed unmodified to ``DataArray.interp``
+            ``method``, ``assume_sorted`` and ``kwargs`` are passed unmodified to
+            ``DataArray.interp``.
 
         See Also
         --------
@@ -731,10 +728,9 @@ class PintDataArrayAccessor:
         indexers = either_dict_or_kwargs(coords, coords_kwargs, "interp")
 
         dims = self.da.dims
+        indexer_units = conversion.extract_indexer_units(indexers)
         indexer_units = {
-            name: conversion.extract_indexer_units(indexer)
-            for name, indexer in indexers.items()
-            if name in dims
+            name: indexer for name, indexer in indexer_units.items() if name in dims
         }
 
         # convert the indexes to the indexer's units
@@ -743,15 +739,12 @@ class PintDataArrayAccessor:
         stripped = conversion.strip_units(converted)
 
         # index
-        stripped_indexers = {
-            name: conversion.strip_indexer_units(indexer)
-            for name, indexer in indexers.items()
-        }
+        stripped_indexers = conversion.strip_indexer_units(indexers)
         interpolated = stripped.interp(
             stripped_indexers,
             method=method,
-            assume_sorted=False,
-            kwargs=None,
+            assume_sorted=assume_sorted,
+            kwargs=kwargs,
         )
         return conversion.attach_units(interpolated, units)
 
@@ -762,7 +755,8 @@ class PintDataArrayAccessor:
         to the units of the indexers first.
 
         .. note::
-            ``kwargs`` is passed unmodified to ``DataArray.interp``
+            ``method``, ``assume_sorted`` and ``kwargs`` are passed unmodified to
+            ``DataArray.interp``.
 
         See Also
         --------
@@ -770,13 +764,14 @@ class PintDataArrayAccessor:
         xarray.DataArray.pint.interp
         xarray.DataArray.interp_like
         """
-        indexer_units = conversion.extract_unit_attributes(other)
+        indexer_units = conversion.extract_units(other)
 
         converted = conversion.convert_units(self.da, indexer_units)
         units = conversion.extract_units(converted)
         stripped = conversion.strip_units(converted)
+        stripped_other = conversion.strip_units(other)
         interpolated = stripped.interp_like(
-            other,
+            stripped_other,
             method=method,
             assume_sorted=assume_sorted,
             kwargs=kwargs,
@@ -804,10 +799,9 @@ class PintDataArrayAccessor:
         indexers = either_dict_or_kwargs(indexers, indexers_kwargs, "sel")
 
         dims = self.da.dims
+        indexer_units = conversion.extract_indexer_units(indexers)
         indexer_units = {
-            name: conversion.extract_indexer_units(indexer)
-            for name, indexer in indexers.items()
-            if name in dims
+            name: indexer for name, indexer in indexer_units.items() if name in dims
         }
 
         # TODO: handle tolerance
@@ -819,18 +813,18 @@ class PintDataArrayAccessor:
             raise KeyError(*e.args) from e
 
         # index
-        stripped_indexers = {
-            name: conversion.strip_indexer_units(indexer)
-            for name, indexer in indexers.items()
-        }
-        indexed = converted.sel(
+        stripped_indexers = conversion.strip_indexer_units(indexers)
+
+        stripped = conversion.strip_units(converted)
+        converted_units = conversion.extract_units(converted)
+        indexed = stripped.sel(
             stripped_indexers,
             method=method,
             tolerance=tolerance,
             drop=drop,
         )
 
-        return indexed
+        return conversion.attach_units(indexed, converted_units)
 
     @property
     def loc(self):
@@ -872,10 +866,7 @@ class PintDataArrayAccessor:
             raise KeyError(*e.args) from e
 
         # index
-        stripped_indexers = {
-            name: conversion.strip_indexer_units(indexer)
-            for name, indexer in converted_indexers.items()
-        }
+        stripped_indexers = conversion.strip_indexer_units(converted_indexers)
         indexed = self.da.drop_sel(
             stripped_indexers,
             errors=errors,
@@ -984,12 +975,6 @@ class PintDatasetAccessor:
             the data into memory. To avoid that, consider converting
             to ``dask`` first (e.g. using ``chunk``).
 
-        .. warning::
-
-            As units in dimension coordinates are not supported until
-            ``xarray`` changes the way it implements indexes, these
-            units will be set as attributes.
-
         .. note::
             Also note that datetime units (i.e. ones that match
             ``{units} since {date}``) in unit attributes will be
@@ -1425,10 +1410,9 @@ class PintDatasetAccessor:
         indexers = either_dict_or_kwargs(indexers, indexers_kwargs, "reindex")
 
         dims = self.ds.dims
+        indexer_units = conversion.extract_indexer_units(indexers)
         indexer_units = {
-            name: conversion.extract_indexer_units(indexer)
-            for name, indexer in indexers.items()
-            if name in dims
+            name: indexer for name, indexer in indexer_units.items() if name in dims
         }
 
         # TODO: handle tolerance
@@ -1436,20 +1420,19 @@ class PintDatasetAccessor:
 
         # convert the indexes to the indexer's units
         converted = conversion.convert_units(self.ds, indexer_units)
+        converted_units = conversion.extract_units(converted)
+        stripped = conversion.strip_units(converted)
 
         # index
-        stripped_indexers = {
-            name: conversion.strip_indexer_units(indexer)
-            for name, indexer in indexers.items()
-        }
-        indexed = converted.reindex(
+        stripped_indexers = conversion.strip_indexer_units(indexers)
+        indexed = stripped.reindex(
             stripped_indexers,
             method=method,
             tolerance=tolerance,
             copy=copy,
             fill_value=fill_value,
         )
-        return indexed
+        return conversion.attach_units(indexed, converted_units)
 
     def reindex_like(
         self, other, method=None, tolerance=None, copy=True, fill_value=NA
@@ -1469,19 +1452,24 @@ class PintDatasetAccessor:
         xarray.Dataset.pint.reindex
         xarray.Dataset.reindex_like
         """
-        indexer_units = conversion.extract_unit_attributes(other)
+        indexer_units = conversion.extract_units(other)
+
+        converted = conversion.convert_units(self.ds, indexer_units)
+        units = conversion.extract_units(converted)
+        stripped = conversion.strip_units(converted)
+        stripped_other = conversion.strip_units(other)
 
         # TODO: handle tolerance
         # TODO: handle fill_value
 
-        converted = conversion.convert_units(self.ds, indexer_units)
-        return converted.reindex_like(
-            other,
+        reindexed = stripped.reindex_like(
+            stripped_other,
             method=method,
             tolerance=tolerance,
             copy=copy,
             fill_value=fill_value,
         )
+        return conversion.attach_units(reindexed, units)
 
     def interp(
         self,
@@ -1497,7 +1485,8 @@ class PintDatasetAccessor:
         to the units of the indexers first.
 
         .. note::
-            ``kwargs`` is passed unmodified to ``Dataset.interp``
+            ``method``, ``assume_sorted`` and ``kwargs`` are passed unmodified to
+            ``DataArray.interp``.
 
         See Also
         --------
@@ -1508,10 +1497,9 @@ class PintDatasetAccessor:
         indexers = either_dict_or_kwargs(coords, coords_kwargs, "interp")
 
         dims = self.ds.dims
+        indexer_units = conversion.extract_indexer_units(indexers)
         indexer_units = {
-            name: conversion.extract_indexer_units(indexer)
-            for name, indexer in indexers.items()
-            if name in dims
+            name: indexer for name, indexer in indexer_units.items() if name in dims
         }
 
         # convert the indexes to the indexer's units
@@ -1520,15 +1508,12 @@ class PintDatasetAccessor:
         stripped = conversion.strip_units(converted)
 
         # index
-        stripped_indexers = {
-            name: conversion.strip_indexer_units(indexer)
-            for name, indexer in indexers.items()
-        }
+        stripped_indexers = conversion.strip_indexer_units(indexers)
         interpolated = stripped.interp(
             stripped_indexers,
             method=method,
-            assume_sorted=False,
-            kwargs=None,
+            assume_sorted=assume_sorted,
+            kwargs=kwargs,
         )
         return conversion.attach_units(interpolated, units)
 
@@ -1539,7 +1524,8 @@ class PintDatasetAccessor:
         converted to the units of the indexers first.
 
         .. note::
-            ``kwargs`` is passed unmodified to ``Dataset.interp``
+            ``method``, ``assume_sorted`` and ``kwargs`` are passed unmodified to
+            ``DataArray.interp``.
 
         See Also
         --------
@@ -1547,13 +1533,14 @@ class PintDatasetAccessor:
         xarray.Dataset.pint.interp
         xarray.Dataset.interp_like
         """
-        indexer_units = conversion.extract_unit_attributes(other)
+        indexer_units = conversion.extract_units(other)
 
         converted = conversion.convert_units(self.ds, indexer_units)
         units = conversion.extract_units(converted)
         stripped = conversion.strip_units(converted)
+        stripped_other = conversion.strip_units(other)
         interpolated = stripped.interp_like(
-            other,
+            stripped_other,
             method=method,
             assume_sorted=assume_sorted,
             kwargs=kwargs,
@@ -1581,10 +1568,9 @@ class PintDatasetAccessor:
         indexers = either_dict_or_kwargs(indexers, indexers_kwargs, "sel")
 
         dims = self.ds.dims
+        indexer_units = conversion.extract_indexer_units(indexers)
         indexer_units = {
-            name: conversion.extract_indexer_units(indexer)
-            for name, indexer in indexers.items()
-            if name in dims
+            name: indexer for name, indexer in indexer_units.items() if name in dims
         }
 
         # TODO: handle tolerance
@@ -1596,18 +1582,18 @@ class PintDatasetAccessor:
             raise KeyError(*e.args) from e
 
         # index
-        stripped_indexers = {
-            name: conversion.strip_indexer_units(indexer)
-            for name, indexer in indexers.items()
-        }
-        indexed = converted.sel(
+        stripped_indexers = conversion.strip_indexer_units(indexers)
+
+        stripped = conversion.strip_units(converted)
+        converted_units = conversion.extract_units(converted)
+        indexed = stripped.sel(
             stripped_indexers,
             method=method,
             tolerance=tolerance,
             drop=drop,
         )
 
-        return indexed
+        return conversion.attach_units(indexed, converted_units)
 
     @property
     def loc(self):
@@ -1651,10 +1637,7 @@ class PintDatasetAccessor:
             raise KeyError(*e.args) from e
 
         # index
-        stripped_indexers = {
-            name: conversion.strip_indexer_units(indexer)
-            for name, indexer in converted_indexers.items()
-        }
+        stripped_indexers = conversion.strip_indexer_units(converted_indexers)
         indexed = self.ds.drop_sel(
             stripped_indexers,
             errors=errors,


=====================================
pint_xarray/conversion.py
=====================================
@@ -2,10 +2,11 @@ import itertools
 import re
 
 import pint
-from xarray import DataArray, Dataset, IndexVariable, Variable
+from xarray import Coordinates, DataArray, Dataset, IndexVariable, Variable
 
-from .compat import call_on_dataset
-from .errors import format_error_message
+from pint_xarray.compat import call_on_dataset
+from pint_xarray.errors import format_error_message
+from pint_xarray.index import PintIndex
 
 no_unit_values = ("none", None)
 unit_attribute_name = "units"
@@ -121,17 +122,38 @@ def attach_units_variable(variable, units):
     return new_obj
 
 
-def dataset_from_variables(variables, coords, attrs):
-    data_vars = {name: var for name, var in variables.items() if name not in coords}
-    coords = {name: var for name, var in variables.items() if name in coords}
+def dataset_from_variables(variables, coordinate_names, indexes, attrs):
+    data_vars = {
+        name: var for name, var in variables.items() if name not in coordinate_names
+    }
+    coords = {name: var for name, var in variables.items() if name in coordinate_names}
+
+    new_coords = Coordinates(coords, indexes=indexes)
+    return Dataset(data_vars=data_vars, coords=new_coords, attrs=attrs)
+
+
+def attach_units_index(index, index_vars, units):
+    if all(unit is None for unit in units.values()):
+        # skip non-quantity indexed variables
+        return index
+
+    if isinstance(index, PintIndex) and index.units != units:
+        raise ValueError(
+            f"cannot attach units to quantified index: {index.units} != {units}"
+        )
 
-    return Dataset(data_vars=data_vars, coords=coords, attrs=attrs)
+    return PintIndex(index=index, units=units)
 
 
 def attach_units_dataset(obj, units):
     attached = {}
     rejected_vars = {}
+
+    indexed_variables = obj.xindexes.variables
     for name, var in obj.variables.items():
+        if name in indexed_variables:
+            continue
+
         unit = units.get(name)
         try:
             converted = attach_units_variable(var, unit)
@@ -139,10 +161,23 @@ def attach_units_dataset(obj, units):
         except ValueError as e:
             rejected_vars[name] = (unit, e)
 
+    indexes, index_vars = obj.xindexes.copy_indexes()
+    for idx, idx_vars in obj.xindexes.group_by_index():
+        idx_units = {name: units.get(name) for name in idx_vars.keys()}
+        try:
+            attached_idx = attach_units_index(idx, idx_vars, idx_units)
+            indexes.update({k: attached_idx for k in idx_vars})
+            index_vars.update(attached_idx.create_variables(idx_vars))
+        except ValueError as e:
+            rejected_vars[name] = (units, e)
+
+    attached.update(index_vars)
+
     if rejected_vars:
         raise ValueError(rejected_vars)
 
-    return dataset_from_variables(attached, obj._coord_names, obj.attrs)
+    reordered = {name: attached[name] for name in obj.variables.keys()}
+    return dataset_from_variables(reordered, obj._coord_names, indexes, obj.attrs)
 
 
 def attach_units(obj, units):
@@ -215,20 +250,64 @@ def convert_units_variable(variable, units):
     return new_obj
 
 
+def convert_units_index(index, index_vars, units):
+    if not isinstance(index, PintIndex):
+        raise ValueError("cannot convert non-quantified index")
+
+    converted_vars = {}
+    failed = {}
+    for name, var in index_vars.items():
+        unit = units.get(name)
+        try:
+            converted = convert_units_variable(var, unit)
+            converted_vars[name] = strip_units_variable(converted)
+        except (ValueError, pint.errors.PintTypeError) as e:
+            failed[name] = e
+
+    if failed:
+        # raise exception group
+        raise ValueError("failed to convert index variables:", failed)
+
+    # TODO: figure out how to pull out `options`
+    converted_index = index.index.from_variables(converted_vars, options={})
+    return PintIndex(index=converted_index, units=units)
+
+
 def convert_units_dataset(obj, units):
     converted = {}
     failed = {}
+    indexed_variables = obj.xindexes.variables
     for name, var in obj.variables.items():
+        if name in indexed_variables:
+            continue
+
         unit = units.get(name)
         try:
             converted[name] = convert_units_variable(var, unit)
         except (ValueError, pint.errors.PintTypeError) as e:
             failed[name] = e
 
+    indexes, index_vars = obj.xindexes.copy_indexes()
+    for idx, idx_vars in obj.xindexes.group_by_index():
+        idx_units = {name: units.get(name) for name in idx_vars.keys()}
+        if all(unit is None for unit in idx_units.values()):
+            continue
+
+        try:
+            converted_index = convert_units_index(idx, idx_vars, idx_units)
+            indexes.update({k: converted_index for k in idx_vars})
+            index_vars.update(converted_index.create_variables())
+        except (ValueError, pint.errors.PintTypeError) as e:
+            names = tuple(idx_vars)
+            failed[names] = e
+
+    converted.update(index_vars)
+
     if failed:
         raise ValueError(failed)
 
-    return dataset_from_variables(converted, obj._coord_names, obj.attrs)
+    reordered = {name: converted[name] for name in obj.variables.keys()}
+    return dataset_from_variables(reordered, obj._coord_names, indexes, obj.attrs)
 
 
 def convert_units(obj, units):
@@ -308,7 +387,12 @@ def strip_units_variable(var):
 def strip_units_dataset(obj):
     variables = {name: strip_units_variable(var) for name, var in obj.variables.items()}
 
-    return dataset_from_variables(variables, obj._coord_names, obj.attrs)
+    indexes = {
+        name: (index.index if isinstance(index, PintIndex) else index)
+        for name, index in obj.xindexes.items()
+    }
+
+    return dataset_from_variables(variables, obj._coord_names, indexes, obj.attrs)
 
 
 def strip_units(obj):
@@ -403,25 +487,31 @@ def convert_indexer_units(indexers, units):
     return converted
 
 
-def extract_indexer_units(indexer):
-    if isinstance(indexer, slice):
-        return slice_extract_units(indexer)
-    elif isinstance(indexer, (DataArray, Variable)):
-        return array_extract_units(indexer.data)
-    else:
-        return array_extract_units(indexer)
+def extract_indexer_units(indexers):
+    def extract(indexer):
+        if isinstance(indexer, slice):
+            return slice_extract_units(indexer)
+        elif isinstance(indexer, (DataArray, Variable)):
+            return array_extract_units(indexer.data)
+        else:
+            return array_extract_units(indexer)
 
+    return {name: extract(indexer) for name, indexer in indexers.items()}
 
-def strip_indexer_units(indexer):
-    if isinstance(indexer, slice):
-        return slice(
-            array_strip_units(indexer.start),
-            array_strip_units(indexer.stop),
-            array_strip_units(indexer.step),
-        )
-    elif isinstance(indexer, DataArray):
-        return strip_units(indexer)
-    elif isinstance(indexer, Variable):
-        return strip_units_variable(indexer)
-    else:
-        return array_strip_units(indexer)
+
+def strip_indexer_units(indexers):
+    def strip(indexer):
+        if isinstance(indexer, slice):
+            return slice(
+                array_strip_units(indexer.start),
+                array_strip_units(indexer.stop),
+                array_strip_units(indexer.step),
+            )
+        elif isinstance(indexer, DataArray):
+            return strip_units(indexer)
+        elif isinstance(indexer, Variable):
+            return strip_units_variable(indexer)
+        else:
+            return array_strip_units(indexer)
+
+    return {name: strip(indexer) for name, indexer in indexers.items()}


=====================================
pint_xarray/index.py
=====================================
@@ -0,0 +1,95 @@
+from xarray import Variable
+from xarray.core.indexes import Index, PandasIndex
+
+from pint_xarray import conversion
+
+
+class PintIndex(Index):
+    def __init__(self, *, index, units):
+        """create a unit-aware MetaIndex
+
+        Parameters
+        ----------
+        index : xarray.Index
+            The wrapped index object.
+        units : mapping of hashable to unit-like
+            The units of the indexed coordinates
+        """
+        self.index = index
+        self.units = units
+
+    def _replace(self, new_index):
+        return self.__class__(index=new_index, units=self.units)
+
+    def create_variables(self, variables=None):
+        index_vars = self.index.create_variables(variables)
+
+        index_vars_units = {}
+        for name, var in index_vars.items():
+            data = conversion.array_attach_units(var.data, self.units[name])
+            var_units = Variable(var.dims, data, attrs=var.attrs, encoding=var.encoding)
+            index_vars_units[name] = var_units
+
+        return index_vars_units
+
+    @classmethod
+    def from_variables(cls, variables, options):
+        if len(variables) != 1:
+            raise ValueError("can only create a default index from single variables")
+
+        units = options.pop("units", None)
+        index = PandasIndex.from_variables(variables, options=options)
+        return cls(index=index, units={index.index.name: units})
+
+    @classmethod
+    def concat(cls, indexes, dim, positions):
+        raise NotImplementedError()
+
+    @classmethod
+    def stack(cls, variables, dim):
+        raise NotImplementedError()
+
+    def unstack(self):
+        raise NotImplementedError()
+
+    def sel(self, labels):
+        converted_labels = conversion.convert_indexer_units(labels, self.units)
+        stripped_labels = conversion.strip_indexer_units(converted_labels)
+
+        return self.index.sel(stripped_labels)
+
+    def isel(self, indexers):
+        subset = self.index.isel(indexers)
+        if subset is None:
+            return None
+
+        return self._replace(subset)
+
+    def join(self, other, how="inner"):
+        raise NotImplementedError()
+
+    def reindex_like(self, other):
+        raise NotImplementedError()
+
+    def equals(self, other):
+        if not isinstance(other, PintIndex):
+            return False
+
+        # for now we require exactly matching units to avoid the potentially expensive conversion
+        if self.units != other.units:
+            return False
+
+        # last to avoid the potentially expensive comparison
+        return self.index.equals(other.index)
+
+    def roll(self, shifts):
+        return self._replace(self.index.roll(shifts))
+
+    def rename(self, name_dict, dims_dict):
+        return self._replace(self.index.rename(name_dict, dims_dict))
+
+    def __getitem__(self, indexer):
+        return self._replace(self.index[indexer])
+
+    def _repr_inline_(self, max_width):
+        return f"{self.__class__.__name__}({self.index.__class__.__name__})"


=====================================
pint_xarray/testing.py
=====================================
@@ -1,4 +1,4 @@
-from . import conversion, formatting
+from pint_xarray import conversion, formatting
 
 
 def assert_units_equal(a, b):


=====================================
pint_xarray/tests/test_accessors.py
=====================================
@@ -6,8 +6,9 @@ import xarray as xr
 from numpy.testing import assert_array_equal
 from pint import Unit, UnitRegistry
 
-from .. import accessors, conversion
-from .utils import (
+from pint_xarray import accessors, conversion
+from pint_xarray.index import PintIndex
+from pint_xarray.tests.utils import (
     assert_equal,
     assert_identical,
     assert_units_equal,
@@ -22,7 +23,8 @@ pytestmark = [
 
 # make sure scalars are converted to 0d arrays so quantities can
 # always be treated like ndarrays
-unit_registry = UnitRegistry(force_ndarray=True)
+from pint_xarray import unit_registry
+
 Quantity = unit_registry.Quantity
 
 nan = np.nan
@@ -159,7 +161,17 @@ class TestQuantifyDataArray:
             arr.pint.quantify({"x": "s"})
 
     def test_dimension_coordinate_array_already_quantified_same_units(self):
-        ds = xr.Dataset(coords={"x": ("x", [10], {"units": unit_registry.Unit("m")})})
+        x = unit_registry.Quantity([10], "m")
+        coords = xr.Coordinates(
+            {"x": x},
+            indexes={
+                "x": PintIndex.from_variables(
+                    {"x": xr.Variable("x", x.magnitude)},
+                    options={"units": x.units},
+                ),
+            },
+        )
+        ds = xr.Dataset(coords=coords)
         arr = ds.x
 
         quantified = arr.pint.quantify({"x": "m"})
@@ -482,18 +494,20 @@ class TestDequantifyDataSet:
             id="Dataset-incompatible units-data",
         ),
         pytest.param(
-            xr.Dataset(coords={"x": ("x", [2, 4], {"units": Unit("s")})}),
+            xr.Dataset(coords=xr.Coordinates({"x": Quantity([2, 4], "s")}, indexes={})),
             {"x": "ms"},
-            xr.Dataset(coords={"x": ("x", [2000, 4000], {"units": Unit("ms")})}),
+            xr.Dataset(
+                coords=xr.Coordinates({"x": Quantity([2000, 4000], "ms")}, indexes={})
+            ),
             None,
-            id="Dataset-compatible units-dims",
+            id="Dataset-compatible units-dims-no index",
         ),
         pytest.param(
-            xr.Dataset(coords={"x": ("x", [2, 4], {"units": Unit("s")})}),
+            xr.Dataset(coords=xr.Coordinates({"x": Quantity([2, 4], "s")}, indexes={})),
             {"x": "mm"},
             None,
             ValueError,
-            id="Dataset-incompatible units-dims",
+            id="Dataset-incompatible units-dims-no index",
         ),
         pytest.param(
             xr.DataArray(Quantity([0, 1], "m"), dims="x"),
@@ -525,25 +539,29 @@ class TestDequantifyDataSet:
         ),
         pytest.param(
             xr.DataArray(
-                [0, 1], dims="x", coords={"x": ("x", [2, 4], {"units": Unit("s")})}
+                [0, 1],
+                dims="x",
+                coords=xr.Coordinates({"x": Quantity([2, 4], "s")}, indexes={}),
             ),
             {"x": "ms"},
             xr.DataArray(
                 [0, 1],
                 dims="x",
-                coords={"x": ("x", [2000, 4000], {"units": Unit("ms")})},
+                coords=xr.Coordinates({"x": Quantity([2000, 4000], "ms")}, indexes={}),
             ),
             None,
-            id="DataArray-compatible units-dims",
+            id="DataArray-compatible units-dims-no index",
         ),
         pytest.param(
             xr.DataArray(
-                [0, 1], dims="x", coords={"x": ("x", [2, 4], {"units": Unit("s")})}
+                [0, 1],
+                dims="x",
+                coords=xr.Coordinates({"x": Quantity([2, 4], "s")}, indexes={}),
             ),
             {"x": "mm"},
             None,
             ValueError,
-            id="DataArray-incompatible units-dims",
+            id="DataArray-incompatible units-dims-no index",
         ),
     ),
 )
@@ -666,13 +684,17 @@ def test_to(obj, units, expected, error):
     ),
 )
 def test_sel(obj, indexers, expected, error):
+    obj_ = obj.pint.quantify()
+
     if error is not None:
         with pytest.raises(error):
-            obj.pint.sel(indexers)
+            obj_.pint.sel(indexers)
     else:
-        actual = obj.pint.sel(indexers)
-        assert_units_equal(actual, expected)
-        assert_identical(actual, expected)
+        expected_ = expected.pint.quantify()
+
+        actual = obj_.pint.sel(indexers)
+        assert_units_equal(actual, expected_)
+        assert_identical(actual, expected_)
 
 
 @pytest.mark.parametrize(
@@ -783,13 +805,17 @@ def test_sel(obj, indexers, expected, error):
     ),
 )
 def test_loc(obj, indexers, expected, error):
+    obj_ = obj.pint.quantify()
+
     if error is not None:
         with pytest.raises(error):
-            obj.pint.loc[indexers]
+            obj_.pint.loc[indexers]
     else:
-        actual = obj.pint.loc[indexers]
-        assert_units_equal(actual, expected)
-        assert_identical(actual, expected)
+        expected_ = expected.pint.quantify()
+
+        actual = obj_.pint.loc[indexers]
+        assert_units_equal(actual, expected_)
+        assert_identical(actual, expected_)
 
 
 @pytest.mark.parametrize(
@@ -1136,72 +1162,73 @@ def test_chunk(obj):
 
 
 @pytest.mark.parametrize(
-    ["obj", "indexers", "expected", "error"],
+    ["obj", "units", "indexers", "expected", "expected_units", "error"],
     (
         pytest.param(
-            xr.Dataset(
-                {
-                    "x": ("x", [10, 20, 30], {"units": unit_registry.Unit("dm")}),
-                    "y": ("y", [60, 120], {"units": unit_registry.Unit("s")}),
-                }
-            ),
+            xr.Dataset({"x": ("x", [10, 20, 30]), "y": ("y", [60, 120])}),
+            {"x": "dm", "y": "s"},
             {"x": Quantity([10, 30, 50], "dm"), "y": Quantity([0, 120, 240], "s")},
-            xr.Dataset(
-                {
-                    "x": ("x", [10, 30, 50], {"units": unit_registry.Unit("dm")}),
-                    "y": ("y", [0, 120, 240], {"units": unit_registry.Unit("s")}),
-                }
-            ),
+            xr.Dataset({"x": ("x", [10, 30, 50]), "y": ("y", [0, 120, 240])}),
+            {"x": "dm", "y": "s"},
             None,
             id="Dataset-identical units",
         ),
         pytest.param(
-            xr.Dataset(
-                {
-                    "x": ("x", [10, 20, 30], {"units": unit_registry.Unit("dm")}),
-                    "y": ("y", [60, 120], {"units": unit_registry.Unit("s")}),
-                }
-            ),
+            xr.Dataset({"x": ("x", [10, 20, 30]), "y": ("y", [60, 120])}),
+            {"x": "dm", "y": "s"},
             {"x": Quantity([0, 1, 3, 5], "m"), "y": Quantity([0, 2, 4], "min")},
-            xr.Dataset(
-                {
-                    "x": ("x", [0, 1, 3, 5], {"units": unit_registry.Unit("m")}),
-                    "y": ("y", [0, 2, 4], {"units": unit_registry.Unit("min")}),
-                }
-            ),
+            xr.Dataset({"x": ("x", [0, 1, 3, 5]), "y": ("y", [0, 2, 4])}),
+            {"x": "m", "y": "min"},
             None,
             id="Dataset-compatible units",
         ),
+        pytest.param(
+            xr.Dataset({"x": ("x", [10, 20, 30]), "y": ("y", [60, 120])}),
+            {"x": "dm", "y": "s"},
+            {"x": Quantity([1, 3], "s"), "y": Quantity([1], "m")},
+            None,
+            {},
+            ValueError,
+            id="Dataset-incompatible units",
+        ),
         pytest.param(
             xr.Dataset(
                 {
-                    "x": ("x", [10, 20, 30], {"units": unit_registry.Unit("dm")}),
-                    "y": ("y", [60, 120], {"units": unit_registry.Unit("s")}),
+                    "a": (("x", "y"), np.array([[0, 1], [2, 3], [4, 5]])),
+                    "x": [10, 20, 30],
+                    "y": [60, 120],
                 }
             ),
-            {"x": Quantity([1, 3], "s"), "y": Quantity([1], "m")},
+            {"a": "kg"},
+            {
+                "x": [15, 25],
+                "y": [75, 105],
+            },
+            xr.Dataset(
+                {
+                    "a": (("x", "y"), np.array([[np.nan, np.nan], [np.nan, np.nan]])),
+                    "x": [15, 25],
+                    "y": [75, 105],
+                }
+            ),
+            {"a": "kg"},
             None,
-            ValueError,
-            id="Dataset-incompatible units",
+            id="Dataset-data units",
         ),
         pytest.param(
             xr.DataArray(
                 [[0, 1], [2, 3], [4, 5]],
                 dims=("x", "y"),
-                coords={
-                    "x": ("x", [10, 20, 30], {"units": unit_registry.Unit("dm")}),
-                    "y": ("y", [60, 120], {"units": unit_registry.Unit("s")}),
-                },
+                coords={"x": ("x", [10, 20, 30]), "y": ("y", [60, 120])},
             ),
+            {"x": "dm", "y": "s"},
             {"x": Quantity([10, 30, 50], "dm"), "y": Quantity([0, 240], "s")},
             xr.DataArray(
                 [[np.nan, np.nan], [np.nan, np.nan], [np.nan, np.nan]],
                 dims=("x", "y"),
-                coords={
-                    "x": ("x", [10, 30, 50], {"units": unit_registry.Unit("dm")}),
-                    "y": ("y", [0, 240], {"units": unit_registry.Unit("s")}),
-                },
+                coords={"x": ("x", [10, 30, 50]), "y": ("y", [0, 240])},
             ),
+            {"x": "dm", "y": "s"},
             None,
             id="DataArray-identical units",
         ),
@@ -1209,20 +1236,16 @@ def test_chunk(obj):
             xr.DataArray(
                 [[0, 1], [2, 3], [4, 5]],
                 dims=("x", "y"),
-                coords={
-                    "x": ("x", [10, 20, 30], {"units": unit_registry.Unit("dm")}),
-                    "y": ("y", [60, 120], {"units": unit_registry.Unit("s")}),
-                },
+                coords={"x": ("x", [10, 20, 30]), "y": ("y", [60, 120])},
             ),
+            {"x": "dm", "y": "s"},
             {"x": Quantity([1, 3, 5], "m"), "y": Quantity([0, 2], "min")},
             xr.DataArray(
                 [[np.nan, 1], [np.nan, 5], [np.nan, np.nan]],
                 dims=("x", "y"),
-                coords={
-                    "x": ("x", [1, 3, 5], {"units": unit_registry.Unit("m")}),
-                    "y": ("y", [0, 2], {"units": unit_registry.Unit("min")}),
-                },
+                coords={"x": ("x", [1, 3, 5]), "y": ("y", [0, 2])},
             ),
+            {"x": "m", "y": "min"},
             None,
             id="DataArray-compatible units",
         ),
@@ -1230,89 +1253,78 @@ def test_chunk(obj):
             xr.DataArray(
                 [[0, 1], [2, 3], [4, 5]],
                 dims=("x", "y"),
-                coords={
-                    "x": ("x", [10, 20, 30], {"units": unit_registry.Unit("dm")}),
-                    "y": ("y", [60, 120], {"units": unit_registry.Unit("s")}),
-                },
+                coords={"x": ("x", [10, 20, 30]), "y": ("y", [60, 120])},
             ),
+            {"x": "dm", "y": "s"},
             {"x": Quantity([10, 30], "s"), "y": Quantity([60], "m")},
             None,
+            {},
             ValueError,
             id="DataArray-incompatible units",
         ),
+        pytest.param(
+            xr.DataArray(
+                np.array([[0, 1], [2, 3], [4, 5]]),
+                dims=("x", "y"),
+                coords={"x": [10, 20, 30], "y": [60, 120]},
+            ),
+            {None: "kg"},
+            {"x": [15, 25], "y": [75, 105]},
+            xr.DataArray(
+                [[np.nan, np.nan], [np.nan, np.nan]],
+                dims=("x", "y"),
+                coords={"x": [15, 25], "y": [75, 105]},
+            ),
+            {None: "kg"},
+            None,
+            id="DataArray-data units",
+        ),
     ),
 )
-def test_reindex(obj, indexers, expected, error):
+def test_reindex(obj, units, indexers, expected, expected_units, error):
+    obj_ = obj.pint.quantify(units)
+
     if error is not None:
         with pytest.raises(error):
             obj.pint.reindex(indexers)
     else:
-        actual = obj.pint.reindex(indexers)
-        assert_units_equal(actual, expected)
-        assert_identical(actual, expected)
+        expected_ = expected.pint.quantify(expected_units)
+
+        actual = obj_.pint.reindex(indexers)
+        assert_units_equal(actual, expected_)
+        assert_identical(actual, expected_)
 
 
 @pytest.mark.parametrize(
-    ["obj", "other", "expected", "error"],
+    ["obj", "units", "other", "other_units", "expected", "expected_units", "error"],
     (
         pytest.param(
-            xr.Dataset(
-                {
-                    "x": ("x", [10, 20, 30], {"units": unit_registry.Unit("dm")}),
-                    "y": ("y", [60, 120], {"units": unit_registry.Unit("s")}),
-                }
-            ),
-            xr.Dataset(
-                {
-                    "x": ("x", [10, 30, 50], {"units": unit_registry.Unit("dm")}),
-                    "y": ("y", [0, 120, 240], {"units": unit_registry.Unit("s")}),
-                }
-            ),
-            xr.Dataset(
-                {
-                    "x": ("x", [10, 30, 50], {"units": unit_registry.Unit("dm")}),
-                    "y": ("y", [0, 120, 240], {"units": unit_registry.Unit("s")}),
-                }
-            ),
+            xr.Dataset({"x": ("x", [10, 20, 30]), "y": ("y", [60, 120])}),
+            {"x": "dm", "y": "s"},
+            xr.Dataset({"x": ("x", [10, 30, 50]), "y": ("y", [0, 120, 240])}),
+            {"x": "dm", "y": "s"},
+            xr.Dataset({"x": ("x", [10, 30, 50]), "y": ("y", [0, 120, 240])}),
+            {"x": "dm", "y": "s"},
             None,
             id="Dataset-identical units",
         ),
         pytest.param(
-            xr.Dataset(
-                {
-                    "x": ("x", [10, 20, 30], {"units": unit_registry.Unit("dm")}),
-                    "y": ("y", [60, 120], {"units": unit_registry.Unit("s")}),
-                }
-            ),
-            xr.Dataset(
-                {
-                    "x": ("x", [0, 1, 3, 5], {"units": unit_registry.Unit("m")}),
-                    "y": ("y", [0, 2, 4], {"units": unit_registry.Unit("min")}),
-                }
-            ),
-            xr.Dataset(
-                {
-                    "x": ("x", [0, 1, 3, 5], {"units": unit_registry.Unit("m")}),
-                    "y": ("y", [0, 2, 4], {"units": unit_registry.Unit("min")}),
-                }
-            ),
+            xr.Dataset({"x": ("x", [10, 20, 30]), "y": ("y", [60, 120])}),
+            {"x": "dm", "y": "s"},
+            xr.Dataset({"x": ("x", [0, 1, 3, 5]), "y": ("y", [0, 2, 4])}),
+            {"x": "m", "y": "min"},
+            xr.Dataset({"x": ("x", [0, 1, 3, 5]), "y": ("y", [0, 2, 4])}),
+            {"x": "m", "y": "min"},
             None,
             id="Dataset-compatible units",
         ),
         pytest.param(
-            xr.Dataset(
-                {
-                    "x": ("x", [10, 20, 30], {"units": unit_registry.Unit("dm")}),
-                    "y": ("y", [60, 120], {"units": unit_registry.Unit("s")}),
-                }
-            ),
-            xr.Dataset(
-                {
-                    "x": ("x", [1, 3], {"units": unit_registry.Unit("s")}),
-                    "y": ("y", [1], {"units": unit_registry.Unit("m")}),
-                }
-            ),
+            xr.Dataset({"x": ("x", [10, 20, 30]), "y": ("y", [60, 120])}),
+            {"x": "dm", "y": "s"},
+            xr.Dataset({"x": ("x", [1, 3]), "y": ("y", [1])}),
+            {"x": "s", "y": "m"},
             None,
+            {},
             ValueError,
             id="Dataset-incompatible units",
         ),
@@ -1320,51 +1332,57 @@ def test_reindex(obj, indexers, expected, error):
             xr.DataArray(
                 [[0, 1], [2, 3], [4, 5]],
                 dims=("x", "y"),
-                coords={
-                    "x": ("x", [10, 20, 30], {"units": unit_registry.Unit("dm")}),
-                    "y": ("y", [60, 120], {"units": unit_registry.Unit("s")}),
-                },
-            ),
-            xr.Dataset(
-                {
-                    "x": ("x", [10, 30, 50], {"units": unit_registry.Unit("dm")}),
-                    "y": ("y", [0, 240], {"units": unit_registry.Unit("s")}),
-                }
+                coords={"x": ("x", [10, 20, 30]), "y": ("y", [60, 120])},
             ),
+            {"x": "dm", "y": "s"},
+            xr.Dataset({"x": ("x", [10, 30, 50]), "y": ("y", [0, 240])}),
+            {"x": "dm", "y": "s"},
             xr.DataArray(
                 [[np.nan, np.nan], [np.nan, np.nan], [np.nan, np.nan]],
                 dims=("x", "y"),
-                coords={
-                    "x": ("x", [10, 30, 50], {"units": unit_registry.Unit("dm")}),
-                    "y": ("y", [0, 240], {"units": unit_registry.Unit("s")}),
-                },
+                coords={"x": ("x", [10, 30, 50]), "y": ("y", [0, 240])},
             ),
+            {"x": "dm", "y": "s"},
             None,
             id="DataArray-identical units",
         ),
         pytest.param(
-            xr.DataArray(
-                [[0, 1], [2, 3], [4, 5]],
-                dims=("x", "y"),
-                coords={
-                    "x": ("x", [10, 20, 30], {"units": unit_registry.Unit("dm")}),
-                    "y": ("y", [60, 120], {"units": unit_registry.Unit("s")}),
-                },
+            xr.Dataset(
+                {
+                    "a": (("x", "y"), [[0, 1], [2, 3], [4, 5]]),
+                    "x": [10, 20, 30],
+                    "y": [60, 120],
+                }
             ),
+            {"a": "kg"},
+            xr.Dataset({"x": [15, 25], "y": [75, 105]}),
+            {},
             xr.Dataset(
                 {
-                    "x": ("x", [1, 3, 5], {"units": unit_registry.Unit("m")}),
-                    "y": ("y", [0, 2], {"units": unit_registry.Unit("min")}),
+                    "a": (("x", "y"), [[np.nan, np.nan], [np.nan, np.nan]]),
+                    "x": [15, 25],
+                    "y": [75, 105],
                 }
             ),
+            {"a": "kg"},
+            None,
+            id="Dataset-data units",
+        ),
+        pytest.param(
+            xr.DataArray(
+                [[0, 1], [2, 3], [4, 5]],
+                dims=("x", "y"),
+                coords={"x": ("x", [10, 20, 30]), "y": ("y", [60, 120])},
+            ),
+            {"x": "dm", "y": "s"},
+            xr.Dataset({"x": ("x", [1, 3, 5]), "y": ("y", [0, 2])}),
+            {"x": "m", "y": "min"},
             xr.DataArray(
                 [[np.nan, 1], [np.nan, 5], [np.nan, np.nan]],
                 dims=("x", "y"),
-                coords={
-                    "x": ("x", [1, 3, 5], {"units": unit_registry.Unit("m")}),
-                    "y": ("y", [0, 2], {"units": unit_registry.Unit("min")}),
-                },
+                coords={"x": ("x", [1, 3, 5]), "y": ("y", [0, 2])},
             ),
+            {"x": "m", "y": "min"},
             None,
             id="DataArray-compatible units",
         ),
@@ -1372,102 +1390,107 @@ def test_reindex(obj, indexers, expected, error):
             xr.DataArray(
                 [[0, 1], [2, 3], [4, 5]],
                 dims=("x", "y"),
-                coords={
-                    "x": ("x", [10, 20, 30], {"units": unit_registry.Unit("dm")}),
-                    "y": ("y", [60, 120], {"units": unit_registry.Unit("s")}),
-                },
-            ),
-            xr.Dataset(
-                {
-                    "x": ("x", [10, 30], {"units": unit_registry.Unit("s")}),
-                    "y": ("y", [60], {"units": unit_registry.Unit("m")}),
-                }
+                coords={"x": ("x", [10, 20, 30]), "y": ("y", [60, 120])},
             ),
+            {"x": "dm", "y": "s"},
+            xr.Dataset({"x": ("x", [10, 30]), "y": ("y", [60])}),
+            {"x": "s", "y": "m"},
             None,
+            {},
             ValueError,
             id="DataArray-incompatible units",
         ),
+        pytest.param(
+            xr.DataArray(
+                [[0, 1], [2, 3], [4, 5]],
+                dims=("x", "y"),
+                coords={"x": [10, 20, 30], "y": [60, 120]},
+            ),
+            {"a": "kg"},
+            xr.Dataset({"x": [15, 25], "y": [75, 105]}),
+            {},
+            xr.DataArray(
+                [[np.nan, np.nan], [np.nan, np.nan]],
+                dims=("x", "y"),
+                coords={"x": [15, 25], "y": [75, 105]},
+            ),
+            {"a": "kg"},
+            None,
+            id="DataArray-data units",
+        ),
     ),
 )
-def test_reindex_like(obj, other, expected, error):
+def test_reindex_like(obj, units, other, other_units, expected, expected_units, error):
+    obj_ = obj.pint.quantify(units)
+    other_ = other.pint.quantify(other_units)
+
     if error is not None:
         with pytest.raises(error):
-            obj.pint.reindex_like(other)
+            obj_.pint.reindex_like(other_)
     else:
-        actual = obj.pint.reindex_like(other)
-        assert_units_equal(actual, expected)
-        assert_identical(actual, expected)
+        expected_ = expected.pint.quantify(expected_units)
+
+        actual = obj_.pint.reindex_like(other_)
+        assert_units_equal(actual, expected_)
+        assert_identical(actual, expected_)
 
 
 @requires_scipy
 @pytest.mark.parametrize(
-    ["obj", "indexers", "expected", "error"],
+    ["obj", "units", "indexers", "expected", "expected_units", "error", "kwargs"],
     (
         pytest.param(
-            xr.Dataset(
-                {
-                    "x": ("x", [10, 20, 30], {"units": unit_registry.Unit("dm")}),
-                    "y": ("y", [60, 120], {"units": unit_registry.Unit("s")}),
-                }
-            ),
+            xr.Dataset({"x": ("x", [10, 20, 30]), "y": ("y", [60, 120])}),
+            {"x": "dm", "y": "s"},
             {"x": Quantity([10, 30, 50], "dm"), "y": Quantity([0, 120, 240], "s")},
-            xr.Dataset(
-                {
-                    "x": ("x", [10, 30, 50], {"units": unit_registry.Unit("dm")}),
-                    "y": ("y", [0, 120, 240], {"units": unit_registry.Unit("s")}),
-                }
-            ),
+            xr.Dataset({"x": ("x", [10, 30, 50]), "y": ("y", [0, 120, 240])}),
+            {"x": "dm", "y": "s"},
+            None,
             None,
             id="Dataset-identical units",
         ),
         pytest.param(
-            xr.Dataset(
-                {
-                    "x": ("x", [10, 20, 30], {"units": unit_registry.Unit("dm")}),
-                    "y": ("y", [60, 120], {"units": unit_registry.Unit("s")}),
-                }
-            ),
+            xr.Dataset({"x": ("x", [10, 20, 30]), "y": ("y", [60, 120])}),
+            {"x": "dm", "y": "s"},
             {"x": Quantity([0, 1, 3, 5], "m"), "y": Quantity([0, 2, 4], "min")},
-            xr.Dataset(
-                {
-                    "x": ("x", [0, 1, 3, 5], {"units": unit_registry.Unit("m")}),
-                    "y": ("y", [0, 2, 4], {"units": unit_registry.Unit("min")}),
-                }
-            ),
+            xr.Dataset({"x": ("x", [0, 1, 3, 5]), "y": ("y", [0, 2, 4])}),
+            {"x": "m", "y": "min"},
+            None,
             None,
             id="Dataset-compatible units",
         ),
         pytest.param(
-            xr.Dataset(
-                {
-                    "x": ("x", [10, 20, 30], {"units": unit_registry.Unit("dm")}),
-                    "y": ("y", [60, 120], {"units": unit_registry.Unit("s")}),
-                }
-            ),
+            xr.Dataset({"x": ("x", [10, 20, 30]), "y": ("y", [60, 120])}),
+            {"x": "dm", "y": "s"},
             {"x": Quantity([1, 3], "s"), "y": Quantity([1], "m")},
             None,
+            {},
             ValueError,
+            None,
             id="Dataset-incompatible units",
         ),
         pytest.param(
             xr.Dataset(
                 {
-                    "a": (("x", "y"), Quantity([[0, 1], [2, 3], [4, 5]], "kg")),
+                    "a": (("x", "y"), np.array([[0, 1], [2, 3], [4, 5]])),
                     "x": [10, 20, 30],
                     "y": [60, 120],
                 }
             ),
+            {"a": "kg"},
             {
                 "x": [15, 25],
                 "y": [75, 105],
             },
             xr.Dataset(
                 {
-                    "a": (("x", "y"), Quantity([[1.25, 1.75], [3.25, 3.75]], "kg")),
+                    "a": (("x", "y"), np.array([[1.25, 1.75], [3.25, 3.75]])),
                     "x": [15, 25],
                     "y": [75, 105],
                 }
             ),
+            {"a": "kg"},
+            None,
             None,
             id="Dataset-data units",
         ),
@@ -1475,20 +1498,17 @@ def test_reindex_like(obj, other, expected, error):
             xr.DataArray(
                 [[0, 1], [2, 3], [4, 5]],
                 dims=("x", "y"),
-                coords={
-                    "x": ("x", [10, 20, 30], {"units": unit_registry.Unit("dm")}),
-                    "y": ("y", [60, 120], {"units": unit_registry.Unit("s")}),
-                },
+                coords={"x": ("x", [10, 20, 30]), "y": ("y", [60, 120])},
             ),
+            {"x": "dm", "y": "s"},
             {"x": Quantity([10, 30, 50], "dm"), "y": Quantity([0, 240], "s")},
             xr.DataArray(
                 [[np.nan, np.nan], [np.nan, np.nan], [np.nan, np.nan]],
                 dims=("x", "y"),
-                coords={
-                    "x": ("x", [10, 30, 50], {"units": unit_registry.Unit("dm")}),
-                    "y": ("y", [0, 240], {"units": unit_registry.Unit("s")}),
-                },
+                coords={"x": ("x", [10, 30, 50]), "y": ("y", [0, 240])},
             ),
+            {"x": "dm", "y": "s"},
+            None,
             None,
             id="DataArray-identical units",
         ),
@@ -1496,20 +1516,17 @@ def test_reindex_like(obj, other, expected, error):
             xr.DataArray(
                 [[0, 1], [2, 3], [4, 5]],
                 dims=("x", "y"),
-                coords={
-                    "x": ("x", [10, 20, 30], {"units": unit_registry.Unit("dm")}),
-                    "y": ("y", [60, 120], {"units": unit_registry.Unit("s")}),
-                },
+                coords={"x": ("x", [10, 20, 30]), "y": ("y", [60, 120])},
             ),
+            {"x": "dm", "y": "s"},
             {"x": Quantity([1, 3, 5], "m"), "y": Quantity([0, 2], "min")},
             xr.DataArray(
                 [[np.nan, 1], [np.nan, 5], [np.nan, np.nan]],
                 dims=("x", "y"),
-                coords={
-                    "x": ("x", [1, 3, 5], {"units": unit_registry.Unit("m")}),
-                    "y": ("y", [0, 2], {"units": unit_registry.Unit("min")}),
-                },
+                coords={"x": ("x", [1, 3, 5]), "y": ("y", [0, 2])},
             ),
+            {"x": "m", "y": "min"},
+            None,
             None,
             id="DataArray-compatible units",
         ),
@@ -1517,114 +1534,99 @@ def test_reindex_like(obj, other, expected, error):
             xr.DataArray(
                 [[0, 1], [2, 3], [4, 5]],
                 dims=("x", "y"),
-                coords={
-                    "x": ("x", [10, 20, 30], {"units": unit_registry.Unit("dm")}),
-                    "y": ("y", [60, 120], {"units": unit_registry.Unit("s")}),
-                },
+                coords={"x": ("x", [10, 20, 30]), "y": ("y", [60, 120])},
             ),
+            {"x": "dm", "y": "s"},
             {"x": Quantity([10, 30], "s"), "y": Quantity([60], "m")},
             None,
+            {},
             ValueError,
+            None,
             id="DataArray-incompatible units",
         ),
         pytest.param(
             xr.DataArray(
-                Quantity([[0, 1], [2, 3], [4, 5]], "kg"),
+                np.array([[0, 1], [2, 3], [4, 5]]),
                 dims=("x", "y"),
-                coords={
-                    "x": [10, 20, 30],
-                    "y": [60, 120],
-                },
+                coords={"x": [10, 20, 30], "y": [60, 120]},
             ),
-            {
-                "x": [15, 25],
-                "y": [75, 105],
-            },
+            {None: "kg"},
+            {"x": [15, 25], "y": [75, 105]},
             xr.DataArray(
-                Quantity([[1.25, 1.75], [3.25, 3.75]], "kg"),
+                [[1.25, 1.75], [3.25, 3.75]],
                 dims=("x", "y"),
-                coords={
-                    "x": [15, 25],
-                    "y": [75, 105],
-                },
+                coords={"x": [15, 25], "y": [75, 105]},
             ),
+            {None: "kg"},
+            None,
             None,
             id="DataArray-data units",
         ),
+        pytest.param(
+            xr.DataArray(
+                [[0, 1], [2, 3], [4, 5]],
+                dims=("x", "y"),
+                coords={"x": ("x", [10, 20, 30]), "y": ("y", [60, 120])},
+            ),
+            {"x": "dm", "y": "s"},
+            {"x": Quantity([1, 3, 5], "m"), "y": Quantity([0, 2], "min")},
+            xr.DataArray(
+                [[0, 1], [0, 5], [0, 0]],
+                dims=("x", "y"),
+                coords={"x": ("x", [1, 3, 5]), "y": ("y", [0, 2])},
+            ),
+            {"x": "m", "y": "min"},
+            None,
+            {"bounds_error": False, "fill_value": 0},
+            id="DataArray-other parameters",
+        ),
     ),
 )
-def test_interp(obj, indexers, expected, error):
+def test_interp(obj, units, indexers, expected, expected_units, error, kwargs):
+    obj_ = obj.pint.quantify(units)
+
     if error is not None:
         with pytest.raises(error):
-            obj.pint.interp(indexers)
+            obj_.pint.interp(indexers, kwargs=kwargs)
     else:
-        actual = obj.pint.interp(indexers)
-        assert_units_equal(actual, expected)
-        assert_identical(actual, expected)
+        expected_ = expected.pint.quantify(expected_units)
+
+        actual = obj_.pint.interp(indexers, kwargs=kwargs)
+        assert_units_equal(actual, expected_)
+        assert_identical(actual, expected_)
 
 
 @requires_scipy
 @pytest.mark.parametrize(
-    ["obj", "other", "expected", "error"],
+    ["obj", "units", "other", "other_units", "expected", "expected_units", "error"],
     (
         pytest.param(
-            xr.Dataset(
-                {
-                    "x": ("x", [10, 20, 30], {"units": unit_registry.Unit("dm")}),
-                    "y": ("y", [60, 120], {"units": unit_registry.Unit("s")}),
-                }
-            ),
-            xr.Dataset(
-                {
-                    "x": ("x", [10, 30, 50], {"units": unit_registry.Unit("dm")}),
-                    "y": ("y", [0, 120, 240], {"units": unit_registry.Unit("s")}),
-                }
-            ),
-            xr.Dataset(
-                {
-                    "x": ("x", [10, 30, 50], {"units": unit_registry.Unit("dm")}),
-                    "y": ("y", [0, 120, 240], {"units": unit_registry.Unit("s")}),
-                }
-            ),
+            xr.Dataset({"x": ("x", [10, 20, 30]), "y": ("y", [60, 120])}),
+            {"x": "dm", "y": "s"},
+            xr.Dataset({"x": ("x", [10, 30, 50]), "y": ("y", [0, 120, 240])}),
+            {"x": "dm", "y": "s"},
+            xr.Dataset({"x": ("x", [10, 30, 50]), "y": ("y", [0, 120, 240])}),
+            {"x": "dm", "y": "s"},
             None,
             id="Dataset-identical units",
         ),
         pytest.param(
-            xr.Dataset(
-                {
-                    "x": ("x", [10, 20, 30], {"units": unit_registry.Unit("dm")}),
-                    "y": ("y", [60, 120], {"units": unit_registry.Unit("s")}),
-                }
-            ),
-            xr.Dataset(
-                {
-                    "x": ("x", [0, 1, 3, 5], {"units": unit_registry.Unit("m")}),
-                    "y": ("y", [0, 2, 4], {"units": unit_registry.Unit("min")}),
-                }
-            ),
-            xr.Dataset(
-                {
-                    "x": ("x", [0, 1, 3, 5], {"units": unit_registry.Unit("m")}),
-                    "y": ("y", [0, 2, 4], {"units": unit_registry.Unit("min")}),
-                }
-            ),
+            xr.Dataset({"x": ("x", [10, 20, 30]), "y": ("y", [60, 120])}),
+            {"x": "dm", "y": "s"},
+            xr.Dataset({"x": ("x", [0, 1, 3, 5]), "y": ("y", [0, 2, 4])}),
+            {"x": "m", "y": "min"},
+            xr.Dataset({"x": ("x", [0, 1, 3, 5]), "y": ("y", [0, 2, 4])}),
+            {"x": "m", "y": "min"},
             None,
             id="Dataset-compatible units",
         ),
         pytest.param(
-            xr.Dataset(
-                {
-                    "x": ("x", [10, 20, 30], {"units": unit_registry.Unit("dm")}),
-                    "y": ("y", [60, 120], {"units": unit_registry.Unit("s")}),
-                }
-            ),
-            xr.Dataset(
-                {
-                    "x": ("x", [1, 3], {"units": unit_registry.Unit("s")}),
-                    "y": ("y", [1], {"units": unit_registry.Unit("m")}),
-                }
-            ),
+            xr.Dataset({"x": ("x", [10, 20, 30]), "y": ("y", [60, 120])}),
+            {"x": "dm", "y": "s"},
+            xr.Dataset({"x": ("x", [1, 3]), "y": ("y", [1])}),
+            {"x": "s", "y": "m"},
             None,
+            {},
             ValueError,
             id="Dataset-incompatible units",
         ),
@@ -1632,49 +1634,39 @@ def test_interp(obj, indexers, expected, error):
             xr.DataArray(
                 [[0, 1], [2, 3], [4, 5]],
                 dims=("x", "y"),
-                coords={
-                    "x": ("x", [10, 20, 30], {"units": unit_registry.Unit("dm")}),
-                    "y": ("y", [60, 120], {"units": unit_registry.Unit("s")}),
-                },
-            ),
-            xr.Dataset(
-                {
-                    "x": ("x", [10, 30, 50], {"units": unit_registry.Unit("dm")}),
-                    "y": ("y", [0, 240], {"units": unit_registry.Unit("s")}),
-                }
+                coords={"x": ("x", [10, 20, 30]), "y": ("y", [60, 120])},
             ),
+            {"x": "dm", "y": "s"},
+            xr.Dataset({"x": ("x", [10, 30, 50]), "y": ("y", [0, 240])}),
+            {"x": "dm", "y": "s"},
             xr.DataArray(
                 [[np.nan, np.nan], [np.nan, np.nan], [np.nan, np.nan]],
                 dims=("x", "y"),
-                coords={
-                    "x": ("x", [10, 30, 50], {"units": unit_registry.Unit("dm")}),
-                    "y": ("y", [0, 240], {"units": unit_registry.Unit("s")}),
-                },
+                coords={"x": ("x", [10, 30, 50]), "y": ("y", [0, 240])},
             ),
+            {"x": "dm", "y": "s"},
             None,
             id="DataArray-identical units",
         ),
         pytest.param(
             xr.Dataset(
                 {
-                    "a": (("x", "y"), Quantity([[0, 1], [2, 3], [4, 5]], "kg")),
+                    "a": (("x", "y"), [[0, 1], [2, 3], [4, 5]]),
                     "x": [10, 20, 30],
                     "y": [60, 120],
                 }
             ),
+            {"a": "kg"},
+            xr.Dataset({"x": [15, 25], "y": [75, 105]}),
+            {},
             xr.Dataset(
                 {
+                    "a": (("x", "y"), [[1.25, 1.75], [3.25, 3.75]]),
                     "x": [15, 25],
                     "y": [75, 105],
                 }
             ),
-            xr.Dataset(
-                {
-                    "a": (("x", "y"), Quantity([[1.25, 1.75], [3.25, 3.75]], "kg")),
-                    "x": [15, 25],
-                    "y": [75, 105],
-                }
-            ),
+            {"a": "kg"},
             None,
             id="Dataset-data units",
         ),
@@ -1682,25 +1674,17 @@ def test_interp(obj, indexers, expected, error):
             xr.DataArray(
                 [[0, 1], [2, 3], [4, 5]],
                 dims=("x", "y"),
-                coords={
-                    "x": ("x", [10, 20, 30], {"units": unit_registry.Unit("dm")}),
-                    "y": ("y", [60, 120], {"units": unit_registry.Unit("s")}),
-                },
-            ),
-            xr.Dataset(
-                {
-                    "x": ("x", [1, 3, 5], {"units": unit_registry.Unit("m")}),
-                    "y": ("y", [0, 2], {"units": unit_registry.Unit("min")}),
-                }
+                coords={"x": ("x", [10, 20, 30]), "y": ("y", [60, 120])},
             ),
+            {"x": "dm", "y": "s"},
+            xr.Dataset({"x": ("x", [1, 3, 5]), "y": ("y", [0, 2])}),
+            {"x": "m", "y": "min"},
             xr.DataArray(
                 [[np.nan, 1], [np.nan, 5], [np.nan, np.nan]],
                 dims=("x", "y"),
-                coords={
-                    "x": ("x", [1, 3, 5], {"units": unit_registry.Unit("m")}),
-                    "y": ("y", [0, 2], {"units": unit_registry.Unit("min")}),
-                },
+                coords={"x": ("x", [1, 3, 5]), "y": ("y", [0, 2])},
             ),
+            {"x": "m", "y": "min"},
             None,
             id="DataArray-compatible units",
         ),
@@ -1708,57 +1692,49 @@ def test_interp(obj, indexers, expected, error):
             xr.DataArray(
                 [[0, 1], [2, 3], [4, 5]],
                 dims=("x", "y"),
-                coords={
-                    "x": ("x", [10, 20, 30], {"units": unit_registry.Unit("dm")}),
-                    "y": ("y", [60, 120], {"units": unit_registry.Unit("s")}),
-                },
-            ),
-            xr.Dataset(
-                {
-                    "x": ("x", [10, 30], {"units": unit_registry.Unit("s")}),
-                    "y": ("y", [60], {"units": unit_registry.Unit("m")}),
-                }
+                coords={"x": ("x", [10, 20, 30]), "y": ("y", [60, 120])},
             ),
+            {"x": "dm", "y": "s"},
+            xr.Dataset({"x": ("x", [10, 30]), "y": ("y", [60])}),
+            {"x": "s", "y": "m"},
             None,
+            {},
             ValueError,
             id="DataArray-incompatible units",
         ),
         pytest.param(
             xr.DataArray(
-                Quantity([[0, 1], [2, 3], [4, 5]], "kg"),
+                [[0, 1], [2, 3], [4, 5]],
                 dims=("x", "y"),
-                coords={
-                    "x": [10, 20, 30],
-                    "y": [60, 120],
-                },
-            ),
-            xr.Dataset(
-                {
-                    "x": [15, 25],
-                    "y": [75, 105],
-                }
+                coords={"x": [10, 20, 30], "y": [60, 120]},
             ),
+            {"a": "kg"},
+            xr.Dataset({"x": [15, 25], "y": [75, 105]}),
+            {},
             xr.DataArray(
-                Quantity([[1.25, 1.75], [3.25, 3.75]], "kg"),
+                [[1.25, 1.75], [3.25, 3.75]],
                 dims=("x", "y"),
-                coords={
-                    "x": [15, 25],
-                    "y": [75, 105],
-                },
+                coords={"x": [15, 25], "y": [75, 105]},
             ),
+            {"a": "kg"},
             None,
             id="DataArray-data units",
         ),
     ),
 )
-def test_interp_like(obj, other, expected, error):
+def test_interp_like(obj, units, other, other_units, expected, expected_units, error):
+    obj_ = obj.pint.quantify(units)
+    other_ = other.pint.quantify(other_units)
+
     if error is not None:
         with pytest.raises(error):
-            obj.pint.interp_like(other)
+            obj_.pint.interp_like(other_)
     else:
-        actual = obj.pint.interp_like(other)
-        assert_units_equal(actual, expected)
-        assert_identical(actual, expected)
+        expected_ = expected.pint.quantify(expected_units)
+
+        actual = obj_.pint.interp_like(other_)
+        assert_units_equal(actual, expected_)
+        assert_identical(actual, expected_)
 
 
 @requires_bottleneck


=====================================
pint_xarray/tests/test_conversion.py
=====================================
@@ -1,16 +1,18 @@
 import numpy as np
+import pandas as pd
 import pint
 import pytest
-from xarray import DataArray, Dataset, Variable
+from xarray import Coordinates, DataArray, Dataset, Variable
+from xarray.core.indexes import PandasIndex
 
 from pint_xarray import conversion
-
-from .utils import (
+from pint_xarray.index import PintIndex
+from pint_xarray.tests.utils import (
     assert_array_equal,
     assert_array_units_equal,
     assert_identical,
-    assert_indexer_equal,
     assert_indexer_units_equal,
+    assert_indexers_equal,
 )
 
 unit_registry = pint.UnitRegistry()
@@ -245,17 +247,22 @@ class TestXarrayFunctions:
 
         q_a = to_quantity(a, units.get("a"))
         q_b = to_quantity(b, units.get("b"))
+        q_x = to_quantity(x, units.get("x"))
         q_u = to_quantity(u, units.get("u"))
 
-        units_x = units.get("x")
+        index = PandasIndex(x, dim="x")
+        if units.get("x") is not None:
+            index = PintIndex(index=index, units=units.get("x"))
 
         obj = Dataset({"a": ("x", a), "b": ("x", b)}, coords={"u": ("x", u), "x": x})
+        coords = Coordinates(
+            coords={"u": Variable("x", q_u), "x": Variable("x", q_x)},
+            indexes={"x": index},
+        )
         expected = Dataset(
             {"a": ("x", q_a), "b": ("x", q_b)},
-            coords={"u": ("x", q_u), "x": x},
+            coords=coords,
         )
-        if units_x is not None:
-            expected.x.attrs["units"] = units_x
 
         if type == "DataArray":
             obj = obj["a"]
@@ -264,6 +271,12 @@ class TestXarrayFunctions:
         actual = conversion.attach_units(obj, units)
         assert_identical(actual, expected)
 
+        if units.get("x") is None:
+            assert not isinstance(actual.xindexes["x"], PintIndex)
+        else:
+            assert isinstance(actual.xindexes["x"], PintIndex)
+            assert actual.xindexes["x"].units == {"x": units.get("x")}
+
     @pytest.mark.parametrize("type", ("DataArray", "Dataset"))
     def test_attach_unit_attributes(self, type):
         units = {"a": "K", "b": "hPa", "u": "m", "x": "s"}
@@ -372,15 +385,19 @@ class TestXarrayFunctions:
         q_u = to_quantity(u, original_units.get("u"))
         q_x = to_quantity(x, original_units.get("x"))
 
+        x_index = PandasIndex(pd.Index(x), "x")
+        if original_units.get("x") is not None:
+            x_index = PintIndex(index=x_index, units={"x": original_units.get("x")})
+
         obj = Dataset(
             {
                 "a": ("x", q_a),
                 "b": ("x", q_b),
             },
-            coords={
-                "u": ("x", q_u),
-                "x": ("x", x, {"units": original_units.get("x")}),
-            },
+            coords=Coordinates(
+                {"u": ("x", q_u), "x": ("x", q_x)},
+                indexes={"x": x_index},
+            ),
         )
         if type == "DataArray":
             obj = obj["a"]
@@ -394,20 +411,22 @@ class TestXarrayFunctions:
         expected_a = convert_quantity(q_a, units.get("a", original_units.get("a")))
         expected_b = convert_quantity(q_b, units.get("b", original_units.get("b")))
         expected_u = convert_quantity(q_u, units.get("u", original_units.get("u")))
-        expected_x = strip_quantity(convert_quantity(q_x, units.get("x")))
+        expected_x = convert_quantity(q_x, units.get("x"))
+        expected_index = PandasIndex(pd.Index(strip_quantity(expected_x)), "x")
+        if units.get("x") is not None:
+            expected_index = PintIndex(
+                index=expected_index, units={"x": units.get("x")}
+            )
+
         expected = Dataset(
             {
                 "a": ("x", expected_a),
                 "b": ("x", expected_b),
             },
-            coords={
-                "u": ("x", expected_u),
-                "x": (
-                    "x",
-                    expected_x,
-                    {"units": units.get("x", original_units.get("x"))},
-                ),
-            },
+            coords=Coordinates(
+                {"u": ("x", expected_u), "x": ("x", expected_x)},
+                indexes={"x": expected_index},
+            ),
         )
 
         if type == "DataArray":
@@ -416,7 +435,7 @@ class TestXarrayFunctions:
         actual = conversion.convert_units(obj, units)
 
         assert conversion.extract_units(actual) == conversion.extract_units(expected)
-        assert_identical(expected, actual)
+        assert_identical(actual, expected)
 
     @pytest.mark.parametrize(
         "units",
@@ -436,15 +455,22 @@ class TestXarrayFunctions:
         u = np.linspace(0, 100, 2)
         x = np.arange(2)
 
+        index = PandasIndex(x, "x")
+        if units.get("x") is not None:
+            index = PintIndex(index=index, units={"x": units.get("x")})
+
         obj = Dataset(
             {
                 "a": ("x", to_quantity(a, units.get("a"))),
                 "b": ("x", to_quantity(b, units.get("b"))),
             },
-            coords={
-                "u": ("x", to_quantity(u, units.get("u"))),
-                "x": ("x", x, {"units": units.get("x")}),
-            },
+            coords=Coordinates(
+                {
+                    "u": ("x", to_quantity(u, units.get("u"))),
+                    "x": ("x", to_quantity(x, units.get("x"))),
+                },
+                indexes={"x": index},
+            ),
         )
         if type == "DataArray":
             obj = obj["a"]
@@ -499,21 +525,33 @@ class TestXarrayFunctions:
             pytest.param(
                 DataArray(
                     dims="x",
-                    data=[0, 4, 3] * unit_registry.m,
-                    coords={"u": ("x", [2, 3, 4] * unit_registry.s)},
+                    data=Quantity([0, 4, 3], "kg"),
+                    coords=Coordinates(
+                        {
+                            "u": ("x", Quantity([2, 3, 4], "s")),
+                            "x": Quantity([0, 1, 2], "m"),
+                        },
+                        indexes={},
+                    ),
                 ),
-                {None: None, "u": None},
+                {None: None, "u": None, "x": None},
                 id="DataArray",
             ),
             pytest.param(
                 Dataset(
                     data_vars={
-                        "a": ("x", [3, 2, 5] * unit_registry.Pa),
-                        "b": ("x", [0, 2, -1] * unit_registry.kg),
+                        "a": ("x", Quantity([3, 2, 5], "Pa")),
+                        "b": ("x", Quantity([0, 2, -1], "kg")),
                     },
-                    coords={"u": ("x", [2, 3, 4] * unit_registry.s)},
+                    coords=Coordinates(
+                        {
+                            "u": ("x", Quantity([2, 3, 4], "s")),
+                            "x": Quantity([0, 1, 2], "m"),
+                        },
+                        indexes={},
+                    ),
                 ),
-                {"a": None, "b": None, "u": None},
+                {"a": None, "b": None, "u": None, "x": None},
                 id="Dataset",
             ),
         ),
@@ -694,100 +732,118 @@ class TestIndexerFunctions:
                 conversion.convert_indexer_units(indexers, units)
         else:
             actual = conversion.convert_indexer_units(indexers, units)
-            assert_indexer_equal(actual["x"], expected["x"])
-            assert_indexer_units_equal(actual["x"], expected["x"])
+            assert_indexers_equal(actual, expected)
+            assert_indexer_units_equal(actual, expected)
 
     @pytest.mark.parametrize(
-        ["indexer", "expected"],
+        ["indexers", "expected"],
         (
-            pytest.param(1, None, id="scalar-no units"),
-            pytest.param(Quantity(1, "m"), Unit("m"), id="scalar-units"),
-            pytest.param(np.array([1, 2]), None, id="array-no units"),
-            pytest.param(Quantity([1, 2], "s"), Unit("s"), id="array-units"),
-            pytest.param(Variable("x", [1, 2]), None, id="Variable-no units"),
+            pytest.param({"x": 1}, {"x": None}, id="scalar-no units"),
+            pytest.param({"x": Quantity(1, "m")}, {"x": Unit("m")}, id="scalar-units"),
+            pytest.param({"x": np.array([1, 2])}, {"x": None}, id="array-no units"),
+            pytest.param(
+                {"x": Quantity([1, 2], "s")}, {"x": Unit("s")}, id="array-units"
+            ),
             pytest.param(
-                Variable("x", Quantity([1, 2], "m")), Unit("m"), id="Variable-units"
+                {"x": Variable("x", [1, 2])}, {"x": None}, id="Variable-no units"
             ),
-            pytest.param(DataArray([1, 2], dims="x"), None, id="DataArray-no units"),
             pytest.param(
-                DataArray(Quantity([1, 2], "s"), dims="x"),
-                Unit("s"),
+                {"x": Variable("x", Quantity([1, 2], "m"))},
+                {"x": Unit("m")},
+                id="Variable-units",
+            ),
+            pytest.param(
+                {"x": DataArray([1, 2], dims="x")}, {"x": None}, id="DataArray-no units"
+            ),
+            pytest.param(
+                {"x": DataArray(Quantity([1, 2], "s"), dims="x")},
+                {"x": Unit("s")},
                 id="DataArray-units",
             ),
-            pytest.param(slice(None), None, id="empty slice-no units"),
-            pytest.param(slice(1, None), None, id="slice-no units"),
+            pytest.param({"x": slice(None)}, {"x": None}, id="empty slice-no units"),
+            pytest.param({"x": slice(1, None)}, {"x": None}, id="slice-no units"),
             pytest.param(
-                slice(Quantity(1, "m"), Quantity(2, "m")),
-                Unit("m"),
+                {"x": slice(Quantity(1, "m"), Quantity(2, "m"))},
+                {"x": Unit("m")},
                 id="slice-identical units",
             ),
             pytest.param(
-                slice(Quantity(1, "m"), Quantity(2000, "mm")),
-                Unit("m"),
+                {"x": slice(Quantity(1, "m"), Quantity(2000, "mm"))},
+                {"x": Unit("m")},
                 id="slice-compatible units",
             ),
             pytest.param(
-                slice(Quantity(1, "m"), Quantity(2, "ms")),
+                {"x": slice(Quantity(1, "m"), Quantity(2, "ms"))},
                 ValueError,
                 id="slice-incompatible units",
             ),
             pytest.param(
-                slice(1, Quantity(2, "ms")),
+                {"x": slice(1, Quantity(2, "ms"))},
                 ValueError,
                 id="slice-incompatible units-mixed",
             ),
             pytest.param(
-                slice(1, Quantity(2, "rad")),
-                Unit("rad"),
+                {"x": slice(1, Quantity(2, "rad"))},
+                {"x": Unit("rad")},
                 id="slice-incompatible units-mixed-dimensionless",
             ),
         ),
     )
-    def test_extract_indexer_units(self, indexer, expected):
-        if expected is not None and not isinstance(expected, Unit):
+    def test_extract_indexer_units(self, indexers, expected):
+        if isinstance(expected, type) and issubclass(expected, Exception):
             with pytest.raises(expected):
-                conversion.extract_indexer_units(indexer)
+                conversion.extract_indexer_units(indexers)
         else:
-            actual = conversion.extract_indexer_units(indexer)
+            actual = conversion.extract_indexer_units(indexers)
             assert actual == expected
 
     @pytest.mark.parametrize(
-        ["indexer", "expected"],
+        ["indexers", "expected"],
         (
-            pytest.param(1, 1, id="scalar-no units"),
-            pytest.param(Quantity(1, "m"), 1, id="scalar-units"),
-            pytest.param(np.array([1, 2]), np.array([1, 2]), id="array-no units"),
-            pytest.param(Quantity([1, 2], "s"), np.array([1, 2]), id="array-units"),
+            pytest.param({"x": 1}, {"x": 1}, id="scalar-no units"),
+            pytest.param({"x": Quantity(1, "m")}, {"x": 1}, id="scalar-units"),
             pytest.param(
-                Variable("x", [1, 2]), Variable("x", [1, 2]), id="Variable-no units"
+                {"x": np.array([1, 2])},
+                {"x": np.array([1, 2])},
+                id="array-no units",
+            ),
+            pytest.param(
+                {"x": Quantity([1, 2], "s")}, {"x": np.array([1, 2])}, id="array-units"
+            ),
+            pytest.param(
+                {"x": Variable("x", [1, 2])},
+                {"x": Variable("x", [1, 2])},
+                id="Variable-no units",
             ),
             pytest.param(
-                Variable("x", Quantity([1, 2], "m")),
-                Variable("x", [1, 2]),
+                {"x": Variable("x", Quantity([1, 2], "m"))},
+                {"x": Variable("x", [1, 2])},
                 id="Variable-units",
             ),
             pytest.param(
-                DataArray([1, 2], dims="x"),
-                DataArray([1, 2], dims="x"),
+                {"x": DataArray([1, 2], dims="x")},
+                {"x": DataArray([1, 2], dims="x")},
                 id="DataArray-no units",
             ),
             pytest.param(
-                DataArray(Quantity([1, 2], "s"), dims="x"),
-                DataArray([1, 2], dims="x"),
+                {"x": DataArray(Quantity([1, 2], "s"), dims="x")},
+                {"x": DataArray([1, 2], dims="x")},
                 id="DataArray-units",
             ),
-            pytest.param(slice(None), slice(None), id="empty slice-no units"),
-            pytest.param(slice(1, None), slice(1, None), id="slice-no units"),
             pytest.param(
-                slice(Quantity(1, "m"), Quantity(2, "m")),
-                slice(1, 2),
+                {"x": slice(None)}, {"x": slice(None)}, id="empty slice-no units"
+            ),
+            pytest.param(
+                {"x": slice(1, None)}, {"x": slice(1, None)}, id="slice-no units"
+            ),
+            pytest.param(
+                {"x": slice(Quantity(1, "m"), Quantity(2, "m"))},
+                {"x": slice(1, 2)},
                 id="slice-units",
             ),
         ),
     )
-    def test_strip_indexer_units(self, indexer, expected):
-        actual = conversion.strip_indexer_units(indexer)
-        if isinstance(indexer, DataArray):
-            assert_identical(actual, expected)
-        else:
-            assert_array_equal(actual, expected)
+    def test_strip_indexer_units(self, indexers, expected):
+        actual = conversion.strip_indexer_units(indexers)
+
+        assert_indexers_equal(actual, expected)


=====================================
pint_xarray/tests/test_index.py
=====================================
@@ -0,0 +1,227 @@
+import numpy as np
+import pandas as pd
+import pytest
+import xarray as xr
+from xarray.core.indexes import IndexSelResult, PandasIndex
+
+from pint_xarray import unit_registry as ureg
+from pint_xarray.index import PintIndex
+
+
+def indexer_equal(first, second):
+    if type(first) is not type(second):
+        return False
+
+    if isinstance(first, np.ndarray):
+        return np.all(first == second)
+    else:
+        return first == second
+
+
+ at pytest.mark.parametrize(
+    "base_index",
+    [
+        PandasIndex(pd.Index([1, 2, 3]), dim="x"),
+        PandasIndex(pd.Index([0.1, 0.2, 0.3]), dim="x"),
+        PandasIndex(pd.Index([1j, 2j, 3j]), dim="y"),
+    ],
+)
+ at pytest.mark.parametrize("units", [ureg.Unit("m"), ureg.Unit("s")])
+def test_init(base_index, units):
+    index = PintIndex(index=base_index, units=units)
+
+    assert index.index.equals(base_index)
+    assert index.units == units
+
+
+def test_replace():
+    old_index = PandasIndex([1, 2, 3], dim="y")
+    new_index = PandasIndex([0.1, 0.2, 0.3], dim="x")
+
+    old = PintIndex(index=old_index, units=ureg.Unit("m"))
+    new = old._replace(new_index)
+
+    assert new.index.equals(new_index)
+    assert new.units == old.units
+    # no mutation
+    assert old.index.equals(old_index)
+
+
+ at pytest.mark.parametrize(
+    ["wrapped_index", "units", "expected"],
+    (
+        pytest.param(
+            PandasIndex(pd.Index([1, 2, 3]), dim="x"),
+            {"x": ureg.Unit("m")},
+            {"x": xr.Variable("x", ureg.Quantity([1, 2, 3], "m"))},
+        ),
+        pytest.param(
+            PandasIndex(pd.Index([1j, 2j, 3j]), dim="y"),
+            {"y": ureg.Unit("ms")},
+            {"y": xr.Variable("y", ureg.Quantity([1j, 2j, 3j], "ms"))},
+        ),
+    ),
+)
+def test_create_variables(wrapped_index, units, expected):
+    index = PintIndex(index=wrapped_index, units=units)
+
+    actual = index.create_variables()
+
+    assert list(actual.keys()) == list(expected.keys())
+    assert all([actual[k].equals(expected[k]) for k in expected.keys()])
+
+
+ at pytest.mark.parametrize(
+    ["labels", "expected"],
+    (
+        ({"x": ureg.Quantity(1, "m")}, IndexSelResult(dim_indexers={"x": 0})),
+        ({"x": ureg.Quantity(3000, "mm")}, IndexSelResult(dim_indexers={"x": 2})),
+        ({"x": ureg.Quantity(0.002, "km")}, IndexSelResult(dim_indexers={"x": 1})),
+        (
+            {"x": ureg.Quantity([0.002, 0.004], "km")},
+            IndexSelResult(dim_indexers={"x": np.array([1, 3])}),
+        ),
+        (
+            {"x": slice(ureg.Quantity(2, "m"), ureg.Quantity(3000, "mm"))},
+            IndexSelResult(dim_indexers={"x": slice(1, 3)}),
+        ),
+    ),
+)
+def test_sel(labels, expected):
+    index = PintIndex(
+        index=PandasIndex(pd.Index([1, 2, 3, 4]), dim="x"), units={"x": ureg.Unit("m")}
+    )
+
+    actual = index.sel(labels)
+
+    assert isinstance(actual, IndexSelResult)
+    assert list(actual.dim_indexers.keys()) == list(expected.dim_indexers.keys())
+    assert all(
+        [
+            indexer_equal(actual.dim_indexers[k], expected.dim_indexers[k])
+            for k in expected.dim_indexers.keys()
+        ]
+    )
+
+
+ at pytest.mark.parametrize(
+    "indexers",
+    ({"y": 0}, {"y": [1, 2]}, {"y": slice(0, None, 2)}, {"y": xr.Variable("y", [1])}),
+)
+def test_isel(indexers):
+    wrapped_index = PandasIndex(pd.Index([1, 2, 3, 4]), dim="y")
+    index = PintIndex(index=wrapped_index, units={"y": ureg.Unit("s")})
+
+    actual = index.isel(indexers)
+
+    wrapped_ = wrapped_index.isel(indexers)
+    if wrapped_ is not None:
+        expected = PintIndex(
+            index=wrapped_index.isel(indexers), units={"y": ureg.Unit("s")}
+        )
+    else:
+        expected = None
+
+    assert (actual is None and expected is None) or actual.equals(expected)
+
+
+ at pytest.mark.parametrize(
+    ["other", "expected"],
+    (
+        (
+            PintIndex(
+                index=PandasIndex(pd.Index([1, 2, 3, 4]), dim="x"),
+                units={"x": ureg.Unit("cm")},
+            ),
+            True,
+        ),
+        (PandasIndex(pd.Index([1, 2, 3, 4]), dim="x"), False),
+        (
+            PintIndex(
+                index=PandasIndex(pd.Index([1, 2, 3, 4]), dim="x"),
+                units={"x": ureg.Unit("m")},
+            ),
+            False,
+        ),
+        (
+            PintIndex(
+                index=PandasIndex(pd.Index([1, 2, 3, 4]), dim="y"),
+                units={"y": ureg.Unit("cm")},
+            ),
+            False,
+        ),
+        (
+            PintIndex(
+                index=PandasIndex(pd.Index([1, 3, 3, 4]), dim="x"),
+                units={"x": ureg.Unit("cm")},
+            ),
+            False,
+        ),
+    ),
+)
+def test_equals(other, expected):
+    index = PintIndex(
+        index=PandasIndex(pd.Index([1, 2, 3, 4]), dim="x"), units={"x": ureg.Unit("cm")}
+    )
+
+    actual = index.equals(other)
+
+    assert actual == expected
+
+
+ at pytest.mark.parametrize(
+    ["shifts", "expected_index"],
+    (
+        ({"x": 0}, PandasIndex(pd.Index([-2, -1, 0, 1, 2]), dim="x")),
+        ({"x": 1}, PandasIndex(pd.Index([2, -2, -1, 0, 1]), dim="x")),
+        ({"x": 2}, PandasIndex(pd.Index([1, 2, -2, -1, 0]), dim="x")),
+        ({"x": -1}, PandasIndex(pd.Index([-1, 0, 1, 2, -2]), dim="x")),
+        ({"x": -2}, PandasIndex(pd.Index([0, 1, 2, -2, -1]), dim="x")),
+    ),
+)
+def test_roll(shifts, expected_index):
+    index = PintIndex(
+        index=PandasIndex(pd.Index([-2, -1, 0, 1, 2]), dim="x"),
+        units={"x": ureg.Unit("m")},
+    )
+
+    actual = index.roll(shifts)
+    expected = index._replace(expected_index)
+
+    assert actual.equals(expected)
+
+
+ at pytest.mark.parametrize("dims_dict", ({"y": "x"}, {"y": "z"}))
+ at pytest.mark.parametrize("name_dict", ({"y2": "y3"}, {"y2": "y1"}))
+def test_rename(name_dict, dims_dict):
+    wrapped_index = PandasIndex(pd.Index([1, 2], name="y2"), dim="y")
+    index = PintIndex(index=wrapped_index, units={"y": ureg.Unit("m")})
+
+    actual = index.rename(name_dict, dims_dict)
+    expected = PintIndex(
+        index=wrapped_index.rename(name_dict, dims_dict), units=index.units
+    )
+
+    assert actual.equals(expected)
+
+
+ at pytest.mark.parametrize("indexer", ([0], slice(0, 2)))
+def test_getitem(indexer):
+    wrapped_index = PandasIndex(pd.Index([1, 2], name="y2"), dim="y")
+    index = PintIndex(index=wrapped_index, units={"y": ureg.Unit("m")})
+
+    actual = index[indexer]
+    expected = PintIndex(index=wrapped_index[indexer], units=index.units)
+
+    assert actual.equals(expected)
+
+
+ at pytest.mark.parametrize("wrapped_index", (PandasIndex(pd.Index([1, 2]), dim="x"),))
+def test_repr_inline(wrapped_index):
+    index = PintIndex(index=wrapped_index, units=ureg.Unit("m"))
+
+    # TODO: parametrize
+    actual = index._repr_inline_(90)
+
+    assert "PintIndex" in actual
+    assert wrapped_index.__class__.__name__ in actual


=====================================
pint_xarray/tests/utils.py
=====================================
@@ -1,5 +1,6 @@
 import re
 from contextlib import contextmanager
+from textwrap import indent
 
 import numpy as np
 import pytest
@@ -7,13 +8,13 @@ from pint import Quantity
 from xarray import DataArray, Variable
 from xarray.testing import assert_equal, assert_identical  # noqa: F401
 
-from ..conversion import (
+from pint_xarray.conversion import (
     array_strip_units,
     extract_indexer_units,
     strip_units,
     strip_units_variable,
 )
-from ..testing import assert_units_equal  # noqa: F401
+from pint_xarray.testing import assert_units_equal  # noqa: F401
 
 
 def importorskip(name):
@@ -97,6 +98,33 @@ def assert_indexer_equal(a, b):
         assert a_ == b_, f"different values: {a_!r} ←→ {b_!r}"
 
 
+def assert_indexers_equal(first, second):
+    __tracebackhide__ = True
+    # same keys
+    assert first.keys() == second.keys(), "different keys"
+
+    errors = {}
+    for name in first:
+        first_value = first[name]
+        second_value = second[name]
+
+        try:
+            assert_indexer_equal(first_value, second_value)
+        except AssertionError as e:
+            errors[name] = e
+
+    if errors:
+        message = "\n".join(
+            ["indexers are not equal:"]
+            + [
+                f" - {name}:\n{indent(str(error), ' ' * 4)}"
+                for name, error in errors.items()
+            ]
+        )
+
+        raise AssertionError(message)
+
+
 def assert_indexer_units_equal(a, b):
     __tracebackhide__ = True
 


=====================================
pyproject.toml
=====================================
@@ -1,29 +1,28 @@
 [project]
 name = "pint-xarray"
 authors = [
-    {name = "Tom Nicholas", email = "tomnicholas1 at googlemail.com"}
+  { name = "Tom Nicholas", email = "tomnicholas1 at googlemail.com" },
 ]
 description = "Physical units interface to xarray using Pint"
-license = {text = "Apache-2"}
+license = { text = "Apache-2" }
 readme = "README.md"
 classifiers = [
-    "Development Status :: 3 - Alpha",
-    "Environment :: Console",
-    "Intended Audience :: Science/Research",
-    "License :: OSI Approved :: Apache Software License",
-    "Operating System :: OS Independent",
-    "Programming Language :: Python",
-    "Programming Language :: Python :: 3.9",
-    "Programming Language :: Python :: 3.10",
-    "Programming Language :: Python :: 3.11",
-    "Programming Language :: Python :: 3.12",
-    "Topic :: Scientific/Engineering",
-]
-requires-python = ">=3.9"
+  "Development Status :: 3 - Alpha",
+  "Environment :: Console",
+  "Intended Audience :: Science/Research",
+  "License :: OSI Approved :: Apache Software License",
+  "Operating System :: OS Independent",
+  "Programming Language :: Python",
+  "Programming Language :: Python :: 3.10",
+  "Programming Language :: Python :: 3.11",
+  "Programming Language :: Python :: 3.12",
+  "Topic :: Scientific/Engineering",
+]
+requires-python = ">=3.10"
 dependencies = [
-    "numpy >= 1.23",
-    "xarray >= 2022.06.0",
-    "pint >= 0.21",
+  "numpy >= 1.23",
+  "xarray >= 2022.06.0",
+  "pint >= 0.21",
 ]
 dynamic = ["version"]
 
@@ -33,8 +32,8 @@ Documentation = "https://pint-xarray.readthedocs.io/en/stable"
 
 [tool.setuptools.packages.find]
 include = [
-    "pint_xarray",
-    "pint_xarray.tests",
+  "pint_xarray",
+  "pint_xarray.tests",
 ]
 
 [build-system]
@@ -47,9 +46,55 @@ fallback_version = "999"
 [tool.pytest.ini_options]
 junit_family = "xunit2"
 
-[tool.isort]
-profile = "black"
-skip_gitignore = "true"
-force_to_top = "true"
-default_section = "THIRDPARTY"
-known_first_party = "pint_xarray"
+[tool.ruff]
+target-version = "py310"
+builtins = ["ellipsis"]
+exclude = [
+  ".git",
+  ".eggs",
+  "build",
+  "dist",
+  "__pycache__",
+]
+line-length = 100
+
+[tool.ruff.lint]
+# E402: module level import not at top of file
+# E501: line too long - let black worry about that
+# E731: do not assign a lambda expression, use a def
+ignore = [
+  "E402",
+  "E501",
+  "E731",
+  "UP038",
+]
+select = [
+  "F",   # Pyflakes
+  "E",   # Pycodestyle
+  "I",   # isort
+  "UP",  # Pyupgrade
+  "TID", # flake8-tidy-imports
+  "W",
+]
+extend-safe-fixes = [
+  "TID252", # absolute imports
+]
+fixable = ["I", "TID252"]
+
+[tool.ruff.lint.isort]
+known-first-party = ["pint_xarray"]
+known-third-party = [
+  "xarray",
+]
+
+[tool.ruff.lint.flake8-tidy-imports]
+# Disallow all relative imports.
+ban-relative-imports = "all"
+
+[tool.coverage.run]
+source = ["pint_xarray"]
+branch = true
+
+[tool.coverage.report]
+show_missing = true
+exclude_lines = ["pragma: no cover", "if TYPE_CHECKING"]



View it on GitLab: https://salsa.debian.org/debian-gis-team/pint-xarray/-/commit/fbb67a3ebcf5aa406a2b7cd7ce64749bff8cf30f

-- 
View it on GitLab: https://salsa.debian.org/debian-gis-team/pint-xarray/-/commit/fbb67a3ebcf5aa406a2b7cd7ce64749bff8cf30f
You're receiving this email because of your account on salsa.debian.org.


-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://alioth-lists.debian.net/pipermail/pkg-grass-devel/attachments/20250810/5747ab65/attachment-0001.htm>


More information about the Pkg-grass-devel mailing list