Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
9ef20fc
feat: Models the core contract needs
Oct 2, 2026
771d48a
Feat: Protocols and Exceptions added for the core contract
Oct 2, 2026
5fe30c2
Feat: Private protocols for the BatchBackend
Oct 2, 2026
89d0bc9
feat: a fake backend to run unit tests against
Oct 6, 2026
af4c436
fix: some code cleanup in the helper functions
Oct 6, 2026
99f82ae
feat: setup for the unit test suite
Oct 6, 2026
e041acf
chore: style fix
Oct 6, 2026
d287307
chore: styling, unit test fixture
Oct 6, 2026
e830545
chore: single letter var `h` refering to hanlde changed to `handle`
Oct 6, 2026
be1e998
fix: unused parameter in `test_injected_failure_is_reported_per_job()…
Oct 6, 2026
2aeb589
chore: Making room in local fork for my documentation so i can easily…
Oct 7, 2026
5024ce9
fix: placed in the wrong folder :/
Oct 7, 2026
2c32012
chore: added testplan
Oct 7, 2026
3bf4210
feat: Models the core contract needs
Oct 2, 2026
0a6a9e1
Feat: Protocols and Exceptions added for the core contract
Oct 2, 2026
36cf7df
Feat: Private protocols for the BatchBackend
Oct 2, 2026
357050e
feat: a fake backend to run unit tests against
Oct 6, 2026
56bce86
fix: some code cleanup in the helper functions
Oct 6, 2026
5923545
feat: setup for the unit test suite
Oct 6, 2026
1f92acf
chore: style fix
Oct 6, 2026
f7cf50c
chore: styling, unit test fixture
Oct 6, 2026
bab3989
chore: single letter var `h` refering to hanlde changed to `handle`
Oct 6, 2026
eb3c644
fix: unused parameter in `test_injected_failure_is_reported_per_job()…
Oct 6, 2026
f727cbb
Merge branch 'feat/core-contract' of github.com:Alexander-Auer/interc…
Oct 7, 2026
feaf5e7
fix: made the OutputSpec optional as it is not suppoerted by all and …
Oct 7, 2026
42dce34
fix: These documents should not exist in this way in the main repository
Oct 7, 2026
d67d380
fix: None check for the outputs in submissionspec
Oct 7, 2026
910b0b0
chore: trailing white spaces
Oct 7, 2026
a9fcca0
chore: fixed pre commit locally
Oct 7, 2026
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
1 change: 1 addition & 0 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -39,3 +39,4 @@ repos:
hooks:
- id: mypy
exclude: tests
additional_dependencies: ["pydantic>=2.0.0"]
6 changes: 6 additions & 0 deletions docs/index.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
# interCEde

Unified interfaces to Computing Elements and batch systems for DIRAC / DiracX.

The contract is described in IC-ADR-001. See the [API reference](user/api-reference.md)
for the protocols, data types and exceptions a consumer may import.
16 changes: 16 additions & 0 deletions docs/user/api-reference.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
# API reference

Everything listed here is importable from `intercede` and follows SemVer.
Anything else, including every name starting with `_`, is internal.

## Protocols

::: intercede.protocols

## Data types

::: intercede.models

## Exceptions

::: intercede.exceptions
25 changes: 25 additions & 0 deletions mkdocs.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
site_name: interCEde
site_description: Unified interfaces to Computing Elements and batch systems for DIRAC / DiracX
repo_url: https://github.com/DIRACGrid/intercede

theme:
name: material
logo: assets/intercede-icon.svg
favicon: assets/intercede-icon.svg

plugins:
- search
- mkdocstrings:
handlers:
python:
paths: [src]
options:
show_source: false
members_order: source
merge_init_into_class: true

