QDash Python Client Guide
QDash provides an auto-generated Python client library for interacting with the QDash API. This client is generated from the OpenAPI specification and provides type-safe access to all API endpoints.
Features
- 🚀 Auto-generated from OpenAPI: Always up-to-date with the latest API
- 🔒 Type-safe: Full type hints and runtime validation with attrs
- ⚡ Async/Sync Support: Choose between synchronous and asynchronous operations
- 🎯 Developer-friendly: Clean API with excellent IDE support
- 📦 Lightweight: Minimal dependencies (httpx + attrs)
Installation
From GitHub (Recommended)
# Install client only (lightweight, no server dependencies)
pip install git+https://github.com/oqtopus-team/qdash.git#subdirectory=src/qdash/client
# From specific branch
pip install git+https://github.com/oqtopus-team/qdash.git@develop#subdirectory=src/qdash/client
# From specific tag/release
pip install git+https://github.com/oqtopus-team/qdash.git@v0.1.0#subdirectory=src/qdash/client
Generate from API Server
If you have a running QDash API server and want to regenerate the client:
# Using the built-in task (recommended)
task generate-python-client
# Or using the script directly
uv run generate-python-client
Quick Start
Basic Usage
from qdash.client import Client
from qdash.client.api.chip import list_chips, fetch_chip
from qdash.client.api.calibration import execute_calib
from qdash.client.models import ExecuteCalibRequest
# Create a client instance
client = Client(base_url="http://localhost:5715")
# Get all quantum chips
response = list_chips.sync_detailed(client=client)
if response.status_code == 200:
chips = response.parsed
for chip in chips:
print(f"Chip: {chip.name}, ID: {chip.id}")
# Get specific chip details
chip_response = fetch_chip.sync_detailed(client=client, chip_name="sample_chip")
if chip_response.status_code == 200:
chip_detail = chip_response.parsed
print(f"Chip {chip_detail.name} has {len(chip_detail.qubits)} qubits")
# Start a calibration
calibration_request = ExecuteCalibRequest(
chip_name="sample_chip",
task_details=[] # Add your task details here
)
result = execute_calib.sync_detailed(client=client, json_body=calibration_request)
if result.status_code == 200:
print(f"Calibration started: {result.parsed}")
Client Configuration
import httpx
from qdash.client import Client, AuthenticatedClient
# Development environment with custom settings
dev_client = Client(
base_url="http://localhost:5715",
timeout=httpx.Timeout(30.0),
raise_on_unexpected_status=True
)
# Production with authentication
prod_client = AuthenticatedClient(
base_url="https://qdash.example.com",
token="your-api-token",
headers={"X-Username": "your-username"},
timeout=httpx.Timeout(15.0)
)
# With custom httpx settings
client = Client(
base_url="http://localhost:5715",
httpx_args={
"event_hooks": {
"request": [lambda req: print(f"Request: {req.method} {req.url}")],
"response": [lambda res: print(f"Response: {res.status_code}")]
}
}
)
Synchronous Usage
from qdash.client import Client
from qdash.client.api.chip import list_chips, fetch_chip
from qdash.client.api.calibration import execute_calib
from qdash.client.models import ExecuteCalibRequest
# Create sync client instance
client = Client(base_url="http://localhost:5715")
# Example: Get chip information
chips_response = list_chips.sync_detailed(client=client)
if chips_response.status_code == 200:
chips = chips_response.parsed
print(f"Found {len(chips)} chips")
for chip in chips:
print(f"Chip Name: {chip.name}, ID: {chip.id}")
# Example: Get specific chip details
if chips:
chip_response = fetch_chip.sync_detailed(
client=client,
chip_name=chips[0].name
)
if chip_response.status_code == 200:
chip_detail = chip_response.parsed
print(f"Chip has {len(chip_detail.qubits)} qubits")
print(f"Couplings: {len(chip_detail.couplings)}")
# Example: Execute calibration
calibration_request = ExecuteCalibRequest(
chip_name="my-chip",
task_details=[]
)
result = execute_calib.sync_detailed(client=client, json_body=calibration_request)
if result.status_code == 200:
print(f"Calibration started: {result.parsed}")
Asynchronous Usage (Recommended for Parallel Operations)
import asyncio
from qdash.client import Client
from qdash.client.api.chip import list_chips, fetch_chip
from qdash.client.api.execution import fetch_execution_lock_status
async def parallel_chip_analysis():
"""Example async workflow for parallel chip analysis."""
async with Client(base_url="http://localhost:5715") as client:
# Check system status first
lock_status = await fetch_execution_lock_status.asyncio_detailed(client=client)
if lock_status.status_code == 200 and not lock_status.parsed.locked:
print("System ready for calibration")
# Get all chips
chips_response = await list_chips.asyncio_detailed(client=client)
if chips_response.status_code != 200:
raise Exception("Failed to fetch chips")
chips = chips_response.parsed
print(f"Analyzing {len(chips)} chips in parallel...")
# Fetch details for multiple chips in parallel
detail_tasks = [
fetch_chip.asyncio_detailed(client=client, chip_name=chip.name)
for chip in chips[:5] # Limit to first 5
]
# Wait for all tasks to complete
results = await asyncio.gather(*detail_tasks, return_exceptions=True)
# Process results
for chip, result in zip(chips[:5], results):
if isinstance(result, Exception):
print(f"Error fetching {chip.name}: {result}")
elif result.status_code == 200:
detail = result.parsed
print(f"{chip.name}: {len(detail.qubits)} qubits, {len(detail.couplings)} couplings")
# Run the async workflow
asyncio.run(parallel_chip_analysis())
Error Handling
from qdash.client import Client
from qdash.client.api.chip import fetch_chip
from qdash.client.errors import UnexpectedStatus
try:
client = Client(
base_url="http://localhost:5715",
raise_on_unexpected_status=True # Raise exceptions on non-2xx responses
)
response = fetch_chip.sync_detailed(client=client, chip_name="nonexistent")
except UnexpectedStatus as e:
print(f"API Error: {e.status_code} - {e.content}")
except Exception as e:
print(f"Unexpected error: {e}")
API Reference
Client Methods
Each API endpoint generates four methods:
sync
: Synchronous call returning parsed data or Nonesync_detailed
: Synchronous call returning Response object with status codeasyncio
: Asynchronous call returning parsed data or Noneasyncio_detailed
: Asynchronous call returning Response object with status code
Available API Modules
api.chip
: Chip management and informationapi.calibration
: Calibration workflow executionapi.execution
: Execution status and locksapi.menu
: Menu and experiment configurationapi.settings
: Application settingsapi.auth
: Authentication endpointsapi.parameter
: Parameter configurationapi.tag
: Tagging systemapi.device_topology
: Device topology managementapi.backend
: Backend operationsapi.file
: File operationsapi.task
: Task management
Response Objects
from qdash.client.types import Response
# Response object structure
response = fetch_chip.sync_detailed(client=client, chip_name="chip1")
response.status_code # HTTP status code
response.content # Raw response content
response.headers # Response headers
response.parsed # Parsed model (if successful)
Working with Models
All request and response models are generated with attrs and provide:
- Full type hints
- Validation
- Serialization/deserialization
- Immutability by default
from qdash.client.models import ChipResponse, ExecuteCalibRequest
# Models are attrs classes
chip = ChipResponse(
name="quantum_chip_1",
id="chip_001",
qubits=[...],
couplings=[...]
)
# Access attributes
print(chip.name)
print(len(chip.qubits))
# Models are immutable by default
# Use evolve to create modified copies
from attrs import evolve
modified_chip = evolve(chip, name="quantum_chip_2")
Best Practices
Use context managers for proper resource cleanup:
pythonwith Client(base_url="...") as client: # Your code here
Check status codes before accessing parsed data:
pythonresponse = api_call.sync_detailed(client=client) if response.status_code == 200: data = response.parsed
Use async for parallel operations to improve performance:
pythonresults = await asyncio.gather(*[task1, task2, task3])
Configure timeouts for production environments:
pythonclient = Client(base_url="...", timeout=httpx.Timeout(10.0))
Handle errors gracefully with proper exception handling
Troubleshooting
Common Issues
Import errors: Make sure the client is installed:
bashpip install git+https://github.com/oqtopus-team/qdash.git#subdirectory=src/qdash/client
Connection errors: Verify the API server is running:
bashcurl http://localhost:5715/docs
Type errors: The client uses strict typing. Check your IDE for type hints.
Outdated client: Regenerate the client after API changes:
bashtask generate-python-client
Contributing
The client is auto-generated. To modify:
- Update the API endpoints in
src/qdash/api/
- Regenerate the client:
task generate-python-client
- Test your changes
- Submit a pull request
License
Apache License 2.0 - See LICENSE for details.