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
5 changes: 4 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -65,4 +65,7 @@ pypi: ## publishes to PyPI
uv build
@echo "\n>>> Publishing to PyPI..."
uv publish
@echo "\n\033[0;32mPyPI release complete! The GitHub Action will now create the GitHub Release.\033[0m"
@echo "\n\033[0;32mPyPI release complete! The GitHub Action will now create the GitHub Release.\033[0m"

tree: ## shows filetree in terminal without uninteresting files
tree -I "*.pyc|*.lock"
5 changes: 4 additions & 1 deletion examples/metadata/get_datacenters.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
import asyncio
import logging
from codesphere import CodesphereSDK

logging.basicConfig(level=logging.INFO)


async def main():
"""Fetches datacenters."""
async with CodesphereSDK() as sdk:
datacenters = await sdk.metadata.datacenters()
datacenters = await sdk.metadata.list_datacenters()
for datacenter in datacenters:
print(datacenter.model_dump_json(indent=2))

Expand Down
3 changes: 3 additions & 0 deletions examples/metadata/get_workspace_base_image.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
import asyncio
import logging
from codesphere import CodesphereSDK

logging.basicConfig(level=logging.INFO)


async def main():
"""Fetches base images."""
Expand Down
3 changes: 3 additions & 0 deletions examples/metadata/get_workspace_plans.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
import asyncio
import logging
from codesphere import CodesphereSDK

logging.basicConfig(level=logging.INFO)


async def main():
"""Fetches workspace plans."""
Expand Down
4 changes: 3 additions & 1 deletion examples/teams/create_team.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
import asyncio
import logging
from codesphere import CodesphereSDK, TeamCreate

logging.basicConfig(level=logging.INFO)


async def main():
try:
async with CodesphereSDK() as sdk:
newTeam = TeamCreate(name="test", dc=2)
created_team = await sdk.teams.create(data=newTeam)
print("\n--- Details for the created team ---")
print(created_team.model_dump_json(indent=2))
except Exception as e:
print(f"An error occurred: {e}")
Expand Down
4 changes: 3 additions & 1 deletion examples/teams/delete_team.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
import asyncio
import logging
from codesphere import CodesphereSDK

logging.basicConfig(level=logging.INFO)


async def main():
try:
async with CodesphereSDK() as sdk:
team_to_delete = await sdk.teams.get(team_id="<id>")
print("\n--- Details of the team to be deleted ---")
print(team_to_delete.model_dump_json(indent=2))
await team_to_delete.delete()
print(f"Team with ID {team_to_delete.id} was successfully deleted.")
Expand Down
11 changes: 5 additions & 6 deletions examples/teams/get_team.py
Original file line number Diff line number Diff line change
@@ -1,16 +1,15 @@
import asyncio

import logging
from codesphere import CodesphereSDK

logging.basicConfig(level=logging.INFO)


async def main():
try:
async with CodesphereSDK() as sdk:
teams = await sdk.teams.list()
print(teams[0].model_dump_json(indent=2))
first_team = await sdk.teams.get(team_id=teams[0].id)
print("\n--- Details for the first team ---")
print(first_team.model_dump_json(indent=2))
team = await sdk.teams.get(team_id="<id>")
print(team.model_dump_json(indent=2))

except Exception as e:
print(f"An error occurred: {e}")
Expand Down
4 changes: 3 additions & 1 deletion examples/teams/list_teams.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import asyncio

import logging
from codesphere import CodesphereSDK

logging.basicConfig(level=logging.INFO)


async def main():
try:
Expand Down
61 changes: 35 additions & 26 deletions src/codesphere/__init__.py
Original file line number Diff line number Diff line change
@@ -1,44 +1,53 @@
from .client import APIHttpClient
from .resources.team.resources import TeamsResource, TeamCreate
from .resources.workspace.resources import WorkspacesResource
from .resources.metadata.resources import MetadataResource
from .resources.workspace.models import (
Workspace,
WorkspaceCreate,
WorkspaceUpdate,
WorkspaceStatus,
)
"""
Codesphere SDK - Python client for the Codesphere API.

This package provides a high-level asynchronous client for interacting
with the `Codesphere Public API <https://codesphere.com/api/swagger-ui/?ref=codesphere.ghost.io#/>`_.

Main Entrypoint:
`from codesphere import CodesphereSDK`

class CodesphereSDK:
def __init__(self, token: str = None):
self._http_client = APIHttpClient()
self.teams: TeamsResource | None = None
self.workspaces: WorkspacesResource | None = None
Basic Usage:
>>> import asyncio
>>> from codesphere import CodesphereSDK
>>>
>>> async def main():
>>> async with CodesphereSDK() as sdk:
>>> teams = await sdk.teams.list()
>>> print(teams)
>>>
>>> asyncio.run(main())
"""

async def __aenter__(self):
"""Wird beim Eintritt in den 'async with'-Block aufgerufen."""
await self._http_client.__aenter__()
import logging
from .client import CodesphereSDK

self.teams = TeamsResource(self._http_client)
self.workspaces = WorkspacesResource(self._http_client)
self.metadata = MetadataResource(self._http_client)
return self
from .cs_types.exceptions.exceptions import CodesphereError, AuthenticationError

