Skip to content

Commit a6de0af

Browse files
MaxGheniscodex
andauthored
Typed contract identity, artifact env sidecars, generic gate evaluator (#106 P0-2) (#152)
Co-authored-by: Codex gpt-5.6-sol <noreply@openai.com>
1 parent eac2078 commit a6de0af

7 files changed

Lines changed: 1440 additions & 18 deletions

File tree

src/populace_dynamics/artifacts.py

Lines changed: 56 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,11 @@
44

55
import json
66
import os
7+
from dataclasses import asdict
78
from pathlib import Path
89

10+
from populace_dynamics.contract import ContractRef, environment_block
11+
912

1013
def _already_exists(destination: Path) -> FileExistsError:
1114
return FileExistsError(
@@ -14,27 +17,67 @@ def _already_exists(destination: Path) -> FileExistsError:
1417
)
1518

1619

17-
def write_new(path: str | Path, data: object) -> None:
18-
"""Write an artifact without permitting an existing file to be replaced.
19-
20-
Strings and bytes are written verbatim. Other values are serialized as
21-
indented JSON with a trailing newline.
22-
"""
23-
destination = Path(path)
24-
if os.path.lexists(destination):
25-
raise _already_exists(destination)
26-
20+
def _payload(data: object) -> str | bytes:
2721
if isinstance(data, (str, bytes)):
28-
payload = data
29-
else:
30-
payload = json.dumps(data, indent=2) + "\n"
22+
return data
23+
return json.dumps(data, indent=2) + "\n"
3124

25+
26+
def _write_exclusive(destination: Path, payload: str | bytes) -> None:
27+
created = False
3228
try:
3329
if isinstance(payload, bytes):
3430
with destination.open("xb") as output:
31+
created = True
3532
output.write(payload)
3633
else:
3734
with destination.open("x", encoding="utf-8", newline="") as output:
35+
created = True
3836
output.write(payload)
3937
except FileExistsError:
4038
raise _already_exists(destination) from None
39+
except BaseException:
40+
if created:
41+
destination.unlink(missing_ok=True)
42+
raise
43+
44+
45+
def write_new(
46+
path: str | Path,
47+
data: object,
48+
*,
49+
sidecar: bool = False,
50+
) -> None:
51+
"""Write an artifact without permitting an existing file to be replaced.
52+
53+
Strings and bytes are written verbatim. Other values are serialized as
54+
indented JSON with a trailing newline. If ``sidecar`` is true, an
55+
environment and contract reference is written to ``<path>.env.json``.
56+
"""
57+
destination = Path(path)
58+
sidecar_destination = Path(f"{destination}.env.json")
59+
targets = (destination, sidecar_destination) if sidecar else (destination,)
60+
for target in targets:
61+
if os.path.lexists(target):
62+
raise _already_exists(target)
63+
64+
artifact_payload = _payload(data)
65+
sidecar_payload = None
66+
if sidecar:
67+
sidecar_payload = _payload(
68+
{
69+
"environment": environment_block(),
70+
"contract": asdict(ContractRef.current()),
71+
}
72+
)
73+
74+
primary_written = False
75+
try:
76+
_write_exclusive(destination, artifact_payload)
77+
primary_written = True
78+
if sidecar_payload is not None:
79+
_write_exclusive(sidecar_destination, sidecar_payload)
80+
except BaseException:
81+
if primary_written:
82+
destination.unlink(missing_ok=True)
83+
raise

src/populace_dynamics/contract.py

Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,119 @@
1+
"""Typed identity helpers for the versioned gate contract."""
2+
3+
from __future__ import annotations
4+
5+
import platform as platform_module
6+
import subprocess
7+
from dataclasses import dataclass
8+
from importlib.metadata import version
9+
from pathlib import Path
10+
11+
__all__ = ["ContractRef", "contract_revision", "environment_block"]
12+
13+
_REPOSITORY_HINT = Path(__file__).resolve().parents[2]
14+
_CONTRACT_PATH = Path("gates.yaml")
15+
16+
17+
def _git(start: Path, *args: str) -> str:
18+
"""Run Git from a path inside the checkout and return stripped stdout."""
19+
try:
20+
return subprocess.run(
21+
["git", *args],
22+
cwd=start,
23+
check=True,
24+
capture_output=True,
25+
text=True,
26+
).stdout.strip()
27+
except (OSError, subprocess.CalledProcessError) as error:
28+
command = " ".join(("git", *args))
29+
raise RuntimeError(
30+
f"Unable to resolve contract identity with `{command}`"
31+
) from error
32+
33+
34+
@dataclass(frozen=True)
35+
class ContractRef:
36+
"""A contract blob and the checkout commit that embeds it.
37+
38+
Attributes:
39+
blob_sha: Git blob identity of the committed contract bytes.
40+
head_sha: Git commit checked out when the reference was captured.
41+
path: Portable repository-relative path to the contract.
42+
"""
43+
44+
blob_sha: str
45+
head_sha: str
46+
path: str
47+
48+
@classmethod
49+
def current(
50+
cls,
51+
root: str | Path | None = None,
52+
path: str | Path = _CONTRACT_PATH,
53+
) -> ContractRef:
54+
"""Capture the contract reference for the current Git checkout.
55+
56+
Args:
57+
root: A directory inside the checkout. The package checkout is
58+
used by default.
59+
path: Contract path, relative to the repository root or absolute.
60+
61+
Returns:
62+
The committed contract blob, checkout HEAD, and relative path.
63+
"""
64+
start = Path(root).resolve() if root is not None else _REPOSITORY_HINT
65+
repository = Path(_git(start, "rev-parse", "--show-toplevel"))
66+
contract_path = Path(path)
67+
if contract_path.is_absolute():
68+
try:
69+
relative_path = contract_path.resolve().relative_to(repository)
70+
except ValueError as error:
71+
raise ValueError(
72+
f"Contract path {contract_path} is outside {repository}"
73+
) from error
74+
else:
75+
relative_path = contract_path
76+
77+
portable_path = relative_path.as_posix()
78+
revisions = _git(
79+
repository,
80+
"rev-parse",
81+
"HEAD",
82+
f"HEAD:{portable_path}",
83+
).splitlines()
84+
if len(revisions) != 2:
85+
raise RuntimeError("Git returned an invalid contract reference")
86+
head_sha, blob_sha = revisions
87+
working_blob_sha = _git(
88+
repository,
89+
"hash-object",
90+
"--",
91+
portable_path,
92+
)
93+
if working_blob_sha != blob_sha:
94+
raise RuntimeError(
95+
f"{portable_path} differs from HEAD; refusing to record "
96+
"an ambiguous contract reference"
97+
)
98+
return cls(
99+
blob_sha=blob_sha,
100+
head_sha=head_sha,
101+
path=portable_path,
102+
)
103+
104+
105+
def contract_revision(root: str | Path | None = None) -> str:
106+
"""Return the Git blob identity of the committed ``gates.yaml``."""
107+
return ContractRef.current(root).blob_sha
108+
109+
110+
def environment_block() -> dict[str, str]:
111+
"""Return runtime versions required to reproduce an artifact."""
112+
return {
113+
"python": platform_module.python_version(),
114+
"numpy": version("numpy"),
115+
"pandas": version("pandas"),
116+
"sklearn": version("scikit-learn"),
117+
"scipy": version("scipy"),
118+
"platform": platform_module.platform(),
119+
}

0 commit comments

Comments
 (0)