Runtime Guides

Package-source copy · Generated into the site so runtime docs are searchable and linkable.

Core Patterns (Python)

This document provides copy-pasteable patterns for the TableTheory Python SDK.

Pattern: Define a model from a dataclass

from dataclasses import dataclass

from tabletheory_py import ModelDefinition, theorydb_field


@dataclass(frozen=True)
class Note:
    pk: str = theorydb_field(roles=["pk"])
    sk: str = theorydb_field(roles=["sk"])
    value: int = theorydb_field()


model = ModelDefinition.from_dataclass(Note, table_name="notes_contract")

Pattern: CRUD with a Table

from tabletheory_py import Table

table = Table(model, client=client)
table.put(Note(pk="A", sk="1", value=123))
note = table.get("A", "1")
table.delete("A", "1")

Pattern: Query + pagination

from tabletheory_py import SortKeyCondition

page1 = table.query("A", sort=SortKeyCondition.begins_with("1"), limit=25)
page2 = table.query("A", cursor=page1.next_cursor) if page1.next_cursor else None

Pattern: Batch + transactions

table.batch_write(puts=[Note(pk="A", sk="2", value=1)], deletes=[("A", "1")])

Pattern: Lazy pagination for large reads

Use query_iter and scan_iter when a caller may stop before consuming every page. The generator fetches the next DynamoDB page only when iteration advances:

for page in table.query_iter("USER#1", limit=100):
    for item in page.items:
        if should_stop(item):
            break
    break  # no additional page is fetched

Pattern: Aggregations are client-side

sum_field, average_field, min_field, max_field, aggregate_field, count_distinct, and group_by(...).execute() operate on an already materialized Python sequence. If that sequence came from query_all, scan_all, or scan_all_segments, every matching item is already in memory. Use these helpers only for bounded result sets. When the planned native count() API lands, prefer it for count-only paths.

Pattern: Streams unmarshalling

from tabletheory_py import unmarshal_stream_record

note = unmarshal_stream_record(model, record, image="NewImage")

Pattern: Deterministic unit tests (strict fakes)

from tabletheory_py.mocks import ANY, FakeDynamoDBClient

fake = FakeDynamoDBClient()
fake.expect("put_item", {"TableName": "notes", "Item": {"PK": ANY, "SK": ANY}})

Pattern: Key design and GSI queries

Keep key attributes explicit in the dataclass metadata. PK/SK and GSI key fields must be stable plaintext values; encrypted fields are rejected for keys because DynamoDB must be able to route and compare key attributes.

from dataclasses import dataclass

from tabletheory_py import IndexSpec, ModelDefinition, SortKeyCondition, Table, theorydb_field


@dataclass(frozen=True)
class User:
    pk: str = theorydb_field(name="PK", roles=["pk"])
    sk: str = theorydb_field(name="SK", roles=["sk"])
    gsi1pk: str = theorydb_field(name="GSI1PK", roles=["gsi1pk"])
    gsi1sk: str = theorydb_field(name="GSI1SK", roles=["gsi1sk"])
    email: str = theorydb_field()


model = ModelDefinition.from_dataclass(
    User,
    table_name="users_contract",
    indexes=[IndexSpec(name="gsi_email", type="GSI", partition="GSI1PK", sort="GSI1SK")],
)
table = Table(model, client=client)
page = table.query("EMAIL#ada@example.com", index_name="gsi_email", limit=25)

Do not request consistent_read=True on GSI queries; DynamoDB rejects strongly consistent GSI reads.

Pattern: Fail-closed encryption

Encrypted fields require explicit KMS configuration. If a model has encrypted=True and Table(...) lacks a kms_key_arn, construction fails closed instead of allowing plaintext fallback.

from dataclasses import dataclass

from tabletheory_py import ModelDefinition, Table, theorydb_field


@dataclass(frozen=True)
class SecretNote:
    pk: str = theorydb_field(roles=["pk"])
    sk: str = theorydb_field(roles=["sk"])
    secret: str = theorydb_field(encrypted=True)


model = ModelDefinition.from_dataclass(SecretNote, table_name="secret_notes")
table = Table(
    model,
    client=client,
    kms_key_arn="arn:aws:kms:us-east-1:111111111111:key/example",
)

For unit tests, inject FakeKmsClient and deterministic random bytes; do not disable encryption in the model.

from tabletheory_py.mocks import FakeDynamoDBClient, FakeKmsClient

fake_kms = FakeKmsClient(plaintext_key=b"\x01" * 32, ciphertext_blob=b"edk")
table = Table(
    model,
    client=FakeDynamoDBClient(),
    kms_key_arn="arn:aws:kms:us-east-1:111111111111:key/test",
    kms_client=fake_kms,
    rand_bytes=lambda n: b"\x02" * n,
)

Pattern: Error handling by TableTheory type

Branch on exported exception types rather than message strings. Version conflicts remain condition failures for backward compatibility while exposing the narrower conflict type.

from tabletheory_py import ConditionFailedError, NotFoundError, VersionConflictError

try:
    table.update("USER#1", "PROFILE", {"nickname": "Ada"}, expected_version=3)
except VersionConflictError:
    # Reload, merge, and retry.
    raise
except ConditionFailedError:
    # Other conditional failure, such as create-if-absent collision.
    raise
except NotFoundError:
    # Missing item.
    raise