Librairie Python modulaire pour construire des clients d’API HTTP synchrones et asynchrones : requêtes REST, auth, retries, timeouts, JSON, erreurs, middlewares et logs. Socle générique extensible pour développer des intégrations d’APIs ou des microservices.
Project description
baobab-api-call
Socle client HTTP Python pour les appels API Baobab : configuration centralisée, transports interchangeables, authentification, retry, middlewares et modèles de requête/réponse immuables.
| Version | 1.0.0 (changelog) |
| Package PyPI | baobab-api-call |
| Python | ≥ 3.11 |
| Licence | MIT |
| Dépôt | github.com/baobabgit/python-baobab-api-call |
Sommaire
- Fonctionnalités
- Installation
- Démarrage rapide
- Transports HTTP
- Configuration
- Authentification
- Retry et résilience
- Middlewares
- Réponses HTTP
- Exceptions
- Développement
- Publication et CI
- Changelog
- Licence
Fonctionnalités
- Clients sync et async avec la même logique métier (construction de requête, auth, retry, middlewares).
- Transports pluggables :
requests,httpx(sync + async),aiohttp(async),urllib(stdlib, sync). - Configuration immuable (
BaobabApiCallClientConfig) : URL de base, en-têtes, timeouts, politique de retry. - Stratégies d’auth : Basic, Bearer, clé API (en-tête ou query).
- Retry avec backoff exponentiel, jitter et codes HTTP / exceptions configurables.
- Middlewares synchrones et asynchrones (chaînes
before_request/after_response/on_error). - Réponses typées : propriétés
is_success,is_error, parsing JSON,raise_for_status(). - Exceptions normalisées : réseau, timeout, HTTP, sérialisation, retry épuisé.
Installation
pip install baobab-api-call==1.0.0
Pour la dernière version 1.x :
pip install "baobab-api-call>=1.0,<2"
Dépendances installées automatiquement : requests, httpx, aiohttp. Le transport urllib utilise la bibliothèque standard (aucun paquet supplémentaire).
Développement
git clone https://github.com/baobabgit/python-baobab-api-call.git
cd python-baobab-api-call
pip install -e ".[dev]"
Démarrage rapide
Client synchrone (httpx)
from baobab_api_call.client.apis.baobab_api_call_sync_api_client import (
BaobabApiCallSyncApiClient,
)
from baobab_api_call.client.config.baobab_api_call_client_config import (
BaobabApiCallClientConfig,
)
from baobab_api_call.transports.httpx.baobab_api_call_sync_httpx_transport import (
BaobabApiCallSyncHttpxTransport,
)
config = BaobabApiCallClientConfig(
base_url="https://api.example.com",
timeout_seconds=30.0,
)
transport = BaobabApiCallSyncHttpxTransport(timeout_seconds=30.0)
with BaobabApiCallSyncApiClient(config, transport) as client:
response = client.get("/v1/items", params={"page": "1"})
response.raise_for_status()
items = response.json()
Client asynchrone (httpx)
import asyncio
from baobab_api_call.client.apis.baobab_api_call_async_api_client import (
BaobabApiCallAsyncApiClient,
)
from baobab_api_call.client.config.baobab_api_call_client_config import (
BaobabApiCallClientConfig,
)
from baobab_api_call.transports.httpx.baobab_api_call_async_httpx_transport import (
BaobabApiCallAsyncHttpxTransport,
)
config = BaobabApiCallClientConfig(base_url="https://api.example.com")
async def main() -> None:
transport = BaobabApiCallAsyncHttpxTransport(timeout_seconds=30.0)
async with BaobabApiCallAsyncApiClient(config, transport) as client:
response = await client.post("/v1/items", json={"name": "demo"})
if response.is_success:
print(response.json())
asyncio.run(main())
Méthodes HTTP exposées
Les deux clients exposent :
| Méthode | Description |
|---|---|
get |
GET |
post |
POST (corps json= ou body=) |
put |
PUT |
patch |
PATCH |
delete |
DELETE |
request |
Appel générique avec verbe HTTP |
Le paramètre path peut être un chemin relatif (fusionné avec base_url) ou une URL absolue http:// / https://.
Transports HTTP
Un transport implémente l’envoi réel sur le réseau. Le client reste identique ; seul le transport change.
| Transport | Module | Mode | Backend |
|---|---|---|---|
BaobabApiCallSyncRequestsTransport |
transports.requests |
Sync | requests |
BaobabApiCallSyncHttpxTransport |
transports.httpx |
Sync | httpx |
BaobabApiCallAsyncHttpxTransport |
transports.httpx |
Async | httpx |
BaobabApiCallAsyncAiohttpTransport |
transports.aiohttp |
Async | aiohttp |
BaobabApiCallSyncUrllibTransport |
transports.urllib |
Sync | urllib.request (stdlib) |
Exemple avec requests :
from baobab_api_call.transports.requests.baobab_api_call_sync_requests_transport import (
BaobabApiCallSyncRequestsTransport,
)
transport = BaobabApiCallSyncRequestsTransport(timeout_seconds=15.0)
Les transports ne lèvent pas d’exception sur un code HTTP 4xx/5xx : c’est la couche réponse (raise_for_status, propriétés is_error, etc.) qui interprète le statut.
Configuration
BaobabApiCallClientConfig regroupe les paramètres partagés par le client :
from baobab_api_call.client.config.baobab_api_call_client_config import (
BaobabApiCallClientConfig,
)
from baobab_api_call.client.policies.baobab_api_call_retry_policy import (
BaobabApiCallRetryPolicy,
)
config = BaobabApiCallClientConfig(
base_url="https://api.example.com/v1",
default_headers={"Accept": "application/json", "User-Agent": "my-app/1.0"},
timeout_seconds=30.0,
auth_strategy=..., # voir section Authentification
retry_policy=BaobabApiCallRetryPolicy(max_attempts=3),
)
| Champ | Rôle |
|---|---|
base_url |
URL de base HTTP(S), normalisée à la construction |
default_headers |
En-têtes fusionnés sur chaque requête |
auth_strategy |
Stratégie d’authentification optionnelle |
timeout_seconds |
Délai global (défaut 30 s si omis) |
timeouts |
Configuration fine (BaobabApiCallTimeoutConfig) |
retry_policy |
Politique de nouvelles tentatives |
middlewares |
Tuple de middlewares sync |
async_middlewares |
Tuple de middlewares async |
json_serializer |
Sérialiseur JSON personnalisé |
logging_config |
Configuration de journalisation |
Authentification
Implémentez ou utilisez une stratégie conforme à BaobabApiCallAuthStrategy. Stratégies fournies :
Basic HTTP
from baobab_api_call.auth.baobab_api_call_basic_auth import BaobabApiCallBasicAuth
auth = BaobabApiCallBasicAuth(username="user", password="secret")
config = BaobabApiCallClientConfig(base_url="https://api.example.com", auth_strategy=auth)
Bearer
from baobab_api_call.auth.baobab_api_call_bearer_auth import BaobabApiCallBearerAuth
auth = BaobabApiCallBearerAuth(token="eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...")
Clé API
from baobab_api_call.auth.baobab_api_call_api_key_auth import BaobabApiCallApiKeyAuth
# En-tête X-API-Key (défaut)
auth = BaobabApiCallApiKeyAuth(api_key="my-key", header_name="X-API-Key")
# Ou schéma Authorization: ApiKey <clé>
auth = BaobabApiCallApiKeyAuth(api_key="my-key", scheme="ApiKey")
L’auth est appliquée après la fusion des en-têtes de la requête ; un en-tête Authorization existant est remplacé par la stratégie.
Retry et résilience
BaobabApiCallRetryPolicy contrôle les nouvelles tentatives en cas d’échec retryable :
from baobab_api_call.client.policies.baobab_api_call_retry_policy import (
BaobabApiCallRetryPolicy,
)
policy = BaobabApiCallRetryPolicy(
max_attempts=4,
retryable_status_codes=frozenset({429, 500, 502, 503, 504}),
backoff_initial=0.2,
backoff_factor=2.0,
backoff_max=30.0,
jitter=True,
)
Par défaut, les exceptions réseau / timeout du socle et les codes 429, 5xx listés ci-dessus sont retryables. Après épuisement des tentatives, le client lève BaobabApiCallRetryException.
Middlewares
Les middlewares enveloppent le transport sans modifier le client public.
Sync — protocole BaobabApiCallMiddleware :
before_request(request) -> requestafter_response(request, response) -> responseon_error(request, error) -> None
Async — protocole BaobabApiCallMiddlewareAsync (mêmes hooks, coroutines).
Enregistrement via la config :
config = BaobabApiCallClientConfig(
base_url="https://api.example.com",
middlewares=(my_sync_middleware,),
async_middlewares=(my_async_middleware,),
)
Les modèles BaobabApiCallRequest et BaobabApiCallResponse sont immuable : retournez une copie (dataclasses.replace) plutôt que de muter les champs.
Réponses HTTP
BaobabApiCallResponse expose notamment :
| Membre | Description |
|---|---|
status_code |
Code HTTP (100–599) |
content |
Corps en bytes |
text |
Corps décodé UTF-8 ou None |
headers |
Dictionnaire d’en-têtes |
json() |
Parse JSON (sérialiseur configurable) |
is_success |
2xx |
is_client_error / is_server_error |
4xx / 5xx |
is_error |
≥ 400 |
raise_for_status() |
Lève BaobabApiCallHttpException si erreur HTTP |
response = client.get("/resource/42")
if response.is_error:
response.raise_for_status()
data = response.json()
Exceptions
Hiérarchie principale (toutes dérivent de BaobabApiCallException) :
| Exception | Cas typique |
|---|---|
BaobabApiCallHttpException |
Erreur HTTP après raise_for_status() |
BaobabApiCallNetworkException |
Connexion, erreur transport |
BaobabApiCallTimeoutException |
Dépassement de délai |
BaobabApiCallSerializationException |
JSON invalide ou non sérialisable |
BaobabApiCallRetryException |
Tentatives de retry épuisées |
BaobabApiCallConfigException |
Configuration invalide |
Développement
Arborescence
src/baobab_api_call/
├── client/ # Clients sync/async, config, retry
├── transports/ # Implémentations HTTP
├── auth/ # Stratégies d'authentification
├── middleware/ # Chaînes de middlewares
├── requests/ # Modèle de requête
├── responses/ # Modèle de réponse
├── exceptions/ # Erreurs publiques
└── utilities/ # Métadonnées, redaction
tests/baobab_api_call/ # Tests unitaires (miroir de src/)
Qualité et tests
# Tests unitaires (sans réseau)
PYTHONPATH=src pytest tests/ -m "not integration"
# Couverture (seuil projet : 90 %)
PYTHONPATH=src coverage run -m pytest tests/ -m "not integration"
coverage report --fail-under=90
# Tests d'intégration (https://httpbin.org, réseau requis)
PYTHONPATH=src pytest tests/baobab_api_call/transports/ -m integration
# Linters
black --check src tests
flake8 src tests
pylint src/baobab_api_call --fail-under=8.5
mypy src/baobab_api_call
bandit -r src/baobab_api_call -ll
Build local
python -m build
# Artefacts dans dist/ : wheel + sdist
Publication et CI
Une release est déclenchée par un tag semver vMAJOR.MINOR.PATCH (ex. v1.0.0) via le workflow .github/workflows/release.yml :
git tag v1.0.0
git push origin v1.0.0
- Contrôles qualité (black, flake8, pylint, mypy, bandit)
- Tests unitaires + couverture ≥ 90 %
- Build wheel / sdist (+ SBOM CycloneDX)
- Publication PyPI (Trusted Publishing OIDC, environment
pypi) - Attachement des artefacts à la GitHub Release (signature Sigstore)
Les tests d’intégration httpbin ne sont pas exécutés dans ce pipeline (dépendance réseau externe).
Changelog
Voir CHANGELOG.md pour l’historique des versions (semver).
Licence
Ce projet est distribué sous licence MIT.
Copyright (c) 2026 baobabgit.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file baobab_api_call-1.0.0.tar.gz.
File metadata
- Download URL: baobab_api_call-1.0.0.tar.gz
- Upload date:
- Size: 85.2 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
a7fb8c3344f8f9537c480c80823d7195fc6eed11815ecab52ef5e31c7e7f111d
|
|
| MD5 |
5e1fd550cf2a4bd4377d6d1ecca10970
|
|
| BLAKE2b-256 |
ab2db1ca6cc04fec9839c07d55aafa634b81f8adcdb7dc6f7fdd07d5c46d445e
|
Provenance
The following attestation bundles were made for baobab_api_call-1.0.0.tar.gz:
Publisher:
release.yml on baobabgit/python-baobab-api-call
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
baobab_api_call-1.0.0.tar.gz -
Subject digest:
a7fb8c3344f8f9537c480c80823d7195fc6eed11815ecab52ef5e31c7e7f111d - Sigstore transparency entry: 1593277218
- Sigstore integration time:
-
Permalink:
baobabgit/python-baobab-api-call@ceb1b994c3023dd2cfd8b43f18282af5500ead42 -
Branch / Tag:
refs/tags/v1.0.0 - Owner: https://github.com/baobabgit
-
Access:
private
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@ceb1b994c3023dd2cfd8b43f18282af5500ead42 -
Trigger Event:
push
-
Statement type:
File details
Details for the file baobab_api_call-1.0.0-py3-none-any.whl.
File metadata
- Download URL: baobab_api_call-1.0.0-py3-none-any.whl
- Upload date:
- Size: 60.0 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
a26b49eb6adb3ed0df8347c60370bb8b174e2c9c9688429d86e50e6c6ae70df7
|
|
| MD5 |
10cfd1c75666cf52c15077979c96b11d
|
|
| BLAKE2b-256 |
8e7979064ed2f1190731868013fa543a34a685af0bc8ef47a86dbde4d0d8b94d
|
Provenance
The following attestation bundles were made for baobab_api_call-1.0.0-py3-none-any.whl:
Publisher:
release.yml on baobabgit/python-baobab-api-call
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
baobab_api_call-1.0.0-py3-none-any.whl -
Subject digest:
a26b49eb6adb3ed0df8347c60370bb8b174e2c9c9688429d86e50e6c6ae70df7 - Sigstore transparency entry: 1593277286
- Sigstore integration time:
-
Permalink:
baobabgit/python-baobab-api-call@ceb1b994c3023dd2cfd8b43f18282af5500ead42 -
Branch / Tag:
refs/tags/v1.0.0 - Owner: https://github.com/baobabgit
-
Access:
private
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@ceb1b994c3023dd2cfd8b43f18282af5500ead42 -
Trigger Event:
push
-
Statement type: