Skip to content

AUCX archives

See the AUCX format specification for the container layout and integrity rules.

Reading and writing

export_aucx

export_aucx(
    experiment: AUCExperiment,
    path: str | Path,
    *,
    overwrite: bool = False,
    exported_at: datetime | None = None,
) -> Path

Write experiment to an AUCX archive and return the written path.

The archive is written to a sibling temporary file, verified by reading it back in full, and only then moved into place — so a failure never leaves a partial or corrupt archive at the destination.

Parameters:

Name Type Description Default
experiment AUCExperiment

The experiment to archive.

required
path str | Path

Destination .aucx path.

required
overwrite bool

Replace an existing file. Refuses by default.

False
exported_at datetime | None

Timestamp recorded in the export provenance. Defaults to the current UTC time; pass a fixed value for byte-identical output.

None

Returns:

Type Description
Path

The path written.

Raises:

Type Description
ArchiveError

if the destination exists and overwrite is False, or if the archive fails its own verification.

Source code in src/openauc/formats/aucx.py
def export_aucx(
    experiment: AUCExperiment,
    path: str | Path,
    *,
    overwrite: bool = False,
    exported_at: datetime | None = None,
) -> Path:
    """Write ``experiment`` to an AUCX archive and return the written path.

    The archive is written to a sibling temporary file, verified by reading it
    back in full, and only then moved into place — so a failure never leaves a
    partial or corrupt archive at the destination.

    Args:
        experiment: The experiment to archive.
        path: Destination ``.aucx`` path.
        overwrite: Replace an existing file. Refuses by default.
        exported_at: Timestamp recorded in the export provenance. Defaults to
            the current UTC time; pass a fixed value for byte-identical output.

    Returns:
        The path written.

    Raises:
        ArchiveError: if the destination exists and ``overwrite`` is False, or
            if the archive fails its own verification.
    """
    from openauc import __version__

    destination = Path(path)
    if destination.exists() and not overwrite:
        raise ArchiveError(
            f"refusing to overwrite existing file: {destination}; "
            "pass overwrite=True to replace it"
        )
    if destination.parent and not destination.parent.exists():
        raise ArchiveError(
            f"destination directory does not exist: {destination.parent}"
        )

    export = AUCXExport(
        software_version=__version__,
        exported_at=exported_at if exported_at is not None else datetime.now(UTC),
        member_count=len(PAYLOAD_MEMBERS) + 1,
    )
    members = _build_members(experiment, export)

    handle, temporary_name = tempfile.mkstemp(
        dir=str(destination.parent or Path()), prefix=".aucx-", suffix=".tmp"
    )
    os.close(handle)
    temporary = Path(temporary_name)
    try:
        _write_zip(temporary, members)
        # Verify the completed archive before it replaces anything.
        read_aucx(temporary)
        os.replace(temporary, destination)
    except BaseException:
        temporary.unlink(missing_ok=True)
        raise
    return destination

read_aucx

read_aucx(path: str | Path) -> AUCExperiment

Read an AUCX archive into an :class:AUCExperiment.

Every checksum is verified before any model is constructed. The returned experiment carries the import provenance stored in the archive, unchanged, so a round trip preserves it; the export record is available separately via :func:inspect_aucx.

Raises:

Type Description
ArchiveError

for unreadable, unsafe or inconsistent archives.

ArchiveIntegrityError

for checksum problems.

ArchiveVersionError

for an unsupported format version.

Source code in src/openauc/formats/aucx.py
def read_aucx(path: str | Path) -> AUCExperiment:
    """Read an AUCX archive into an :class:`AUCExperiment`.

    Every checksum is verified before any model is constructed. The returned
    experiment carries the *import* provenance stored in the archive, unchanged,
    so a round trip preserves it; the export record is available separately via
    :func:`inspect_aucx`.

    Raises:
        ArchiveError: for unreadable, unsafe or inconsistent archives.
        ArchiveIntegrityError: for checksum problems.
        ArchiveVersionError: for an unsupported format version.
    """
    target = Path(path)
    payloads, manifest = _read_verified(target)
    observations = _observations_from(manifest, payloads, target)
    return _experiment_from(payloads, observations, target)

