Skip to main content

datalake-medallion-sdk

CI PyPI License: MIT OR Apache-2.0 Python 3.12+

Contract-enforced Bronze-layer ingestion for medallion data lakes, with a Rust core and a Python API.

Every record is checked against a JSON Schema data contract before anything is written. Valid records become one Hive-partitioned Parquet object with lineage columns; invalid records go to a dead-letter prefix with the reason attached. Fields the contract marks as PII are hashed before they reach storage, in the data and in the dead letters alike; masking is on by default and can be turned off per client or per call.

import asyncio
from datalake_medallion_sdk import BronzeIngestionClient, ContractRegistry

registry = ContractRegistry.from_directory("contracts")  # compiles every contract, all-or-nothing
client = BronzeIngestionClient("acme-lake", registry)  # AWS default credential chain

report = asyncio.run(
    client.ingest(
        [
            {
                "event_id": "e1",
                "occurred_at": "2026-10-01T12:00:00Z",
                "user_email": "ana@example.com",
            },
            {"event_id": 42},
        ],
        tenant_id="acme-001",
        contract="security/login_event/1.0.0",
    )
)
print(report.records_ok, report.records_quarantined, report.rejections)
# 1 1 {'ERR_SCHEMA_VIOLATION': 1}

Install

pip install datalake-medallion-sdk

Wheels are abi3 (one wheel per platform for CPython 3.12+) for Linux (glibc and musl, x86_64 and aarch64), macOS on Apple silicon and Windows x86_64. Other platforms, including Intel macOS, build from the source distribution and need a Rust toolchain. There are no mandatory Python dependencies: Arrow input uses the PyCapsule interface, so pyarrow and Polars are optional.

What happens to a record

Level Check On failure
0 Contracts compile at boot; hooks must target known contracts; contract fields may not use the envelope prefix ContractBootError
1 Tenant id: 3–63 chars of [a-z0-9_-], no leading/trailing or doubled separators, not a reserved word TenantViolationError
2 Record is a JSON object, within the size limit (10 MiB default), without fields in the reserved envelope namespace quarantined
3 JSON Schema (formats asserted); in strict mode, no undeclared fields quarantined
4 Optional host hook may accept, rewrite or reject; rewritten records are validated again quarantined
5 date-time fields not more than 1 h (configurable) in the future quarantined
6 PII fields hashed (SHA-256, or HMAC-SHA256 with your key); on by default, optional applied when enabled
7 Each value fits the Parquet column type derived from the contract quarantined
8 Lineage columns appended always applied

With on_invalid="raise" the client writes nothing and raises the typed error (SchemaViolationError, HookError, …) for the first invalid record instead of quarantining.

Error messages and dead-letter entries never contain record values: schema errors are rendered with values masked, and hook exceptions are recorded by type name only.

Data contracts

A contract is a JSON Schema document plus a few keywords under an extension prefix (x-dl- by default, configurable per registry):

{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "type": "object",
  "x-dl-domain": "security",
  "x-dl-datatype": "login_event",
  "x-dl-schema_version": "1.0.0",
  "x-dl-flexibility_mode": "strict",
  "required": ["event_id", "occurred_at"],
  "properties": {
    "event_id":    {"type": "string"},
    "occurred_at": {"type": "string", "format": "date-time"},
    "attempts":    {"type": "integer", "minimum": 0},
    "user_email":  {"type": "string", "x-dl-pii": true}
  }
}

The key domain/datatype/schema_version (here security/login_event/1.0.0) selects the contract at ingest time. Flexibility modes:

  • strict (default): schema enforced, undeclared fields rejected.
  • permissive: schema enforced, undeclared fields kept as JSON in _dl_extra.
  • schema_on_read: no schema enforcement; PII masking (when enabled), type coercion and lineage still apply.

The Parquet schema comes from the contract, not from the data, so every object of a contract version has the same columns: string → utf8, integer → int64, number → float64, boolean → bool, anything else (objects, arrays, unions) → JSON text. When masking is on, PII fields are stored as their 64-char hex digest (utf8); when it is off they keep their declared type. Keep one masking mode per contract if a PII field is not a string, so its column type stays stable.

Contracts must be self-contained: $ref to remote URLs or files is not resolved, so loading contracts never performs network I/O.

Object layout

s3://<bucket>/bronze/tenant_id=<tenant>/data_type=<datatype>/year=YYYY/month=MM/day=DD/<batch_id>.parquet
s3://<bucket>/dlq/tenant_id=<tenant>/data_type=<datatype>/error_code=<code>/year=YYYY/month=MM/day=DD/<batch_id>.jsonl

Prefixes and the order of the tenant_id/data_type partitions (partition_order="data_type_first") are configurable. Each ingest call writes at most one Parquet object plus one dead-letter object per error code. Dead letters are written first, so retrying a failed call never duplicates Bronze rows.

Lineage columns (prefix _dl_, configurable):

