Skip to content

API Reference

DataSluice ships three public API areas: the package root (catalog models, typed catalog errors, and the retained data-plane facade), the public catalog contract suite, and the explicit platform packages.

Package root

Shared catalog models, typed catalog errors, and the direct-resource data-plane facade (DataSluice, DirectResourceLocator, OpenedResource, normalized records, and envelopes):

DataSluice — one Python interface for open-data discovery, extraction, format normalization, and pipeline integration.

Artifact dataclass

A strict, immutable schema-v1 materialization envelope.

Source code in src/datasluice/domain/artifact.py
@dataclass(frozen=True)
class Artifact:
    """A strict, immutable schema-v1 materialization envelope."""

    uri: str
    media_type: str
    size: int
    content_digest: Digest
    blob_digest: Digest
    provenance: ArtifactProvenance
    metadata: Mapping[str, object] = field(default_factory=dict)
    extensions: Mapping[str, object] = field(default_factory=dict)

    def __post_init__(self) -> None:
        _public_uri(self.uri, "uri")
        if not isinstance(self.media_type, str) or not self.media_type:
            raise _contract_error("media_type")
        if type(self.size) is not int or self.size < 0:
            raise _contract_error("size")
        if not isinstance(self.content_digest, Digest) or not isinstance(self.blob_digest, Digest):
            raise _contract_error("digest")
        if not isinstance(self.provenance, ArtifactProvenance):
            raise _contract_error("provenance")
        metadata = _freeze_json(self.metadata, "metadata")
        if not isinstance(metadata, Mapping):
            raise _contract_error("metadata")
        object.__setattr__(self, "metadata", metadata)
        object.__setattr__(self, "extensions", _freeze_extensions(self.extensions))

    def to_dict(self) -> dict[str, object]:
        """Return a fresh JSON-safe Artifact envelope."""
        return {
            "schema_version": 1,
            "kind": "artifact",
            "uri": self.uri,
            "media_type": self.media_type,
            "size": self.size,
            "content_digest": self.content_digest.to_dict(),
            "blob_digest": self.blob_digest.to_dict(),
            "provenance": self.provenance.to_dict(),
            "metadata": _thaw_json(self.metadata),
            "extensions": _thaw_json(self.extensions),
        }

    @classmethod
    def from_dict(cls, value: object) -> Artifact:
        """Decode one strict schema-v1 Artifact envelope."""
        data = _object_dict(value, "artifact")
        if set(data) != _ARTIFACT_KEYS:
            raise _contract_error("artifact")
        if data["schema_version"] != 1 or type(data["schema_version"]) is not int or data["kind"] != "artifact":
            raise _contract_error("artifact")
        uri = data["uri"]
        media_type = data["media_type"]
        size = data["size"]
        metadata = data["metadata"]
        extensions = data["extensions"]
        if not isinstance(uri, str) or not isinstance(media_type, str) or type(size) is not int:
            raise _contract_error("artifact")
        return cls(
            uri=uri,
            media_type=media_type,
            size=size,
            content_digest=Digest.from_dict(data["content_digest"]),
            blob_digest=Digest.from_dict(data["blob_digest"]),
            provenance=ArtifactProvenance.from_dict(data["provenance"]),
            metadata=_object_dict(metadata, "metadata"),
            extensions=_object_dict(extensions, "extensions"),
        )

from_dict(value) classmethod

Decode one strict schema-v1 Artifact envelope.

Source code in src/datasluice/domain/artifact.py
@classmethod
def from_dict(cls, value: object) -> Artifact:
    """Decode one strict schema-v1 Artifact envelope."""
    data = _object_dict(value, "artifact")
    if set(data) != _ARTIFACT_KEYS:
        raise _contract_error("artifact")
    if data["schema_version"] != 1 or type(data["schema_version"]) is not int or data["kind"] != "artifact":
        raise _contract_error("artifact")
    uri = data["uri"]
    media_type = data["media_type"]
    size = data["size"]
    metadata = data["metadata"]
    extensions = data["extensions"]
    if not isinstance(uri, str) or not isinstance(media_type, str) or type(size) is not int:
        raise _contract_error("artifact")
    return cls(
        uri=uri,
        media_type=media_type,
        size=size,
        content_digest=Digest.from_dict(data["content_digest"]),
        blob_digest=Digest.from_dict(data["blob_digest"]),
        provenance=ArtifactProvenance.from_dict(data["provenance"]),
        metadata=_object_dict(metadata, "metadata"),
        extensions=_object_dict(extensions, "extensions"),
    )

to_dict()

Return a fresh JSON-safe Artifact envelope.

Source code in src/datasluice/domain/artifact.py
def to_dict(self) -> dict[str, object]:
    """Return a fresh JSON-safe Artifact envelope."""
    return {
        "schema_version": 1,
        "kind": "artifact",
        "uri": self.uri,
        "media_type": self.media_type,
        "size": self.size,
        "content_digest": self.content_digest.to_dict(),
        "blob_digest": self.blob_digest.to_dict(),
        "provenance": self.provenance.to_dict(),
        "metadata": _thaw_json(self.metadata),
        "extensions": _thaw_json(self.extensions),
    }

ArtifactProvenance dataclass

Typed provenance for one materialized Artifact.

Source code in src/datasluice/domain/artifact.py
@dataclass(frozen=True)
class ArtifactProvenance:
    """Typed provenance for one materialized Artifact."""

    source_locator: ResourceLocator
    resource_identity: str
    created_at: datetime
    materialization_mode: str
    transforms: tuple[str, ...] = ()

    def __post_init__(self) -> None:
        if not hasattr(self.source_locator, "to_dict") or not _is_sha256(self.resource_identity):
            raise _contract_error("provenance")
        if not isinstance(self.created_at, datetime) or self.created_at.tzinfo is None:
            raise _contract_error("provenance.created_at")
        if self.materialization_mode not in {"parquet", "raw"}:
            raise _contract_error("provenance.materialization_mode")
        if not isinstance(self.transforms, tuple) or not all(isinstance(value, str) for value in self.transforms):
            raise _contract_error("provenance.transforms")

    def to_dict(self) -> dict[str, object]:
        """Return a fresh JSON-safe provenance envelope."""
        created_at = self.created_at.astimezone(UTC).isoformat().replace("+00:00", "Z")
        return {
            "source_locator": self.source_locator.to_dict(),
            "resource_identity": self.resource_identity,
            "created_at": created_at,
            "materialization_mode": self.materialization_mode,
            "transforms": list(self.transforms),
        }

    @classmethod
    def from_dict(cls, value: object) -> ArtifactProvenance:
        """Decode one strict provenance envelope."""
        data = _object_dict(value, "provenance")
        if set(data) != _PROVENANCE_KEYS:
            raise _contract_error("provenance")
        source_locator = data["source_locator"]
        resource_identity = data["resource_identity"]
        created_at = data["created_at"]
        materialization_mode = data["materialization_mode"]
        transforms = data["transforms"]
        if (
            not isinstance(resource_identity, str)
            or not isinstance(created_at, str)
            or not isinstance(materialization_mode, str)
            or not isinstance(transforms, list)
            or not all(isinstance(transform, str) for transform in transforms)
        ):
            raise _contract_error("provenance")
        try:
            parsed_created_at = datetime.fromisoformat(created_at.replace("Z", "+00:00"))
        except ValueError as exc:
            raise _contract_error("provenance.created_at") from exc
        if parsed_created_at.tzinfo is None:
            raise _contract_error("provenance.created_at")
        from datasluice.application import resource_locator_from_dict

        return cls(
            source_locator=resource_locator_from_dict(_object_dict(source_locator, "provenance.source_locator")),
            resource_identity=resource_identity,
            created_at=parsed_created_at,
            materialization_mode=materialization_mode,
            transforms=cast(tuple[str, ...], tuple(transforms)),
        )

from_dict(value) classmethod

Decode one strict provenance envelope.

Source code in src/datasluice/domain/artifact.py
@classmethod
def from_dict(cls, value: object) -> ArtifactProvenance:
    """Decode one strict provenance envelope."""
    data = _object_dict(value, "provenance")
    if set(data) != _PROVENANCE_KEYS:
        raise _contract_error("provenance")
    source_locator = data["source_locator"]
    resource_identity = data["resource_identity"]
    created_at = data["created_at"]
    materialization_mode = data["materialization_mode"]
    transforms = data["transforms"]
    if (
        not isinstance(resource_identity, str)
        or not isinstance(created_at, str)
        or not isinstance(materialization_mode, str)
        or not isinstance(transforms, list)
        or not all(isinstance(transform, str) for transform in transforms)
    ):
        raise _contract_error("provenance")
    try:
        parsed_created_at = datetime.fromisoformat(created_at.replace("Z", "+00:00"))
    except ValueError as exc:
        raise _contract_error("provenance.created_at") from exc
    if parsed_created_at.tzinfo is None:
        raise _contract_error("provenance.created_at")
    from datasluice.application import resource_locator_from_dict

    return cls(
        source_locator=resource_locator_from_dict(_object_dict(source_locator, "provenance.source_locator")),
        resource_identity=resource_identity,
        created_at=parsed_created_at,
        materialization_mode=materialization_mode,
        transforms=cast(tuple[str, ...], tuple(transforms)),
    )

to_dict()

Return a fresh JSON-safe provenance envelope.

Source code in src/datasluice/domain/artifact.py
def to_dict(self) -> dict[str, object]:
    """Return a fresh JSON-safe provenance envelope."""
    created_at = self.created_at.astimezone(UTC).isoformat().replace("+00:00", "Z")
    return {
        "source_locator": self.source_locator.to_dict(),
        "resource_identity": self.resource_identity,
        "created_at": created_at,
        "materialization_mode": self.materialization_mode,
        "transforms": list(self.transforms),
    }

CatalogError

Bases: DataSluiceError

Base normalized error with a portable safe next action.

Source code in src/datasluice/errors/catalog.py
class CatalogError(DataSluiceError):
    """Base normalized error with a portable safe next action."""

    def __init__(
        self,
        message: str,
        *,
        operation: str,
        platform: CatalogPlatform | str,
        capability_state: str | None = None,
        safe_action: str,
    ) -> None:
        if not isinstance(message, str) or not message:
            raise ValueError("Catalog error messages must be non-empty strings.")
        if not isinstance(operation, str) or not operation:
            raise ValueError("Catalog error operations must be non-empty strings.")
        if not isinstance(safe_action, str) or not safe_action:
            raise ValueError("Catalog errors require a safe next action.")
        super().__init__(message)
        self.operation = operation
        self.platform = _platform_value(platform)
        self.capability_state = capability_state
        self.safe_action = safe_action

CatalogId dataclass

A platform- and resource-kind-scoped opaque catalog identifier.

Source code in src/datasluice/domain/catalog/ids.py
@dataclass(frozen=True)
class CatalogId:
    """A platform- and resource-kind-scoped opaque catalog identifier."""

    platform: CatalogPlatform
    resource_kind: ResourceKind
    value: str

    def __post_init__(self) -> None:
        if not isinstance(self.platform, CatalogPlatform):
            raise _contract_error("catalog_id.platform")
        if not isinstance(self.resource_kind, ResourceKind):
            raise _contract_error("catalog_id.resource_kind")
        if not isinstance(self.value, str) or not self.value:
            raise _contract_error("catalog_id.value")

    def to_dict(self) -> dict[str, object]:
        """Return a fresh JSON-safe catalog identifier envelope."""
        return {
            "schema_version": 1,
            "kind": "catalog_id",
            "platform": self.platform.value,
            "resource_kind": self.resource_kind.value,
            "value": self.value,
        }

    @classmethod
    def from_dict(cls, value: object) -> CatalogId:
        """Decode one strict schema-v1 catalog identifier envelope."""
        if not isinstance(value, dict) or set(value) != _ID_KEYS:
            raise _contract_error("catalog_id")
        if value["schema_version"] != 1 or type(value["schema_version"]) is not int or value["kind"] != "catalog_id":
            raise _contract_error("catalog_id")
        platform = value["platform"]
        resource_kind = value["resource_kind"]
        native_value = value["value"]
        if not isinstance(platform, str) or not isinstance(resource_kind, str) or not isinstance(native_value, str):
            raise _contract_error("catalog_id")
        return cls(CatalogPlatform(platform), ResourceKind(resource_kind), native_value)

from_dict(value) classmethod

Decode one strict schema-v1 catalog identifier envelope.

Source code in src/datasluice/domain/catalog/ids.py
@classmethod
def from_dict(cls, value: object) -> CatalogId:
    """Decode one strict schema-v1 catalog identifier envelope."""
    if not isinstance(value, dict) or set(value) != _ID_KEYS:
        raise _contract_error("catalog_id")
    if value["schema_version"] != 1 or type(value["schema_version"]) is not int or value["kind"] != "catalog_id":
        raise _contract_error("catalog_id")
    platform = value["platform"]
    resource_kind = value["resource_kind"]
    native_value = value["value"]
    if not isinstance(platform, str) or not isinstance(resource_kind, str) or not isinstance(native_value, str):
        raise _contract_error("catalog_id")
    return cls(CatalogPlatform(platform), ResourceKind(resource_kind), native_value)

to_dict()

Return a fresh JSON-safe catalog identifier envelope.

Source code in src/datasluice/domain/catalog/ids.py
def to_dict(self) -> dict[str, object]:
    """Return a fresh JSON-safe catalog identifier envelope."""
    return {
        "schema_version": 1,
        "kind": "catalog_id",
        "platform": self.platform.value,
        "resource_kind": self.resource_kind.value,
        "value": self.value,
    }

CatalogPlatform dataclass

An explicit platform identity for one catalog deployment family.

Source code in src/datasluice/domain/catalog/ids.py
@dataclass(frozen=True)
class CatalogPlatform:
    """An explicit platform identity for one catalog deployment family."""

    value: str

    CKAN: ClassVar[CatalogPlatform]
    UDATA: ClassVar[CatalogPlatform]
    SOCRATA: ClassVar[CatalogPlatform]

    def __post_init__(self) -> None:
        if not isinstance(self.value, str) or _VALUE_RE.fullmatch(self.value) is None:
            raise _contract_error("platform")

    def __str__(self) -> str:
        """Return the JSON platform value."""
        return self.value

__str__()

Return the JSON platform value.

Source code in src/datasluice/domain/catalog/ids.py
def __str__(self) -> str:
    """Return the JSON platform value."""
    return self.value

CatalogUnavailableError

Bases: CatalogError

Raised when a catalog deployment or circuit is unavailable.

Source code in src/datasluice/errors/catalog.py
class CatalogUnavailableError(CatalogError):
    """Raised when a catalog deployment or circuit is unavailable."""

ChecksumMismatchError

Bases: DownloadError

Raised when a downloaded file's checksum does not match.

Source code in src/datasluice/exceptions.py
class ChecksumMismatchError(DownloadError):
    """Raised when a downloaded file's checksum does not match."""

    def __init__(self, message: str, expected: str | None = None, actual: str | None = None) -> None:
        super().__init__(message)
        self.expected = expected
        self.actual = actual

ConfigError

Bases: DataSluiceError

