Skip to content

Feat: Core contract (Models, Protocols, exceptions) issue #16 - #27

Open
Alexander-Auer wants to merge 33 commits into
DIRACGrid:mainfrom
Alexander-Auer:feat/core-contract
Open

Alexander-Auer wants to merge 33 commits into
DIRACGrid:mainfrom
Alexander-Auer:feat/core-contract

Conversation

@Alexander-Auer

@Alexander-Auer Alexander-Auer commented Oct 7, 2026 •

Copy link
Copy Markdown

feat: core contract (protocols, data types, typed errors)

Closes #16.

Note

Merge order. This PR is the first of four. Each later PR builds on the one before it and should be merged only after it:

Implements the Tier-A contract of IC-ADR-001: the protocols, data types and exceptions, with an in-memory FakeBackend and a unit conformance suite. There are no real backends and no registry yet.

What's included

Public API (intercede, exported via __all__)

  • Protocols (async, bulk, @runtime_checkable):
    • essential: Submitter, StatusReporter → JobBackend (an async context manager)
    • optional: OutputRetriever, Killable, Purgeable, Diagnosable
  • Data types (frozen pydantic models, extra="forbid"): SubmissionSpec, CopyVars, FileRef, OutputSpec, OutputMember, ContainerSpec, Resources, Submission, JobHandle, JobID, JobStatus, StatusMap, JobOutput, Diagnostics, OpOutcome
  • Exceptions: InterCEdeError → ConfigurationError, AuthenticationError, ResourceUnavailableError, SpecificationRejectedError

Internal (intercede._backends)

  • batch/protocols.py: the shapes of Transport and Scheduler, with no implementations
  • fake.py: FakeBackend, implementing every capability, with hooks for tests. mypy checks that it conforms to each protocol.

Package hygiene

  • py.typed, and strict mypy with the pydantic plugin (runs in CI through pre-commit)
  • pydantic>=2 as the only runtime dependency
  • an mkdocs API reference stub: pixi run -e docs docs

Contract rules enforced

  • File ownership: submit() reads local inputs before it returns and never keeps caller paths.
  • Lifecycle: construction does no I/O, closing is idempotent, and a closed backend raises.
  • Partial failure: whole-operation failures raise. Per-job failures come back as OpOutcome(ok=False, reason=...), and unknown jobs come back as UNKNOWN, never dropped.
  • Refuse at submit: a spec the backend cannot honour (URL staging, a container) raises SpecificationRejectedError.
  • Path containment: file names must stay inside the working directory, and output members never land outside dest.
  • Secrets: CopyVars.secrets uses SecretBytes, so it never appears in repr, logs or JSON.
  • Optional outputs: SubmissionSpec.outputs is optional, because not every backend can retrieve output and a consumer should only ask for it when it needs it.

Deviations from the ADR (feedback welcome)

  1. Per-job maps are keyed by JobHandle, not JobID. A job ID is only unique within one resource, so keying by ID would let job 1234 on two clusters overwrite each other. Handles are hashable through a key built from backend, resource and id, with : escaped inside each part.
  2. Submission.handles is a Mapping[int, JobHandle] keyed by copy index. With a list, one refused copy shifts every later handle. With a map, handles[i] always belongs to copies[i].
  3. OpOutcome[T] is generic and carries a value. This lets get_output and get_diagnostics report per-job failures too. kill and purge use OpOutcome[None], and a validator keeps ok, value and reason consistent.
  4. Scheduler gains parse_submit(raw), parse_status(raw, ids) and a workdir, which the composed backend needs. Transport gains the lifecycle methods.

If these are accepted, IC-ADR-001 should be updated to match.

Deliberately left out

Tests

  • tests/unit/test_models.py: validation rules (hashing, secrets, name checks, the OpOutcome and Submission invariants)
  • tests/unit/test_protocols.py: isinstance narrowing per capability
  • tests/unit/test_conformance.py: contract tests that use only the public API, through a backend fixture that honours backend_only and xfail_backend, so they can be reused against real backends
  • tests/unit/test_fake_backend.py: partial failure, refusal, path containment, repeatable output

All tests run against FakeBackend; no real scheduler or CE is involved.

How to check

pixi run -e py313 pytest
pixi run -e pre-commit pre-commit run --all-files
pixi run -e docs docs

alexander and others added 30 commits October 2, 2026 16:54
* feat: Models the core contract needs

* Feat: Protocols and Exceptions added for the core contract

* Feat: Private protocols for the BatchBackend

* feat: a fake backend to run unit tests against

* fix: some code cleanup in the helper functions

* feat: setup for the unit test suite

* chore: style fix

* chore: styling, unit test fixture

* chore: single letter var `h` refering to hanlde changed to `handle`

* fix: unused parameter in `test_injected_failure_is_reported_per_job()` removed

* feat: Models the core contract needs

* Feat: Protocols and Exceptions added for the core contract

* Feat: Private protocols for the BatchBackend

* feat: a fake backend to run unit tests against

* fix: some code cleanup in the helper functions

* feat: setup for the unit test suite

* chore: style fix

* chore: styling, unit test fixture

* chore: single letter var `h` refering to hanlde changed to `handle`

* fix: unused parameter in `test_injected_failure_is_reported_per_job()` removed

* fix: made the OutputSpec optional as it is not suppoerted by all and a consumer should choose to use it or not

---------

Co-authored-by: alexander <alexander.auer@cern.ch>
@Alexander-Auer
Alexander-Auer marked this pull request as draft October 9, 2026 11:33
@Alexander-Auer
Alexander-Auer marked this pull request as ready for review October 19, 2026 07:00

This branch has not been deployed

No deployments
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.

[Feature]: Core contract (protocols, data types, typed errors)

1 participant