Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .flake8
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
[flake8]
max-line-length = 88
38 changes: 38 additions & 0 deletions .github/workflows/publish.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
name: Publish to PyPI

on:
push:
tags:
- '[0-9]+.[0-9]+.[0-9]+*'

jobs:
publish:
runs-on: ubuntu-latest
environment: pypi
permissions:
id-token: write # IMPORTANT: this permission is mandatory for trusted publishing

steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0 # Fetch all history for setuptools_scm to work properly

- name: Set up Python
uses: actions/setup-python@v4
with:
python-version: '3.11'

- name: Install build dependencies
run: |
python -m pip install --upgrade pip
python -m pip install build

- name: Build package
run: python -m build

- name: Publish to PyPI
uses: pypa/gh-action-pypi-publish@release/v1
with:
# Using trusted publishing, no API token needed
# Make sure to configure trusted publishing in PyPI settings
verbose: true
40 changes: 40 additions & 0 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
name: Test

on:
pull_request:
branches: [ main ]
push:
branches: [ main ]

jobs:
test:
runs-on: ubuntu-latest
strategy:
matrix:
python-version: ['3.8', '3.9', '3.10', '3.11', '3.12']

steps:
- uses: actions/checkout@v4

- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v4
with:
python-version: ${{ matrix.python-version }}

- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install -e .[dev]

- name: Run tests
run: pytest

- name: Run linting
run: |
flake8 replicated tests examples
mypy replicated

- name: Check formatting
run: |
black --check replicated tests examples
isort --check-only replicated tests examples
23 changes: 21 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
.PHONY: install dev test lint format clean build upload
.PHONY: install dev test lint format clean build upload ci

# Install package
install:
Expand Down Expand Up @@ -39,6 +39,25 @@ upload: build
twine check dist/*
twine upload dist/*

# Run all checks
# Run all checks (CI simulation - no formatting, just checking)
ci:
@echo "🔍 Running all CI checks locally..."
@echo "📦 Installing dependencies..."
@python3 -m pip install -e .[dev] > /dev/null 2>&1
@echo "✅ Dependencies installed"
@echo "🧪 Running tests..."
@python3 -m pytest
@echo "✅ Tests passed"
@echo "🔍 Running linting..."
@python3 -m flake8 replicated tests examples
@python3 -m mypy replicated
@echo "✅ Linting passed"
@echo "🎨 Checking formatting..."
@python3 -m black --check replicated tests examples
@python3 -m isort --check-only replicated tests examples
@echo "✅ Formatting passed"
@echo "🎉 ALL CI CHECKS PASSED! Ready to push! 🎉"

# Run all checks (formats code first)
check: format lint test
@echo "All checks passed!"
30 changes: 30 additions & 0 deletions check.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
#!/bin/bash

set -e

echo "🔍 Running all CI checks locally..."
echo "=================================="

echo "📦 Installing dependencies..."
python3 -m pip install -e ".[dev]" > /dev/null 2>&1
echo "✅ Dependencies installed"

echo ""
echo "🧪 Running tests..."
python3 -m pytest
echo "✅ Tests passed"

echo ""
echo "🔍 Running linting..."
python3 -m flake8 replicated tests examples
python3 -m mypy replicated
echo "✅ Linting passed"

echo ""
echo "🎨 Checking formatting..."
python3 -m black --check replicated tests examples
python3 -m isort --check-only replicated tests examples
echo "✅ Formatting passed"

echo ""
echo "🎉 ALL CI CHECKS PASSED! Ready to push! 🎉"
13 changes: 8 additions & 5 deletions examples/async_example.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,19 @@
"""

import asyncio

from replicated import AsyncReplicatedClient, InstanceStatus


async def main():
# Initialize the async client with your publishable key and app slug
async with AsyncReplicatedClient(
publishable_key="replicated_pk_...", # Replace with your actual key
app_slug="my-app" # Replace with your app slug
app_slug="my-app", # Replace with your app slug
) as client:
# Create a customer (or fetch an existing one)
customer = await client.customer.get_or_create(email_address="user@example.com")
email = "user@example.com"
customer = await client.customer.get_or_create(email_address=email)
print(f"Customer ID: {customer.customer_id}")

# Get or create the associated instance
Expand All @@ -24,19 +27,19 @@ async def main():
await asyncio.gather(
instance.send_metric("cpu_usage", 0.83),
instance.send_metric("memory_usage", 0.67),
instance.send_metric("disk_usage", 0.45)
instance.send_metric("disk_usage", 0.45),
)
print("Metrics sent successfully")

# Set the instance status and version concurrently
await asyncio.gather(
instance.set_status(InstanceStatus.RUNNING),
instance.set_version("1.2.0")
instance.set_version("1.2.0"),
)
print("Instance status set to RUNNING and version set to 1.2.0")

print("Example completed successfully!")


if __name__ == "__main__":
asyncio.run(main())
asyncio.run(main())
10 changes: 6 additions & 4 deletions examples/sync_example.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,18 @@
Synchronous example of using the Replicated Python SDK.
"""

from replicated import ReplicatedClient, InstanceStatus
from replicated import InstanceStatus, ReplicatedClient


def main():
# Initialize the client with your publishable key and app slug
with ReplicatedClient(
publishable_key="replicated_pk_...", # Replace with your actual key
app_slug="my-app" # Replace with your app slug
app_slug="my-app", # Replace with your app slug
) as client:
# Create a customer (or fetch an existing one)
customer = client.customer.get_or_create(email_address="user@example.com")
email = "user@example.com"
customer = client.customer.get_or_create(email_address=email)
print(f"Customer ID: {customer.customer_id}")

# Get or create the associated instance
Expand All @@ -37,4 +39,4 @@ def main():


if __name__ == "__main__":
main()
main()
9 changes: 8 additions & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -61,12 +61,19 @@ target-version = ['py38']
profile = "black"
line_length = 88

[tool.flake8]
max-line-length = 88

[tool.mypy]
python_version = "3.8"
python_version = "3.9"
strict = true
warn_return_any = true
warn_unused_configs = true

[[tool.mypy.overrides]]
module = "replicated.resources"
disable_error_code = ["arg-type", "override", "unused-ignore"]

[tool.pytest.ini_options]
testpaths = ["tests"]
python_files = ["test_*.py"]
Expand Down
10 changes: 5 additions & 5 deletions replicated/__init__.py
Original file line number Diff line number Diff line change
@@ -1,22 +1,22 @@
from .client import ReplicatedClient
from .async_client import AsyncReplicatedClient
from .client import ReplicatedClient
from .enums import InstanceStatus
from .exceptions import (
ReplicatedError,
ReplicatedAPIError,
ReplicatedAuthError,
ReplicatedRateLimitError,
ReplicatedError,
ReplicatedNetworkError,
ReplicatedRateLimitError,
)

__version__ = "1.0.0"
__all__ = [
"ReplicatedClient",
"AsyncReplicatedClient",
"AsyncReplicatedClient",
"InstanceStatus",
"ReplicatedError",
"ReplicatedAPIError",
"ReplicatedAuthError",
"ReplicatedRateLimitError",
"ReplicatedNetworkError",
]
]
8 changes: 4 additions & 4 deletions replicated/async_client.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from typing import Dict, Optional
from typing import Any, Dict

from .http_client import AsyncHTTPClient
from .services import AsyncCustomerService
Expand All @@ -19,7 +19,7 @@ def __init__(
self.app_slug = app_slug
self.base_url = base_url
self.timeout = timeout

self.http_client = AsyncHTTPClient(
base_url=base_url,
timeout=timeout,
Expand All @@ -31,7 +31,7 @@ async def __aenter__(self) -> "AsyncReplicatedClient":
await self.http_client.__aenter__()
return self

async def __aexit__(self, exc_type, exc_val, exc_tb) -> None:
async def __aexit__(self, exc_type: Any, exc_val: Any, exc_tb: Any) -> None:
await self.http_client.__aexit__(exc_type, exc_val, exc_tb)

def _get_auth_headers(self) -> Dict[str, str]:
Expand All @@ -41,4 +41,4 @@ def _get_auth_headers(self) -> Dict[str, str]:
if dynamic_token:
return {"Authorization": f"Bearer {dynamic_token}"}
else:
return {"Authorization": f"Bearer {self.publishable_key}"}
return {"Authorization": f"Bearer {self.publishable_key}"}
8 changes: 4 additions & 4 deletions replicated/client.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from typing import Dict, Optional
from typing import Any, Dict

from .http_client import SyncHTTPClient
from .services import CustomerService
Expand All @@ -19,7 +19,7 @@ def __init__(
self.app_slug = app_slug
self.base_url = base_url
self.timeout = timeout

self.http_client = SyncHTTPClient(
base_url=base_url,
timeout=timeout,
Expand All @@ -31,7 +31,7 @@ def __enter__(self) -> "ReplicatedClient":
self.http_client.__enter__()
return self

def __exit__(self, exc_type, exc_val, exc_tb) -> None:
def __exit__(self, exc_type: Any, exc_val: Any, exc_tb: Any) -> None:
self.http_client.__exit__(exc_type, exc_val, exc_tb)

def _get_auth_headers(self) -> Dict[str, str]:
Expand All @@ -41,4 +41,4 @@ def _get_auth_headers(self) -> Dict[str, str]:
if dynamic_token:
return {"Authorization": f"Bearer {dynamic_token}"}
else:
return {"Authorization": f"Bearer {self.publishable_key}"}
return {"Authorization": f"Bearer {self.publishable_key}"}
3 changes: 2 additions & 1 deletion replicated/enums.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,6 @@

class InstanceStatus(Enum):
"""Instance status enumeration."""

RUNNING = "running"
DEGRADED = "degraded"
DEGRADED = "degraded"
8 changes: 6 additions & 2 deletions replicated/exceptions.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from typing import Optional, Dict, Any
from typing import Any, Dict, Optional


class ReplicatedError(Exception):
Expand Down Expand Up @@ -31,19 +31,23 @@ def __str__(self) -> str:

class ReplicatedAPIError(ReplicatedError):
"""Raised when the API returns an error response."""

pass


class ReplicatedAuthError(ReplicatedError):
"""Raised when authentication fails."""

pass


class ReplicatedRateLimitError(ReplicatedError):
"""Raised when rate limit is exceeded."""

pass


class ReplicatedNetworkError(ReplicatedError):
"""Raised when network communication fails."""
pass

pass
Loading