Raised when configuration is invalid or incomplete.

Source code in src/datasluice/exceptions.py
class ConfigError(DataSluiceError):
    """Raised when configuration is invalid or incomplete."""

CredentialScope dataclass

Host-scoped policy controlling where credentials may be sent.

Attributes:

Name Type Description
allowed_hosts tuple[str, ...]

Hostnames the credential may be sent to.

allowed_schemes tuple[str, ...]

URL schemes the credential may travel over.

send_on_redirect bool

Whether credentials are retained on redirects to allowed hosts.

Source code in src/datasluice/domain/credentials.py
@dataclass(frozen=True)
class CredentialScope:
    """Host-scoped policy controlling where credentials may be sent.

    Attributes:
        allowed_hosts: Hostnames the credential may be sent to.
        allowed_schemes: URL schemes the credential may travel over.
        send_on_redirect: Whether credentials are retained on redirects to allowed hosts.
    """

    allowed_hosts: tuple[str, ...] = field(default_factory=tuple)
    allowed_schemes: tuple[str, ...] = ("https",)
    send_on_redirect: bool = False

DataSluice

Canonical public facade for discovery, resource access, and materialization.

Source code in src/datasluice/application.py
class DataSluice:
    """Canonical public facade for discovery, resource access, and materialization."""

    def __init__(
        self,
        *,
        session: Any | None = None,
        reader: Any | None = None,
        **session_kwargs: Any,
    ) -> None:
        if session is not None and session_kwargs:
            raise DataSluiceError("session= cannot be combined with session configuration")
        self._owns_session_dependencies = session is None
        self._owns_reader = reader is None
        self._session = session if session is not None else DataSluiceSession(**session_kwargs)
        self._reader = reader if reader is not None else DataPlaneResourceReader(transport=self._session._transport)
        self._services = _ApplicationServices(self._session, self._reader)
        self._owned_closeables = self._collect_owned_closeables(session_kwargs)
        self._closed = False

    def open_catalog[T](self, factory: Callable[[CatalogConnectorContext], T], context: CatalogConnectorContext) -> T:
        """Return one explicit caller-selected canonical catalog connector."""
        self._ensure_open()
        return self._session.open_catalog(factory, context)

    def resolve(self, locator: DirectResourceLocator) -> Resource:
        """Resolve one public locator into the canonical Resource model."""
        self._ensure_open()
        return self._services.resolve(locator)

    def open(self, resource: Resource | DirectResourceLocator) -> OpenedResource:
        """Return a lazy, single-use OpenedResource wrapper."""
        self._ensure_open()
        return self._services.open(resource)

    def materialize(
        self,
        resource: Resource | DirectResourceLocator,
        destination_uri: str,
        *,
        mode: str = "parquet",
    ) -> Any:
        """Materialize one Resource or ResourceLocator into an Artifact."""
        return self._services.materialize(resource, destination_uri, mode=mode)

    def download_many(self, resources: list[Resource], destination: str) -> list[dict[str, object]]:
        """Raw bulk-copy resources into a destination directory."""
        self._ensure_open()
        return self._services.download_many(resources, destination)

    def close(self) -> None:
        """Close this facade and any resource wrappers it owns."""
        if self._closed:
            return
        self._closed = True
        first_error: BaseException | None = None
        for closeable in self._owned_closeables:
            try:
                closeable.close()
            except BaseException as exc:
                if first_error is None:
                    first_error = exc
        if first_error is not None:
            raise first_error

    def __enter__(self) -> DataSluice:
        self._ensure_open()
        return self

    def __exit__(self, *exc: Any) -> None:
        self.close()

    def _ensure_open(self) -> None:
        if self._closed:
            raise StreamClosedError("DataSluice is closed")

    def _collect_owned_closeables(self, session_kwargs: Mapping[str, Any]) -> tuple[Any, ...]:
        candidates: list[Any] = []
        if self._owns_reader:
            candidates.append(self._reader)
        if self._owns_session_dependencies:
            if session_kwargs.get("transport") is None:
                candidates.append(self._session._transport)
            if session_kwargs.get("cache") is None:
                candidates.append(self._session._cache)
            if session_kwargs.get("storage") is None:
                candidates.append(self._session.storage)
            if session_kwargs.get("state_store") is None:
                candidates.append(self._session.state_store)
            if session_kwargs.get("plugins") is None:
                candidates.append(self._session.plugins)
        closeables: list[Any] = []
        seen: set[int] = set()
        for candidate in candidates:
            if candidate is None or not hasattr(candidate, "close") or id(candidate) in seen:
                continue
            seen.add(id(candidate))
            closeables.append(candidate)
        return tuple(closeables)

close()

Close this facade and any resource wrappers it owns.

Source code in src/datasluice/application.py
def close(self) -> None:
    """Close this facade and any resource wrappers it owns."""
    if self._closed:
        return
    self._closed = True
    first_error: BaseException | None = None
    for closeable in self._owned_closeables:
        try:
            closeable.close()
        except BaseException as exc:
            if first_error is None:
                first_error = exc
    if first_error is not None:
        raise first_error

download_many(resources, destination)

Raw bulk-copy resources into a destination directory.

Source code in src/datasluice/application.py
def download_many(self, resources: list[Resource], destination: str) -> list[dict[str, object]]:
    """Raw bulk-copy resources into a destination directory."""
    self._ensure_open()
    return self._services.download_many(resources, destination)

materialize(resource, destination_uri, *, mode='parquet')

Materialize one Resource or ResourceLocator into an Artifact.

Source code in src/datasluice/application.py
def materialize(
    self,
    resource: Resource | DirectResourceLocator,
    destination_uri: str,
    *,
    mode: str = "parquet",
) -> Any:
    """Materialize one Resource or ResourceLocator into an Artifact."""
    return self._services.materialize(resource, destination_uri, mode=mode)

open(resource)

Return a lazy, single-use OpenedResource wrapper.

Source code in src/datasluice/application.py
def open(self, resource: Resource | DirectResourceLocator) -> OpenedResource:
    """Return a lazy, single-use OpenedResource wrapper."""
    self._ensure_open()
    return self._services.open(resource)

open_catalog(factory, context)

Return one explicit caller-selected canonical catalog connector.

Source code in src/datasluice/application.py
def open_catalog[T](self, factory: Callable[[CatalogConnectorContext], T], context: CatalogConnectorContext) -> T:
    """Return one explicit caller-selected canonical catalog connector."""
    self._ensure_open()
    return self._session.open_catalog(factory, context)

resolve(locator)

Resolve one public locator into the canonical Resource model.

Source code in src/datasluice/application.py
def resolve(self, locator: DirectResourceLocator) -> Resource:
    """Resolve one public locator into the canonical Resource model."""
    self._ensure_open()
    return self._services.resolve(locator)

DataSluiceError

Bases: Exception

Base exception for all DataSluice errors.

Source code in src/datasluice/exceptions.py
class DataSluiceError(Exception):
    """Base exception for all DataSluice errors."""

Dataset dataclass

A dataset is a logical grouping of one or more resources.

Attributes:

Name Type Description
id str

Portal-native dataset identifier.

title str | None

Human-readable dataset title.

name str | None

Machine-friendly slug or name.

description str | None

Longer free-text description (may contain Markdown/HTML).

resources list[Resource]

List of downloadable resources within this dataset.

organization Organization | None

Publishing organization, if known.

license License | None

Default license for resources in this dataset.

tags list[str]

Free-form tags or keywords.

themes list[str]

Categorization themes or groups.

language list[str]

ISO language code(s) for the data.

created str | None

ISO-8601 creation timestamp.

modified str | None

ISO-8601 last-modified timestamp.

metadata_modified str | None

ISO-8601 timestamp of last metadata change.

url str | None

Canonical URL to the dataset on the portal.

extra dict[str, Any]

Portal-native fields not captured above.

Source code in src/datasluice/domain/dataset.py
@dataclass(frozen=True)
class Dataset:
    """A dataset is a logical grouping of one or more resources.

    Attributes:
        id: Portal-native dataset identifier.
        title: Human-readable dataset title.
        name: Machine-friendly slug or name.
        description: Longer free-text description (may contain Markdown/HTML).
        resources: List of downloadable resources within this dataset.
        organization: Publishing organization, if known.
        license: Default license for resources in this dataset.
        tags: Free-form tags or keywords.
        themes: Categorization themes or groups.
        language: ISO language code(s) for the data.
        created: ISO-8601 creation timestamp.
        modified: ISO-8601 last-modified timestamp.
        metadata_modified: ISO-8601 timestamp of last metadata change.
        url: Canonical URL to the dataset on the portal.
        extra: Portal-native fields not captured above.
    """

    id: str
    title: str | None = None
    name: str | None = None
    description: str | None = None
    resources: list[Resource] = field(default_factory=list)
    organization: Organization | None = None
    license: License | None = None
    tags: list[str] = field(default_factory=list)
    themes: list[str] = field(default_factory=list)
    language: list[str] = field(default_factory=list)
    created: str | None = None
    modified: str | None = None
    metadata_modified: str | None = None
    url: str | None = None
    extra: dict[str, Any] = field(default_factory=dict)

DatasetRecord dataclass

A normalized immutable dataset record.

Source code in src/datasluice/domain/catalog/models.py
@dataclass(frozen=True)
class DatasetRecord:
    """A normalized immutable dataset record."""

    id: CatalogId
    name: str
    description: str | None = None
    extensions: Mapping[str, object] = field(default_factory=dict)

    def __post_init__(self) -> None:
        if not isinstance(self.id, CatalogId) or self.id.resource_kind != ResourceKind.DATASET:
            raise _contract_error("dataset.id")
        if not isinstance(self.name, str) or not self.name:
            raise _contract_error("dataset.name")
        _optional_string(self.description, "dataset.description")
        object.__setattr__(self, "extensions", _freeze_extensions(self.extensions))

    def to_dict(self) -> dict[str, object]:
        """Return a fresh JSON-safe dataset envelope."""
        return {
            "schema_version": 1,
            "kind": "dataset",
            "id": self.id.to_dict(),
            "name": self.name,
            "description": self.description,
            "extensions": _thaw_json(self.extensions),
        }

    @classmethod
    def from_dict(cls, value: object) -> DatasetRecord:
        """Decode one strict schema-v1 dataset envelope."""
        data = _strict_envelope(
            value,
            "dataset",
            "dataset",
            frozenset({"schema_version", "kind", "id", "name", "description", "extensions"}),
        )
        name = data["name"]
        if not isinstance(name, str):
            raise _contract_error("dataset.name")
        return cls(
            id=CatalogId.from_dict(data["id"]),
            name=name,
            description=_optional_string(data["description"], "dataset.description"),
            extensions=_object_dict(data["extensions"], "dataset.extensions"),
        )

from_dict(value) classmethod

Decode one strict schema-v1 dataset envelope.

Source code in src/datasluice/domain/catalog/models.py
@classmethod
def from_dict(cls, value: object) -> DatasetRecord:
    """Decode one strict schema-v1 dataset envelope."""
    data = _strict_envelope(
        value,
        "dataset",
        "dataset",
        frozenset({"schema_version", "kind", "id", "name", "description", "extensions"}),
    )
    name = data["name"]
    if not isinstance(name, str):
        raise _contract_error("dataset.name")
    return cls(
        id=CatalogId.from_dict(data["id"]),
        name=name,
        description=_optional_string(data["description"], "dataset.description"),
        extensions=_object_dict(data["extensions"], "dataset.extensions"),
    )

to_dict()

Return a fresh JSON-safe dataset envelope.

Source code in src/datasluice/domain/catalog/models.py
def to_dict(self) -> dict[str, object]:
    """Return a fresh JSON-safe dataset envelope."""
    return {
        "schema_version": 1,
        "kind": "dataset",
        "id": self.id.to_dict(),
        "name": self.name,
        "description": self.description,
        "extensions": _thaw_json(self.extensions),
    }

DetectionResult dataclass

Outcome of portal auto-detection with confidence and evidence.

Attributes:

Name Type Description
portal_type str | None

Identified portal type, or None when undetected.

confidence float

Confidence score in the range [0.0, 1.0].

evidence Sequence[DetectionEvidence]

Evidence records supporting the detection.

extra Mapping[str, Any]

Portal-native detection fields not captured above.

Source code in src/datasluice/domain/detection.py
@dataclass(frozen=True)
class DetectionResult:
    """Outcome of portal auto-detection with confidence and evidence.

    Attributes:
        portal_type: Identified portal type, or ``None`` when undetected.
        confidence: Confidence score in the range ``[0.0, 1.0]``.
        evidence: Evidence records supporting the detection.
        extra: Portal-native detection fields not captured above.
    """

    portal_type: str | None
    confidence: float = 0.0
    evidence: Sequence[DetectionEvidence] = field(default_factory=tuple)
    extra: Mapping[str, Any] = field(default_factory=dict)

    def __post_init__(self) -> None:
        if not 0.0 <= self.confidence <= 1.0:
            raise ValueError(f"confidence must be in [0.0, 1.0], got {self.confidence}")
        if not isinstance(self.evidence, tuple):
            object.__setattr__(self, "evidence", tuple(self.evidence))
        if not isinstance(self.extra, MappingProxyType):
            object.__setattr__(self, "extra", MappingProxyType(dict(self.extra)))

Digest dataclass

A structured SHA-256 digest.

Source code in src/datasluice/domain/artifact.py
@dataclass(frozen=True)
class Digest:
    """A structured SHA-256 digest."""

    algorithm: str
    value: str

    def __post_init__(self) -> None:
        if self.algorithm != "sha256" or not _is_sha256(self.value):
            raise _contract_error("digest")

    def to_dict(self) -> dict[str, str]:
        """Return a fresh JSON-safe digest envelope."""
        return {"algorithm": self.algorithm, "value": self.value}

    @classmethod
    def from_dict(cls, value: object) -> Digest:
        """Decode one strict digest envelope."""
        data = _object_dict(value, "digest")
        if set(data) != _DIGEST_KEYS:
            raise _contract_error("digest")
        algorithm = data["algorithm"]
        digest = data["value"]
        if not isinstance(algorithm, str) or not isinstance(digest, str):
            raise _contract_error("digest")
        return cls(algorithm=algorithm, value=digest)

from_dict(value) classmethod

Decode one strict digest envelope.

Source code in src/datasluice/domain/artifact.py
@classmethod
def from_dict(cls, value: object) -> Digest:
    """Decode one strict digest envelope."""
    data = _object_dict(value, "digest")
    if set(data) != _DIGEST_KEYS:
        raise _contract_error("digest")
    algorithm = data["algorithm"]
    digest = data["value"]
    if not isinstance(algorithm, str) or not isinstance(digest, str):
        raise _contract_error("digest")
    return cls(algorithm=algorithm, value=digest)

to_dict()

Return a fresh JSON-safe digest envelope.

Source code in src/datasluice/domain/artifact.py
def to_dict(self) -> dict[str, str]:
    """Return a fresh JSON-safe digest envelope."""
    return {"algorithm": self.algorithm, "value": self.value}

DirectResourceLocator dataclass

A validated, serializable direct resource reference.

