Python API¶
The pyodcs package wraps the Rust implementation via PyO3. All parsing and validation semantics match the odcs crate.
Installation¶
pip install pyodcs
Module overview¶
import pyodcs
pyodcs.__version__ # package version
pyodcs.UPSTREAM_SPEC_VERSION # "3.1.0"
pyodcs.UPSTREAM_REPOSITORY_URL # upstream ODCS GitHub URL
pyodcs.CODES # dict of diagnostic code constants
pyodcs.VALIDATION_PHASES # dict of validation phase name constants (since 0.6.0)
CODES¶
Maps short names to stable odcs:* identifiers:
pyodcs.CODES["INVALID_KIND"] # "odcs:invalid-kind"
See diagnostics.md for when each code fires.
VALIDATION_PHASES¶
Maps short names to validationPhase JSON values (metadata, not error codes):
pyodcs.VALIDATION_PHASES["DOCUMENT"] # "document"
pyodcs.VALIDATION_PHASES["JSON_SCHEMA"] # "jsonSchema"
Validation reports include validationPhase on each validation-stage diagnostic. Parse-stage diagnostics omit the field.
For choosing between parse_and_validate, parse, and validate_result, see API decision guide.
Recommended quick start¶
import pyodcs
report = pyodcs.parse_and_validate(open("contract.yaml", "rb").read(), format="yaml")
assert pyodcs.is_valid(report)
Data shapes¶
Parse result (parse(), parse_file())¶
{
"contract": {...} | None, # parsed contract dict when parse succeeded
"report": {
"diagnostics": [...]
}
}
Validation report (validate(), parse_and_validate(), validate_result())¶
{
"diagnostics": [
{
"id": "odcs:missing-required-field",
"severity": "error",
"stage": "validation",
"category": "structure",
"message": "...",
"object_ref": "id",
"remediation": None
}
]
}
validate_result() may add internal cache keys (_odcs_validated) — do not rely on them in application code.
A report is valid when is_valid(report) is True (no error-severity diagnostics). is_valid() also accepts a parse result dict and reads diagnostics from report.
Parsing¶
parse(content, format="yaml")¶
Parse a document from text or bytes. Returns a dict with contract and report keys.
result = pyodcs.parse(open("contract.yaml", "rb").read(), format="yaml")
contract = result["contract"] # dict or None
report = result["report"] # {"diagnostics": [...]}
format may be "yaml", "yml", or "json".
parse_file(path)¶
Parse from a file path. Raises FileNotFoundError when the file is missing. Raises ValueError for unsupported file extensions.
result = pyodcs.parse_file("examples/minimal.odcs.yaml")
Validation¶
validate(contract)¶
Validate a parsed contract dict. Returns {"diagnostics": [...]}. Validation includes pinned ODCS v3.1.0 JSON Schema checks.
validation = pyodcs.validate(contract)
validate_result(result)¶
Merge parse-time and validation diagnostics from a parse() / parse_file() result.
report = pyodcs.validate_result(result)
parse_and_validate(content, format="yaml")¶
Parse and validate in one step. Returns {"diagnostics": [...]}.
report = pyodcs.parse_and_validate(content, format="yaml")
parse_and_validate_paths(primary, deps=None, *, includes=None, registry=None)¶
Parse and validate a primary contract with optional dependency paths and registry root (since 0.9.0).
report = pyodcs.parse_and_validate_paths(
"consumer.yaml",
registry="./contracts/",
)
Registry helpers (0.9.0+)¶
indexed = pyodcs.registry_index_and_save("./contracts/")
entry = pyodcs.registry_lookup("./contracts/", "provider-contract")
entries = pyodcs.registry_list("./contracts/")
registry_index builds an in-memory index without writing to disk. registry_load reads an existing index file.
Each registry helper returns JSON-compatible dicts. Index operations return:
{
"entries": [
{
"id": "provider-contract",
"version": "1.0.0",
"path": "contracts/provider.yaml",
"apiVersion": "v3.1.0",
"tags": [],
"contentHash": "...",
"indexedAt": "...",
}
],
"report": {"diagnostics": [...]}
}
registry_lookup returns an entry dict or None. registry_list returns a list of entry dicts.
Compatibility diff¶
Compare two parsed contract dicts:
old = pyodcs.parse_file("old.yaml")["contract"]
new = pyodcs.parse_file("new.yaml")["contract"]
report = pyodcs.diff(old, new)
Report shape¶
{
"hasBreaking": True,
"changes": [
{
"kind": "breaking",
"code": "odcs:compatibility-breaking",
"message": "removed property 'email'",
"path": "schema[customers].properties[email]",
}
],
}
| Field | Description |
|---|---|
hasBreaking |
True when any change has kind: "breaking" |
changes |
List of changes with kind, code, message, path |
pinned_schema(*, json_metadata=False)¶
Return the pinned ODCS v3.1.0 JSON Schema dict.
schema = pyodcs.pinned_schema()
metadata = pyodcs.pinned_schema(json_metadata=True)
is_valid(report)¶
Returns True when no error-level diagnostics are present.
assert pyodcs.is_valid(report)
Inspection¶
inspect(contract)¶
Human-readable summary string.
print(pyodcs.inspect(contract))
inspect_summary(contract)¶
Structured summary dict (same fields as odcs inspect --json).
summary = pyodcs.inspect_summary(contract)
# {"id", "name", "version", "apiVersion", "kind", "status",
# "schemaCount", "qualityCount"}
quality_rules_count(contract)¶
Count nested quality rules across all schema objects and properties.
count = pyodcs.quality_rules_count(contract)
CLI¶
The pyodcs console script mirrors the Rust odcs CLI:
pyodcs validate examples/minimal.odcs.yaml
pyodcs validate consumer.yaml --dep provider.yaml
pyodcs validate consumer.yaml --registry ./contracts/
pyodcs inspect examples/minimal.odcs.yaml --json
pyodcs diagnostics examples/minimal.odcs.yaml
pyodcs diff old.yaml new.yaml
pyodcs registry index ./contracts/
pyodcs registry lookup ./contracts/ provider-contract
pyodcs schema
pyodcs schema --json
pyodcs schema --url-only
pyodcs version
pyodcs version --json
Exit codes match the Rust CLI: 0 valid, 1 validation error, 2 parse/I/O failure.
Error handling pattern¶
import pyodcs
import sys
try:
result = pyodcs.parse_file("contract.yaml")
except (FileNotFoundError, OSError, ValueError) as e:
print(e, file=sys.stderr)
sys.exit(2)
report = pyodcs.validate_result(result)
if not pyodcs.is_valid(report):
for d in report["diagnostics"]:
print(f"{d['id']}: {d['message']}")
sys.exit(1)
Rust parity¶
| Python | Rust |
|---|---|
parse() |
parse() |
parse_file() |
parse_file() |
validate() |
validate() |
parse_and_validate() |
parse_and_validate() |
diagnostic_codes() / CODES |
Diagnostic code constants |
validation_phases() / VALIDATION_PHASES |
Validation phase name constants (since 0.6.0) |
parse_and_validate_paths() |
parse_and_validate_set_with_registry() |
diff() |
diff() |
registry_*() |
index_registry, load_registry, Registry |
pinned_schema() |
odcs schema |
See also cli.md and diagnostics.md.