Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
82 changes: 8 additions & 74 deletions .github/workflows/ci_tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,14 @@ jobs:
tox_env: 'py312-alldeps-dask-enforce-cov'
codecov_flags: 'dask'

# array-api-strict on a non-default device, a CPU-only proxy for
# CuPy device behavior. Promoted from the expected-failures job:
# the suite is green on this backend, so failures now fail CI.
- name: 'ubuntu-py313-strict'
os: ubuntu-latest
python: '3.13'
tox_env: 'py313-strict'

- name: 'windows-py312'
os: windows-latest
python: '3.12'
Expand Down Expand Up @@ -131,77 +139,3 @@ jobs:
uses: codecov/codecov-action@fb8b3582c8e4def4969c97caa2f19720cb33a72f # v7.0.0
with:
flags: ${{ matrix.codecov_flags }}

# The strict array-API job is in its own matrix so that its failures --
# expected until the remaining array-API bugs are fixed -- stay visible
# without cancelling the main test matrix above. The test step uses
# continue-on-error so the job (and the PR checks rollup) stays green;
# the real outcome is reported as a warning annotation, in the step
# summary, and via a per-matrix-entry strict-job-outcome-* artifact that
# strict_status.yml turns into a check run on the PR.
ci-tests-expected-failures:
name: ${{ matrix.name }}
runs-on: ${{ matrix.os }}
if: "!(contains(github.event.head_commit.message, '[skip ci]') || contains(github.event.head_commit.message, '[ci skip]'))"
strategy:
matrix:
include:
- name: 'ubuntu-py313-strict'
os: ubuntu-latest
python: '3.13'
tox_env: 'py313-strict'

steps:
- name: Check out repository
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
fetch-depth: 0
- name: Set up Python ${{ matrix.python }}
uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0
with:
python-version: ${{ matrix.python }}
- name: Install base dependencies
run: |
python -m pip install --upgrade pip
python -m pip install tox wheel
- name: Print Python env
run: |
python --version
python -m pip list
- name: Run tests
id: tests
continue-on-error: true
run: |
tox -e ${{ matrix.tox_env }} -- ${{ matrix.toxposargs }}
# always() so an outcome file is recorded even when an earlier step
# failed; a skipped/cancelled tests step is recorded as "error" (an
# infrastructure failure) rather than being conflated with an expected
# test failure. The JSON schema here is the contract with
# strict_status.yml: {"name": "<matrix name>", "state": "success" |
# "failure" | "error"}.
- name: Record test outcome
if: always()
run: |
case "${{ steps.tests.outcome }}" in
success) state='success' ;;
failure) state='failure' ;;
*) state='error' ;;
esac
printf '{"name": "%s", "state": "%s"}\n' '${{ matrix.name }}' "$state" > strict-outcome.json
if [ "$state" = "failure" ]; then
echo "::warning::${{ matrix.tox_env }} tests failed (expected until the remaining array-API bugs are fixed)"
echo ":warning: **${{ matrix.name }}**: tests **failed** (expected until the remaining array-API bugs are fixed)" >> "$GITHUB_STEP_SUMMARY"
elif [ "$state" = "success" ]; then
echo ":tada: **${{ matrix.name }}**: tests **passed** -- the expected-failures carve-out for this job can be retired" >> "$GITHUB_STEP_SUMMARY"
else
echo "::warning::${{ matrix.name }} job hit an infrastructure error before the tests step completed (outcome: ${{ steps.tests.outcome }})"
echo ":x: **${{ matrix.name }}**: job hit an **infrastructure error** before the tests step completed (outcome: ${{ steps.tests.outcome }})" >> "$GITHUB_STEP_SUMMARY"
fi
- name: Upload test outcome
if: always()
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # 7.0.1
with:
# Include the matrix entry name so the artifact names stay unique if
# this matrix grows (upload-artifact v4+ errors on duplicate names).
name: strict-job-outcome-${{ matrix.name }}
path: strict-outcome.json
147 changes: 0 additions & 147 deletions .github/workflows/strict_status.yml

This file was deleted.

6 changes: 6 additions & 0 deletions CHANGES.rst
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,9 @@ New Features
Other Changes and Additions
^^^^^^^^^^^^^^^^^^^^^^^^^^^

