Async-first Python SDK and CLI for Rackspace Spot
Project description
rsspot
rsspot is a Python SDK and CLI for Rackspace Spot with unified sync/async ergonomics, sqlite-backed runtime state, and profile-based configuration.
Highlights
- Unified client API:
SpotClientfor sync + async access on one objectAsyncSpotClientfor explicit async-only usage
- Persistent sqlite state (
state.db) for:- defaults/preferences
- HTTP cache
- redacted CLI command history
- VM registration ledger entries
- Config precedence + migration:
- new default path:
~/.config/rsspot/config.yml - legacy
~/.spot_configauto-imported/migrated
- new default path:
- Strong typing via
pydanticmodels - Multi-profile workflows + global singleton helpers
- OpenAPI sync/index scripts for upstream tracking
Install
uv sync
Quickstart (CLI)
uv run rsspot configure \
--profile default \
--org <org-name> \
--region us-central-dfw-1 \
--refresh-token "$SPOT_REFRESH_TOKEN"
uv run rsspot organizations list --output table
Pricing Explorer + Builder
# default output is a rich table when --output is not explicitly set
uv run rsspot pricing list --region us-central-dfw-1 --class gp,ch --min-cpu 4 --gen 2 --nodes 5
# explicit machine-readable output stays json/yaml
uv run rsspot -o json pricing list --class gp --max-cpu 8 --nodes 10
# generate 3 recommendation strategies (max_performance, max_value, balanced)
uv run rsspot pricing build --nodes 5 --risk med --classes gp,ch,mh
# spread balanced strategy across multiple pools and constrain cluster hourly spend
uv run rsspot pricing build --nodes 5 --balanced --risk low --max-hour 0.25
# constrain by total cluster monthly spend
uv run rsspot pricing build --nodes 5 --min-month 50 --max-month 180
Quickstart (SDK)
Unified client
from rsspot import SpotClient
client = SpotClient(profile="default")
orgs = client.organizations.list()
print([org.name for org in orgs.organizations])
client.close()
import asyncio
from rsspot import SpotClient
async def main() -> None:
client = SpotClient(profile="default")
orgs = await client.aorganizations.list()
print([org.name for org in orgs.organizations])
await client.aclose()
asyncio.run(main())
Async-only client
import asyncio
from rsspot import AsyncSpotClient
async def main() -> None:
async with AsyncSpotClient(profile="default") as client:
regions = await client.regions.list()
print([r.name for r in regions])
asyncio.run(main())
Configuration
Resolution precedence:
- Runtime config dict/model passed to client constructor
- Explicit
config_path=... - Env path (
RSSPOT_CONFIG,RSSPOT_CONFIG_FILE,SPOT_CONFIG_FILE) - Default search in
~/.config/rsspot/config.{yml,yaml,toml,json} - Legacy fallback
~/.spot_config(auto-migrated)
Expected schema is identical across config.yaml, config.toml, and config.json.
config.yaml
version: "2"
default_profile: prod
active_profile: prod
state_path: /Users/you/.config/rsspot/state.db
profiles:
prod:
org: sparkai
region: us-central-dfw-1
refresh_token: replace-me
base_url: https://spot.rackspace.com
oauth_url: https://spot.rackspace.com
request_timeout_seconds: 30
verify_ssl: true
preferences:
default_profile: prod
default_org: sparkai
default_region: us-central-dfw-1
retry:
max_attempts: 4
base_delay: 0.2
max_delay: 2.5
jitter: 0.2
cache:
enabled: true
default_ttl: 5
max_entries: 1000
backend: sqlite
config.toml
version = "2"
default_profile = "prod"
active_profile = "prod"
state_path = "/Users/you/.config/rsspot/state.db"
[profiles.prod]
org = "sparkai"
region = "us-central-dfw-1"
refresh_token = "replace-me"
base_url = "https://spot.rackspace.com"
oauth_url = "https://spot.rackspace.com"
request_timeout_seconds = 30
verify_ssl = true
[preferences]
default_profile = "prod"
default_org = "sparkai"
default_region = "us-central-dfw-1"
[retry]
max_attempts = 4
base_delay = 0.2
max_delay = 2.5
jitter = 0.2
[cache]
enabled = true
default_ttl = 5
max_entries = 1000
backend = "sqlite"
config.json
{
"version": "2",
"default_profile": "prod",
"active_profile": "prod",
"state_path": "/Users/you/.config/rsspot/state.db",
"profiles": {
"prod": {
"org": "sparkai",
"region": "us-central-dfw-1",
"refresh_token": "replace-me",
"base_url": "https://spot.rackspace.com",
"oauth_url": "https://spot.rackspace.com",
"request_timeout_seconds": 30,
"verify_ssl": true
}
},
"preferences": {
"default_profile": "prod",
"default_org": "sparkai",
"default_region": "us-central-dfw-1"
},
"retry": {
"max_attempts": 4,
"base_delay": 0.2,
"max_delay": 2.5,
"jitter": 0.2
},
"cache": {
"enabled": true,
"default_ttl": 5,
"max_entries": 1000,
"backend": "sqlite"
}
}
Notes:
profiles.<name>is where profile-specific credentials/selectors live.- Top-level
retry/cacheare defaults; profile-levelretry/cachecan override them. - Legacy aliases like
refreshToken,clientId, andbaseUrlare still accepted.
VM Registration Workflow Primitives
rsspot includes registration state helpers for composing with external orchestrators (including separate omni-sdk scripts) without taking a direct dependency.
Core entrypoint:
rsspot.workflows.RegistrationWorkflow
OpenAPI Tracking
uv run python scripts/sync_openapi.py
uv run python scripts/generate_openapi_index.py
Development
uv run ruff check src tests
uv run mypy src
uv run pytest -q
Changelog
See CHANGELOG.md.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file rsspot-0.3.0.tar.gz.
File metadata
- Download URL: rsspot-0.3.0.tar.gz
- Upload date:
- Size: 92.7 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: uv/0.10.6 {"installer":{"name":"uv","version":"0.10.6","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
e7048a2f867461eb3ee4ff3f898e684837bded8333e0457822acb7edeb758fe3
|
|
| MD5 |
473b3bdda87e44f9207ea58375972869
|
|
| BLAKE2b-256 |
24a9c85d029e13c393f7b63e9eeb4d110b09457f3e22945a340923ceb8e2417f
|
File details
Details for the file rsspot-0.3.0-py3-none-any.whl.
File metadata
- Download URL: rsspot-0.3.0-py3-none-any.whl
- Upload date:
- Size: 51.7 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: uv/0.10.6 {"installer":{"name":"uv","version":"0.10.6","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
895479fc68c9b6222a297b9c791a609f6749c4cf2b8aa9b98201d1c4173189a9
|
|
| MD5 |
b7ba01b7d99c6b7bab9ba04ad1b5197d
|
|
| BLAKE2b-256 |
6c322577a1bc25b27fadb611549eea3e26b7618eb77931666144544dfe92c3bf
|