Metadata-Version: 2.4
Name: switchpilot
Version: 0.2.0
Summary: Official server-side Python SDK for SwitchPilot feature flags
License-Expression: MIT
License-File: LICENSE
Requires-Python: >=3.10
Requires-Dist: httpx<1,>=0.27
Provides-Extra: dev
Requires-Dist: build<2,>=1.2; extra == 'dev'
Requires-Dist: pytest<10,>=8; extra == 'dev'
Requires-Dist: ruff<1,>=0.15; extra == 'dev'
Requires-Dist: twine<7,>=6; extra == 'dev'
Description-Content-Type: text/markdown

# switchpilot

The official server-side Python SDK for SwitchPilot feature flags.

## Install

```sh
pip install switchpilot
```

## Quick start

Create one reusable synchronous client. Existing integrations require no changes.

```python
import os
from switchpilot import SwitchPilot

client = SwitchPilot(api_key=os.environ["SWITCHPILOT_API_KEY"])
enabled = client.is_enabled("new-checkout")
```

## Cache and reliability

The default cache TTL is 60 seconds. `is_enabled()` and `get_all()` evaluate flags locally while the cache is fresh; they do not make one HTTP request per evaluation. `refresh()` requests a synchronization explicitly.

The SDK automatically sends the last ETag as `If-None-Match`. A `304 Not Modified` renews cache freshness without parsing a body. Concurrent threads share one refresh.

The last valid `200` response is retained as Last Known Good. If a refresh fails, it remains usable until:

```text
stale_expires_at = last_successful_refresh_at + cache_ttl + stale_if_error
```

The stale-if-error window defaults to 5 minutes. The fallback is used only when no usable configuration exists. A missing flag continues to use the per-call fallback, then the client fallback.

```python
client = SwitchPilot(
    api_key=os.environ["SWITCHPILOT_API_KEY"],
    cache_ttl=60,
    stale_if_error=300,
    fallback=False,
    timeout=2,
)
```

`cache_ttl=0` preserves the existing behavior of revalidating each evaluation, while Last Known Good can still be used within `stale_if_error` after failure.

## Methods

```python
client.is_enabled("new-checkout")
client.is_enabled("new-checkout", True)  # per-call fallback
client.get_all()                          # defensive copy
client.refresh()                          # manual synchronization
client.get_status()                       # local; never performs I/O
```

`get_status()` returns an immutable dataclass with cache freshness, stale use, refresh timestamps, ETag, safe error information, and `rate_limited_until`. It never exposes the API key.

On `429 Too Many Requests`, the SDK respects `Retry-After`, performs no retry loop, and serves Last Known Good when usable. Calls before the deadline do not contact the API.

## Server-side only

Python 3.10 or newer is required. The package includes typing metadata. Never expose the project API key to a browser or logs. Redirect following is disabled so Authorization cannot be forwarded to another origin.

For an editable local install:

```sh
pip install -e sdks/python
```
