Skip to content

Fix numpy-isms in strict-backend test bodies (test hygiene) - #990

Merged
mwcraig merged 2 commits into
astropy:mainfrom
mwcraig:test-hygiene-strict
Aug 25, 2026
Merged

Fix numpy-isms in strict-backend test bodies (test hygiene)#990
mwcraig merged 2 commits into
astropy:mainfrom
mwcraig:test-hygiene-strict

Conversation

@mwcraig

@mwcraig mwcraig commented Aug 24, 2026

Copy link
Copy Markdown
Member

Summary

Test-only hygiene batch for the array-API migration (#971). Many
array-api-strict test failures were caused by numpy-only idioms in the
test bodies, not by bugs in ccdproc source. This PR fixes only files
under ccdproc/tests/.

Strict suite: 85 failed, 366 passed → 65 failed, 386 passed (36
skipped / 46 xfailed / 5 xpassed unchanged). Numpy backend stays at 0
failed (503 passed); dask backend on the four changed test files also
stays at 0 failed.

No CHANGES.rst entry — this is a test-only change.

Fixed causes (itemized)

  1. Test suite: test_subtract_overscan calls numpy-only .copy() on array-API arrays #969.copy() is not an array-API method. Replaced
    arr.copy() with xp.asarray(arr, copy=True) in
    test_ccdproc.py::test_subtract_overscan. Closes all 6 parametrized
    failures for that test as numpy-isms; two of the six
    ([True-False-False], [True-False-True], the median=True cases)
    still fail afterward because they hit the separate, already-tracked
    xp.median gap at core.py:629 (another agent is fixing that on a
    different branch) — the .copy() fix itself is still correct and is
    kept per the issue's scope.
  2. Test suite: raw np.zeros_like on backend-aware arrays in test_ccdproc_logging.py and test_rebin.py #970 — raw np.zeros_like mixed with backend arrays.
    test_ccdproc_logging.py::test_implicit_logging now builds its bias
    frame with xp.zeros_like (now passes). test_rebin.py::test_rebin_ccddata[True-True]
    was updated the same way, but it still fails afterward: astropy's
    CCDData.mask setter (astropy/nddata/compat.py) forces
    np.asarray(value, dtype=np.bool_), which can't convert an array on
    a non-default strict device. That's an astropy-internals limitation
    outside a test-only fix, so it's kept per the issue's scope and noted
    as a residual failure.
  3. test__overscan_schange used xp.allclose, which isn't in the
    array-API standard. Replaced with xp.all(xpx.isclose(...)).
  4. test_combiner.py numpy-only method calls (.mean(), .all()) on
    backend arrays, replaced with xp.mean(...) / xp.all(...):
    test_combiner_minmax, test_combiner_minmax_max,
    test_combiner_minmax_min.
  5. Broader numpy-isms diagnosed from actual strict tracebacks:
    • Single-axis indexing (arr[i]) on arrays with ndim > 1, which
      the array-API standard requires an explicit ellipsis for
      (arr[i, ...]): test_subtract_overscan_fails,
      test_user_supplied_combine_func_that_relies_on_masks[sum_combine].
    • int/float mixed arithmetic that strict's dtype promotion rejects:
      test_trim_with_wcs_alters_wcs (xp.asarray(shape) / 2 on an int
      array), test_pixelwise_weights, test_combiner_sum_weighted,
      test_combiner_sum_weighted_by_pixel,
      test_combiner_sum_weighted_with_mask,
      test_combiner_uncertainty_average,
      test_combiner_uncertainty_average_mask,
      test_combiner_uncertainty_median_mask,
      test_combiner_uncertainty_sum_mask (int-literal arrays passed to
      xp.std/xp.sqrt, and an unwrapped np.float64 from
      astropy.stats.median_absolute_deviation multiplied against a
      strict Array, following the existing float(...) cast pattern
      already used elsewhere in that file).
    • xp.asarray(list_of_arrays) ("Nested Arrays are not allowed"),
      replaced with xp.stack(...):
      test_user_supplied_combine_func_that_relies_on_masks[sum_combine].

Failures examined and deliberately left (with why)

  • test_ccd_process[*], test_ccd_process_gain_corrected,
    test_subtract_overscan[True-False-*] (median path) — the known,
    already-tracked xp.median gap at core.py:629; another agent is
    fixing this on a separate branch, explicitly out of scope here.
  • test_flat_correct*, test_gain_correct*, test_gain.py::* — source
    bugs in gain_correct/flat_correct (core.py:934, core.py:1023,
    core.py:1037): xp.asarray(...) called without device=, causing
    "Arrays from two different devices" errors from ccdproc source, not
    the test body.
  • test_flat_correct_data_uncertainty — intentionally uses a raw numpy
    array as uncertainty; the test's own comment documents this as a
    regression test for astropy's NDUncertainty explicitly checking for
    numpy.ndarray, to be removed once that's fixed upstream.
  • test_flat_correct_deviation, test_sigma_func_for_ccddata,
    test_combiner_median/average/sum/dtype,
    test_average_combine_uncertainty, test_median_combine_uncertainty,
    test_sum_combine_uncertainty, test_combine_result_uncertainty_and_mask[*]
    fail inside astropy.stats.median_absolute_deviation or
    CCDData.mask/Quantity construction forcing numpy conversion of a
    non-default-device strict array; astropy-internals, not test-body.
  • test_combiner_sigmaclip_high/low/single_pix — the test intentionally
    passes astropy.stats.median_absolute_deviation as dev_func, which
    forces a numpy conversion inside combiner.py's sigma_clipping;
    same astropy-internals limitation.
  • test_combine_average_fitsimages, test_combine_numpyndarray,
    test_combiner_image_file_collection_input,
    test_combine_image_file_collection_input,
    test_combine_average_ccddata, test_combine_limitedmem_fitsimages,
    test_combine_limitedmem_scale_fitsimages,
    test_combine_ccd_with_uncertainty_and_mask_from_fits[*] — explicitly
    out of scope per the task brief (combine() file-input handling
    returns plain numpy arrays).
  • test_clip_extrema_3d/alone/via_combine/with_other_rejection
    explicitly out of scope per the task brief (clip_extrema fancy
    indexing).
  • test_combiner_with_scaling, test_combiner_result_dtype,
    test_combine_overwrite_output, test_combiner_with_scaling_uncertainty[*]
    astropy ndarithmetic's np.result_type(ref, operand) can't convert
    a strict Array's dtype; astropy-internals ("Could not convert Array"
    pattern named as out of scope in the task brief).
  • test_writeable_after_combine[*]CCDData.write/mask conversion
    forcing numpy on a non-default device; source/astropy-internals.
  • test_3d_combiner_with_scaling, test_combiner_with_scaling (helper
    scale_by_mean) — tried fixing the .mean() numpy-ism, but it then
    exposed a genuine ccdproc source bug: Combiner.scaling's setter
    (combiner.py:327) calls xp.asarray(list_of_arrays) on a list of
    strict Arrays ("Nested Arrays are not allowed. Use stack instead."),
    and for test_combiner_with_scaling the test is blocked even earlier
    by the astropy ndarithmetic issue above. Per the task's revert
    policy for non-numbered items, these test-body changes were reverted
    back to the pre-existing (still-numpy) form and left failing.
  • test_transform_image[True-True] — the test's user-supplied tran = lambda arr: 10 * arr is applied generically to data, uncertainty, and
    mask by transform_image; ccdproc's array-API wrapper coerces the
    mask to bool before this call, so 10 * bool_array hits strict's
    "Only numeric dtypes are allowed in __rmul__". Making the test's
    transform function dtype-aware would change what the test exercises
    rather than being a simple hygiene fix, so left as-is.
  • test_unit_mismatch_behaves_as_expected — fails inside astropy's
    Quantity.__new__ forcing a numpy conversion via
    _arithmetic_data/_prepare_then_do_arithmetic; astropy-internals.
  • test_image_collection.py::test_generator_ccds_without_unit — fails
    only when run as part of the full suite (passes in isolation),
    indicating cross-test state leakage unrelated to array-API numpy-isms;
    left as a pre-existing, order-dependent issue outside this batch's
    scope.

