macss-service-client
A service client abstraction for Python with the Result pattern for explicit success/failure handling.
Currently ships with an HTTP implementation using httpx. The architecture is designed to support additional transport layers in the future (see Roadmap).
Also available in Dart: service_client · TypeScript: @macss/service-client
Features
- Transport-agnostic interface —
ServiceClient(Protocol) defines the contract;HttpServiceClientimplements it for HTTP - Result pattern with
dataclass+Union— exhaustive checking viamatch/case ServiceFailurebase class for typed service errors (extensible via inheritance)- Configurable base URL, headers, and timeout
- Fully async with
httpx.AsyncClient - Typed with
py.typedmarker (PEP 561)
Installation
pip install macss-service-client
Usage
The example below follows an MVC structure: the View (main) delegates to a Controller,
which calls the Service. The service returns a Result that the controller resolves via
match/case.
Model
from dataclasses import dataclass
from typing import Any
@dataclass(frozen=True, slots=True)
class ToDo:
id: int
title: str
is_completed: bool
@staticmethod
def from_json(json: dict[str, Any]) -> "ToDo":
return ToDo(
id=json["id"],
title=json["title"],
is_completed=json["completed"],
)
Error
from service_client import ServiceFailure
class ToDoFailure(ServiceFailure):
pass
Service
from service_client import (
HttpClientException,
HttpServiceClient,
Result,
ServiceClient,
ServiceClientConfig,
ServiceRequest,
failure,
success,
)
_config = ServiceClientConfig(
base_url="https://jsonplaceholder.typicode.com",
default_headers={"Content-Type": "application/json"},
timeout=30.0,
)
_client: ServiceClient | None = None
def _get_service() -> ServiceClient:
global _client
if _client is None:
_client = HttpServiceClient(_config)
return _client
async def get_todo(todo_id: int) -> Result[ToDo, ToDoFailure]:
request = ServiceRequest.http(
method="GET",
endpoint=f"todos/{todo_id}",
error_message="Failed to fetch TODO",
)
try:
response = await _get_service().send(request)
return success(ToDo.from_json(response.data))
except HttpClientException as exc:
return failure(
ToDoFailure(
status_code=exc.status_code,
message=str(exc.args[0]),
response_body=exc.response,
)
)
Controller
The controller returns the Result directly — the view decides how to render each case:
from service_client import Result
class TodoController:
async def fetch_todo(self, todo_id: int) -> Result[ToDo, ToDoFailure]:
return await get_todo(todo_id)
View (main)
The view uses match/case to handle success and failure:
import asyncio
from service_client import Success, Failure
async def main() -> None:
controller = TodoController()
result = await controller.fetch_todo(1)
match result:
case Success(value=todo):
print(f"{todo.id}: {todo.title}")
case Failure(error=err):
print(f"Error {err.status_code}: {err.message}")
asyncio.run(main())
License
MIT © ccisne.dev
Release files for macss-service-client 0.2.1
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| macss_service_client-0.2.1.tar.gz | 9.4 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| macss_service_client-0.2.1-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 20.5 kB
Release files / macss_service_client-0.2.1.tar.gz
| Download URL | macss_service_client-0.2.1.tar.gz |
|---|---|
| Size | 9.4 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
4c29f8093585f98c1d332a9c247448fb3134a6ea2de827760932a9b9f31e14be
|
|
BLAKE2b-256 checksum How to use checksums |
289b34de89f21bc7b75c0306f3dbef94c1a855e5415e1acd1ba08b273ab0604f
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.11.0
|
Release files / macss_service_client-0.2.1-py3-none-any.whl
| Download URL | macss_service_client-0.2.1-py3-none-any.whl |
|---|---|
| Size | 11.0 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
0728139b98c7974e45af96e180c3136143507f1ea700ba878239cf7966db732a
|
|
BLAKE2b-256 checksum How to use checksums |
c5031166dead86c3c8b6e1c36ca0e1d7a39b95b51bba5ba031e4fa74ce2ae371
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.11.0
|