Inspection and validation

inspect_aucx

inspect_aucx(path: str | Path) -> AUCXInfo

Verify an archive's integrity and report what it declares.

This is container-level inspection only. It reports nothing about the structural or scientific standing of the experiment inside.

Source code in src/openauc/formats/aucx.py
def inspect_aucx(path: str | Path) -> AUCXInfo:
    """Verify an archive's integrity and report what it declares.

    This is container-level inspection only. It reports nothing about the
    structural or scientific standing of the experiment inside.
    """
    target = Path(path)
    payloads, manifest = _read_verified(target)
    block = manifest.get("observations")
    observations_block = block if isinstance(block, dict) else {}
    experiment_payload = _loads(payloads[EXPERIMENT_MEMBER], EXPERIMENT_MEMBER, target)
    metadata = experiment_payload.get("metadata")
    experiment_id = (
        str(metadata.get("experiment_id")) if isinstance(metadata, dict) else None
    )
    try:
        mode = RadiusAxisMode(str(observations_block.get("mode")))
    except ValueError as exc:
        raise ArchiveError(f"{target.name}: invalid radius axis mode: {exc}") from exc
    return AUCXInfo(
        path=str(target),
        aucx_format_version=str(manifest["aucx_format_version"]),
        export=_export_record(payloads, target),
        members=tuple(sorted(payloads)),
        checksum_verified=True,
        radius_axis_mode=mode,
        n_scans=int(observations_block.get("n_scans", 0)),
        n_points=int(observations_block.get("n_points", 0)),
        scan_ids=tuple(str(s) for s in observations_block.get("scan_ids", [])),
        radius_unit=Unit(
            str(observations_block.get("radius_unit", Unit.UNKNOWN.value))
        ),
        signal_unit=Unit(
            str(observations_block.get("signal_unit", Unit.UNKNOWN.value))
        ),
        experiment_id=experiment_id,
    )

validate_aucx

validate_aucx(path: str | Path) -> ArchiveValidationReport

Check an archive's integrity without raising.

Container-level validation only: readability, safety, member agreement and checksums. Structural and readiness validation of the experiment inside remain separate — load the archive and use experiment.validate().

Source code in src/openauc/formats/aucx.py
def validate_aucx(path: str | Path) -> ArchiveValidationReport:
    """Check an archive's integrity without raising.

    Container-level validation only: readability, safety, member agreement and
    checksums. Structural and readiness validation of the experiment inside
    remain separate — load the archive and use ``experiment.validate()``.
    """
    target = Path(path)
    try:
        info = inspect_aucx(target)
    except ArchiveVersionError as exc:
        return _failed(target, "unsupported_format_version", exc)
    except ArchiveIntegrityError as exc:
        return _failed(target, "checksum_verification_failed", exc)
    except ArchiveError as exc:
        return _failed(target, "archive_unreadable", exc)
    try:
        read_aucx(target)
    except ArchiveError as exc:
        return _failed(target, "archive_contents_invalid", exc, info=info)
    return ArchiveValidationReport(path=str(target), info=info)

Records

AUCXInfo

Bases: _Frozen

What an archive declares about itself, after integrity verification.

to_dict

to_dict() -> dict[str, Any]

Serialise to plain JSON-friendly Python types.

Source code in src/openauc/formats/aucx.py
def to_dict(self) -> dict[str, Any]:
    """Serialise to plain JSON-friendly Python types."""
    return self.model_dump(mode="json")

AUCXExport

Bases: _Frozen

The export event that produced an archive.

Distinct from the experiment's own import provenance, which records where the data originally came from and travels with the model unchanged.

ArchiveValidationReport dataclass

ArchiveValidationReport(
    path: str,
    issues: tuple[ArchiveIssue, ...] = (),
    info: AUCXInfo | None = None,
)

Container-level validation only.

This reports whether an archive is readable and internally intact. It makes no structural or scientific judgement about the experiment inside — use experiment.validate() and experiment.assess_readiness() for those, after loading.

is_valid property

is_valid: bool

True when no integrity problem was found.

ArchiveIssue dataclass

ArchiveIssue(code: str, message: str)

One archive-integrity finding. Not a structural or scientific finding.