Skip to content

ci(uv): install with uv and move the lazy-namespace check into the suite - #17

Merged
AlexanderSoftCode merged 2 commits into
mainfrom
ci/uv
Oct 30, 2026
Merged

AlexanderSoftCode merged 2 commits into
mainfrom
ci/uv

Conversation

@AlexanderSoftCode

Copy link
Copy Markdown
Owner

Context

CI spent most of each install step inside pip, and tests.yml checked a few things the suite already covers, or should. This PR moves both workflows to uv, stops manual release runs from publishing to PyPI, and moves the lazy-namespace check into the suite. Two commits, neither mechanical: 58df4e6 (uv and the publish guard) and 798d4d9 (namespace test, stub fix, CI steps removed).

uv (58df4e6)

Step times from the latest runs on main:

step time
tests.yml Install package (pip upgrade + pip install -e .) 5–6 s of an 18–20 s job
release Install build tooling (pip upgrade + build + twine) 9 s
release Install the wheel (4 verify jobs) 6–15 s

astral-sh/setup-uv replaces those with uv pip install --system -e ., uv build + uvx twine check --strict, and uv pip install --system dist/*.whl + uv pip check --system. It's pinned to v10.3.3, since there's no floating v10 tag, so updates are manual. setup-python still provides the interpreter, so every later python step is unchanged. uv's cache is off because restoring it costs about as much as downloading numpy and safetensors. As with pip, dependencies resolve to their latest release on every run, and there's no lockfile.

The time saved is a few seconds per job (the jobs run in parallel), plus around 15–20 s across the release build and verify jobs. The two publish jobs (~45 s together) are mostly upload time and don't change.

Manual release runs

publish-pypi had no if:, so a workflow_dispatch run from a branch went on to publish to PyPI if that version wasn't released yet, unless the pypi environment's protection rules stopped it. It now runs only on release: published. A manual run builds, verifies and stops at TestPyPI (skip-existing), which is also how this PR's release.yml changes can be tested.

Lazy namespace check (798d4d9)

tests.yml checked a hand-written list of 20 top-level names, so a new public name was only covered if someone also edited the workflow. tests/config/test_lazy_namespace.py instead finds every __init__.pyi under aether/, and for each package it checks that:

  • the stub's re-exports match its __all__
  • the stub's __all__ matches the runtime __all__ built from the lazy map
  • every name resolves

That enforces the CLAUDE.md rule that a public name goes in both the lazy map and the .pyi.

On main it fails for the top-level aether package. ae.Accuracy, ae.Optimizer, ae.Preprocess and ae.deserialize resolve at runtime because the top-level map merges the subpackage maps, but aether/__init__.pyi doesn't list them, so editors had no autocomplete for them. They're now in the stub.

Steps removed from tests.yml because the suite covers them:

  • Verify package imports: the suite imports aether first.
  • Verify lazy namespace resolves: replaced by the test above.
  • Verify required dependencies are declared: on a clean install, a missing dependency already fails the suite's imports.
  • Confirm runner has no GPU backend: config.py only enables CuPy when a device is present, so a cupy package leaking into the dependencies would skip the GPU tests, not fake them.

release.yml keeps its check that every submodule imports without a GPU backend.

Behavior changes

  • Editors and type checkers now see Accuracy, Optimizer, Preprocess and deserialize on top-level aether. Runtime is unchanged.
  • A manual run of release.yml no longer publishes to PyPI.
  • None to seeding or serialization.

Testing

  • python3 -m unittest discover tests: 1144 tests pass on NumPy and CuPy (65 skipped).
  • test_lazy_map_matches_stub_and_resolves fails on main's aether/__init__.pyi on both backends and passes with the stub fix. The file takes ~6 ms, and a fresh process running it isn't measurably slower than a bare import aether.

Every job spent 5-15 s in pip: upgrading pip, then installing two
dependencies (tests), build + twine (release build) or the wheel
(release verify). astral-sh/setup-uv (pinned to v10.3.3; there is no
floating v10 tag) replaces those steps:

  tests.yml    uv pip install --system -e .
  build        uv build, uvx twine check --strict
  verify       uv pip install --system dist/*.whl, uv pip check --system

setup-python still provides the interpreter, so `python` in later steps
is unchanged and 3.15 comes from the runner's tool cache. uv caching is
off: restoring a cache costs about what downloading numpy and
safetensors does. Dependencies still resolve to the latest release on
every run, as with pip; there is no lockfile.

publish-pypi now only runs on a published release. A workflow_dispatch
run builds, verifies and stops at TestPyPI (skip-existing), so the
release workflow can be exercised from a branch without publishing it.
tests.yml checked a hand-written list of 20 top-level names, so a new
public name was only covered if someone also edited the workflow.
tests/config/test_lazy_namespace.py walks every __init__.pyi under
aether/ instead and, per package, checks that the stub's re-exports
match its __all__, that the stub's __all__ matches the runtime __all__
built from the lazy map, and that every name resolves. That enforces
the rule that a public name goes in both the lazy map and the stub.

It found four names the top-level map resolves but aether/__init__.pyi
did not list: Accuracy, Optimizer, Preprocess and deserialize (merged in
from the subpackage maps), so editors had no completion for them. They
are now in the stub; nothing changes at runtime.

tests.yml drops the steps the suite already covers: the import check
(the suite imports aether first), the lazy-namespace list, the declared
dependencies check (a missing one fails the suite's imports on a clean
install) and the no-CuPy check (config only enables CuPy with a device,
so a leaked cupy dependency would skip the GPU tests, not fake them).
release.yml keeps its import-every-submodule check.
@AlexanderSoftCode
AlexanderSoftCode merged commit 9a0b7ef into main Oct 30, 2026
5 checks passed
@AlexanderSoftCode
AlexanderSoftCode deleted the ci/uv branch October 30, 2026 06:24
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