Skip to content

Python xarray conversion: CI never runs test_xarray.py; follow-ups to #6937 #6950

Open

Description

@hjmjohnson

#6937 fixes image_from_xarray() assigning origin and spacing to the wrong axes
for 4D images, a bug present since at least ITK 5.3. It survived because ITK's CI
never executes test_xarray.py. This issue lists the CI fix first, then the
remaining test and code follow-ups, each with explicit instructions.

1. Make CI run the xarray tests (highest value)

Two independent gates turn the test into a silent pass in every CI build:

  • Install xarray in the Python pipelines. Add xarray to the pip line in
    Testing/ContinuousIntegration/AzurePipelinesLinuxPython.yml (line 80),
    AzurePipelinesMacOSPython.yml (line 83), and
    AzurePipelinesWindowsPython.yml (line 62). Today the test hits its
    except ImportError: branch, prints "xarray not imported. Skipping xarray
    conversion tests", and exits 0.
  • Report the skip instead of passing. In
    Wrapping/Generators/Python/Tests/CMakeLists.txt (test PythonXarrayTest,
    line 223), add
    set_tests_properties(PythonXarrayTest PROPERTIES SKIP_REGULAR_EXPRESSION "xarray not imported")
    so CDash shows the test as skipped, not passed, wherever xarray is absent.
  • Wrap 4D images in at least one Python CI build. The 4D block in
    test_xarray.py (guarded by "(<itkCType unsigned char>, 4)" in itk.Image.GetTypesAsList(), line 136) only runs when 4D images are
    wrapped. Source builds default to ITK_WRAP_IMAGE_DIMS = 2;3
    (Wrapping/WrappingOptions.cmake, line 241). Set
    ITK_WRAP_IMAGE_DIMS:STRING=2;3;4 in one Python dashboard configuration
    (Linux is the cheapest). The PyPI wheels already wrap 4D.
  • Verify on the CI log that PythonXarrayTest executes the 4D asserts.

2. Tests to add in Wrapping/Generators/Python/Tests/test_xarray.py

All inside the existing 4D block:

  • 4D round trip (fails before BUG: Fix axis order in image_from_xarray origin/spacing #6937, passes after; verified locally on
    the ITK 5.4.7 wheel):
    python rt = itk.image_from_xarray(itk.xarray_from_image(image)) assert np.allclose(rt.GetSpacing(), image.GetSpacing()) assert np.allclose(rt.GetOrigin(), image.GetOrigin())
  • 4D input stored in non-canonical order: a DataArray with
    dims=["x", "y", "z", "t"] and distinct per-axis coords must give spacing
    (sx, sy, sz, st) and origin (ox, oy, oz, ot).
  • 4D vector image (dims=["t", "z", "y", "x", "c"]), guarded by the
    matching wrapped type, to cover the "c" path.

3. Code follow-ups in Wrapping/Generators/Python/itk/support/xarray.py

  • Non-suffix dimension sets raise ValueError. ordered_dims = ("t", "z", "y", "x")[-image_dimension:] (line 115) assumes the present dims
    are a suffix of (t, z, y, x). dims=("t", "y", "x"), ("t", "x"), or
    ("z", "x") fail with ValueError: tuple.index(x): x not in tuple. Either
    support them with
    ordered_dims = tuple(d for d in ("t", "z", "y", "x") if d in data_array.dims)
    plus a test, or raise a clear ValueError that names the supported
    combinations.
  • Remove the dead sort. After BUG: Fix axis order in image_from_xarray origin/spacing #6937, image_dims is used only for its
    length. Replace lines 112 to 114 with a direct count, for example
    image_dimension = len({"t", "z", "y", "x"}.intersection(data_array.dims)).
  • Remove the duplicate direction assignment in xarray_from_image
    (lines 66 and 85 compute the same value).
  • Size-1 axes lose their origin. if coords.shape[0] > 1: (line 133)
    skips both origin and spacing for a single-sample axis, so a single time
    point gets origin 0. Set the origin whenever coords exist and keep the
    spacing default for size-1 axes.
Evidence (local verification of #6937)

Isolated venv, ITK 5.4.7 wheel (wraps 4D unsigned char), xarray 2026.9.0. The
wheel's image_from_xarray is identical to the code #6937 changed; the "after"
column adds only #6937's one-line change.

Check Before #6937 After #6937
#6937's test_xarray.py fails at the new spacing assert passes
4D round trip image to xarray to image spacing (2,5,4,3), origin (100,30,20,10) preserved
4D DataArray in x,y,z,t order scrambled correct
3D (z,y,x) with coords correct unchanged
dims (t,y,x) ValueError ValueError

xarray_from_image always names a 4D image's fourth axis t, so before #6937
every 4D image round-tripped through xarray lost its geometry, not only
hand-built DataArrays.

The CI pip lines install numpy, dask, distributed, and lxml, but no
xarray; no pixi environment or workflow installs it either.

Activity

  1. added
    type:InfrastructureInfrastructure/ecosystem related changes, such as CMake or buildbots
    type:TestingEnsure that the purpose of a class is met/the results on a wide set of test cases are correct
    on Oct 17, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    area:Python wrappingPython bindings for a classtype:InfrastructureInfrastructure/ecosystem related changes, such as CMake or buildbotstype:TestingEnsure that the purpose of a class is met/the results on a wide set of test cases are correct

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions