@@ -23,9 +23,10 @@ bodies are modelled as typed Pythonic abstractions instead.
2323- ** ` mypy --strict ` clean.** Every public signature is typed; no ` Any ` in
2424 public API; no unused ` # type: ignore ` comments.
2525- ** ` ruff ` and ` ruff format ` clean** (rule set in ` pyproject.toml ` ).
26- - ** No runtime dependencies.** ` core ` ships against the standard library only.
27- If you reach for a third-party package, stop — model it as an adapter
28- behind ` HttpClient ` or ` Serde ` .
26+ - ** No runtime dependencies, with one sanctioned exception: ` furl ` .** ` core `
27+ otherwise ships against the standard library only. ` furl ` powers
28+ ` http.common.Url ` parse/serialise; do not introduce any other third-party
29+ runtime dep — model it as an adapter behind ` HttpClient ` or ` Serde ` instead.
2930- ** Immutable data with slots.** Models are
3031 ` @dataclass(frozen=True, slots=True) ` ; mutate via ` dataclasses.replace ` or
3132 the ` with_* ` helpers. Builders are a Java idiom — Python's keyword and
@@ -64,26 +65,37 @@ bodies are modelled as typed Pythonic abstractions instead.
6465- ** Function-size cap: 50 lines.** Aim 10–25. Refactor when you push past.
6566- ** Commit style:** ` chore: ` for refactors/cleanup; ` feat: ` for new features;
6667 ` fix: ` for bug fixes; ` docs: ` for documentation-only changes.
68+ - ** MIT licence header on every ` .py ` file.** The project is MIT-licensed
69+ (` LICENSE.md ` at the root and in each package). Every Python source file —
70+ src and tests alike — starts with the two-line header before the module
71+ docstring:
72+
73+ ``` python
74+ # Copyright (c) 2026 dexpace and Omar Aljarrah.
75+ # Licensed under the MIT License. See LICENSE.md in the repository root for details.
76+ ```
6777
6878## Repository Layout
6979
70- The repo is a ` uv ` -managed workspace. Each member under ` packages/ ` is its own
71- distribution; PEP 420 namespace packages let them share the ` dexpace.sdk.* `
72- prefix. Commands run from the workspace root via ` uv run … ` — ` uv sync `
73- provisions the virtualenv with both packages installed in editable mode.
80+ The repo is a ` uv ` -managed workspace of five distributions. Each member under
81+ ` packages/ ` is its own distribution; PEP 420 namespace packages let them share
82+ the ` dexpace.sdk.* ` prefix. Commands run from the workspace root via
83+ ` uv run … ` — ` uv sync ` provisions the virtualenv with all packages installed
84+ in editable mode.
7485
7586```
7687python-sdk/
77- ├── LICENSE.md
88+ ├── LICENSE.md # MIT (also copied into each package)
7889├── README.md
7990├── CLAUDE.md
8091├── pyproject.toml # workspace root (uv workspace, dev deps,
8192│ # ruff/mypy/pytest config)
8293├── uv.lock
83- ├── .github/workflows/ci.yml # pytest + mypy + ruff on 3.12 and 3.13
84- ├── docs/ # cross-package documentation
94+ ├── .github/workflows/ci.yml # pytest + mypy + ruff, python matrix 3.8–3.13
95+ ├── docs/ # cross-package documentation: architecture,
96+ │ # auth, bodies, errors, http, pipelines
8597└── packages/
86- ├── dexpace-sdk-core/ # zero-dependency toolkit (no transports)
98+ ├── dexpace-sdk-core/ # toolkit (no transports); only runtime dep: furl
8799 │ ├── pyproject.toml
88100 │ ├── README.md
89101 │ ├── src/dexpace/sdk/core/
@@ -99,40 +111,49 @@ python-sdk/
99111 │ │ │ ├── context/ # CallContext, DispatchContext,
100112 │ │ │ │ # RequestContext, ExchangeContext,
101113 │ │ │ │ # ContextStore
102- │ │ │ └── auth/ # Credential, BearerTokenPolicy,
103- │ │ │ # BasicAuthPolicy, KeyCredentialPolicy
104- │ │ ├── pipeline/ # Pipeline, Policy ABC, RetryPolicy,
105- │ │ │ # LoggingPolicy, TracingPolicy, async twins
114+ │ │ │ └── auth/ # TokenCredential, BearerTokenPolicy,
115+ │ │ │ # BasicAuthPolicy, KeyCredentialPolicy,
116+ │ │ │ # ChallengeHandler (Basic/Digest/Composite),
117+ │ │ │ # AuthenticateChallenge, TokenCache
118+ │ │ ├── pipeline/ # Pipeline/AsyncPipeline, Policy/AsyncPolicy,
119+ │ │ │ │ # Stage, StagedPipelineBuilder, defaults,
120+ │ │ │ │ # sans-io + transport runners under the hood
121+ │ │ │ │
122+ │ │ │ ├── policies/ # retry, redirect, logging, tracing,
123+ │ │ │ │ # set_date (+ async twins)
124+ │ │ │ └── step/ # PipelineStep, StepMetadata, RetryConfig
106125 │ │ ├── client/ # HttpClient + AsyncHttpClient Protocols
126+ │ │ ├── config/ # Configuration
107127 │ │ ├── serde/ # Serde, Serializer, Deserializer Protocols
108128 │ │ ├── errors/ # SDK-level exception hierarchy
109- │ │ └── instrumentation/ # InstrumentationContext, Span, Tracer,
110- │ │ # TracingScope, noops
111- │ └── tests/ # pytest suite — http/, context/,
112- │ # pipeline/, auth/, serde/, sse/, errors/,
113- │ # instrumentation/
114- └── dexpace-sdk-http-stdlib/ # reference stdlib transports
115- ├── pyproject.toml # depends on dexpace-sdk-core
116- ├── README.md
117- ├── src/dexpace/sdk/http/stdlib/
118- │ ├── urllib_http_client.py # UrllibHttpClient (sync, blocking)
119- │ └── asyncio_http_client.py # AsyncioHttpClient (async, stdlib only)
120- └── tests/
129+ │ │ ├── instrumentation/ # InstrumentationContext, Span, Tracer,
130+ │ │ │ # TracingScope, noops
131+ │ │ └── util/ # clock, proxy helpers
132+ │ └── tests/ # pytest suite — auth/, config/, context/,
133+ │ # errors/, http/, instrumentation/,
134+ │ # pipeline/, serde/, sse/, util/
135+ ├── dexpace-sdk-http-stdlib/ # reference stdlib transports:
136+ │ │ # UrllibHttpClient, AsyncioHttpClient
137+ │ └── src/dexpace/sdk/http/stdlib/
138+ ├── dexpace-sdk-http-httpx/ # httpx transports (sync + async)
139+ │ └── src/dexpace/sdk/http/httpx/
140+ ├── dexpace-sdk-http-aiohttp/ # aiohttp transport (async)
141+ │ └── src/dexpace/sdk/http/aiohttp/
142+ └── dexpace-sdk-http-requests/ # requests transport (sync)
143+ └── src/dexpace/sdk/http/requests/
121144```
122145
123- Planned follow-up packages (not yet present): ` dexpace-sdk-http-httpx ` ,
124- ` dexpace-sdk-http-aiohttp ` , ` dexpace-sdk-http-requests ` . Each will live under
125- ` packages/ ` as a separate distribution, depend on ` dexpace-sdk-core ` , and
126- adapt its third-party HTTP library to the ` HttpClient ` / ` AsyncHttpClient `
127- Protocols. Namespace packaging (no ` __init__.py ` at ` src/dexpace/ ` ,
128- ` src/dexpace/sdk/ ` , or ` src/dexpace/sdk/http/ ` ) is mandatory for every
129- package so the ` dexpace.sdk ` prefix stays shared.
146+ Every transport package depends on ` dexpace-sdk-core ` and adapts its HTTP
147+ library to the ` HttpClient ` / ` AsyncHttpClient ` Protocols. Namespace
148+ packaging (no ` __init__.py ` at ` src/dexpace/ ` , ` src/dexpace/sdk/ ` , or
149+ ` src/dexpace/sdk/http/ ` ) is mandatory for every package so the ` dexpace.sdk `
150+ prefix stays shared.
130151
131152### Common commands (run from the workspace root)
132153
133154``` bash
134155uv sync # install workspace + dev tools
135- uv run pytest -q # walk both packages' test suites
156+ uv run pytest -q # walk all five packages' test suites
136157uv run mypy --strict # type-check everything in `files = [...]`
137158uv run ruff check # lint
138159uv run ruff format --check # formatting gate
@@ -162,13 +183,18 @@ Layered, bottom-up:
162183 → ` ExchangeContext ` , all carrying an ` InstrumentationContext ` for tracing
163184 correlation. The thread-safe ` ContextStore ` is keyed by trace id; entries
164185 evict on ` CallContext.close() ` .
165- 4 . ** ` pipeline/step/PipelineStep ` ** — a Protocol with signature
166- ` (input, context) -> output ` . Steps compose into chains; the provisional
167- ` RetryableStep ` adds a retry hook (M2 replaces it with a ` Policy ` ABC
168- that wraps the downstream chain — see ` to-implement.md ` ). ` StepMetadata `
169- and ` RetryConfig ` provide optional configuration.
186+ 4 . ** ` pipeline ` ** — ` Policy ` (and ` AsyncPolicy ` ) wrap the downstream chain;
187+ ` Pipeline ` / ` AsyncPipeline ` run an ordered set of policies grouped into
188+ ` Stage ` s via ` StagedPipelineBuilder ` . Shipped policies: retry, redirect,
189+ logging, tracing, set-date (each with an async twin under
190+ ` pipeline/policies/ ` ). ` default_pipeline() ` / ` default_async_pipeline() `
191+ assemble the standard stack. The lower-level ` pipeline/step/PipelineStep `
192+ Protocol (` (input, context) -> output ` ) plus ` StepMetadata ` / ` RetryConfig `
193+ remain for custom composition.
1701945 . ** ` client/HttpClient ` ** — single-method Protocol
171- (` execute(request) -> Response ` ). Transport is ** not** provided by ` core ` .
195+ (` execute(request) -> Response ` ). Transport is ** not** provided by ` core ` ;
196+ the ` dexpace-sdk-http-* ` packages (stdlib, httpx, aiohttp, requests) each
197+ adapt one HTTP library to the Protocol.
172198
173199## Things That Will Bite You
174200
0 commit comments