Test plan

  • CCDPROC_ARRAY_LIBRARY=array-api-strict tox -e strict (from the
    main checkout's tox env): 85 failed, 366 passed → 65 failed, 386
    passed.
  • CCDPROC_ARRAY_LIBRARY=numpy full suite: 503 passed, 0 failed (no
    regression).
  • CCDPROC_ARRAY_LIBRARY=dask on the four changed test files: 181
    passed, 0 failed.
  • ruff check / ruff format --check clean on changed files.

Part of #971

🤖 Generated with Claude Code

https://claude.ai/code/session_01RQMJZUaaxfqGDk41GLSaFK

Many array-api-strict test failures were caused by numpy-only idioms in
the test bodies themselves rather than bugs in ccdproc source: .copy()
method calls on backend arrays, raw np.zeros_like mixed with backend
arrays, xp.allclose (not in the array-API standard), numpy-only
.mean()/.sum()/.std()/.all() method calls, int/float mixed arithmetic
that strict's dtype promotion rejects, and single-axis indexing on
arrays with ndim > 1 (which needs an explicit ellipsis under the
standard). This is a test-only hygiene change; no CHANGES.rst entry.

Fixes astropy#969
Fixes astropy#970
Part of astropy#971

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RQMJZUaaxfqGDk41GLSaFK
@codecov

codecov Bot commented Aug 24, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 97.42%. Comparing base (3e644d8) to head (288d55a).
⚠️ Report is 1 commits behind head on main.

Additional details and impacted files
@@           Coverage Diff           @@
##             main     #990   +/-   ##
=======================================
  Coverage   97.42%   97.42%           
=======================================
  Files           9        9           
  Lines        1749     1749           
=======================================
  Hits         1704     1704           
  Misses         45       45           
Flag Coverage Δ
dask 96.55% <ø> (ø)
jax 96.61% <ø> (ø)
numpy 97.31% <ø> (ø)

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

Comment thread ccdproc/tests/test_combiner.py
Comment thread ccdproc/tests/test_combiner.py
Comment thread ccdproc/tests/test_combiner.py Outdated
Review feedback on astropy#990: the explicit dtype at every literal reference
array was noise. Float literals give the same float64 arrays, so the
strict backend still sees floating input to xp.std and no int64/float64
promotion in the comparisons. The one dtype= left in the PR is the
crpix line in test_ccdproc.py, whose input is ccd_data.shape.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RQMJZUaaxfqGDk41GLSaFK
@mwcraig
mwcraig merged commit 5ce2fea into astropy:main Aug 25, 2026
19 checks passed
@mwcraig
mwcraig deleted the test-hygiene-strict branch August 25, 2026 13:34
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant