> ## Documentation Index
> Fetch the complete documentation index at: https://docs.bijection.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Python consumer SDKs

> Generate an installable, typed Python SDK for selected public business contracts

Generate business models, scoped IDs, collection and relationship descriptors,
operations, functions, and declared questions from the same public contract used
by the TypeScript SDK. Application developers install the generated package and
supply business credentials at runtime. Importing it performs no network I/O.

## Generate a package

Copy exact resource references from `bijection discover --format json` into
`bijection.sdk.json`. Select the endpoints of each relationship and the target
collection of each object-bound operation.

```json theme={"theme":{"light":"github-light-default","dark":"github-dark-default"}}
{
  "version": 2,
  "resources": [
    "collection:orders",
    "collection:customers",
    "relation:orders/customer",
    "operation:orders:approve",
    "function:analytics:revenue"
  ],
  "targets": [
    {
      "language": "python",
      "package": {
        "name": "acme-business",
        "module": "acme_business",
        "version": "0.1.0"
      },
      "output": "./generated/python-sdk"
    },
    {
      "language": "typescript",
      "package": { "name": "@acme/business", "version": "0.1.0" },
      "output": "./generated/typescript-sdk"
    }
  ]
}
```

Each language target is optional. The Python distribution name and import module
are separate; the module is one lowercase Python identifier. Generated names use
snake case for attributes and PascalCase for models. Ambiguous normalization is
refused. Python 3.12 or later is required.

```bash theme={"theme":{"light":"github-light-default","dark":"github-dark-default"}}
bijection sdk generate --config bijection.sdk.json --url "$BIJECTION_URL"
python -m build ./generated/python-sdk --outdir ./dist/python
python -m pip install ./dist/python/acme_business-0.1.0-py3-none-any.whl
```

Generation captures public metadata once at a fixed timestamp. The adjacent
`bijection.sdk.lock.json` contains the selected contract and exact runtime pins.
The executable digest is independent of target languages, package names and
versions. Commit the config, lock and generated sources. The package contains
`py.typed`, `contract.json`, selected resource documentation and sync/async
examples. It pins the Python runtime and includes its async and observation
dependencies. Generation and building never publish a distribution.

```bash theme={"theme":{"light":"github-light-default","dark":"github-dark-default"}}
bijection sdk generate --config bijection.sdk.json --frozen --check
bijection sdk diff --config bijection.sdk.json --url "$BIJECTION_URL" --check
```

Frozen generation is offline and rejects changed resource or language selections.
Diff reports executable and documentation changes and contracts that cannot
generate supported bindings. Outputs must be disjoint and owned by the generator;
unrelated files and symlinks are refused. Review drift, choose package versions
and regenerate without `--frozen`. `--python-runtime-version` overrides the CLI's
Python compatibility pin when testing a specific runtime release.

## Typed business objects and composition

The names below illustrate a selected order/customer ontology. Read the generated
`models.py`, `docs/index.md` and `examples/usage.py` for your actual contract.

```python theme={"theme":{"light":"github-light-default","dark":"github-dark-default"}}
from acme_business import create_workspace, Order, OrdersId
from bijection.credentials import StaticToken
from bijection.contracts import MISSING
from bijection.queries import measures

with create_workspace(
    deployment_url=deployment_url,
    token_source=StaticToken(business_session_token),
) as client:
    order = client.collections.orders.get(OrdersId(order_id))
    if order is not None and order.status is not MISSING:
        print(order.status)

    orders = client.collections.orders.query()
    open_orders = orders.where(Order.fields.status.eq("open"))
    customers = open_orders.related(Order.links.customer)
    rows = open_orders.order_by(Order.fields.amount.desc()).read().rows
    projection = open_orders.select(Order.fields.status, Order.fields.amount)
    totals = open_orders.group_by(Order.fields.status).aggregate(
        measures.count(), Order.fields.amount.sum()
    )
    for row in totals.read().rows:
        status, = row.keys
        count, amount = row.metrics
```

Expressions are immutable and lazy. Filters capture arguments when constructed.
Use `&` and `|` for conjunction and disjunction; Python `and`/`or` are refused.
Disjunction lowers to a union and requires admitted object identity. Object sets
support `union`, `intersect` and `subtract` within the same workspace and collection.
Relationships are generated only for selected edges and check both endpoints.
Unkeyed remote rowsets expose row composition without identity navigation.

Selected results are typed tuples; grouped rows have typed `keys` and `metrics`
tuples. Optional selected values and keys include `Missing` in their types.
They lose object navigation. Use `order_results(0, descending=True)` after
projection or grouping to order by result column position (keys precede metrics).
A named generated result model can be selected
with `as_model(ModelType)`; its schema must match the prepared result, including
when there are no rows. Numeric, integer, decimal and nullable descriptors expose
their supported measures. Native admission remains authoritative about available
operators, ordering, index access and compute execution.

Models are frozen dataclasses. Omitted or undisclosed properties are `MISSING`;
explicit null is `None`. Check absence by identity. Exact integers decode as
`Int64`; plain Python integers are schema-encoded for bigint arguments. Bytes,
special floating-point values, signed zero, scoped IDs and semantic constraints
retain their native meanings. Root and component IDs have different generated
types. A selected ID domain does not automatically expose a collection reader.

## Reads, continuation and diagnostics

`describe()` reports preparation, bounds, input digest and execution support.
`explain()` obtains the existing planner's explanation. A native `read()` must
return a complete bounded result; it never silently executes external work.
Use explicit `run()` for retained or external execution.