validation:
links:
# The developer docs link to repository files outside docs/ (e.g. renovate.json),
# which resolve on GitHub but not in the built site.
not_found: info
22 changes: 18 additions & 4 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,9 @@ requires-python = ">=3.11"
keywords = []
license = { text = "GPL-3.0-only" }
dynamic = ["version"]
dependencies = []
dependencies = [
"pydantic>=2"
]
classifiers = [
"Intended Audience :: Science/Research",
"License :: OSI Approved :: GNU General Public License v3 (GPLv3)",
Expand All @@ -39,9 +41,6 @@ testing = [
"ty",
]

[tool.setuptools.packages.find]
where = ["src"]

[tool.pytest.ini_options]
pythonpath = ["src"]
testpaths = ["tests"]
Expand All @@ -51,6 +50,10 @@ markers = [
"xfail_backend: strict xfail a test on the named backend fixture(s)",
]

[tool.mypy]
strict = true
plugins = ["pydantic.mypy"]
files = ["src"]

[tool.ruff]
src = ["src", "tests"]
Expand Down Expand Up @@ -113,6 +116,7 @@ py311 = { features = ["testing", "py311"] }
py312 = { features = ["testing", "py312"] }
py313 = { features = ["testing", "py313"] }
py314 = { features = ["testing", "py314"] }
docs = { features = ["docs"], solve-group = "default" }

[tool.pixi.feature.testing.pypi-dependencies]
intercede = { path = ".", editable = true }
Expand All @@ -125,6 +129,16 @@ pre-commit = "*"
pre-commit = { cmd = "pre-commit", description = "Run pre-commit" }


# Features for building the documentation
[tool.pixi.feature.docs.pypi-dependencies]
intercede = { path = ".", editable = true }
mkdocs = "<2"
mkdocs-material = "*"
mkdocstrings = { version = "*", extras = ["python"] }
[tool.pixi.feature.docs.tasks]
docs = { cmd = "mkdocs build --strict", description = "Build the documentation" }


[tool.pixi.tasks]
unit-test = { cmd = "pytest -v", description = "Run the unit tests" }

Expand Down
72 changes: 69 additions & 3 deletions src/intercede/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,76 @@
from importlib.metadata import PackageNotFoundError
from importlib.metadata import version as get_version

from intercede.exceptions import (
AuthenticationError,
ConfigurationError,
InterCEdeError,
ResourceUnavailableError,
SpecificationRejectedError,
)
from intercede.models import (
ContainerSpec,
CopyVars,
Diagnostics,
FileRef,
JobHandle,
JobID,
JobOutput,
JobStatus,
OpOutcome,
OutputMember,
OutputSpec,
Resources,
StatusMap,
Submission,
SubmissionSpec,
)
from intercede.protocols import (
Diagnosable,
JobBackend,
Killable,
OutputRetriever,
Purgeable,
StatusReporter,
Submitter,
)

try:
__version__ = get_version(__name__)
version = __version__
except PackageNotFoundError:
version = "Unknown"
__version__ = "unknown"
version = __version__

__all__ = ["__version__"]
__all__ = [
"__version__",
# protocols
"Diagnosable",
"JobBackend",
"Killable",
"OutputRetriever",
"Purgeable",
"StatusReporter",
"Submitter",
# data types
"ContainerSpec",
"CopyVars",
"Diagnostics",
"FileRef",
"JobHandle",
"JobID",
"JobOutput",
"JobStatus",
"OpOutcome",
"OutputMember",
"OutputSpec",
"Resources",
"StatusMap",
"Submission",
"SubmissionSpec",
# exceptions
"AuthenticationError",
"ConfigurationError",
"InterCEdeError",
"ResourceUnavailableError",
"SpecificationRejectedError",
]
1 change: 1 addition & 0 deletions src/intercede/_backends/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
"""Internal backend implementations. Nothing here is public API."""
1 change: 1 addition & 0 deletions src/intercede/_backends/batch/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
"""The composed backend: a transport combined with a scheduler (IC-ADR-001 §4)."""
78 changes: 78 additions & 0 deletions src/intercede/_backends/batch/protocols.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
"""Internal collaborator protocols of the composed backend (IC-ADR-001 §4).

Shapes only: transports and schedulers are implemented in later issues.
"""

from __future__ import annotations

from collections.abc import Mapping, Sequence
from pathlib import Path
from types import TracebackType
from typing import Protocol, Self, runtime_checkable

from pydantic import BaseModel, ConfigDict

from intercede.models import CopyVars, JobID, JobStatus, SubmissionSpec

Command = Sequence[str] # an argv: only argv and staged files ever cross the transport
# One resource, so its own IDs are unique: no need for handles here.
SchedulerStatusMap = Mapping[JobID, JobStatus]


class CommandResult(BaseModel):
"""The outcome of running one command on the resource."""

model_config = ConfigDict(frozen=True)

returncode: int
stdout: str
stderr: str


@runtime_checkable
class Transport(Protocol):
"""How InterCEde reaches a resource: local shell, SSH, ...

Transports hold connections, so they share the backend lifecycle: cheap
construction with no I/O, and an idempotent close.
"""

async def __aenter__(self) -> Self: ...

async def __aexit__(
self,
exc_type: type[BaseException] | None,
exc: BaseException | None,
tb: TracebackType | None,
) -> None: ...

async def run(self, argv: Command) -> CommandResult: ...

async def put(self, local: Path, remote: str) -> None: ...

async def get(self, remote: str, local: Path) -> None: ...


@runtime_checkable
class Scheduler(Protocol):
"""How work is queued: builds commands locally and parses their output."""

stages_own_files: bool # does the scheduler move the sandbox itself?

def submit_command(
self, spec: SubmissionSpec, copies: Sequence[CopyVars], workdir: str
) -> Command:
"""Build the command that queues every copy; ``workdir`` is where the sandbox was staged."""
...

def parse_submit(self, raw: str) -> Sequence[JobID]:
"""Read the IDs from the submit command's output, one per copy, in copy order."""
...

def status_command(self, ids: Sequence[JobID]) -> Command: ...

def parse_status(self, raw: str, ids: Sequence[JobID]) -> SchedulerStatusMap:
"""Return a status for every id asked about; ids missing from ``raw`` are ``UNKNOWN``."""
...

def kill_command(self, ids: Sequence[JobID]) -> Command: ...
Loading
Loading