Column Type Content
_dl_ingest_id utf8 ULID per row, monotonic within the batch
_dl_batch_id utf8 ULID of the call; names the objects
_dl_tenant_id utf8 tenant
_dl_data_type, _dl_schema_version, _dl_schema_hash utf8 contract identity and SHA-256
_dl_received_at timestamp(µs, UTC) ingest time
_dl_pii_masking utf8 sha256, hmac-sha256 or none
_dl_sdk_version utf8 SDK version

Inputs

ingest accepts a dict, a list of dicts, JSON str/bytes, or any object implementing the Arrow PyCapsule interface, such as a pyarrow Table/RecordBatch or a Polars DataFrame. Arrow rows go through the same validation as JSON. Validation and encoding run in Rust outside the GIL.

Hooks

from datalake_medallion_sdk import HookRegistry, PreIngestHookResult

hooks = HookRegistry()


@hooks.register("security/login_event/1.0.0")
def drop_test_accounts(record: dict) -> PreIngestHookResult:
    if record.get("user_email", "").endswith("@test.invalid"):
        return PreIngestHookResult.reject("ERR_TEST_ACCOUNT", "test account")
    return PreIngestHookResult.accept(record)


client = BronzeIngestionClient("acme-lake", registry, hooks=hooks.as_mapping())

Operations

  • Credentials: the AWS default chain (environment, profile, SSO, web identity, ECS/EC2 roles) unless aws_access_key_id/aws_secret_access_key are passed. The SDK never creates buckets; the writer role needs s3:PutObject on the data and dead-letter prefixes only.
  • S3-compatible stores: aws_endpoint_url= (path-style addressing is enabled automatically).
  • PII masking: on by default. BronzeIngestionClient(..., pii_masking=False) turns it off for the client; ingest(..., pii_masking=True|False) overrides it for one call. IngestReport.pii_masking and the _dl_pii_masking column record what was applied.
  • Pseudonymization key: pass pii_hmac_key= (16+ bytes, kept outside the lake) to use HMAC-SHA256; unkeyed SHA-256 of low-entropy values such as e-mail addresses can be reversed by dictionary attack.
  • Retries: @datalake_retry() retries StorageError only, with exponential backoff and jitter.
  • Metrics: @emit_metrics(sink) records duration, failures and record counts to logging, CloudWatch Embedded Metric Format or any MetricsSink.
  • Concurrency: one client per process can serve every tenant; the S3 connection is created once and reused.

Performance

Measured 2026-10-13 with Criterion on a laptop (AMD Ryzen 7 7730U, 8 cores, Windows 11, release build), contract with 4 fields of which 1 is PII. prepare covers every validation level, PII hashing and Parquet encoding, without network I/O.

Benchmark Records Mean Throughput
prepare, JSON text input 100,000 192 ms 0.52 M records/s
prepare, Arrow input 100,000 212 ms 0.47 M records/s
Contract check, valid records 10,000 1.96 ms 5.1 M records/s
Contract check, invalid records (errors rendered) 10,000 22.4 ms 0.45 M records/s
PII masking, SHA-256, 2 fields 10,000 4.3 ms 2.3 M records/s
PII masking, HMAC-SHA256, 2 fields 10,000 5.2 ms 1.9 M records/s

Reproduce with cargo bench. Upload time depends on the network and is not included.

Rust crate

The core is usable from Rust (BronzePipeline, ContractRegistry, the ObjectStorage port with S3 and in-memory adapters). See the crate documentation.

Development

cargo test --all-targets            # Rust unit tests
pip install -e ".[dev]"             # builds the extension with maturin
pytest pytests                      # Python tests, including end-to-end against an in-process S3 emulator (moto)
cargo clippy --all-targets -- -D warnings && ruff check python pytests && mypy

Minimum supported Rust version: 1.94.1.

Scope

Version 0.1 covers Bronze-layer ingestion to S3 and S3-compatible stores. Silver/Gold transformations, table formats (Iceberg/Delta), catalog registration and other object stores are not part of this release.

License

Licensed under either of Apache License 2.0 or MIT at your option. Unless you state otherwise, any contribution you submit for inclusion is dual licensed as above, without additional terms.

Metadata

Release files for datalake-medallion-sdk 0.1.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for datalake-medallion-sdk 0.1.0
File Size Uploaded
datalake_medallion_sdk-0.1.0.tar.gz 95.2 kB Details

Built distributions (wheels)

Table of built distributions (wheels) for datalake-medallion-sdk 0.1.0
File
datalake_medallion_sdk-0.1.0-cp312-abi3-win_amd64.whl CPython 3.12 abi3 Windows x86-64 Details
datalake_medallion_sdk-0.1.0-cp312-abi3-musllinux_1_2_x86_64.whl CPython 3.12 abi3 Linux musl 1.2+ x86-64 Details
datalake_medallion_sdk-0.1.0-cp312-abi3-musllinux_1_2_aarch64.whl CPython 3.12 abi3 Linux musl 1.2+ ARM64 Details
datalake_medallion_sdk-0.1.0-cp312-abi3-manylinux_2_28_x86_64.whl CPython 3.12 abi3 Linux glibc 2.28+ x86-64 Details
datalake_medallion_sdk-0.1.0-cp312-abi3-manylinux_2_28_aarch64.whl CPython 3.12 abi3 Linux glibc 2.28+ ARM64 Details
datalake_medallion_sdk-0.1.0-cp312-abi3-macosx_11_0_arm64.whl CPython 3.12 abi3 macOS 11.0+ ARM64 Details