Source code in src/datasluice/application.py
@dataclass(frozen=True)
class DirectResourceLocator:
    """A validated, serializable direct resource reference."""

    uri: str
    format: str | None = None
    media_type: str | None = None
    extensions: Mapping[str, object] = field(default_factory=dict)

    def __post_init__(self) -> None:
        _validate_uri(self.uri, "uri")
        if self.format is not None and not isinstance(self.format, str):
            raise _contract_error("format")
        if self.media_type is not None and not isinstance(self.media_type, str):
            raise _contract_error("media_type")
        object.__setattr__(self, "extensions", _freeze_extensions(self.extensions))

    def to_dict(self) -> dict[str, object]:
        """Return a fresh, secret-free locator envelope."""
        from datasluice.domain.artifact import _thaw_json

        return {
            "schema_version": 1,
            "kind": "direct",
            "uri": sanitize_uri(self.uri),
            "format": self.format,
            "media_type": self.media_type,
            "extensions": _thaw_json(self.extensions),
        }

    @classmethod
    def from_dict(cls, value: object) -> DirectResourceLocator:
        """Decode one strict direct locator envelope."""
        data = _object_dict(value, "direct")
        if set(data) != _DIRECT_LOCATOR_KEYS:
            raise _contract_error("direct")
        if data["schema_version"] != 1 or type(data["schema_version"]) is not int or data["kind"] != "direct":
            raise _contract_error("direct")
        uri = data["uri"]
        format_name = data["format"]
        media_type = data["media_type"]
        extensions = data["extensions"]
        if (
            not isinstance(uri, str)
            or format_name is not None
            and not isinstance(format_name, str)
            or media_type is not None
            and not isinstance(media_type, str)
        ):
            raise _contract_error("direct")
        return cls(
            uri=uri,
            format=format_name,
            media_type=media_type,
            extensions=_object_dict(extensions, "extensions"),
        )

from_dict(value) classmethod

Decode one strict direct locator envelope.

Source code in src/datasluice/application.py
@classmethod
def from_dict(cls, value: object) -> DirectResourceLocator:
    """Decode one strict direct locator envelope."""
    data = _object_dict(value, "direct")
    if set(data) != _DIRECT_LOCATOR_KEYS:
        raise _contract_error("direct")
    if data["schema_version"] != 1 or type(data["schema_version"]) is not int or data["kind"] != "direct":
        raise _contract_error("direct")
    uri = data["uri"]
    format_name = data["format"]
    media_type = data["media_type"]
    extensions = data["extensions"]
    if (
        not isinstance(uri, str)
        or format_name is not None
        and not isinstance(format_name, str)
        or media_type is not None
        and not isinstance(media_type, str)
    ):
        raise _contract_error("direct")
    return cls(
        uri=uri,
        format=format_name,
        media_type=media_type,
        extensions=_object_dict(extensions, "extensions"),
    )

to_dict()

Return a fresh, secret-free locator envelope.

Source code in src/datasluice/application.py
def to_dict(self) -> dict[str, object]:
    """Return a fresh, secret-free locator envelope."""
    from datasluice.domain.artifact import _thaw_json

    return {
        "schema_version": 1,
        "kind": "direct",
        "uri": sanitize_uri(self.uri),
        "format": self.format,
        "media_type": self.media_type,
        "extensions": _thaw_json(self.extensions),
    }

DownloadError

Bases: DataSluiceError

Raised when a resource download fails.

Source code in src/datasluice/exceptions.py
class DownloadError(DataSluiceError):
    """Raised when a resource download fails."""

ForbiddenError

Bases: CatalogError

Raised when known credentials lack the required permission or role.

Source code in src/datasluice/errors/catalog.py
class ForbiddenError(CatalogError):
    """Raised when known credentials lack the required permission or role."""

FormatError

Bases: DataSluiceError

Raised when a resource cannot be parsed in the expected format.

Source code in src/datasluice/exceptions.py
class FormatError(DataSluiceError):
    """Raised when a resource cannot be parsed in the expected format."""

HttpDownload dataclass

Bases: ResourceAccess

Resource fetched over HTTP(S).

Attributes:

Name Type Description
url str

Absolute URL to download.

method str

HTTP method (default "GET").

kind str

Discriminator, always "http_download".

Source code in src/datasluice/domain/access.py
@dataclass(frozen=True, kw_only=True)
class HttpDownload(ResourceAccess):
    """Resource fetched over HTTP(S).

    Attributes:
        url: Absolute URL to download.
        method: HTTP method (default ``"GET"``).
        kind: Discriminator, always ``"http_download"``.
    """

    url: str
    method: str = "GET"
    kind: str = field(init=False, default="http_download")

License dataclass

A license under which an open-data resource or dataset is published.

Attributes:

Name Type Description
id str

Canonical license identifier (e.g. "CC-BY-4.0").

title str | None

Human-readable license name.

url str | None

URL to the full license text.

Source code in src/datasluice/domain/license.py
@dataclass(frozen=True)
class License:
    """A license under which an open-data resource or dataset is published.

    Attributes:
        id: Canonical license identifier (e.g. ``"CC-BY-4.0"``).
        title: Human-readable license name.
        url: URL to the full license text.
    """

    id: str
    title: str | None = None
    url: str | None = None

LocalFile dataclass

Bases: ResourceAccess

Resource available on the local filesystem.

Attributes:

Name Type Description
path str

Local filesystem path.

kind str

Discriminator, always "local_file".

Source code in src/datasluice/domain/access.py
@dataclass(frozen=True, kw_only=True)
class LocalFile(ResourceAccess):
    """Resource available on the local filesystem.

    Attributes:
        path: Local filesystem path.
        kind: Discriminator, always ``"local_file"``.
    """

    path: str
    kind: str = field(init=False, default="local_file")

NativeCatalogError

Bases: DataSluiceError

A redacted, bounded platform-native failure retained for native services.

Source code in src/datasluice/errors/catalog.py
class NativeCatalogError(DataSluiceError):
    """A redacted, bounded platform-native failure retained for native services."""

    def __init__(
        self,
        message: str,
        *,
        operation: str,
        platform: CatalogPlatform | str,
        status_code: int | None = None,
        vendor_code: str | None = None,
        retry_after: float | None = None,
        metadata: Mapping[str, object] | None = None,
    ) -> None:
        if not isinstance(message, str) or not message:
            raise ValueError("Native catalog error messages must be non-empty strings.")
        if not isinstance(operation, str) or not operation:
            raise ValueError("Native catalog error operations must be non-empty strings.")
        if status_code is not None and (type(status_code) is not int or not 100 <= status_code <= 599):
            raise ValueError("Native catalog error status codes must be valid HTTP status codes.")
        if vendor_code is not None and (not isinstance(vendor_code, str) or len(vendor_code) > _MAX_TEXT_LENGTH):
            raise ValueError("Native catalog error vendor codes must be bounded strings.")
        if retry_after is not None and (
            (type(retry_after) is not int and type(retry_after) is not float) or retry_after < 0
        ):
            raise ValueError("Native catalog error Retry-After must be a non-negative number.")
        super().__init__(_redact_message(message))
        self.operation = operation
        self.platform = _platform_value(platform)
        self.status_code = status_code
        self.vendor_code = vendor_code
        self.retry_after = float(retry_after) if retry_after is not None else None
        self.metadata = _bounded_metadata(metadata)

NativeRecord dataclass

A lossless immutable envelope for one platform-native record.

Source code in src/datasluice/domain/catalog/models.py
@dataclass(frozen=True)
class NativeRecord:
    """A lossless immutable envelope for one platform-native record."""

    platform: CatalogPlatform
    resource_kind: ResourceKind
    id: CatalogId
    payload: Mapping[str, object]
    extensions: Mapping[str, object] = field(default_factory=dict)

    def __post_init__(self) -> None:
        if not isinstance(self.platform, CatalogPlatform) or not isinstance(self.resource_kind, ResourceKind):
            raise _contract_error("native_record.identity")
        if not isinstance(self.id, CatalogId) or (self.id.platform, self.id.resource_kind) != (
            self.platform,
            self.resource_kind,
        ):
            raise _contract_error("native_record.id")
        payload = _freeze_json(self.payload, "native_record.payload")
        if not isinstance(payload, Mapping):
            raise _contract_error("native_record.payload")
        object.__setattr__(self, "payload", payload)
        object.__setattr__(self, "extensions", _freeze_extensions(self.extensions))

    def to_dict(self) -> dict[str, object]:
        """Return a fresh JSON-safe native record envelope."""
        return {
            "schema_version": 1,
            "kind": "native_record",
            "platform": self.platform.value,
            "resource_kind": self.resource_kind.value,
            "id": self.id.to_dict(),
            "payload": _thaw_json(self.payload),
            "extensions": _thaw_json(self.extensions),
        }

    @classmethod
    def from_dict(cls, value: object) -> NativeRecord:
        """Decode one strict schema-v1 native record envelope."""
        data = _strict_envelope(
            value,
            "native_record",
            "native_record",
            frozenset({"schema_version", "kind", "platform", "resource_kind", "id", "payload", "extensions"}),
        )
        platform = data["platform"]
        resource_kind = data["resource_kind"]
        if not isinstance(platform, str) or not isinstance(resource_kind, str):
            raise _contract_error("native_record.identity")
        return cls(
            platform=CatalogPlatform(platform),
            resource_kind=ResourceKind(resource_kind),
            id=CatalogId.from_dict(data["id"]),
            payload=_object_dict(data["payload"], "native_record.payload"),
            extensions=_object_dict(data["extensions"], "native_record.extensions"),
        )

from_dict(value) classmethod

Decode one strict schema-v1 native record envelope.

Source code in src/datasluice/domain/catalog/models.py
@classmethod
def from_dict(cls, value: object) -> NativeRecord:
    """Decode one strict schema-v1 native record envelope."""
    data = _strict_envelope(
        value,
        "native_record",
        "native_record",
        frozenset({"schema_version", "kind", "platform", "resource_kind", "id", "payload", "extensions"}),
    )
    platform = data["platform"]
    resource_kind = data["resource_kind"]
    if not isinstance(platform, str) or not isinstance(resource_kind, str):
        raise _contract_error("native_record.identity")
    return cls(
        platform=CatalogPlatform(platform),
        resource_kind=ResourceKind(resource_kind),
        id=CatalogId.from_dict(data["id"]),
        payload=_object_dict(data["payload"], "native_record.payload"),
        extensions=_object_dict(data["extensions"], "native_record.extensions"),
    )

to_dict()

Return a fresh JSON-safe native record envelope.

Source code in src/datasluice/domain/catalog/models.py
def to_dict(self) -> dict[str, object]:
    """Return a fresh JSON-safe native record envelope."""
    return {
        "schema_version": 1,
        "kind": "native_record",
        "platform": self.platform.value,
        "resource_kind": self.resource_kind.value,
        "id": self.id.to_dict(),
        "payload": _thaw_json(self.payload),
        "extensions": _thaw_json(self.extensions),
    }

ObjectStorage dataclass

Bases: ResourceAccess

Resource stored in object storage (S3, GCS, Azure Blob).

Attributes:

Name Type Description
uri str

Object URI (e.g. s3://bucket/key).

kind str

Discriminator, always "object_storage".

Source code in src/datasluice/domain/access.py
@dataclass(frozen=True, kw_only=True)
class ObjectStorage(ResourceAccess):
    """Resource stored in object storage (S3, GCS, Azure Blob).

    Attributes:
        uri: Object URI (e.g. ``s3://bucket/key``).
        kind: Discriminator, always ``"object_storage"``.
    """

    uri: str
    kind: str = field(init=False, default="object_storage")

OpenedResource

Lazy, single-use application wrapper over a Resource reader.

Source code in src/datasluice/application.py
class OpenedResource:
    """Lazy, single-use application wrapper over a Resource reader."""

    def __init__(self, resource: Resource, *, source_locator: ResourceLocator, reader: Any) -> None:
        self._resource = resource
        self._source_locator = source_locator
        self._reader = reader
        self._pipeline: Any | None = None
        self._raw_stream: Any | None = None
        self._transformed_stream: Any | None = None
        self._consumed = False
        self._closed = False
        self._manual_iteration = False

    @property
    def is_open(self) -> bool:
        """Whether the underlying data stream is currently open."""
        return self._raw_stream is not None and not self._closed

    def transform(self, pipeline: Any) -> OpenedResource:
        """Attach one transform pipeline without opening the resource."""
        self._ensure_available()
        self._pipeline = pipeline
        return self

    def iter_batches(self) -> Iterator[Any]:
        """Iterate batches once, closing every stream when iteration finishes."""
        self._ensure_available()
        if not self._manual_iteration:
            raise OpenedResourceConsumedError("Manual batch iteration requires an OpenedResource context manager")
        return self._iter_batches()

    def __iter__(self) -> Iterator[Any]:
        return self.iter_batches()

    def to_arrow(self) -> Any:
        """Consume this resource into an Arrow Table."""
        from datasluice.integrations.arrow import to_arrow

        return self._consume(to_arrow)

    def to_pandas(self) -> Any:
        """Consume this resource into a pandas DataFrame."""
        from datasluice.integrations.pandas import to_pandas

        return self._consume(to_pandas)

    def to_polars(self) -> Any:
        """Consume this resource into a polars DataFrame."""
        from datasluice.integrations.polars import to_polars

        return self._consume(to_polars)

    def to_duckdb(self, **kwargs: Any) -> Any:
        """Consume this resource into a DuckDB relation."""
        from datasluice.integrations.duckdb import to_duckdb

        return self._consume(lambda stream: to_duckdb(stream, **kwargs))

    def materialize(self, destination_uri: str, *, mode: str = "parquet") -> Any:
        """Materialize this resource once and return its Artifact envelope."""
        transforms = () if self._pipeline is None else tuple(type(step).__name__ for step in self._pipeline.steps)
        return self._consume(
            lambda stream: materialize(
                self._resource,
                destination_uri=destination_uri,
                source_locator=self._source_locator,
                stream=stream,
                mode=mode,
                transforms=transforms,
            )
        )

    def close(self) -> None:
        """Close an opened stream or prevent future consumption."""
        if self._closed:
            return
        self._finish(self._raw_stream, self._transformed_stream)

    def __enter__(self) -> OpenedResource:
        self._ensure_available()
        self._manual_iteration = True
        return self

    def __exit__(self, *exc: Any) -> None:
        try:
            self.close()
        finally:
            self._manual_iteration = False

    def _iter_batches(self) -> Iterator[Any]:
        raw_stream, stream = self._begin()
        try:
            yield from stream.iter_batches()
        except BaseException:
            self._finish_after_failure(raw_stream, stream)
            raise
        else:
            self._finish(raw_stream, stream)

    def _consume(self, operation: Callable[[Any], Any]) -> Any:
        raw_stream, stream = self._begin()
        try:
            result = operation(stream)
        except BaseException:
            self._finish_after_failure(raw_stream, stream)
            raise
        else:
            self._finish(raw_stream, stream)
            return result

    def _begin(self) -> tuple[Any, Any]:
        self._ensure_available()
        self._consumed = True
        raw_stream: Any | None = None
        stream: Any | None = None
        try:
            raw_stream = read_stream(self._resource, reader=self._reader)
            self._raw_stream = raw_stream
            stream = raw_stream if self._pipeline is None else run_transform_pipeline(raw_stream, self._pipeline)
            self._transformed_stream = stream
            return raw_stream, stream
        except BaseException:
            self._finish_after_failure(raw_stream, stream)
            raise

    def _finish(self, raw_stream: Any | None, stream: Any | None) -> None:
        self._raw_stream = None
        self._transformed_stream = None
        self._closed = True
        first_error: BaseException | None = None
        for candidate in (stream, raw_stream):
            if candidate is None or candidate is raw_stream and stream is raw_stream:
                continue
            try:
                candidate.close()
            except BaseException as exc:
                if first_error is None:
                    first_error = exc
        if raw_stream is not None and stream is raw_stream:
            try:
                raw_stream.close()
            except BaseException as exc:
                if first_error is None:
                    first_error = exc
        if first_error is not None:
            raise first_error

    def _finish_after_failure(self, raw_stream: Any | None, stream: Any | None) -> None:
        try:
            self._finish(raw_stream, stream)
        except BaseException:
            pass

    def _ensure_available(self) -> None:
        if self._closed or self._consumed:
            raise OpenedResourceConsumedError("OpenedResource has already been consumed or closed")

is_open property

Whether the underlying data stream is currently open.

close()

Close an opened stream or prevent future consumption.

Source code in src/datasluice/application.py
def close(self) -> None:
    """Close an opened stream or prevent future consumption."""
    if self._closed:
        return
    self._finish(self._raw_stream, self._transformed_stream)

iter_batches()

Iterate batches once, closing every stream when iteration finishes.

Source code in src/datasluice/application.py
def iter_batches(self) -> Iterator[Any]:
    """Iterate batches once, closing every stream when iteration finishes."""
    self._ensure_available()
    if not self._manual_iteration:
        raise OpenedResourceConsumedError("Manual batch iteration requires an OpenedResource context manager")
    return self._iter_batches()

materialize(destination_uri, *, mode='parquet')

Materialize this resource once and return its Artifact envelope.

Source code in src/datasluice/application.py
def materialize(self, destination_uri: str, *, mode: str = "parquet") -> Any:
    """Materialize this resource once and return its Artifact envelope."""
    transforms = () if self._pipeline is None else tuple(type(step).__name__ for step in self._pipeline.steps)
    return self._consume(
        lambda stream: materialize(
            self._resource,
            destination_uri=destination_uri,
            source_locator=self._source_locator,
            stream=stream,
            mode=mode,
            transforms=transforms,
        )
    )

to_arrow()

Consume this resource into an Arrow Table.

Source code in src/datasluice/application.py
def to_arrow(self) -> Any:
    """Consume this resource into an Arrow Table."""
    from datasluice.integrations.arrow import to_arrow

    return self._consume(to_arrow)

to_duckdb(**kwargs)

Consume this resource into a DuckDB relation.

Source code in src/datasluice/application.py
def to_duckdb(self, **kwargs: Any) -> Any:
    """Consume this resource into a DuckDB relation."""
    from datasluice.integrations.duckdb import to_duckdb

    return self._consume(lambda stream: to_duckdb(stream, **kwargs))

to_pandas()

Consume this resource into a pandas DataFrame.

Source code in src/datasluice/application.py
def to_pandas(self) -> Any:
    """Consume this resource into a pandas DataFrame."""
    from datasluice.integrations.pandas import to_pandas

    return self._consume(to_pandas)

to_polars()

Consume this resource into a polars DataFrame.

Source code in src/datasluice/application.py
def to_polars(self) -> Any:
    """Consume this resource into a polars DataFrame."""
    from datasluice.integrations.polars import to_polars

    return self._consume(to_polars)

transform(pipeline)

Attach one transform pipeline without opening the resource.

Source code in src/datasluice/application.py
def transform(self, pipeline: Any) -> OpenedResource:
    """Attach one transform pipeline without opening the resource."""
    self._ensure_available()
    self._pipeline = pipeline
    return self

OpenedResourceConsumedError

Bases: DataSluiceError

Raised when an opened resource is consumed or closed more than once.

Source code in src/datasluice/exceptions.py
class OpenedResourceConsumedError(DataSluiceError):
    """Raised when an opened resource is consumed or closed more than once."""

Organization dataclass

An organization or publisher of open-data datasets.

Attributes:

Name Type Description
id str

Portal-native organization identifier.

name str | None

Display name of the organization.

title str | None

Alternative human-readable title.

description str | None

Longer description, if available.

url str | None

URL to the organization's page on the portal.

logo_url str | None

URL to the organization's logo image.

created str | None

ISO-8601 creation timestamp, if available.

extra dict[str, Any]

Portal-native fields not captured above.

Source code in src/datasluice/domain/organization.py
@dataclass(frozen=True)
class Organization:
    """An organization or publisher of open-data datasets.

    Attributes:
        id: Portal-native organization identifier.
        name: Display name of the organization.
        title: Alternative human-readable title.
        description: Longer description, if available.
        url: URL to the organization's page on the portal.
        logo_url: URL to the organization's logo image.
        created: ISO-8601 creation timestamp, if available.
        extra: Portal-native fields not captured above.
    """

    id: str
    name: str | None = None
    title: str | None = None
    description: str | None = None
    url: str | None = None
    logo_url: str | None = None
    created: str | None = None
    extra: dict[str, Any] = field(default_factory=dict)

Query dataclass

Portal-agnostic search parameters.

Attributes:

Name Type Description
text str | None

Free-text search query.

tags list[str]

Filter by one or more tags.

organizations list[str]

Filter by organization name(s).

groups list[str]

Filter by group or theme name(s).

res_format str | None

Filter by resource format (e.g. "CSV").

license_id str | None

Filter by license identifier.

sort str | None

Sort field and direction (e.g. "metadata_modified desc").

limit int

Maximum number of results to return.

offset int

Number of results to skip (for pagination).

Source code in src/datasluice/domain/query.py
@dataclass(frozen=True)
class Query:
    """Portal-agnostic search parameters.

    Attributes:
        text: Free-text search query.
        tags: Filter by one or more tags.
        organizations: Filter by organization name(s).
        groups: Filter by group or theme name(s).
        res_format: Filter by resource format (e.g. ``"CSV"``).
        license_id: Filter by license identifier.
        sort: Sort field and direction (e.g. ``"metadata_modified desc"``).
        limit: Maximum number of results to return.
        offset: Number of results to skip (for pagination).
    """

    text: str | None = None
    tags: list[str] = field(default_factory=list)
    organizations: list[str] = field(default_factory=list)
    groups: list[str] = field(default_factory=list)
    res_format: str | None = None
    license_id: str | None = None
    sort: str | None = None
    limit: int = 100
    offset: int = 0

QueryAccess dataclass

Bases: ResourceAccess

Resource accessed via a query endpoint (SQL/SoQL/datastore).

Attributes:

Name Type Description
endpoint str

Query endpoint URL.

query_language str

Query language identifier (empty when unspecified).

kind str

Discriminator, always "query".

Source code in src/datasluice/domain/access.py
@dataclass(frozen=True, kw_only=True)
class QueryAccess(ResourceAccess):
    """Resource accessed via a query endpoint (SQL/SoQL/datastore).

    Attributes:
        endpoint: Query endpoint URL.
        query_language: Query language identifier (empty when unspecified).
        kind: Discriminator, always ``"query"``.
    """

    endpoint: str
    query_language: str = ""
    kind: str = field(init=False, default="query")

Resource dataclass

A single downloadable resource (file) within a dataset.

Attributes:

Name Type Description
id str

Portal-native resource identifier.

name str | None

Human-readable resource name or title.

url str | None

Direct download URL.

format str | None

Canonical file format (e.g. "CSV", "JSON").

media_type str | None

IANA media type if known (e.g. "text/csv").

description str | None

Optional longer description.

size int | None

File size in bytes, if known.

license License | None

License under which this resource is published.

created str | None

ISO-8601 creation timestamp, if available.

modified str | None

ISO-8601 last-modified timestamp, if available.

access ResourceAccess | None

How the resource is reached (HTTP, object storage, local file, query). Defaults to HttpDownload(url=resource.url) when unset.

schema Schema | None

Advisory portal-native column descriptors. Readers infer the Arrow schema from actual data; this field is display-only.

extra dict[str, Any]

Portal-native fields not captured above.

Source code in src/datasluice/domain/resource.py
@dataclass(frozen=True, kw_only=True)
class Resource:
    """A single downloadable resource (file) within a dataset.

    Attributes:
        id: Portal-native resource identifier.
        name: Human-readable resource name or title.
        url: Direct download URL.
        format: Canonical file format (e.g. ``"CSV"``, ``"JSON"``).
        media_type: IANA media type if known (e.g. ``"text/csv"``).
        description: Optional longer description.
        size: File size in bytes, if known.
        license: License under which this resource is published.
        created: ISO-8601 creation timestamp, if available.
        modified: ISO-8601 last-modified timestamp, if available.
        access: How the resource is reached (HTTP, object storage, local file, query).
            Defaults to ``HttpDownload(url=resource.url)`` when unset.
        schema: Advisory portal-native column descriptors. Readers infer
            the Arrow schema from actual data; this field is display-only.
        extra: Portal-native fields not captured above.
    """

    id: str
    name: str | None = None
    url: str | None = None
    format: str | None = None
    media_type: str | None = None
    description: str | None = None
    size: int | None = None
    license: License | None = None
    created: str | None = None
    modified: str | None = None
    access: ResourceAccess | None = None
    schema: Schema | None = None
    extra: dict[str, Any] = field(default_factory=dict)

    @classmethod
    def normalize_format(cls, raw: str | None) -> str | None:
        """Normalise a raw format string or media type to canonical form."""
        if raw is None:
            return None
        return _FORMAT_ALIASES.get(raw.lower(), raw.upper().strip())

normalize_format(raw) classmethod

Normalise a raw format string or media type to canonical form.

Source code in src/datasluice/domain/resource.py
@classmethod
def normalize_format(cls, raw: str | None) -> str | None:
    """Normalise a raw format string or media type to canonical form."""
    if raw is None:
        return None
    return _FORMAT_ALIASES.get(raw.lower(), raw.upper().strip())

ResourceAccess dataclass

Base descriptor for how a resource is accessed.

Subclasses discriminate on kind so match-dispatch can route to the correct reader without complex isinstance chains.

Attributes:

Name Type Description
kind str

Discriminator string identifying the access variant.

extra Mapping[str, Any]

Portal-native access fields not captured above.

Source code in src/datasluice/domain/access.py
@dataclass(frozen=True, kw_only=True)
class ResourceAccess:
    """Base descriptor for how a resource is accessed.

    Subclasses discriminate on ``kind`` so match-dispatch
    can route to the correct reader without complex isinstance chains.

    Attributes:
        kind: Discriminator string identifying the access variant.
        extra: Portal-native access fields not captured above.
    """

    kind: str
    extra: Mapping[str, Any] = field(default_factory=dict)

    def __post_init__(self) -> None:
        if not isinstance(self.extra, MappingProxyType):
            object.__setattr__(self, "extra", MappingProxyType(dict(self.extra)))

ResourceKind dataclass

An explicit resource kind scoped by a CatalogId.

Source code in src/datasluice/domain/catalog/ids.py
@dataclass(frozen=True)
class ResourceKind:
    """An explicit resource kind scoped by a CatalogId."""

    value: str

    DATASET: ClassVar[ResourceKind]
    RESOURCE: ClassVar[ResourceKind]
    ORGANIZATION: ClassVar[ResourceKind]
    USER: ClassVar[ResourceKind]

    def __post_init__(self) -> None:
        if not isinstance(self.value, str) or _VALUE_RE.fullmatch(self.value) is None:
            raise _contract_error("resource_kind")

    def __str__(self) -> str:
        """Return the JSON resource-kind value."""
        return self.value

__str__()

Return the JSON resource-kind value.

Source code in src/datasluice/domain/catalog/ids.py
def __str__(self) -> str:
    """Return the JSON resource-kind value."""
    return self.value

ResourceResolutionError

Bases: DataSluiceError

Raised when a public resource locator cannot select exactly one resource.

Source code in src/datasluice/exceptions.py
class ResourceResolutionError(DataSluiceError):
    """Raised when a public resource locator cannot select exactly one resource."""

ResultEnvelope dataclass

A typed immutable result collection with its catalog metadata.

Source code in src/datasluice/domain/catalog/models.py
@dataclass(frozen=True)
class ResultEnvelope[T]:
    """A typed immutable result collection with its catalog metadata."""

    items: tuple[T, ...]
    page: PageInfo | None = None
    warnings: tuple[WarningRecord, ...] = ()
    platform: PlatformMetadata | None = None

    def __post_init__(self) -> None:
        if not isinstance(self.items, tuple):
            object.__setattr__(self, "items", tuple(self.items))
        if self.page is not None and not isinstance(self.page, PageInfo):
            raise _contract_error("result_envelope.page")
        if not isinstance(self.warnings, tuple):
            object.__setattr__(self, "warnings", tuple(self.warnings))
        if not all(isinstance(warning, WarningRecord) for warning in self.warnings):
            raise _contract_error("result_envelope.warnings")
        if self.platform is not None and not isinstance(self.platform, PlatformMetadata):
            raise _contract_error("result_envelope.platform")
        if not all(hasattr(item, "to_dict") for item in self.items):
            raise _contract_error("result_envelope.items")

    def to_dict(self) -> dict[str, object]:
        """Return a fresh JSON-safe result envelope."""
        return {
            "schema_version": 1,
            "kind": "result_envelope",
            "items": [item.to_dict() for item in self.items],  # ty: ignore[unresolved-attribute]: validated in post-init
            "page": self.page.to_dict() if self.page is not None else None,
            "warnings": [warning.to_dict() for warning in self.warnings],
            "platform": self.platform.to_dict() if self.platform is not None else None,
        }

    @classmethod
    def from_dict(cls, value: object, *, item_decoder: Callable[[object], T]) -> ResultEnvelope[T]:
        """Decode one strict schema-v1 result envelope with an item decoder."""
        data = _strict_envelope(
            value,
            "result_envelope",
            "result_envelope",
            frozenset({"schema_version", "kind", "items", "page", "warnings", "platform"}),
        )
        items = data["items"]
        warnings = data["warnings"]
        page = data["page"]
        platform = data["platform"]
        if not isinstance(items, list) or not isinstance(warnings, list):
            raise _contract_error("result_envelope")
        return cls(
            items=tuple(item_decoder(item) for item in items),
            page=PageInfo.from_dict(page) if page is not None else None,
            warnings=tuple(WarningRecord.from_dict(warning) for warning in warnings),
            platform=PlatformMetadata.from_dict(platform) if platform is not None else None,
        )

from_dict(value, *, item_decoder) classmethod

Decode one strict schema-v1 result envelope with an item decoder.

Source code in src/datasluice/domain/catalog/models.py
@classmethod
def from_dict(cls, value: object, *, item_decoder: Callable[[object], T]) -> ResultEnvelope[T]:
    """Decode one strict schema-v1 result envelope with an item decoder."""
    data = _strict_envelope(
        value,
        "result_envelope",
        "result_envelope",
        frozenset({"schema_version", "kind", "items", "page", "warnings", "platform"}),
    )
    items = data["items"]
    warnings = data["warnings"]
    page = data["page"]
    platform = data["platform"]
    if not isinstance(items, list) or not isinstance(warnings, list):
        raise _contract_error("result_envelope")
    return cls(
        items=tuple(item_decoder(item) for item in items),
        page=PageInfo.from_dict(page) if page is not None else None,
        warnings=tuple(WarningRecord.from_dict(warning) for warning in warnings),
        platform=PlatformMetadata.from_dict(platform) if platform is not None else None,
    )

to_dict()

Return a fresh JSON-safe result envelope.

Source code in src/datasluice/domain/catalog/models.py
def to_dict(self) -> dict[str, object]:
    """Return a fresh JSON-safe result envelope."""
    return {
        "schema_version": 1,
        "kind": "result_envelope",
        "items": [item.to_dict() for item in self.items],  # ty: ignore[unresolved-attribute]: validated in post-init
        "page": self.page.to_dict() if self.page is not None else None,
        "warnings": [warning.to_dict() for warning in self.warnings],
        "platform": self.platform.to_dict() if self.platform is not None else None,
    }

Schema dataclass

Schema describing the columns of a tabular resource.

Attributes:

Name Type Description
name str

Logical name for the schema.

columns Sequence[dict[str, Any]]

Column descriptors (name, type, nullable, and portal-native fields).

version str

Schema evolution version.

extra Mapping[str, Any]

Portal-native schema fields not captured above.

Source code in src/datasluice/domain/schema.py
@dataclass(frozen=True)
class Schema:
    """Schema describing the columns of a tabular resource.

    Attributes:
        name: Logical name for the schema.
        columns: Column descriptors (name, type, nullable, and portal-native fields).
        version: Schema evolution version.
        extra: Portal-native schema fields not captured above.
    """

    name: str
    columns: Sequence[dict[str, Any]] = field(default_factory=tuple)
    version: str = "1"
    extra: Mapping[str, Any] = field(default_factory=dict)

    def __post_init__(self) -> None:
        if not isinstance(self.columns, tuple):
            object.__setattr__(self, "columns", tuple(self.columns))
        if not isinstance(self.extra, MappingProxyType):
            object.__setattr__(self, "extra", MappingProxyType(dict(self.extra)))

SearchResult dataclass

A paginated page of search results.

Attributes:

Name Type Description
datasets list[Dataset]

Datasets returned in this page.

total int

Total number of matching datasets across all pages.

page int

Current page number (1-based).

page_size int

Number of results per page.

has_next bool

Whether additional pages are available.

Source code in src/datasluice/domain/result.py
@dataclass
class SearchResult:
    """A paginated page of search results.

    Attributes:
        datasets: Datasets returned in this page.
        total: Total number of matching datasets across all pages.
        page: Current page number (1-based).
        page_size: Number of results per page.
        has_next: Whether additional pages are available.
    """

    datasets: list[Dataset] = field(default_factory=list)
    total: int = 0
    page: int = 1
    page_size: int = 100
    has_next: bool = False

    def __iter__(self) -> Iterator[Dataset]:
        return iter(self.datasets)

    def __len__(self) -> int:
        return len(self.datasets)

StateStoreError

Bases: DataSluiceError

Raised when a state store cannot read or write durable sync state.

A direct child of :class:DataSluiceError because store I/O failures are neither portal, download, nor format errors: a corrupt or wrong-version JSON envelope is a data-integrity failure of the local/remote state file. Fails loud (never silently treats corrupt state as "no state") because staleness is worse than a loud failure.

Source code in src/datasluice/exceptions.py
class StateStoreError(DataSluiceError):
    """Raised when a state store cannot read or write durable sync state.

    A direct child of :class:`DataSluiceError` because store I/O failures are
    neither portal, download, nor format errors: a corrupt or wrong-version
    JSON envelope is a data-integrity failure of the local/remote state file.
    Fails loud (never silently treats corrupt state as "no state") because
    staleness is worse than a loud failure.
    """

StreamAccess dataclass

Bases: ResourceAccess

Resource consumed as a streaming endpoint.

Attributes:

Name Type Description
url str

Stream URL.

kind str

Discriminator, always "stream".

Source code in src/datasluice/domain/access.py
@dataclass(frozen=True, kw_only=True)
class StreamAccess(ResourceAccess):
    """Resource consumed as a streaming endpoint.

    Attributes:
        url: Stream URL.
        kind: Discriminator, always ``"stream"``.
    """

    url: str
    kind: str = field(init=False, default="stream")

SyncState dataclass

Incremental synchronization state for a resource or connector.

Attributes:

Name Type Description
cursor dict[str, str]

Mapping of resource IDs to watermark values.

partitions dict[str, Any]

Partition progress metadata for parallel syncs.

last_synced_at str | None

ISO-8601 timestamp of the last successful sync.

extra dict[str, Any]

Connector-native sync fields not captured above.

Source code in src/datasluice/domain/sync_state.py
@dataclass(frozen=True)
class SyncState:
    """Incremental synchronization state for a resource or connector.

    Attributes:
        cursor: Mapping of resource IDs to watermark values.
        partitions: Partition progress metadata for parallel syncs.
        last_synced_at: ISO-8601 timestamp of the last successful sync.
        extra: Connector-native sync fields not captured above.
    """

    cursor: dict[str, str] = field(default_factory=dict)
    partitions: dict[str, Any] = field(default_factory=dict)
    last_synced_at: str | None = None
    extra: dict[str, Any] = field(default_factory=dict)

SyncStateConflictError

Bases: StateStoreError

Raised when a state write loses an optimistic compare-and-swap race.

The version read before the write had already been replaced by a concurrent writer; the caller must re-read and re-apply their mutation rather than silently overwrite another writer's state.

Source code in src/datasluice/exceptions.py
class SyncStateConflictError(StateStoreError):
    """Raised when a state write loses an optimistic compare-and-swap race.

    The version read before the write had already been replaced by a
    concurrent writer; the caller must re-read and re-apply their mutation
    rather than silently overwrite another writer's state.
    """

UnauthenticatedError

Bases: CatalogError

Raised when credentials are absent, invalid, or expired.

Source code in src/datasluice/errors/catalog.py
class UnauthenticatedError(CatalogError):
    """Raised when credentials are absent, invalid, or expired."""

UnsupportedCapabilityError

Bases: CatalogError

Raised when a deployment does not support a requested operation.

Source code in src/datasluice/errors/catalog.py
class UnsupportedCapabilityError(CatalogError):
    """Raised when a deployment does not support a requested operation."""

resource_locator_from_dict(value)

Decode one strict, tagged ResourceLocator envelope.

Source code in src/datasluice/application.py
def resource_locator_from_dict(value: object) -> DirectResourceLocator:
    """Decode one strict, tagged ResourceLocator envelope."""
    data = _object_dict(value, "locator")
    kind = data.get("kind")
    if kind == "direct":
        return DirectResourceLocator.from_dict(data)
    raise _contract_error("kind")

Public catalog contract suite

The executable contract API for built-in and third-party connectors — normalized client Protocols, pinned reference fixtures, the compliance runner and report, certification, and namespaced manifest types. This is the only public entry point for contract execution and certification:

Public executable contracts for catalog connectors.

AsyncCatalogClient

Bases: Protocol

Asynchronous normalized catalog client surface.

Source code in src/datasluice/contracts/catalog/protocols.py
@runtime_checkable
class AsyncCatalogClient(Protocol):
    """Asynchronous normalized catalog client surface."""

    @property
    def datasets(self) -> AsyncDatasetService:
        """Return normalized dataset operations."""

    @property
    def resources(self) -> AsyncResourceService:
        """Return normalized resource operations."""

    @property
    def organizations(self) -> AsyncOrganizationService:
        """Return normalized organization operations."""

    def capability(self, operation_id: str) -> CapabilityState:
        """Return the effective non-dispatching capability classification."""

    def platform_metadata(self) -> Mapping[str, object]:
        """Return safe platform metadata."""

    async def aclose(self) -> None:
        """Release owned asynchronous resources."""

    async def __aenter__(self) -> Self:
        """Enter a managed asynchronous client context."""

    async def __aexit__(
        self,
        exc_type: type[BaseException] | None,
        exc_value: BaseException | None,
        traceback: TracebackType | None,
    ) -> None:
        """Close resources on context exit."""

datasets property

Return normalized dataset operations.

organizations property

Return normalized organization operations.

resources property

Return normalized resource operations.

__aenter__() async

Enter a managed asynchronous client context.

Source code in src/datasluice/contracts/catalog/protocols.py
async def __aenter__(self) -> Self:
    """Enter a managed asynchronous client context."""

__aexit__(exc_type, exc_value, traceback) async

Close resources on context exit.

Source code in src/datasluice/contracts/catalog/protocols.py
async def __aexit__(
    self,
    exc_type: type[BaseException] | None,
    exc_value: BaseException | None,
    traceback: TracebackType | None,
) -> None:
    """Close resources on context exit."""

aclose() async

Release owned asynchronous resources.

Source code in src/datasluice/contracts/catalog/protocols.py
async def aclose(self) -> None:
    """Release owned asynchronous resources."""

capability(operation_id)

Return the effective non-dispatching capability classification.

Source code in src/datasluice/contracts/catalog/protocols.py
def capability(self, operation_id: str) -> CapabilityState:
    """Return the effective non-dispatching capability classification."""

platform_metadata()

Return safe platform metadata.

Source code in src/datasluice/contracts/catalog/protocols.py
def platform_metadata(self) -> Mapping[str, object]:
    """Return safe platform metadata."""

CaseOutcome dataclass

Immutable evidence for one catalog contract case execution.

Source code in src/datasluice/contracts/catalog/report.py
@dataclass(frozen=True, slots=True)
class CaseOutcome:
    """Immutable evidence for one catalog contract case execution."""

    operation_id: str
    mode: Literal["sync", "async"]
    capability: Literal["available", "unavailable"]
    state: Literal["passed", "failed", "blocked"]
    tier: str = "core"
    warnings: tuple[str, ...] = ()
    evidence: Mapping[str, object] = field(default_factory=dict)
    platform_metadata: Mapping[str, object] = field(default_factory=dict)

    def __post_init__(self) -> None:
        if not isinstance(self.operation_id, str) or not self.operation_id or len(self.operation_id) > 256:
            raise _report_error("outcome.operation_id")
        if self.mode not in {"sync", "async"} or self.capability not in {"available", "unavailable"}:
            raise _report_error("outcome")
        if self.state not in _OUTCOME_STATES or not isinstance(self.tier, str) or not self.tier or len(self.tier) > 64:
            raise _report_error("outcome")
        if (
            not isinstance(self.warnings, tuple)
            or len(self.warnings) > 32
            or not all(isinstance(warning, str) for warning in self.warnings)
        ):
            raise _report_error("outcome.warnings")
        object.__setattr__(
            self, "warnings", tuple(_sanitize_text(warning, "outcome.warnings") for warning in self.warnings)
        )
        object.__setattr__(self, "evidence", _sanitize_mapping(self.evidence, _EVIDENCE_FIELDS, "outcome.evidence"))
        object.__setattr__(
            self,
            "platform_metadata",
            _sanitize_mapping(self.platform_metadata, _METADATA_FIELDS, "outcome.platform_metadata"),
        )

    @property
    def case_id(self) -> str:
        """Return the stable runner-owned case identity."""
        return f"{self.operation_id}[{self.tier}][{self.mode}]"

    def to_dict(self) -> dict[str, object]:
        """Return a fresh JSON-safe case outcome envelope."""
        return {
            "operation_id": self.operation_id,
            "mode": self.mode,
            "capability": self.capability,
            "state": self.state,
            "tier": self.tier,
            "warnings": list(self.warnings),
            "evidence": _thaw_json(self.evidence),
            "platform_metadata": _thaw_json(self.platform_metadata),
        }

    @classmethod
    def from_dict(cls, value: object) -> CaseOutcome:
        """Decode one strict JSON-safe case outcome envelope."""
        fields = {"operation_id", "mode", "capability", "state", "tier", "warnings", "evidence", "platform_metadata"}
        if not isinstance(value, dict) or set(value) != fields:
            raise _report_error("outcome")
        warnings = value["warnings"]
        evidence = value["evidence"]
        metadata = value["platform_metadata"]
        if (
            not isinstance(value["operation_id"], str)
            or value["mode"] not in {"sync", "async"}
            or value["capability"] not in {"available", "unavailable"}
            or value["state"] not in _OUTCOME_STATES
            or not isinstance(value["tier"], str)
            or not isinstance(warnings, list)
            or not all(isinstance(warning, str) for warning in warnings)
            or not isinstance(evidence, dict)
            or not isinstance(metadata, dict)
        ):
            raise _report_error("outcome")
        return cls(
            operation_id=value["operation_id"],
            mode=cast(Literal["sync", "async"], value["mode"]),
            capability=cast(Literal["available", "unavailable"], value["capability"]),
            state=cast(Literal["passed", "failed", "blocked"], value["state"]),
            tier=value["tier"],
            warnings=tuple(warnings),
            evidence=evidence,
            platform_metadata=metadata,
        )

case_id property

Return the stable runner-owned case identity.

from_dict(value) classmethod

Decode one strict JSON-safe case outcome envelope.

Source code in src/datasluice/contracts/catalog/report.py
@classmethod
def from_dict(cls, value: object) -> CaseOutcome:
    """Decode one strict JSON-safe case outcome envelope."""
    fields = {"operation_id", "mode", "capability", "state", "tier", "warnings", "evidence", "platform_metadata"}
    if not isinstance(value, dict) or set(value) != fields:
        raise _report_error("outcome")
    warnings = value["warnings"]
    evidence = value["evidence"]
    metadata = value["platform_metadata"]
    if (
        not isinstance(value["operation_id"], str)
        or value["mode"] not in {"sync", "async"}
        or value["capability"] not in {"available", "unavailable"}
        or value["state"] not in _OUTCOME_STATES
        or not isinstance(value["tier"], str)
        or not isinstance(warnings, list)
        or not all(isinstance(warning, str) for warning in warnings)
        or not isinstance(evidence, dict)
        or not isinstance(metadata, dict)
    ):
        raise _report_error("outcome")
    return cls(
        operation_id=value["operation_id"],
        mode=cast(Literal["sync", "async"], value["mode"]),
        capability=cast(Literal["available", "unavailable"], value["capability"]),
        state=cast(Literal["passed", "failed", "blocked"], value["state"]),
        tier=value["tier"],
        warnings=tuple(warnings),
        evidence=evidence,
        platform_metadata=metadata,
    )

to_dict()

Return a fresh JSON-safe case outcome envelope.

Source code in src/datasluice/contracts/catalog/report.py
def to_dict(self) -> dict[str, object]:
    """Return a fresh JSON-safe case outcome envelope."""
    return {
        "operation_id": self.operation_id,
        "mode": self.mode,
        "capability": self.capability,
        "state": self.state,
        "tier": self.tier,
        "warnings": list(self.warnings),
        "evidence": _thaw_json(self.evidence),
        "platform_metadata": _thaw_json(self.platform_metadata),
    }

CatalogCertification dataclass

Immutable proof that a report satisfies one manifest and fixture binding.

Source code in src/datasluice/contracts/catalog/certification.py
@dataclass(frozen=True, slots=True)
class CatalogCertification:
    """Immutable proof that a report satisfies one manifest and fixture binding."""

    connector_id: ConnectorId
    profile_version: str
    fixture_fingerprint: str
    contract_schema_version: str
    report_fingerprint: str
    outcome_count: int

CatalogContractCase dataclass

One deterministic catalog operation/state/mode contract case.

Source code in src/datasluice/contracts/catalog/runner.py
@dataclass(frozen=True, slots=True)
class CatalogContractCase:
    """One deterministic catalog operation/state/mode contract case."""

    operation_id: str
    outcome: FixtureOutcome = "core"
    mode: ContractMode = "sync"
    dataset_id: str | None = None

    def __post_init__(self) -> None:
        if not self.operation_id or self.outcome not in _OUTCOMES or self.mode not in {"sync", "async"}:
            raise ValueError("Catalog contract cases require a declared operation, outcome, and mode.")
        if self.dataset_id is not None and not self.dataset_id:
            raise ValueError("Catalog contract dataset IDs must be non-empty when supplied.")

    @property
    def pytest_id(self) -> str:
        """Return the stable identifier used by parametrized pytest cases."""
        return f"{self.operation_id}[{self.outcome}][{self.mode}]"

pytest_id property

Return the stable identifier used by parametrized pytest cases.

CertificationRecord dataclass

Versioned identity of a connector's public contract report.

Source code in src/datasluice/domain/catalog/extensions.py
@dataclass(frozen=True, slots=True)
class CertificationRecord:
    """Versioned identity of a connector's public contract report."""

    connector_id: ConnectorId
    contract_schema_version: str
    profile_version: str
    report_version: str
    report_id: str

    def __post_init__(self) -> None:
        for name, value in (
            ("contract schema version", self.contract_schema_version),
            ("profile version", self.profile_version),
            ("report version", self.report_version),
            ("report identity", self.report_id),
        ):
            if not value.strip():
                raise ValueError(f"Certification {name} is required.")

ComplianceReport dataclass

Schema-versioned aggregate evidence from catalog contract execution.

Source code in src/datasluice/contracts/catalog/report.py
@dataclass(frozen=True, slots=True)
class ComplianceReport:
    """Schema-versioned aggregate evidence from catalog contract execution."""

    SCHEMA_VERSION: ClassVar[int] = 1

    outcomes: tuple[CaseOutcome, ...]
    connector_id: str | None = None
    manifest_version: str | None = None
    profile_version: str | None = None
    fixture_fingerprint: str | None = None
    contract_schema_version: str | None = None
    generated_at: str | None = None
    expected_case_ids: tuple[str, ...] = ()
    warnings: tuple[str, ...] = ()
    platform_metadata: Mapping[str, object] = field(default_factory=dict)

    def __post_init__(self) -> None:
        if (
            not isinstance(self.outcomes, tuple)
            or not self.outcomes
            or len(self.outcomes) > 4096
            or not all(isinstance(outcome, CaseOutcome) for outcome in self.outcomes)
            or len({outcome.case_id for outcome in self.outcomes}) != len(self.outcomes)
        ):
            raise _report_error("report.outcomes")
        for name in (
            "connector_id",
            "manifest_version",
            "profile_version",
            "fixture_fingerprint",
            "contract_schema_version",
            "generated_at",
        ):
            object.__setattr__(self, name, _identity(getattr(self, name), f"report.{name}"))
        expected = self.expected_case_ids or tuple(outcome.case_id for outcome in self.outcomes)
        if (
            not isinstance(expected, tuple)
            or len(expected) > 4096
            or not all(isinstance(case_id, str) and case_id and len(case_id) <= 384 for case_id in expected)
        ):
            raise _report_error("report.expected_case_ids")
        object.__setattr__(self, "expected_case_ids", tuple(sorted(set(expected))))
        if (
            not isinstance(self.warnings, tuple)
            or len(self.warnings) > 32
            or not all(isinstance(warning, str) for warning in self.warnings)
        ):
            raise _report_error("report.warnings")
        object.__setattr__(
            self, "warnings", tuple(_sanitize_text(warning, "report.warnings") for warning in self.warnings)
        )
        object.__setattr__(
            self,
            "platform_metadata",
            _sanitize_mapping(self.platform_metadata, _METADATA_FIELDS, "report.platform_metadata"),
        )

    @property
    def gaps(self) -> tuple[str, ...]:
        """Return explicit missing or non-passing required case evidence."""
        outcomes = {outcome.case_id: outcome for outcome in self.outcomes}
        return tuple(
            f"{case_id}: missing" if case_id not in outcomes else f"{case_id}: {outcomes[case_id].state}"
            for case_id in self.expected_case_ids
            if case_id not in outcomes or outcomes[case_id].state != "passed"
        )

    @property
    def is_compliant(self) -> bool:
        """Return whether all runner-owned required evidence passed."""
        return not self.gaps

    @property
    def fingerprint(self) -> str:
        """Return the stable SHA-256 fingerprint of the report envelope."""
        payload = json.dumps(self.to_dict(), sort_keys=True, separators=(",", ":")).encode("utf-8")
        return hashlib.sha256(payload).hexdigest()

    @property
    def report_id(self) -> str:
        """Return the certificate-compatible report identity."""
        return f"sha256:{self.fingerprint}"

    @property
    def coverage_by_mode(self) -> dict[str, int]:
        """Return deterministic outcome coverage counts by execution mode."""
        return self._coverage("mode")

    @property
    def coverage_by_state(self) -> dict[str, int]:
        """Return deterministic outcome coverage counts by execution state."""
        return self._coverage("state")

    @property
    def coverage_by_tier(self) -> dict[str, int]:
        """Return deterministic outcome coverage counts by declared contract tier."""
        return self._coverage("tier")

    def _coverage(self, field_name: Literal["mode", "state", "tier"]) -> dict[str, int]:
        counts: dict[str, int] = {}
        for outcome in self.outcomes:
            value = getattr(outcome, field_name)
            counts[value] = counts.get(value, 0) + 1
        return dict(sorted(counts.items()))

    def to_dict(self) -> dict[str, object]:
        """Return a strict, deterministic, JSON-safe compliance report envelope."""
        return {
            "schema_version": self.SCHEMA_VERSION,
            "connector_id": self.connector_id,
            "manifest_version": self.manifest_version,
            "profile_version": self.profile_version,
            "fixture_fingerprint": self.fixture_fingerprint,
            "contract_schema_version": self.contract_schema_version,
            "generated_at": self.generated_at,
            "expected_case_ids": list(self.expected_case_ids),
            "outcomes": [outcome.to_dict() for outcome in self.outcomes],
            "warnings": list(self.warnings),
            "platform_metadata": _thaw_json(self.platform_metadata),
        }

    def write_json(self, path: Path | str) -> None:
        """Write this report only to the caller-selected local path."""
        Path(path).write_text(json.dumps(self.to_dict(), sort_keys=True, separators=(",", ":")), encoding="utf-8")

    @classmethod
    def from_dict(cls, value: object) -> ComplianceReport:
        """Decode one strict JSON-safe compliance report envelope."""
        fields = {
            "schema_version",
            "connector_id",
            "manifest_version",
            "profile_version",
            "fixture_fingerprint",
            "contract_schema_version",
            "generated_at",
            "expected_case_ids",
            "outcomes",
            "warnings",
            "platform_metadata",
        }
        if not isinstance(value, dict) or set(value) != fields or value["schema_version"] != cls.SCHEMA_VERSION:
            raise _report_error("report")
        identities = (
            "connector_id",
            "manifest_version",
            "profile_version",
            "fixture_fingerprint",
            "contract_schema_version",
            "generated_at",
        )
        if (
            not all(value[name] is None or isinstance(value[name], str) for name in identities)
            or not isinstance(value["expected_case_ids"], list)
            or not all(isinstance(case_id, str) for case_id in value["expected_case_ids"])
            or not isinstance(value["outcomes"], list)
            or not isinstance(value["warnings"], list)
            or not all(isinstance(warning, str) for warning in value["warnings"])
            or not isinstance(value["platform_metadata"], dict)
        ):
            raise _report_error("report")
        return cls(
            outcomes=tuple(CaseOutcome.from_dict(outcome) for outcome in value["outcomes"]),
            connector_id=cast(str | None, value["connector_id"]),
            manifest_version=cast(str | None, value["manifest_version"]),
            profile_version=cast(str | None, value["profile_version"]),
            fixture_fingerprint=cast(str | None, value["fixture_fingerprint"]),
            contract_schema_version=cast(str | None, value["contract_schema_version"]),
            generated_at=cast(str | None, value["generated_at"]),
            expected_case_ids=tuple(value["expected_case_ids"]),
            warnings=tuple(value["warnings"]),
            platform_metadata=value["platform_metadata"],
        )

coverage_by_mode property

Return deterministic outcome coverage counts by execution mode.

coverage_by_state property

Return deterministic outcome coverage counts by execution state.

coverage_by_tier property

Return deterministic outcome coverage counts by declared contract tier.

fingerprint property

Return the stable SHA-256 fingerprint of the report envelope.

gaps property

Return explicit missing or non-passing required case evidence.

is_compliant property

Return whether all runner-owned required evidence passed.

report_id property

Return the certificate-compatible report identity.

from_dict(value) classmethod

Decode one strict JSON-safe compliance report envelope.

Source code in src/datasluice/contracts/catalog/report.py
@classmethod
def from_dict(cls, value: object) -> ComplianceReport:
    """Decode one strict JSON-safe compliance report envelope."""
    fields = {
        "schema_version",
        "connector_id",
        "manifest_version",
        "profile_version",
        "fixture_fingerprint",
        "contract_schema_version",
        "generated_at",
        "expected_case_ids",
        "outcomes",
        "warnings",
        "platform_metadata",
    }
    if not isinstance(value, dict) or set(value) != fields or value["schema_version"] != cls.SCHEMA_VERSION:
        raise _report_error("report")
    identities = (
        "connector_id",
        "manifest_version",
        "profile_version",
        "fixture_fingerprint",
        "contract_schema_version",
        "generated_at",
    )
    if (
        not all(value[name] is None or isinstance(value[name], str) for name in identities)
        or not isinstance(value["expected_case_ids"], list)
        or not all(isinstance(case_id, str) for case_id in value["expected_case_ids"])
        or not isinstance(value["outcomes"], list)
        or not isinstance(value["warnings"], list)
        or not all(isinstance(warning, str) for warning in value["warnings"])
        or not isinstance(value["platform_metadata"], dict)
    ):
        raise _report_error("report")
    return cls(
        outcomes=tuple(CaseOutcome.from_dict(outcome) for outcome in value["outcomes"]),
        connector_id=cast(str | None, value["connector_id"]),
        manifest_version=cast(str | None, value["manifest_version"]),
        profile_version=cast(str | None, value["profile_version"]),
        fixture_fingerprint=cast(str | None, value["fixture_fingerprint"]),
        contract_schema_version=cast(str | None, value["contract_schema_version"]),
        generated_at=cast(str | None, value["generated_at"]),
        expected_case_ids=tuple(value["expected_case_ids"]),
        warnings=tuple(value["warnings"]),
        platform_metadata=value["platform_metadata"],
    )

to_dict()

Return a strict, deterministic, JSON-safe compliance report envelope.

Source code in src/datasluice/contracts/catalog/report.py
def to_dict(self) -> dict[str, object]:
    """Return a strict, deterministic, JSON-safe compliance report envelope."""
    return {
        "schema_version": self.SCHEMA_VERSION,
        "connector_id": self.connector_id,
        "manifest_version": self.manifest_version,
        "profile_version": self.profile_version,
        "fixture_fingerprint": self.fixture_fingerprint,
        "contract_schema_version": self.contract_schema_version,
        "generated_at": self.generated_at,
        "expected_case_ids": list(self.expected_case_ids),
        "outcomes": [outcome.to_dict() for outcome in self.outcomes],
        "warnings": list(self.warnings),
        "platform_metadata": _thaw_json(self.platform_metadata),
    }

write_json(path)

Write this report only to the caller-selected local path.

Source code in src/datasluice/contracts/catalog/report.py
def write_json(self, path: Path | str) -> None:
    """Write this report only to the caller-selected local path."""
    Path(path).write_text(json.dumps(self.to_dict(), sort_keys=True, separators=(",", ":")), encoding="utf-8")

ConnectorId dataclass

Namespaced stable identifier for one connector distribution.

Source code in src/datasluice/domain/catalog/extensions.py
@dataclass(frozen=True, slots=True)
class ConnectorId:
    """Namespaced stable identifier for one connector distribution."""

    vendor: str
    platform: str

    def __post_init__(self) -> None:
        if not _IDENTIFIER.fullmatch(self.vendor) or not _IDENTIFIER.fullmatch(self.platform):
            raise ValueError("Connector IDs must use lowercase vendor/platform identifiers.")
        if self.vendor == "datasluice" and self.platform not in _BUILTIN_PLATFORMS:
            raise ValueError("The datasluice vendor namespace is reserved for built-in connector IDs.")

    @classmethod
    def parse(cls, value: str) -> ConnectorId:
        """Parse a ``vendor/platform`` connector identifier."""
        parts = value.split("/")
        if len(parts) != 2 or not all(parts):
            raise ValueError("Connector IDs must use vendor/platform form.")
        return cls(vendor=parts[0], platform=parts[1])

    @property
    def is_builtin(self) -> bool:
        """Return whether this identifier names a maintained built-in connector."""
        return self.vendor == "datasluice"

    def __str__(self) -> str:
        """Return the canonical namespaced connector ID."""
        return f"{self.vendor}/{self.platform}"

is_builtin property

Return whether this identifier names a maintained built-in connector.

__str__()

Return the canonical namespaced connector ID.

Source code in src/datasluice/domain/catalog/extensions.py
def __str__(self) -> str:
    """Return the canonical namespaced connector ID."""
    return f"{self.vendor}/{self.platform}"

parse(value) classmethod

Parse a vendor/platform connector identifier.

Source code in src/datasluice/domain/catalog/extensions.py
@classmethod
def parse(cls, value: str) -> ConnectorId:
    """Parse a ``vendor/platform`` connector identifier."""
    parts = value.split("/")
    if len(parts) != 2 or not all(parts):
        raise ValueError("Connector IDs must use vendor/platform form.")
    return cls(vendor=parts[0], platform=parts[1])

ConnectorManifest dataclass

Inspectable third-party connector metadata without runtime activation.

Source code in src/datasluice/domain/catalog/extensions.py
@dataclass(frozen=True, slots=True)
class ConnectorManifest:
    """Inspectable third-party connector metadata without runtime activation."""

    connector_id: ConnectorId
    entry_point: str
    profile_version: str
    optional_requirements: tuple[OptionalInstallRequirement, ...]
    certification: CertificationRecord | None
    activation_policy: ActivationPolicy = ActivationPolicy.INACTIVE
    overrides: ConnectorId | None = None

    def __post_init__(self) -> None:
        if not _ENTRY_POINT.fullmatch(self.entry_point):
            raise ValueError("Connector entry point must be a module:factory reference.")
        if not self.profile_version.strip():
            raise ValueError("Connector profile version is required.")
        object.__setattr__(self, "optional_requirements", tuple(self.optional_requirements))
        if self.connector_id.is_builtin:
            if self.overrides is not None:
                raise ValueError("Built-in connectors cannot declare overrides.")
            return
        if not self.optional_requirements:
            raise ValueError("Third-party manifests require optional dependency extra hints.")
        if self.certification is None:
            raise ValueError("Third-party manifests require certification metadata.")
        if self.certification.connector_id != self.connector_id:
            raise ValueError("Certification connector identity must match the manifest.")
        if self.certification.profile_version != self.profile_version:
            raise ValueError("Certification profile version must match the manifest.")
        if self.overrides is not None and not self.overrides.is_builtin:
            raise ValueError("Connector overrides may only name a built-in connector.")

    def is_activated(self, selected_connector_id: ConnectorId | None) -> bool:
        """Return whether an explicit caller selection activates this manifest."""
        return selected_connector_id == self.connector_id

    def require_activation(self, selected_connector_id: ConnectorId | None) -> None:
        """Reject implicit activation, inactive manifests, and silent built-in overrides."""
        if self.activation_policy is not ActivationPolicy.EXPLICIT:
            raise ValueError("Connector manifests must declare explicit activation to be selectable.")
        if not self.is_activated(selected_connector_id):
            raise ValueError("Connector activation requires explicit caller selection of the connector ID.")

is_activated(selected_connector_id)

Return whether an explicit caller selection activates this manifest.

Source code in src/datasluice/domain/catalog/extensions.py
def is_activated(self, selected_connector_id: ConnectorId | None) -> bool:
    """Return whether an explicit caller selection activates this manifest."""
    return selected_connector_id == self.connector_id

require_activation(selected_connector_id)

Reject implicit activation, inactive manifests, and silent built-in overrides.

Source code in src/datasluice/domain/catalog/extensions.py
def require_activation(self, selected_connector_id: ConnectorId | None) -> None:
    """Reject implicit activation, inactive manifests, and silent built-in overrides."""
    if self.activation_policy is not ActivationPolicy.EXPLICIT:
        raise ValueError("Connector manifests must declare explicit activation to be selectable.")
    if not self.is_activated(selected_connector_id):
        raise ValueError("Connector activation requires explicit caller selection of the connector ID.")

DeclaredCapabilityProfile dataclass

Immutable reviewed declaration for a pinned platform API version.

Source code in src/datasluice/domain/catalog/profiles.py
@dataclass(frozen=True, slots=True)
class DeclaredCapabilityProfile:
    """Immutable reviewed declaration for a pinned platform API version."""

    profile_version: str
    schema_version: str
    platform_api_version: str
    official_source_uri: str
    source_accessed_at: date
    fixture_fingerprint: str
    operations: Mapping[OperationId, OperationSpec]

    def __post_init__(self) -> None:
        _require_text("profile version", self.profile_version)
        _require_text("schema version", self.schema_version)
        _require_text("platform API version", self.platform_api_version)
        _require_text("fixture fingerprint", self.fixture_fingerprint)
        source = urlsplit(self.official_source_uri)
        if source.scheme != "https" or not source.netloc or source.username or source.password:
            raise ValueError("Official source URI must be a sanitized HTTPS URI.")
        operation_map = dict(self.operations)
        if not operation_map:
            raise ValueError("Declared profiles cannot have missing operation IDs.")
        for operation_id, operation in operation_map.items():
            if operation_id != operation.id:
                raise ValueError(
                    "Declared profiles cannot contain duplicate operation IDs or mismatched operation keys."
                )
        object.__setattr__(self, "operations", MappingProxyType(operation_map))

ReferenceCase dataclass

One declared deterministic capability outcome.

Source code in src/datasluice/contracts/catalog/fixtures/__init__.py
@dataclass(frozen=True, slots=True)
class ReferenceCase:
    """One declared deterministic capability outcome."""

    operation_id: ReferenceOperationId
    outcome: str
    credential_class: str | None = None

ReferenceFixtureSet dataclass

A profile-validated, immutable reference fixture collection.

Source code in src/datasluice/contracts/catalog/fixtures/__init__.py
@dataclass(frozen=True, slots=True)
class ReferenceFixtureSet:
    """A profile-validated, immutable reference fixture collection."""

    platform: str
    profile_version: str
    fingerprint: str
    cases: tuple[ReferenceCase, ...]
    declared_operations: frozenset[ReferenceOperationId]
    evidence: Mapping[str, object]

    @property
    def success_cases(self) -> tuple[ReferenceCase, ...]:
        """Return cases that deterministically reach the reference executor."""
        return tuple(
            case
            for case in self.cases
            if case.outcome in {"core", "optional", "authenticated-success", "async-pending"}
        )

success_cases property

Return cases that deterministically reach the reference executor.

SyncCatalogClient

Bases: Protocol

Synchronous normalized catalog client surface.

Source code in src/datasluice/contracts/catalog/protocols.py
@runtime_checkable
class SyncCatalogClient(Protocol):
    """Synchronous normalized catalog client surface."""

    @property
    def datasets(self) -> SyncDatasetService:
        """Return normalized dataset operations."""

    @property
    def resources(self) -> SyncResourceService:
        """Return normalized resource operations."""

    @property
    def organizations(self) -> SyncOrganizationService:
        """Return normalized organization operations."""

    def capability(self, operation_id: str) -> CapabilityState:
        """Return the effective non-dispatching capability classification."""

    def platform_metadata(self) -> Mapping[str, object]:
        """Return safe platform metadata."""

    def close(self) -> None:
        """Release owned synchronous resources."""

    def __enter__(self) -> Self:
        """Enter a managed synchronous client context."""

    def __exit__(
        self,
        exc_type: type[BaseException] | None,
        exc_value: BaseException | None,
        traceback: TracebackType | None,
    ) -> None:
        """Close resources on context exit."""

datasets property

Return normalized dataset operations.

organizations property

Return normalized organization operations.

resources property

Return normalized resource operations.

__enter__()

Enter a managed synchronous client context.

Source code in src/datasluice/contracts/catalog/protocols.py
def __enter__(self) -> Self:
    """Enter a managed synchronous client context."""

__exit__(exc_type, exc_value, traceback)

Close resources on context exit.

Source code in src/datasluice/contracts/catalog/protocols.py
def __exit__(
    self,
    exc_type: type[BaseException] | None,
    exc_value: BaseException | None,
    traceback: TracebackType | None,
) -> None:
    """Close resources on context exit."""

capability(operation_id)

Return the effective non-dispatching capability classification.

Source code in src/datasluice/contracts/catalog/protocols.py
def capability(self, operation_id: str) -> CapabilityState:
    """Return the effective non-dispatching capability classification."""

close()

Release owned synchronous resources.

Source code in src/datasluice/contracts/catalog/protocols.py
def close(self) -> None:
    """Release owned synchronous resources."""

platform_metadata()

Return safe platform metadata.

Source code in src/datasluice/contracts/catalog/protocols.py
def platform_metadata(self) -> Mapping[str, object]:
    """Return safe platform metadata."""

catalog_contract_cases(fixture_set)

Generate stable sync and async cases from one pinned fixture set.

Source code in src/datasluice/contracts/catalog/runner.py
def catalog_contract_cases(fixture_set: ReferenceFixtureSet) -> tuple[CatalogContractCase, ...]:
    """Generate stable sync and async cases from one pinned fixture set."""
    if not isinstance(fixture_set, ReferenceFixtureSet):
        raise TypeError("Catalog contract cases require a pinned reference fixture set.")
    cases = [
        CatalogContractCase(
            operation_id=str(reference_case.operation_id),
            outcome=cast(FixtureOutcome, reference_case.outcome),
            mode=mode,
        )
        for reference_case in fixture_set.cases
        for mode in ("sync", "async")
    ]
    return tuple(sorted(cases, key=lambda case: (case.operation_id, case.outcome, case.mode)))

certify_catalog_report(*, manifest, profile, fixture_set, cases, report, selected_connector_id)

Certify complete runner-owned evidence without discovering or activating plugins.

Source code in src/datasluice/contracts/catalog/certification.py
def certify_catalog_report(
    *,
    manifest: ConnectorManifest,
    profile: DeclaredCapabilityProfile,
    fixture_set: ReferenceFixtureSet,
    cases: Iterable[CatalogContractCase],
    report: ComplianceReport,
    selected_connector_id: ConnectorId | None,
) -> CatalogCertification:
    """Certify complete runner-owned evidence without discovering or activating plugins."""
    manifest.require_activation(selected_connector_id)
    if manifest.connector_id.platform != fixture_set.platform:
        raise ValueError("Certification connector platform must match the pinned fixture platform.")
    if (
        manifest.profile_version != fixture_set.profile_version
        or profile.profile_version != fixture_set.profile_version
    ):
        raise ValueError("Certification profile version must match the pinned fixture profile.")
    if profile.fixture_fingerprint != fixture_set.fingerprint:
        raise ValueError("Certification fixture fingerprint must match the pinned fixture set.")
    expected_case_ids = tuple(sorted(case.pytest_id for case in cases))
    if not expected_case_ids or len(set(expected_case_ids)) != len(expected_case_ids):
        raise ValueError("Certification requires a finite unique set of declared contract cases.")
    if (
        report.connector_id != str(manifest.connector_id)
        or report.profile_version != fixture_set.profile_version
        or report.fixture_fingerprint != fixture_set.fingerprint
        or report.contract_schema_version != str(ComplianceReport.SCHEMA_VERSION)
    ):
        raise ValueError(
            "Certification report identity must match the manifest, profile, fixture, and contract schema."
        )
    if tuple(report.expected_case_ids) != expected_case_ids or {outcome.case_id for outcome in report.outcomes} != set(
        expected_case_ids
    ):
        raise ValueError("Certification requires complete case evidence from the declared runner matrix.")
    if not report.is_compliant:
        raise ValueError("Certification requires a compliant report with every required case passing.")
    if manifest.certification is not None and (
        manifest.certification.contract_schema_version != report.contract_schema_version
        or manifest.certification.profile_version != report.profile_version
        or manifest.certification.report_id != report.report_id
    ):
        raise ValueError("Certification manifest metadata must bind the exact compliant report.")
    return CatalogCertification(
        connector_id=manifest.connector_id,
        profile_version=fixture_set.profile_version,
        fixture_fingerprint=fixture_set.fingerprint,
        contract_schema_version=str(ComplianceReport.SCHEMA_VERSION),
        report_fingerprint=report.fingerprint,
        outcome_count=len(report.outcomes),
    )

load_reference_fixture_set(platform, *, cases_path=None)

Load one profile-bound fixture set without network access or ambient state.

Source code in src/datasluice/contracts/catalog/fixtures/__init__.py
def load_reference_fixture_set(platform: str, *, cases_path: Path | None = None) -> ReferenceFixtureSet:
    """Load one profile-bound fixture set without network access or ambient state."""
    if platform not in {"ckan", "udata", "socrata"}:
        raise ValueError("Reference fixtures require a declared platform.")
    path = cases_path or _FIXTURES / platform / "cases.json"
    evidence_path = path.with_name("evidence.json")
    profile_path = _matching_profile(platform)
    profile = _object(_read_json(profile_path), "profile")
    cases_document = _object(_read_json(path), "cases")
    evidence = _object(_read_json(evidence_path), "evidence")
    fingerprint = hashlib.sha256(path.read_bytes()).hexdigest()
    if profile.get("schema_version") != "1.0" or cases_document.get("schema_version") != "1.0":
        raise ValueError("Reference fixtures require schema version 1.0.")
    if profile.get("platform") != platform or cases_document.get("platform") != platform:
        raise ValueError("Reference fixture platform does not match its profile.")
    if profile.get("profile_version") != cases_document.get("profile_version"):
        raise ValueError("Reference fixture profile version does not match its cases.")
    if profile.get("fixture_fingerprint") != fingerprint:
        raise ValueError("Reference fixture fingerprint does not match the checked-in cases.")
    declared_operations = frozenset(
        _operation_id(entry) for entry in _list(profile.get("operations"), "profile.operations")
    )
    cases = tuple(
        _case(entry, platform, declared_operations) for entry in _list(cases_document.get("cases"), "cases.cases")
    )
    if not cases or not isinstance(evidence.get("platform_version"), str):
        raise ValueError("Reference fixtures require evidence and at least one declared case.")
    return ReferenceFixtureSet(
        platform=platform,
        profile_version=str(profile["profile_version"]),
        fingerprint=fingerprint,
        cases=cases,
        declared_operations=declared_operations,
        evidence=MappingProxyType(dict(evidence)),
    )

run_catalog_contract(case, *, sync_client, async_client, fixture_set=None)

Execute a finite catalog contract matrix and retain every case outcome.

Source code in src/datasluice/contracts/catalog/runner.py
def run_catalog_contract(
    case: CatalogContractCase | Iterable[CatalogContractCase],
    *,
    sync_client: SyncCatalogClient,
    async_client: AsyncCatalogClient,
    fixture_set: ReferenceFixtureSet | None = None,
) -> ComplianceReport:
    """Execute a finite catalog contract matrix and retain every case outcome."""
    if isinstance(case, CatalogContractCase):
        if fixture_set is None:
            return _run_tracer_case(case, sync_client, async_client)
        cases = (case,)
    else:
        cases = tuple(case)
    if not cases or not all(isinstance(contract_case, CatalogContractCase) for contract_case in cases):
        raise ValueError("Catalog contract execution requires one or more declared cases.")
    if fixture_set is None:
        raise ValueError("An exhaustive catalog contract matrix requires its pinned fixture set.")
    expected = catalog_contract_cases(fixture_set)
    if not set(cases) <= set(expected):
        raise ValueError("Catalog contract cases must be generated from the pinned fixture set.")
    outcomes = _run_reference_cases(cases, sync_client, async_client, fixture_set)
    return ComplianceReport(
        outcomes=outcomes,
        connector_id=f"datasluice/{fixture_set.platform}",
        manifest_version="reference-v1",
        profile_version=fixture_set.profile_version,
        fixture_fingerprint=fixture_set.fingerprint,
        contract_schema_version=str(ComplianceReport.SCHEMA_VERSION),
        expected_case_ids=tuple(contract_case.pytest_id for contract_case in cases),
        platform_metadata=sync_client.platform_metadata(),
    )

Platform packages

Each platform exports exactly one adapter façade class and one factory function; imports are always explicit and package-level:

Canonical public CKAN connector contract.

CKANAdapter

Expose injected CKAN service projections without a transport implementation.

Source code in src/datasluice/connectors/catalog/ckan/adapter.py
class CKANAdapter:
    """Expose injected CKAN service projections without a transport implementation."""

    def __init__(
        self,
        *,
        context: CatalogConnectorContext,
        normalized_sync: SyncCatalogClient,
        normalized_async: AsyncCatalogClient,
        native_sync: SyncCKANServices,
        native_async: AsyncCKANServices,
        effective_profile: EffectiveCapabilityProfile,
    ) -> None:
        self._sync_executor = SyncManagedExecutor(context)
        self._async_executor = AsyncManagedExecutor(context)
        self.normalized_sync = normalized_sync
        self.normalized_async = normalized_async
        self.native_sync = native_sync
        self.native_async = native_async
        self.effective_profile = effective_profile

    def close(self) -> None:
        """Release the synchronous executor only when the context owns it."""
        self._sync_executor.close()

    def __enter__(self) -> CKANAdapter:
        """Enter the synchronous façade context."""
        return self

    def __exit__(
        self,
        exc_type: type[BaseException] | None,
        exc_value: BaseException | None,
        traceback: TracebackType | None,
    ) -> None:
        """Release synchronous resources on context exit."""
        self.close()

    async def aclose(self) -> None:
        """Release the asynchronous executor only when the context owns it."""
        await self._async_executor.aclose()

    async def __aenter__(self) -> CKANAdapter:
        """Enter the asynchronous façade context."""
        return self

    async def __aexit__(
        self,
        exc_type: type[BaseException] | None,
        exc_value: BaseException | None,
        traceback: TracebackType | None,
    ) -> None:
        """Release asynchronous resources on context exit."""
        await self.aclose()

__aenter__() async

Enter the asynchronous façade context.

Source code in src/datasluice/connectors/catalog/ckan/adapter.py
async def __aenter__(self) -> CKANAdapter:
    """Enter the asynchronous façade context."""
    return self

__aexit__(exc_type, exc_value, traceback) async

Release asynchronous resources on context exit.

Source code in src/datasluice/connectors/catalog/ckan/adapter.py
async def __aexit__(
    self,
    exc_type: type[BaseException] | None,
    exc_value: BaseException | None,
    traceback: TracebackType | None,
) -> None:
    """Release asynchronous resources on context exit."""
    await self.aclose()

__enter__()

Enter the synchronous façade context.

Source code in src/datasluice/connectors/catalog/ckan/adapter.py
def __enter__(self) -> CKANAdapter:
    """Enter the synchronous façade context."""
    return self

__exit__(exc_type, exc_value, traceback)

Release synchronous resources on context exit.

Source code in src/datasluice/connectors/catalog/ckan/adapter.py
def __exit__(
    self,
    exc_type: type[BaseException] | None,
    exc_value: BaseException | None,
    traceback: TracebackType | None,
) -> None:
    """Release synchronous resources on context exit."""
    self.close()

aclose() async

Release the asynchronous executor only when the context owns it.

Source code in src/datasluice/connectors/catalog/ckan/adapter.py
async def aclose(self) -> None:
    """Release the asynchronous executor only when the context owns it."""
    await self._async_executor.aclose()

close()

Release the synchronous executor only when the context owns it.

Source code in src/datasluice/connectors/catalog/ckan/adapter.py
def close(self) -> None:
    """Release the synchronous executor only when the context owns it."""
    self._sync_executor.close()

create_ckan_connector(ctx)

Construct a CKAN façade from explicit typed service projections.

Source code in src/datasluice/connectors/catalog/ckan/factory.py
def create_ckan_connector(ctx: CatalogConnectorContext) -> CKANAdapter:
    """Construct a CKAN façade from explicit typed service projections."""
    if not isinstance(ctx, CatalogConnectorContext):
        raise TypeError("CKAN connectors require a CatalogConnectorContext.")
    if not isinstance(ctx.sync_executor, SyncCatalogOperationExecutor):
        raise ValueError("CKAN connectors require a synchronous catalog executor.")
    if not isinstance(ctx.async_executor, AsyncCatalogOperationExecutor):
        raise ValueError("CKAN connectors require an asynchronous catalog executor.")
    if type(ctx.manages_sync_executor) is not bool or type(ctx.manages_async_executor) is not bool:
        raise ValueError("CKAN connector executor ownership must be explicit booleans.")
    if ctx.normalized_sync is None or ctx.normalized_async is None:
        raise ValueError("CKAN connectors require normalized sync and async service projections.")
    if ctx.native_sync is None or ctx.native_async is None:
        raise ValueError("CKAN connectors require CKAN-native sync and async service projections.")
    profile = _require_ckan_profile(ctx.effective_profile)
    return CKANAdapter(
        context=ctx,
        normalized_sync=cast(SyncCatalogClient, ctx.normalized_sync),
        normalized_async=cast(AsyncCatalogClient, ctx.normalized_async),
        native_sync=cast(SyncCKANServices, ctx.native_sync),
        native_async=cast(AsyncCKANServices, ctx.native_async),
        effective_profile=profile,
    )

Canonical public uData connector contract.

UDataAdapter

Expose injected uData service projections without a transport implementation.

Source code in src/datasluice/connectors/catalog/udata/adapter.py
class UDataAdapter:
    """Expose injected uData service projections without a transport implementation."""

    def __init__(
        self,
        *,
        context: CatalogConnectorContext,
        normalized_sync: SyncCatalogClient,
        normalized_async: AsyncCatalogClient,
        native_sync: SyncUDataServices,
        native_async: AsyncUDataServices,
        effective_profile: EffectiveCapabilityProfile,
    ) -> None:
        self._sync_executor = SyncManagedExecutor(context)
        self._async_executor = AsyncManagedExecutor(context)
        self.normalized_sync = normalized_sync
        self.normalized_async = normalized_async
        self.native_sync = native_sync
        self.native_async = native_async
        self.effective_profile = effective_profile

    def close(self) -> None:
        """Release the synchronous executor only when the context owns it."""
        self._sync_executor.close()

    def __enter__(self) -> UDataAdapter:
        """Enter the synchronous façade context."""
        return self

    def __exit__(
        self,
        exc_type: type[BaseException] | None,
        exc_value: BaseException | None,
        traceback: TracebackType | None,
    ) -> None:
        """Release synchronous resources on context exit."""
        self.close()

    async def aclose(self) -> None:
        """Release the asynchronous executor only when the context owns it."""
        await self._async_executor.aclose()

    async def __aenter__(self) -> UDataAdapter:
        """Enter the asynchronous façade context."""
        return self

    async def __aexit__(
        self,
        exc_type: type[BaseException] | None,
        exc_value: BaseException | None,
        traceback: TracebackType | None,
    ) -> None:
        """Release asynchronous resources on context exit."""
        await self.aclose()

__aenter__() async

Enter the asynchronous façade context.

Source code in src/datasluice/connectors/catalog/udata/adapter.py
async def __aenter__(self) -> UDataAdapter:
    """Enter the asynchronous façade context."""
    return self

__aexit__(exc_type, exc_value, traceback) async

Release asynchronous resources on context exit.

Source code in src/datasluice/connectors/catalog/udata/adapter.py
async def __aexit__(
    self,
    exc_type: type[BaseException] | None,
    exc_value: BaseException | None,
    traceback: TracebackType | None,
) -> None:
    """Release asynchronous resources on context exit."""
    await self.aclose()

__enter__()

Enter the synchronous façade context.

Source code in src/datasluice/connectors/catalog/udata/adapter.py
def __enter__(self) -> UDataAdapter:
    """Enter the synchronous façade context."""
    return self

__exit__(exc_type, exc_value, traceback)

Release synchronous resources on context exit.

Source code in src/datasluice/connectors/catalog/udata/adapter.py
def __exit__(
    self,
    exc_type: type[BaseException] | None,
    exc_value: BaseException | None,
    traceback: TracebackType | None,
) -> None:
    """Release synchronous resources on context exit."""
    self.close()

aclose() async

Release the asynchronous executor only when the context owns it.

Source code in src/datasluice/connectors/catalog/udata/adapter.py
async def aclose(self) -> None:
    """Release the asynchronous executor only when the context owns it."""
    await self._async_executor.aclose()

close()

Release the synchronous executor only when the context owns it.

Source code in src/datasluice/connectors/catalog/udata/adapter.py
def close(self) -> None:
    """Release the synchronous executor only when the context owns it."""
    self._sync_executor.close()

create_udata_connector(ctx)

Construct a uData façade from explicit typed service projections.

Source code in src/datasluice/connectors/catalog/udata/factory.py
def create_udata_connector(ctx: CatalogConnectorContext) -> UDataAdapter:
    """Construct a uData façade from explicit typed service projections."""
    if not isinstance(ctx, CatalogConnectorContext):
        raise TypeError("uData connectors require a CatalogConnectorContext.")
    if not isinstance(ctx.sync_executor, SyncCatalogOperationExecutor):
        raise ValueError("uData connectors require a synchronous catalog executor.")
    if not isinstance(ctx.async_executor, AsyncCatalogOperationExecutor):
        raise ValueError("uData connectors require an asynchronous catalog executor.")
    if type(ctx.manages_sync_executor) is not bool or type(ctx.manages_async_executor) is not bool:
        raise ValueError("uData connector executor ownership must be explicit booleans.")
    if ctx.normalized_sync is None or ctx.normalized_async is None:
        raise ValueError("uData connectors require normalized sync and async service projections.")
    if ctx.native_sync is None or ctx.native_async is None:
        raise ValueError("uData connectors require uData-native sync and async service projections.")
    profile = _require_udata_profile(ctx.effective_profile)
    return UDataAdapter(
        context=ctx,
        normalized_sync=cast(SyncCatalogClient, ctx.normalized_sync),
        normalized_async=cast(AsyncCatalogClient, ctx.normalized_async),
        native_sync=cast(SyncUDataServices, ctx.native_sync),
        native_async=cast(AsyncUDataServices, ctx.native_async),
        effective_profile=profile,
    )

Canonical public Socrata connector contract.

SocrataAdapter

Expose injected Socrata service projections without a transport implementation.

Source code in src/datasluice/connectors/catalog/socrata/adapter.py
class SocrataAdapter:
    """Expose injected Socrata service projections without a transport implementation."""

    def __init__(
        self,
        *,
        context: CatalogConnectorContext,
        normalized_sync: SyncCatalogClient,
        normalized_async: AsyncCatalogClient,
        native_sync: SyncSocrataServices,
        native_async: AsyncSocrataServices,
        effective_profile: EffectiveCapabilityProfile,
    ) -> None:
        self._sync_executor = SyncManagedExecutor(context)
        self._async_executor = AsyncManagedExecutor(context)
        self.normalized_sync = normalized_sync
        self.normalized_async = normalized_async
        self.native_sync = native_sync
        self.native_async = native_async
        self.effective_profile = effective_profile

    def close(self) -> None:
        """Release the synchronous executor only when the context owns it."""
        self._sync_executor.close()

    def __enter__(self) -> SocrataAdapter:
        """Enter the synchronous façade context."""
        return self

    def __exit__(
        self,
        exc_type: type[BaseException] | None,
        exc_value: BaseException | None,
        traceback: TracebackType | None,
    ) -> None:
        """Release synchronous resources on context exit."""
        self.close()

    async def aclose(self) -> None:
        """Release the asynchronous executor only when the context owns it."""
        await self._async_executor.aclose()

    async def __aenter__(self) -> SocrataAdapter:
        """Enter the asynchronous façade context."""
        return self

    async def __aexit__(
        self,
        exc_type: type[BaseException] | None,
        exc_value: BaseException | None,
        traceback: TracebackType | None,
    ) -> None:
        """Release asynchronous resources on context exit."""
        await self.aclose()

__aenter__() async

Enter the asynchronous façade context.

Source code in src/datasluice/connectors/catalog/socrata/adapter.py
async def __aenter__(self) -> SocrataAdapter:
    """Enter the asynchronous façade context."""
    return self

__aexit__(exc_type, exc_value, traceback) async

Release asynchronous resources on context exit.

Source code in src/datasluice/connectors/catalog/socrata/adapter.py
async def __aexit__(
    self,
    exc_type: type[BaseException] | None,
    exc_value: BaseException | None,
    traceback: TracebackType | None,
) -> None:
    """Release asynchronous resources on context exit."""
    await self.aclose()

__enter__()

Enter the synchronous façade context.

Source code in src/datasluice/connectors/catalog/socrata/adapter.py
def __enter__(self) -> SocrataAdapter:
    """Enter the synchronous façade context."""
    return self

__exit__(exc_type, exc_value, traceback)

Release synchronous resources on context exit.

Source code in src/datasluice/connectors/catalog/socrata/adapter.py
def __exit__(
    self,
    exc_type: type[BaseException] | None,
    exc_value: BaseException | None,
    traceback: TracebackType | None,
) -> None:
    """Release synchronous resources on context exit."""
    self.close()

aclose() async

Release the asynchronous executor only when the context owns it.

Source code in src/datasluice/connectors/catalog/socrata/adapter.py
async def aclose(self) -> None:
    """Release the asynchronous executor only when the context owns it."""
    await self._async_executor.aclose()

close()

Release the synchronous executor only when the context owns it.

Source code in src/datasluice/connectors/catalog/socrata/adapter.py
def close(self) -> None:
    """Release the synchronous executor only when the context owns it."""
    self._sync_executor.close()

create_socrata_connector(ctx)

Construct a Socrata façade from explicit typed service projections.

Source code in src/datasluice/connectors/catalog/socrata/factory.py
def create_socrata_connector(ctx: CatalogConnectorContext) -> SocrataAdapter:
    """Construct a Socrata façade from explicit typed service projections."""
    if not isinstance(ctx, CatalogConnectorContext):
        raise TypeError("Socrata connectors require a CatalogConnectorContext.")
    if not isinstance(ctx.sync_executor, SyncCatalogOperationExecutor):
        raise ValueError("Socrata connectors require a synchronous catalog executor.")
    if not isinstance(ctx.async_executor, AsyncCatalogOperationExecutor):
        raise ValueError("Socrata connectors require an asynchronous catalog executor.")
    if type(ctx.manages_sync_executor) is not bool or type(ctx.manages_async_executor) is not bool:
        raise ValueError("Socrata connector executor ownership must be explicit booleans.")
    if ctx.normalized_sync is None or ctx.normalized_async is None:
        raise ValueError("Socrata connectors require normalized sync and async service projections.")
    if ctx.native_sync is None or ctx.native_async is None:
        raise ValueError("Socrata connectors require Socrata-native sync and async service projections.")
    profile = _require_socrata_profile(ctx.effective_profile)
    return SocrataAdapter(
        context=ctx,
        normalized_sync=cast(SyncCatalogClient, ctx.normalized_sync),
        normalized_async=cast(AsyncCatalogClient, ctx.normalized_async),
        native_sync=cast(SyncSocrataServices, ctx.native_sync),
        native_async=cast(AsyncSocrataServices, ctx.native_async),
        effective_profile=profile,
    )

The datasluice.connectors.catalog namespace itself re-exports nothing. Installable named connector extras are owned by Phase 2 packaging work and are not part of this package surface.