async def __aexit__(self, exc_type, exc_val, exc_tb):
"""Wird beim Verlassen des 'async with'-Blocks aufgerufen."""
await self._http_client.__aexit__(exc_type, exc_val, exc_tb)
from .resources.team import Team, TeamCreate, TeamBase
from .resources.workspace import (
Workspace,
WorkspaceCreate,
WorkspaceUpdate,
WorkspaceStatus,
)
from .resources.metadata import Datacenter, Characteristic, WsPlan, Image

logging.getLogger("codesphere").addHandler(logging.NullHandler())

__all__ = [
"CodesphereSDK",
"CodesphereError",
"AuthenticationError",
"Team",
"TeamCreate",
"TeamInList",
"TeamBase",
"Workspace",
"WorkspaceCreate",
"WorkspaceUpdate",
"WorkspaceStatus",
"Datacenter",
"Characteristic",
"WsPlan",
"Image",
]
105 changes: 65 additions & 40 deletions src/codesphere/client.py
Original file line number Diff line number Diff line change
@@ -1,50 +1,75 @@
import httpx
from pydantic import BaseModel
from typing import Optional, Any
from functools import partial
"""
Codesphere SDK Client

from .config import settings
This module provides the main client class, CodesphereSDK.
"""

from .cs_types.rest.http_client import APIHttpClient
from .resources.metadata.resources import MetadataResource
from .resources.team.resources import TeamsResource
from .resources.workspace.resources import WorkspacesResource

class APIHttpClient:
def __init__(self, base_url: str = "https://codesphere.com/api"):
self._token = settings.token.get_secret_value()
self._base_url = base_url or str(settings.base_url)
self.client: Optional[httpx.AsyncClient] = None

# Dynamically create get, post, put, patch, delete methods
for method in ["get", "post", "put", "patch", "delete"]:
setattr(self, method, partial(self.request, method.upper()))
class CodesphereSDK:
"""The main entrypoint for interacting with the `Codesphere Public API <https://codesphere.com/api/swagger-ui/?ref=codesphere.ghost.io#/>`_.

async def __aenter__(self):
timeout_config = httpx.Timeout(
settings.client_timeout_connect, read=settings.client_timeout_read
)
self.client = httpx.AsyncClient(
base_url=self._base_url,
headers={"Authorization": f"Bearer {self._token}"},
timeout=timeout_config,
)
return self
This class manages the HTTP client, its lifecycle,
and provides access to the various API resources.

Primary usage is via an asynchronous context manager:

Usage:
>>> import asyncio
>>> from codesphere import CodesphereSDK
>>>
>>> async def main():
>>> async with CodesphereSDK() as sdk:
>>> teams = await sdk.teams.list()
>>> print(teams)
>>>
>>> asyncio.run(main())

Attributes:
teams (TeamsResource): Access to Team API operations.
workspaces (WorkspacesResource): Access to Workspace API operations.
metadata (MetadataResource): Access to Metadata API operations.
"""

teams: TeamsResource
"""Access to the Team API. (e.g., `sdk.teams.list()`)"""

async def __aexit__(self, exc_type: Any, exc_val: Any, exc_tb: Any):
if self.client:
await self.client.aclose()
workspaces: WorkspacesResource
"""Access to the Workspace API. (e.g., `sdk.workspaces.list()`)"""

async def request(
self, method: str, endpoint: str, **kwargs: Any
) -> httpx.Response:
if not self.client:
raise RuntimeError(
"APIHttpClient must be used within an 'async with' statement."
)
metadata: MetadataResource
"""Access to the Metadata API. (e.g., `sdk.metadata.list_plans()`)"""

# If a 'json' payload is a Pydantic model, automatically convert it.
if "json" in kwargs and isinstance(kwargs["json"], BaseModel):
kwargs["json"] = kwargs["json"].model_dump(exclude_none=True)
def __init__(self):
self._http_client = APIHttpClient()
self.teams = TeamsResource(self._http_client)
self.workspaces = WorkspacesResource(self._http_client)
self.metadata = MetadataResource(self._http_client)

print(f"{method} {endpoint} {kwargs}")
async def open(self):
"""Manually opens the underlying HTTP client session.

Required for manual lifecycle control when not using `async with`.

Usage:
>>> sdk = CodesphereSDK()
>>> await sdk.open()
>>> # ... API calls ...
>>> await sdk.close()
"""
await self._http_client.open()

async def close(self):
"""Manually closes the underlying HTTP client session."""
await self._http_client.close()

async def __aenter__(self):
await self.open()
return self

response = await self.client.request(method, endpoint, **kwargs)
response.raise_for_status()
return response
async def __aexit__(self, exc_type, exc_val, exc_tb):
await self._http_client.close(exc_type, exc_val, exc_tb)
2 changes: 0 additions & 2 deletions src/codesphere/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,6 @@
class Settings(BaseSettings):
"""
API Client Settings

TODO: add List of available env vars
"""

model_config = SettingsConfigDict(
Expand Down
Empty file.
Loading
Loading