Collection and composition `page()` return `rows`, `basis`, `binding`,
`next_cursor` and `end`. Continuations retain the original selection, native
position, caller and read timestamp. Contract drift, expired history or changed
authority produce errors; the SDK does not restart at latest. Relationship pages
retain target identities even when their document is unavailable.

```python theme={"theme":{"light":"github-light-default","dark":"github-dark-default"}}
query = client.collections.orders.query()
for page in query.pages(page_size=100):
    consume(page.rows)
```

Composition pages slice a complete bounded native result at its original basis;
they do not turn a bounded read into an unbounded scan. Structured `ReadError`
values carry `code`, `step`, `recovery` and native data when available. Regenerate
on contract mismatch and explicitly restart when history has expired.

## Retained requests and durable recovery

```python theme={"theme":{"light":"github-light-default","dark":"github-dark-default"}}
from bijection.answers import FileRequests, ReadRequirement
from acme_business import create_workspace

client = create_workspace(
    deployment_url=deployment_url,
    token_source=token_source,
    requests=FileRequests(
        private_journal_directory,
        deployment=deployment_url,
        namespace=authenticated_principal,
    ),
)

with client.collections.orders.query().run(
    request_id=original_request_id,
    read=ReadRequirement.fixed(),
    max_rows=1000,
) as request:
    answer = request.result(timeout=60)
    for page in answer.outputs.rows.pages(page_size=100):
        consume(page.rows)
```

`execution` passes an explicit existing executor selection to native admission.
Bijection continues to use external compute. `ReadRequirement` exposes observed,
fixed and snapshot consistency. A held answer can supply `from_answer` for a
compatible subsequent request in the same workspace. No answer grants new access.

Requests expose status, result, cancel and close. `collect(max_rows=...)` requires
an explicit materialization bound. Pages verify immutable answer coordinates,
schemas and row counts. Context managers release request ownership on exit.
Closing marks a handle inactive immediately; an uncertain remote close remains
in the journal for retry. Native retention bounds still apply.

The journal stores immutable original inputs and request identities before
submission, including the prepared input digest. It stores no credentials. After
a process restart, `resume(id)` inspects the captured request and `retry(id)`
replays its exact original submission. A pending close replays only close.
`recoverable()` lists retained identities. Dynamic resume returns a generic
handle; generated question submissions retain their named output types. Local
timeouts detach the caller and keep the journal so an uncertain submission can be
recovered. Explicitly close or cancel work that should no longer be retained.

Ordinary generated functions validate declared inputs and results. Operations
capture typed arguments and retain the original request for preview, recovery,
proposal review, approval and invocation. Invoke recovery runs before dispatch
and a lost response does not authorize a new business request. Generated questions
return named outputs with typed table or singleton access.

`captured_operation.propose()` returns a protected `Proposal` with the exact native
reference and preview. An authorized independent reviewer can pass that reference
to the generated operation's `review`, `approve` or `revoke` methods. Invocation
with `proposal=proposal` binds the saved proposal to the original request. Captured
operations also retain the native deployment, incarnation and request owner;
recovery refuses substitution of that lifetime.

## Asynchronous applications and subscriptions

```python theme={"theme":{"light":"github-light-default","dark":"github-dark-default"}}
from acme_business import create_async_workspace, Order

async with create_async_workspace(
    deployment_url=deployment_url, token_source=token_source
) as client:
    query = client.collections.orders.query().where(Order.fields.amount.gt(10))
    rows = (await query.read(timeout=10)).rows
    async with await query.run() as request:
        answer = await request.result(timeout=60)
        rows = await answer.outputs.rows.collect(max_rows=1000)
```

The async transport uses asynchronous HTTP I/O and shares protocol effects and
decoders with the sync client. Arguments are captured before awaiting. Async
request cleanup is shielded from task cancellation; unresolved remote cleanup
remains recoverable.

`query.watch(callback)` returns an owned native WebSocket subscription for
compositions admitting native dependency observation. Callbacks receive typed
`QueryObservation` values: loading, ready, reconnecting or unavailable. Only ready
has a current result. Reconnect clears prior values and waits for a fresh native
transition; permission failures also clear values. Sync observations close with
`close()` or a context manager; async observations use `await close()` or
`async with`. Closing the workspace closes its observations. Retained execution
is not emulated by polling a live query.

## Arrow, DataFrames and external compute

Install the generated package's `tabular` extra to add Arrow and pandas.

```python theme={"theme":{"light":"github-light-default","dark":"github-dark-default"}}
from bijection.tabular import to_arrow, from_arrow

table = to_arrow(rows, Order.__schema__)
native_rows = from_arrow(table, Order.__schema__)
```

Conversions require an explicit native row schema. They preserve exact integer
columns and record schema, component scope and absence policy in metadata. The
default `missing="preserve"` adds presence information, including nested objects;
`missing="null"` explicitly collapses absence and null, and `missing="error"`
refuses omitted properties. Heterogeneous or undeclared representations refuse
conversion. Decimal strings remain exact strings rather than inferred floats.

Retained tables provide bounded `arrow_batches(batch_rows=...)` and
`dataframe(max_rows=...)`. Generated dataset/artifact reference types reuse
`bijection_compute.references`. `computation_reference(value, expected_schema)`
validates the declared native reference; the external compute host still admits
and opens it. An Arrow table or DataFrame is local data and does not create a
native dataset reference or compute authority.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.