Snail Orbit Python Client
Type-safe Python client for the Snail Orbit project management API.
Installation
uv add snail-orbit-client
# or
pip install snail-orbit-client
Usage
Sync Client
from snail_orbit_client import SnailOrbitClient
client = SnailOrbitClient(
base_url='https://your-snail-orbit.example.com',
token='your-api-token'
)
# Get current user
profile = client.auth.get_profile()
print(f'Logged in as: {profile.name}')
# List issues
for issue in client.issues.list(q='priority:high and status:open'):
print(f'{issue.id_readable}: {issue.subject}')
# Get specific issue
issue = client.issues.get('issue-id')
# Access custom fields
priority_field = issue.fields.get('priority')
if priority_field:
print(f'Priority: {priority_field.value}')
Async Client
import asyncio
from snail_orbit_client import SnailOrbitAsyncClient
async def main():
async with SnailOrbitAsyncClient(
base_url='https://your-snail-orbit.example.com',
token='your-api-token'
) as client:
profile = await client.auth.get_profile()
async for issue in client.issues.list(q='status:open'):
print(f'{issue.id_readable}: {issue.subject}')
asyncio.run(main())
API Resources
client.auth # Authentication and profile
client.users # User operations
client.projects # Project operations
client.issues # Issue CRUD operations
client.custom_fields # Custom field definitions
client.activity # Activity tracking
Issues
client.issues.list(q=None, search=None) # List/query issues
client.issues.get(issue_id) # Get by ID
client.issues.get_by_readable_id(readable_id) # Get by readable ID (e.g., 'PRJ-123')
client.issues.create(issue_data) # Create issue
client.issues.update(issue_id, issue_data) # Update issue
# Comments
client.issues.get_comments(issue_id)
client.issues.create_comment(issue_id, comment_data)
client.issues.update_comment(issue_id, comment_id, comment_data)
client.issues.delete_comment(issue_id, comment_id)
# Tags
client.issues.add_tag(issue_id, tag_id)
client.issues.remove_tag(issue_id, tag_id)
# Attachments
client.issues.list_attachments(issue_id) # List attachments
client.issues.add_attachment(issue_id, attachment_input) # Add attachment
client.issues.remove_attachment(issue_id, attachment_id) # Remove attachment
client.issues.download_attachment(issue_id, attachment_id) # Stream download
client.issues.upload_and_attach(issue_id, file) # Upload and attach in one call
File Uploads
# Upload a file (loads into memory)
result = client.upload_file(b'content', filename='doc.pdf')
result = client.upload_file('/path/to/file.pdf')
result = client.upload_file(open('file.pdf', 'rb'))
# Streaming upload (memory-efficient for large files)
def read_chunks():
with open('large_file.zip', 'rb') as f:
while chunk := f.read(8192):
yield chunk
result = client.stream_upload(read_chunks(), 'large_file.zip')
# Attach uploaded file to issue
from snail_orbit_client.models import IssueAttachmentInput
client.issues.add_attachment(issue_id, IssueAttachmentInput(id=result.id))
# Or use the convenience method
attachment = client.issues.upload_and_attach(issue_id, '/path/to/file.pdf')
Downloading Attachments
# Stream to file (memory-efficient)
with open('output.pdf', 'wb') as f:
for chunk in client.issues.download_attachment(issue_id, attachment_id):
f.write(chunk)
# Collect in memory (if you need bytes)
content = b''.join(client.issues.download_attachment(issue_id, attachment_id))
# Async streaming to file
async with aiofiles.open('output.pdf', 'wb') as f:
async for chunk in client.issues.download_attachment(issue_id, attachment_id):
await f.write(chunk)
Search and Filtering
# Issue query language
client.issues.list(q='priority:high and status:open')
client.issues.list(q='assignee:me and project:myproject')
# Text search
client.issues.list(search='database bug')
# Users and projects use filter parameter
client.users.list(search='john', filter='is_active___eq:true')
client.projects.list(filter='created_at___gte:2024-01-01')
Configuration
from snail_orbit_client import SnailOrbitClient, ClientConfig
config = ClientConfig(
timeout=30.0, # Request timeout in seconds
max_retries=3, # Maximum retry attempts
retry_delay=1.0, # Base delay between retries
)
client = SnailOrbitClient(
base_url='https://your-snail-orbit.example.com',
token='your-token',
config=config
)
JWT Authentication
# JWT signing with service credentials
client = SnailOrbitClient(
base_url='https://api.snail-orbit.com',
token=('key-id', 'secret', 'user-id')
)
Error Handling
from snail_orbit_client.exceptions import (
SnailOrbitError,
AuthenticationError,
NotFoundError,
ValidationError,
RateLimitError,
)
try:
issue = client.issues.get('invalid-id')
except NotFoundError:
print('Issue not found')
except AuthenticationError:
print('Authentication failed')
except ValidationError as e:
print(f'Validation errors: {e.validation_errors}')
except RateLimitError as e:
print(f'Rate limited, retry after {e.retry_after}s')
Metadata
Release files for snail-orbit-client 0.13.2
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| snail_orbit_client-0.13.2-py3-none-any.whl | Python 3 | none | any | Details |
Release files / snail_orbit_client-0.13.2-py3-none-any.whl
| Download URL | snail_orbit_client-0.13.2-py3-none-any.whl |
|---|---|
| Size | 40.7 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
d1068257681eaa35c1339319895f02add666b16d7b980080303c68c9deced051
|
|
BLAKE2b-256 checksum How to use checksums |
4c95ce379d5746aaf60dfb25a8e9c21d2b86fda27e22ac3c7fa1c93c24e1ca17
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
uv/0.9.13 {"installer":{"name":"uv","version":"0.9.13"},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
|