Total release size: 37.1 MB

Release files / datalake_medallion_sdk-0.1.0.tar.gz

Download URL datalake_medallion_sdk-0.1.0.tar.gz
Size 95.2 kB
Tags Source
SHA-256 checksum
How to use checksums
c6a0d447dbcbb979fb3ae5481340599b0bbfb228ea24cfc8a459d56212cbe01f
BLAKE2b-256 checksum
How to use checksums
aa1001229e1a915c3176923a23a33da42fb16b11c710af97df3f27e8d7024a36
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 17, 2026.

Transparency log

Release files / datalake_medallion_sdk-0.1.0-cp312-abi3-win_amd64.whl

Download URL datalake_medallion_sdk-0.1.0-cp312-abi3-win_amd64.whl
Size 5.8 MB
Tags CPython 3.12 Windows x86-64 abi3
SHA-256 checksum
How to use checksums
2e55122c7ef6505b7d9b1924fc90875ec956615577d84e92f327e14f85f840b5
BLAKE2b-256 checksum
How to use checksums
1a36f555e467b9ebed095776f0cba79caea0e17e9258881855c1478b64a75b18
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 17, 2026.

Transparency log

Release files / datalake_medallion_sdk-0.1.0-cp312-abi3-musllinux_1_2_x86_64.whl

Download URL datalake_medallion_sdk-0.1.0-cp312-abi3-musllinux_1_2_x86_64.whl
Size 6.8 MB
Tags CPython 3.12 Linux musl 1.2+ x86-64 abi3
SHA-256 checksum
How to use checksums
7719f5e2b16d6562d1ad0090d085016b4a60e7afa94a23b0434d7cf25be794e8
BLAKE2b-256 checksum
How to use checksums
4e5728ea499f9bccd6adf59d98b0ffd30474fd3c3faebcf77b1f1fe80b7da044
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 17, 2026.

Transparency log

Release files / datalake_medallion_sdk-0.1.0-cp312-abi3-musllinux_1_2_aarch64.whl

Download URL datalake_medallion_sdk-0.1.0-cp312-abi3-musllinux_1_2_aarch64.whl
Size 6.4 MB
Tags CPython 3.12 Linux musl 1.2+ ARM64 abi3
SHA-256 checksum
How to use checksums
7a20c1670293eb971501e7eb20a16252cbd97ca41c10d4beea36582352f31eea
BLAKE2b-256 checksum
How to use checksums
58ddcd0dae40fbcc12001b81c6f69ace64a80cef6fe77891ada00e5d7d9d0208
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 17, 2026.

Transparency log

Release files / datalake_medallion_sdk-0.1.0-cp312-abi3-manylinux_2_28_x86_64.whl

Download URL datalake_medallion_sdk-0.1.0-cp312-abi3-manylinux_2_28_x86_64.whl
Size 6.6 MB
Tags CPython 3.12 Linux glibc 2.28+ x86-64 abi3
SHA-256 checksum
How to use checksums
e50a1fb5d2c0d64065c59818502e035a347734b0ad984ca1385099b1c820e1de
BLAKE2b-256 checksum
How to use checksums
50fffed07793942e94c8c6acfe62d1b633406220e1558dd62543c860b36010d9
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 17, 2026.

Transparency log

Release files / datalake_medallion_sdk-0.1.0-cp312-abi3-manylinux_2_28_aarch64.whl

Download URL datalake_medallion_sdk-0.1.0-cp312-abi3-manylinux_2_28_aarch64.whl
Size 6.2 MB
Tags CPython 3.12 Linux glibc 2.28+ ARM64 abi3
SHA-256 checksum
How to use checksums
b5d19a08797b2b7e8e0268f4662d235b04db5d24ae77a0be30e23b8173894e5c
BLAKE2b-256 checksum
How to use checksums
f8144e93d120a0837aeb863d334bb2bf382a7bc767e381e40a94cfdd044abd24
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 17, 2026.

Transparency log

Release files / datalake_medallion_sdk-0.1.0-cp312-abi3-macosx_11_0_arm64.whl

Download URL datalake_medallion_sdk-0.1.0-cp312-abi3-macosx_11_0_arm64.whl
Size 5.2 MB
Tags CPython 3.12 abi3 macOS 11.0+ ARM64
SHA-256 checksum
How to use checksums
3cb8177ca3e2b4866c78f402a362a9d1f66471949d903cab7f74c2b62a3ee8d4
BLAKE2b-256 checksum
How to use checksums
58c1ec44f254ae924ca3144f3b646474a673d63b376b8908b032c941059df051
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 17, 2026.

Transparency log

Release history Release notifications | RSS feed

This release

0.1.0 This release

7 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page