Skip to content

feat(styxkit): add cross-backend convenience package - #13

Merged
nx10 merged 2 commits into
mainfrom
feat/styxkit
Jun 6, 2026
Merged

feat(styxkit): add cross-backend convenience package#13
nx10 merged 2 commits into
mainfrom
feat/styxkit

Conversation

@nx10

@nx10 nx10 commented Jun 6, 2026

Copy link
Copy Markdown
Contributor

@

What

Adds styxkit, a small convenience layer on top of the Styx runtime that offers one-call runner selection across whichever backend packages are installed.

import styxkit
styxkit.use_docker()            # or use_local / use_dry / use_podman / use_singularity / use_graph
runner = styxkit.use_auto()     # detect best available (docker > podman > singularity > local)

Why

The published niwrap 1.0.0 became a pure meta-package, so import niwrap; niwrap.use_docker() (and the whole use_*() family) disappeared in the v2 migration. In v1 those were generated into niwrap/__init__.py. Rather than re-emit that logic from the compiler, it belongs in the runtime - colocated with the runner classes it wraps, so a constructor change and its wrapper move together instead of drifting across repos. The generated niwrap will re-export styxkit instead.

Layering: styxdefs (base contract) -> individual backends -> styxkit on top. Acyclic: nothing in the lower tiers imports styxkit, and styxkit imports backends lazily, so it hard-depends only on styxdefs.

Design

  • Lazy + friendly: a use_*() for a backend you have not installed raises a ModuleNotFoundError naming the package/extra to install. Extras: docker / podman / singularity / graph / all.
  • Returns the runner from each use_*() (saves a get_global_runner() round-trip).
  • use_auto() / resolve_runner() pick the first container backend that is both installed and on PATH, passing the detected executable (apptainer vs singularity); falls back to local.
  • use_graph(base=None) wraps the current global runner, since GraphRunner decorates rather than being a leaf runner.
  • Patterns distilled from childmindresearch/rbc (the field-test). FreeSurfer-license and cache helpers intentionally left out as too domain-specific for the runtime.

Tests / gates

  • 10 new tests (lazy friendly-error, auto-detection + executable passthrough, global-runner registration).
  • ruff check + ruff format --check, mypy packages/, pytest packages/ all green repo-wide (70 passed / 1 skipped).
  • Wired into the uv workspace sources, the publish tag triggers (styxkit-v*), and the docs pdoc matrix.

Follow-ups (separate)

  • styx2 emitProject: emit a thin niwrap/__init__.py = from styxkit import * + the catalog @type dispatcher; depend on styxkit[all]. Publish styxkit to PyPI before niwrap regenerates against it.
  • styxcache.CachingRunner should forward base-runner attributes (would retire rbc's _CacheProxyingRunner shim).
    @

nx10 added 2 commits June 6, 2026 16:56
@
feat(styxkit): add cross-backend convenience package

styxkit sits on top of styxdefs and the backend packages, offering
one-call runner selection (use_local/use_dry/use_docker/use_podman/
use_singularity/use_graph) plus use_auto()/resolve_runner() PATH
auto-detection. Backends are imported lazily so styxkit hard-depends
only on styxdefs; each use_* for a missing backend raises a friendly
error naming the package/extra to install.

This is the runtime home for the runner-config sugar that styx v1
generated into niwraps __init__.py (and which the v2 niwrap meta
package dropped). The styx2 emitProject will re-export it instead of
re-emitting the logic.

- new package at packages/styxkit (0.1.0), MIT, py.typed, extras for
  docker/podman/singularity/graph/all
- wired into uv workspace sources + publish/docs CI matrices
- 10 tests; ruff/mypy/pytest green repo-wide
@
@
fix(styxkit): address review - honest use_dry signature + don't mask transitive import errors

- use_dry() takes no kwargs (DryRunner.__init__ is zero-arg; the old
  **kwargs signature would TypeError on any argument)
- _runner_factory only translates ModuleNotFoundError when the backend
  package itself is missing (exc.name == module); a transitive missing
  dep inside an installed backend now surfaces as-is instead of a
  misleading "install styxkit[x]"
- tests: guard real-backend tests with pytest.importorskip; the friendly
  -error test sets exc.name; new test locks in the transitive-error path
@
@nx10

nx10 commented Jun 6, 2026

Copy link
Copy Markdown
Contributor Author

Review summary (2 agents: correctness + packaging)

Ran two adversarial review passes before merge.

Correctness found two real bugs CI did not exercise, both now fixed in 427a7d1:

  • use_dry(**kwargs) would TypeError on any argument since DryRunner.__init__ is zero-arg. Now use_dry() takes no args.
  • _runner_factory's except ModuleNotFoundError was too broad: an installed backend failing to import a transitive dep (e.g. styxcontainer_common) got relabeled "install styxkit[docker]". It now only translates when the backend package itself is missing (exc.name == module), otherwise re-raises as-is. Added a regression test.
  • Also verified the _BACKENDS class/kwarg names against the real backend constructors (all match), and confirmed use_graph defaulting to the current global runner is sound (get_global_runner() never returns None).

Packaging verdict was mergeable; confirmed build-system / version pins / layout / README match the sibling packages, and the publish (styxkit-v*) + docs matrix wiring is correct. One non-blocking follow-up: the published [project.optional-dependencies] extras are unpinned - intentional for now (matches the unpinned-runner-deps convention) and revisitable before the first styxkit-v0.1.0 tag.

Test count 10 -> 11 (added pytest.importorskip guards + the transitive-error test). ruff / mypy / pytest green repo-wide.

@nx10
nx10 merged commit b016eb2 into main Jun 6, 2026
3 checks passed
@nx10
nx10 deleted the feat/styxkit branch June 6, 2026 21:13
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