Skip to content

Conversation

@joshrotenberg
Copy link
Collaborator

@joshrotenberg joshrotenberg commented Jan 22, 2026

Summary

Implements #563 - Python bindings for Redis Cloud and Enterprise APIs using PyO3.

Features

CloudClient (Redis Cloud API)

  • Subscriptions (list, get)
  • Databases (list, get)
  • Raw API access (get, post, put, delete)
  • from_env() with flexible env var support

EnterpriseClient (Redis Enterprise API)

  • Cluster info, stats, license
  • Databases (list, get, stats)
  • Nodes (list, get, stats)
  • Users (list, get)
  • Raw API access
  • from_env() constructor

Developer Experience

  • Both async and sync APIs for all methods
  • Type stubs (.pyi) for IDE autocomplete
  • Proper error handling - Rust errors mapped to Python exceptions
  • Comprehensive tests - 17 unit tests

Tested Against

  • Redis Enterprise: Docker instance via docker-compose (cluster, nodes, databases, users, stats)
  • Redis Cloud: Live account (subscriptions, databases across multiple subscriptions)

Usage

from redisctl import CloudClient, EnterpriseClient

# Redis Cloud
cloud = CloudClient.from_env()  # or CloudClient(api_key="...", api_secret="...")
result = cloud.subscriptions_sync()
for sub in result.get("subscriptions", []):
    print(f"{sub[\"id\"]}: {sub[\"name\"]}")

# Redis Enterprise  
enterprise = EnterpriseClient.from_env()  # or with explicit params
for db in enterprise.databases_sync():
    print(f"{db[\"uid\"]}: {db[\"name\"]}")

# Async
import asyncio
async def main():
    result = await cloud.subscriptions()
asyncio.run(main())

Building

cd crates/redisctl-python
pip install maturin
maturin build --release
pip install ../../target/wheels/redisctl-*.whl

CI

  • GitHub Actions workflow builds wheels for:
    • Linux (x86_64)
    • macOS (x86_64, arm64)
    • Windows (x86_64)
  • Runs 17 unit tests on Linux

Bug Fixes

This PR also fixes incorrect stats endpoint paths in redis-enterprise:

  • /v1/bdbs/stats/{uid} (was /v1/bdbs/{uid}/stats)
  • /v1/nodes/stats/{uid} (was /v1/nodes/{uid}/stats)

Technical Details

  • PyO3 0.23 for Rust-Python interop
  • pyo3-async-runtimes 0.23 for async/await (Tokio ↔ asyncio bridge)
  • maturin for building wheels
  • Crate excluded from default workspace members (requires Python dev headers)

TODO (follow-up work)

  • Add more handlers (ACLs, connectivity, tasks, alerts, etc.)
  • Add create/update/delete operations
  • Publish to PyPI

Closes #563

Implements issue #563 - Python bindings for Redis Cloud and Enterprise APIs.

Features:
- CloudClient for Redis Cloud API (subscriptions, databases)
- EnterpriseClient for Redis Enterprise API (cluster, databases, nodes, users)
- Both async and sync API variants for all methods
- Raw API access methods (get, post, put, delete)
- Proper error mapping to Python exceptions
- Type stubs for IDE support

Built with:
- PyO3 0.23 for Rust-Python interop
- pyo3-async-runtimes for async/await support (Tokio <-> asyncio)
- maturin for building wheels

Usage:
  from redisctl import CloudClient, EnterpriseClient

  # Sync
  client = CloudClient(api_key='...', api_secret='...')
  subs = client.subscriptions_sync()

  # Async
  subs = await client.subscriptions()
The Redis Enterprise API uses /v1/{resource}/stats/{uid} pattern,
not /v1/{resource}/{uid}/stats. Fixed:
- bdb.rs: stats() and metrics() paths
- nodes.rs: stats() path

Discovered while testing Python bindings against Docker instance.
Supports multiple environment variable names for compatibility:
- API Key: REDIS_CLOUD_API_KEY, REDIS_CLOUD_ACCOUNT_KEY
- API Secret: REDIS_CLOUD_API_SECRET, REDIS_CLOUD_SECRET_KEY, REDIS_CLOUD_USER_KEY
- Base URL: REDIS_CLOUD_BASE_URL, REDIS_CLOUD_API_URL
- Add GitHub Actions workflow for building Python wheels on Linux, macOS, Windows
- Add 17 unit tests for CloudClient and EnterpriseClient
- Add comprehensive README with usage examples
- Update type stubs with CloudClient.from_env() method
@joshrotenberg joshrotenberg marked this pull request as ready for review January 22, 2026 21:39
- Updated pyo3 from 0.23 to 0.27
- Updated pyo3-async-runtimes from 0.23 to 0.27
- Replaced deprecated Python::with_gil with Python::attach
- Replaced deprecated PyObject with Py<PyAny>
- Replaced deprecated py.allow_threads with py.detach
- Replaced deprecated downcast with cast

This addresses the security vulnerability RUSTSEC-2025-0020 in PyO3
versions prior to 0.24.1.
The Python bindings crate uses lib name 'redisctl' which collides with
the main CLI crate when generating docs. Since Python bindings have
their own documentation via type stubs (.pyi) and README, we disable
rustdoc generation for this crate.
Use if-let with && condition instead of is_some() + unwrap()
- Builds wheels for Linux (x86_64, aarch64), macOS (x86_64, arm64), Windows
- Uses PyO3/maturin-action for optimized builds
- Supports both TestPyPI and PyPI via workflow_dispatch
- Triggers automatically on GitHub releases
- Uses trusted publishing (OIDC) - no API tokens needed
@joshrotenberg joshrotenberg merged commit d7ce449 into main Jan 23, 2026
31 checks passed
@joshrotenberg joshrotenberg deleted the feat/python-bindings branch January 23, 2026 17:06
This was referenced Jan 20, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

feat: Add Python bindings via PyO3

2 participants