Metadata-Version: 2.4
Name: ix-sdk
Version: 0.7.1
Summary: Generated Python SDK for ix
License-Expression: LicenseRef-Proprietary
License-File: LICENSE
Requires-Python: >=3.13
Description-Content-Type: text/markdown

# ix-sdk

Generated Python bindings for [ix](https://ix.dev). The Rust `ix-sdk` owns
every platform verb; Unibind generates the asyncio classes, typed records,
exception classes and `.pyi` stubs from that contract, so the Python surface
never drifts from the platform.

Python 3.13 or newer. The package ships `py.typed` and generated stubs, so
pyright and mypy see the same methods and records the native module exports.

## boot a machine

The credential resolves from `IX_TOKEN`, then the ix config file written by
`ix login`, so there is no client to configure. The machine answers by the
time `create` returns.

```python
import asyncio

import ix_sdk


async def main() -> None:
    client = ix_sdk.Client()
    machine = await client.machines().create(ix_sdk.CreateMachineOptions(name="sdk-example"))
    try:
        result = await machine.exec_checked(["uname", "-a"])
        print(result.stdout.strip())
    finally:
        await machine.delete()


asyncio.run(main())
```

`async with` deletes a machine the handle booted at the end of the block,
exception or not. Bind it with a plain `=` when the machine is meant to
outlive the program; a machine outlives the process that made it, and
`client.machines().connect(machine_id)` reattaches later.

## boot from your repository's flake

Pass a sha-pinned flake reference as the template and the platform builds it
in-guest on first use, publishes the result to the region's template cache,
and boots every later create warm from that cache. Builds are single-flight
region-wide, and `create_stream` carries the live build log:

```python
options = ix_sdk.CreateMachineOptions(
    template="github:owner/repo/0123456789abcdef0123456789abcdef01234567#ci-runner",
)
stream = await client.machines().create_stream(options)
async for frame in stream:
    if frame.stderr:
        print(frame.stderr, end="")
    if frame.finished:
        machine = client.machines().connect(frame.machine_id)
```

## everything else

Every verb is a method on the generated surface: `machine.exec`,
`machine.read_file`, `machine.write_file`, `machine.snapshot`,
`machine.tail_logs`, `client.keys().create`, and so on. Streams are
`async for` iterables, and dropping the iterator cancels the work behind it.

Records are typed objects with documented read-only properties and a readable
`repr`, and every record is dict-like: `dict(machine)`, `{**machine}` and
`machine["status"]` all work, so a list of records drops straight into a
DataFrame.

The one piece of language-owned sugar is `Repl`, a stateful interpreter
inside a machine:

```python
repl = await ix_sdk.Repl.open(machine, "python")
await repl.exec("x = 21")
out = await repl.exec("print(x * 2)")
```

## docs

- Python SDK: <https://ix.dev/docs/sdk/python>
- Platform docs: <https://ix.dev/docs>
- TypeScript twin: [`@indexable/sdk` on npm](https://www.npmjs.com/package/@indexable/sdk)