- The ``py313-strict`` CI job is now part of the regular CI matrix and fails
CI on a test failure; the expected-failures carve-out and the
``strict_status.yml`` reporting workflow are retired. [#1005]
- Add triage tooling for array-API backend failures: ``backend_xfail`` /
``backend_skip`` markers, an escape-site report
(``CCDPROC_TRIAGE_ESCAPES=1``), and an escape logger
Expand All @@ -37,6 +40,9 @@ Other Changes and Additions
Bug Fixes
^^^^^^^^^

- ``flat_correct`` now checks the flat's mask with ``xp.any`` instead of the
``mask.any()`` method, which arrays of spec-only array-API namespaces do
not have. [#1005]
- Exclude masked and clipped pixels, and their weights, when computing weighted
average combinations. [#952]
- ``cosmicray_median`` now honors the mask of a ``CCDData`` or masked-array
Expand Down
2 changes: 1 addition & 1 deletion ccdproc/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -1266,7 +1266,7 @@ def flat_correct(ccd, flat, min_value=None, norm_value=None, xp=None):
# Set masked values to unity; the array element remains masked, but the data
# value is set to unity to avoid runtime divide-by-zero errors that are due
# to a masked value being set to 0.
if _flat_normed.mask is not None and _flat_normed.mask.any():
if _flat_normed.mask is not None and xp.any(_flat_normed.mask):
_flat_normed.data = xpx.at(_flat_normed.data)[_flat_normed.mask].set(1.0)

# divide through the flat
Expand Down
11 changes: 6 additions & 5 deletions ccdproc/tests/test_ccdproc.py
Original file line number Diff line number Diff line change
Expand Up @@ -754,6 +754,12 @@ def test_subtract_dark_fails():
subtract_dark(ccd_data, small_master)


@pytest.mark.backend_xfail(
"array-api-strict",
reason="astropy's arithmetic mixin builds the Quantity with "
"``data << unit``, and array-api-strict rejects the astropy unit "
"operand (astropy is not array-API aware, see #936)",
)
def test_unit_mismatch_behaves_as_expected():
ccd_data = ccd_data_func()
"""
Expand Down Expand Up @@ -818,11 +824,6 @@ def test_flat_correct():
assert flat_data.header == ccd_data.header


@pytest.mark.backend_xfail(
"array-api-strict",
reason="the CCDData array-API wrapper passes the Python bool type to "
"array-api-strict when copying a mask",
)
def test_flat_correct_masked_flat_with_immutable_array():
ccd_values = [[8.0, 8.0], [8.0, 8.0]]
flat_values = [[2.0, 0.0], [4.0, 8.0]]
Expand Down
17 changes: 16 additions & 1 deletion ccdproc/tests/test_rebin.py
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,22 @@ def test_rebin_smaller():


# test rebinning with ccddata object
@pytest.mark.parametrize("mask_data, uncertainty", [(False, False), (True, True)])
@pytest.mark.parametrize(
"mask_data, uncertainty",
[
(False, False),
pytest.param(
True,
True,
marks=pytest.mark.backend_xfail(
"array-api-strict",
reason="astropy's CCDData.mask setter coerces the mask with "
"np.asarray, which fails for an array-api-strict array on "
"the non-default device",
),
),
],
)
def test_rebin_ccddata(mask_data, uncertainty):
ccd_data = ccd_data_func(data_size=10)
if mask_data:
Expand Down
22 changes: 6 additions & 16 deletions docs/array_api.rst
Original file line number Diff line number Diff line change
Expand Up @@ -129,22 +129,12 @@ Reading the strict CI signal
~~~~~~~~~~~~~~~~~~~~~~~~~~~~

The ``ubuntu-py313-strict`` job in the main CI matrix runs the test suite
against `array-api-strict`_, but it is marked ``continue-on-error``, so it
always shows green in the pull-request checks rollup regardless of the test
outcome. The real result is posted as a separate check run, also named
``ubuntu-py313-strict``, by the "Strict array API status" workflow. Its
conclusion is:

+ ``success`` -- the strict test suite passed;
+ ``neutral``, with a "failing (expected)" message -- the strict suite
failed, which is expected while known array-API bugs remain;
+ ``failure`` -- the job itself broke (an infrastructure problem rather
than the expected test failures).

Because that workflow is triggered by ``workflow_run``, it executes from the
repository's default branch: the separate check only appears once
the workflow file exists on the default branch, and changes to it take
effect only after they are merged.
against `array-api-strict`_ on a non-default device, a CPU-only proxy for
GPU-style device behavior. It is a regular matrix job: a test failure fails
the job and the pull-request checks rollup, like any other backend. A
failure there usually means a numpy-ism (a numpy-only method or type, a
missing ``device=``) crept into a code path that the more permissive
backends accept silently; reproduce it locally with ``tox -e strict``.

What limitations should I be aware of?
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Expand Down