Stop referencing a private function as a cross-reference #303
Workflow file for this run
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: tests | |
| # Every commit is tested exactly once, whether or not it belongs to a pull request. | |
| # | |
| # Both events used to be unfiltered, so a branch with an open PR ran the whole workflow | |
| # twice per push -- two identical builds of the same commit, which with the Coverage job | |
| # is around twelve minutes of duplicated runner time. GitHub attaches check runs to the | |
| # commit, not to the event, so the push-triggered run already appears on the pull request: | |
| # the second run bought nothing. | |
| # | |
| # `push` matches every branch, so any branch is covered, including one with no pull | |
| # request open (an earlier [main, dev] filter silently skipped every push to | |
| # dev-plotting) and one whose name follows no convention. Tags are excluded because a | |
| # release tag points at a commit that was already tested on its branch. | |
| on: | |
| push: | |
| # '**' matches every branch, including names containing a slash. It has to be spelled | |
| # out: a push filter naming only tags (even `tags-ignore: ['**']`) makes GitHub treat | |
| # the workflow as tag-only and run it for no branch push at all -- which silently | |
| # disabled every push build here until a pull request showed zero runs. Naming only | |
| # branches has the mirror effect, which is the intent: tags do not build, since a | |
| # release tag points at a commit its branch already tested. | |
| branches: ['**'] | |
| pull_request: | |
| # Superseded runs are not worth paying for: pushing twice to a branch in quick succession | |
| # cancels the older build. Not on main, where each commit is a merge that deserves its own | |
| # verification. | |
| concurrency: | |
| group: ${{ github.workflow }}-${{ github.ref }} | |
| cancel-in-progress: ${{ github.ref != 'refs/heads/main' }} | |
| jobs: | |
| pytest: | |
| # Pull-request events are kept only for forks. A branch in this repository fires a | |
| # push event, which is what runs the job above; a fork's branch does not, so without | |
| # this its pull requests would get no CI at all -- a hole that would open silently the | |
| # day the repository goes public and accepts an outside contribution. | |
| if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name != github.repository | |
| runs-on: ubuntu-latest | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| # `requires-python` is ">=3.10" with no upper bound, so pip will install this | |
| # package on 3.13 and later. Testing only up to 3.12 meant claiming interpreters | |
| # nothing ran. 3.13 is added here first; its classifier follows once this job has | |
| # been green, rather than the other way round. | |
| python-version: ['3.10', '3.11', '3.12', '3.13'] | |
| steps: | |
| - uses: actions/checkout@v7 | |
| - name: Set up Python ${{ matrix.python-version }} | |
| uses: actions/setup-python@v7 | |
| with: | |
| python-version: ${{ matrix.python-version }} | |
| - name: Install dependencies | |
| run: | | |
| python -m pip install --upgrade pip | |
| pip install -e '.[test]' | |
| - name: Run test suite | |
| # -n auto: the suite is embarrassingly parallel -- xdist gives each worker its | |
| # own process, so the module-level state some tests touch (disabled engines, the | |
| # once-per-session warning dedup) is isolated rather than shared. Measured 19m00s | |
| # -> 9m58s on four workers. More workers buy nothing: one test accounts for 596 s | |
| # of that, so it alone sets the floor. | |
| run: pytest tests/ -v -n auto | |
| coverage: | |
| # Deliberately a separate job rather than a fourth axis of the matrix above, or a flag | |
| # added to it. Instrumentation is not cheap here: measured locally, the suite goes | |
| # from 188 s to 394 s, a factor of 2.1 -- an unusually large penalty, because the hot | |
| # path is a per-slab Python loop rather than time spent inside numpy. Since what a | |
| # line or branch is exercised by does not depend on the interpreter, running it on all | |
| # three would pay that cost three times for one number. As its own job it runs in | |
| # parallel with the matrix, so it does not delay the pass/fail signal from the tests | |
| # themselves, but note it is the longest job in the workflow. | |
| # | |
| # (Python 3.12's sys.monitoring backend, COVERAGE_CORE=sysmon, does not help: it | |
| # cannot measure branches before 3.14, so coverage warns and silently falls back to | |
| # the tracing core. Verified -- it made no difference to the wall time.) | |
| name: Coverage | |
| # Same fork-only rule as the matrix job above. | |
| if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name != github.repository | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 30 | |
| env: | |
| # Read here at job level rather than on the upload step itself, because a step's own | |
| # `env:` block is not visible to that step's `if:` condition -- this is what lets the | |
| # Codecov step below switch itself off cleanly when the secret does not exist. | |
| CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} | |
| steps: | |
| - uses: actions/checkout@v7 | |
| - name: Set up Python | |
| uses: actions/setup-python@v7 | |
| with: | |
| python-version: '3.12' | |
| - name: Install dependencies | |
| run: | | |
| python -m pip install --upgrade pip | |
| pip install -e '.[test]' | |
| - name: Run test suite with coverage | |
| # Settings (branch coverage, which sources, what is omitted, and the 90% floor) | |
| # live in [tool.coverage] in pyproject.toml, so a local `pytest --cov` measures and | |
| # gates exactly as CI does -- no threshold is passed on the command line here, | |
| # which would let the two drift apart. | |
| run: pytest tests/ -q -n auto --cov --cov-report=term-missing --cov-report=xml | |
| - name: Publish coverage summary | |
| # Puts the table on the workflow run's summary page, so the number is readable | |
| # without opening the log or downloading anything. | |
| run: | | |
| { | |
| echo '### Coverage (statements and branches)' | |
| echo | |
| python -m coverage report --format=markdown | |
| } >> "$GITHUB_STEP_SUMMARY" | |
| - name: Upload coverage to Codecov | |
| # Dormant until a CODECOV_TOKEN repository secret exists: without one the condition | |
| # is false and the step is skipped, so this costs nothing and cannot fail while the | |
| # repository is private and no badge is wanted yet. Creating that secret is the | |
| # entire act of switching coverage reporting on -- at which point Codecov also | |
| # starts commenting per-PR diff coverage, which is the part that actually changes | |
| # behaviour, since it shows the coverage of the lines in front of you. | |
| # | |
| # Nothing is sent anywhere until the secret is deliberately added. | |
| # | |
| # fail_ci_if_error is on for the same reason ruff is blocking: an upload that | |
| # quietly fails would leave a badge frozen at a stale number under a green | |
| # checkmark, which is worse than a visible failure. It cannot affect the tests' | |
| # own pass/fail signal, which comes from the matrix job above. | |
| if: env.CODECOV_TOKEN != '' | |
| uses: codecov/codecov-action@v7 | |
| with: | |
| files: ./coverage.xml | |
| fail_ci_if_error: true | |
| - name: Upload coverage report | |
| # coverage.xml is the standard Cobertura format every coverage service consumes. | |
| # Publishing it as an artifact keeps the report available regardless of whether the | |
| # Codecov step above is active, and is what makes the figure inspectable today. | |
| uses: actions/upload-artifact@v7 | |
| with: | |
| name: coverage-xml | |
| path: coverage.xml | |
| if-no-files-found: error |