# Use the Python SDK

**Applies to:** current development source bundles. Obtain a matched client/server release through [early-access support](mailto:info@simplemagic.com); registry publication is not available yet. Paths below are relative to the supplied source checkout.

Thread-safe synchronous TCP client. Requires Python 3.10+; no runtime dependencies.
This first release has not been published to PyPI.

## Install and use

From the repository root:

```sh
python3 -m pip install ./python-smkv
```

```python
from smkv import Client, SmkvError

with Client("127.0.0.1:7379", pool_size=4, timeout=5) as client:
    client.put(b"greeting", b"hello", sync=True, ttl_ms=60000)
    print(client.get(b"greeting"))
    users = client.table("users")
    users.put(b"42", {"name": "Ada", "visits": 1})
    users.update(b"42", {"visits": 2})
    print(users.get(b"42", columns=["name"]))
    try:
        client.get(b"missing")
    except SmkvError as error:
        if error.code != "not_found":
            raise
```

Run `python3 python-smkv/examples/basic.py 127.0.0.1:7379` against a disposable node.
Keys and raw values are `bytes`; empty values are valid. Columns accept bytes,
str, signed 64-bit int, float, and bool. Integers outside int64 are rejected.

## Cluster connections

```python
from smkv import ClusterClient

with ClusterClient(["127.0.0.1:7381"], timeout=5) as cluster:
    print(cluster.get(b"greeting"))
    # Explicitly allow stale data:
    print(cluster.get_replica(b"greeting"))
```

Seeds are **management** endpoints. Routing includes the table in row keys.
`refresh()` updates topology; `topology` returns a copy. `table(...).get_replica`
reads a replica when used on a cluster client.

## Options, errors, and security

The default pool has four connections per node, a five-second operation deadline
and idle timeout, 1 KiB keys, and 1 MiB values. Configure `pool_size`, `timeout`,
`idle_timeout`, `max_key_bytes`, and `max_value_bytes`. Per-call `timeout` can
shorten the deadline, which covers pool wait and socket I/O. Native DNS resolution
cannot be interrupted; use IP endpoints when a strict end-to-end bound is needed.
Clients can be shared across threads, but must be created separately after fork.
`close()` cancels outstanding socket I/O and closes the pool.

Writes accept `sync`, `replica_ack`, and `durable_replica_ack`; defaults do not
request per-write disk synchronization. `ttl_ms` on put/update uses milliseconds:
omitted means no expiry for put and preserves expiry for update; zero expires
immediately. Delete does not accept TTL. Atomic `update(key, set_columns, remove)`
preserves columns not mentioned; `get(key, columns=...)` projects columns.

`SmkvError.code` identifies server/transport failures; `status` is the numeric
server status when present. An error with `outcome_unknown=True` means a write may
have completed. Do not blindly retry it. Only explicit `wrong_owner` is retried
by the cluster client, within the original deadline.

Plaintext and no authentication are defaults. For verified TLS and bearer auth:

```python
import ssl
from smkv import Security

security = Security(tls=ssl.create_default_context(cafile="ca.pem"),
                    credential={"name": "app", "token": token})
client = Client("db.example.com:7379", security=security)
```

Load `token` from your secret provider. Cluster `security` and
`management_security` are separate options; configure both when both ports require
security. TLS always verifies the certificate and hostname. There is no downgrade.

Scans, batch operations, and an async Python API are not included yet.
See [shared contract](sdk-clients.html) and [tests](sdk-clients.html#initial-release-limits).

## Logical databases

Select the database when constructing the client (or cluster client):

```python
Client("127.0.0.1:7379", database="orders")
```

Omitting the option uses `default` and preserves existing wire keys. Named
databases require database-aware development servers (cluster protocol 5, or 6
with experimental group sync). Names use 1–64 ASCII letters, digits, `_` or `-`.
The selection scopes raw keys and all table operations, including replica reads.
The complete database-prefixed key is hashed for routing and counts against the
key-size limit. Databases share cluster capacity and durability settings.
