python-sendparcel 0.1.1__tar.gz → 0.3.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- python_sendparcel-0.3.0/CHANGELOG.md +71 -0
- {python_sendparcel-0.1.1 → python_sendparcel-0.3.0}/PKG-INFO +45 -37
- {python_sendparcel-0.1.1 → python_sendparcel-0.3.0}/README.md +24 -12
- {python_sendparcel-0.1.1 → python_sendparcel-0.3.0}/docs/provider-authoring.md +38 -18
- {python_sendparcel-0.1.1 → python_sendparcel-0.3.0}/pyproject.toml +27 -24
- {python_sendparcel-0.1.1 → python_sendparcel-0.3.0}/src/sendparcel/__init__.py +15 -12
- python_sendparcel-0.3.0/src/sendparcel/batch.py +305 -0
- python_sendparcel-0.3.0/src/sendparcel/concurrent.py +71 -0
- {python_sendparcel-0.1.1 → python_sendparcel-0.3.0}/src/sendparcel/enums.py +1 -0
- python_sendparcel-0.3.0/src/sendparcel/factory.py +33 -0
- python_sendparcel-0.3.0/src/sendparcel/flow.py +268 -0
- python_sendparcel-0.3.0/src/sendparcel/fsm.py +106 -0
- python_sendparcel-0.3.0/src/sendparcel/logging.py +137 -0
- python_sendparcel-0.3.0/src/sendparcel/protocols.py +75 -0
- python_sendparcel-0.3.0/src/sendparcel/provider.py +242 -0
- {python_sendparcel-0.1.1 → python_sendparcel-0.3.0}/src/sendparcel/providers/dummy.py +7 -20
- {python_sendparcel-0.1.1 → python_sendparcel-0.3.0}/src/sendparcel/registry.py +69 -28
- {python_sendparcel-0.1.1 → python_sendparcel-0.3.0}/src/sendparcel/types.py +31 -4
- {python_sendparcel-0.1.1 → python_sendparcel-0.3.0}/tests/conftest.py +54 -1
- python_sendparcel-0.3.0/tests/test_batch.py +464 -0
- {python_sendparcel-0.1.1 → python_sendparcel-0.3.0}/tests/test_builtin_dummy_provider.py +32 -14
- python_sendparcel-0.3.0/tests/test_concurrent.py +167 -0
- {python_sendparcel-0.1.1 → python_sendparcel-0.3.0}/tests/test_enums.py +19 -0
- {python_sendparcel-0.1.1 → python_sendparcel-0.3.0}/tests/test_flow.py +106 -53
- {python_sendparcel-0.1.1 → python_sendparcel-0.3.0}/tests/test_fsm.py +5 -5
- {python_sendparcel-0.1.1 → python_sendparcel-0.3.0}/tests/test_integration.py +22 -24
- {python_sendparcel-0.1.1 → python_sendparcel-0.3.0}/tests/test_protocols.py +32 -0
- python_sendparcel-0.3.0/tests/test_provider_enhancements.py +376 -0
- {python_sendparcel-0.1.1 → python_sendparcel-0.3.0}/tests/test_public_api.py +8 -4
- {python_sendparcel-0.1.1 → python_sendparcel-0.3.0}/tests/test_registry.py +11 -0
- {python_sendparcel-0.1.1 → python_sendparcel-0.3.0}/tests/test_types.py +10 -0
- python_sendparcel-0.1.1/CHANGELOG.md +0 -20
- python_sendparcel-0.1.1/src/sendparcel/flow.py +0 -193
- python_sendparcel-0.1.1/src/sendparcel/fsm.py +0 -99
- python_sendparcel-0.1.1/src/sendparcel/protocols.py +0 -26
- python_sendparcel-0.1.1/src/sendparcel/provider.py +0 -92
- python_sendparcel-0.1.1/src/sendparcel/validators.py +0 -21
- python_sendparcel-0.1.1/tests/test_validators.py +0 -66
- {python_sendparcel-0.1.1 → python_sendparcel-0.3.0}/.github/workflows/ci.yml +0 -0
- {python_sendparcel-0.1.1 → python_sendparcel-0.3.0}/.github/workflows/ecosystem-smoke.yml +0 -0
- {python_sendparcel-0.1.1 → python_sendparcel-0.3.0}/.github/workflows/release.yml +0 -0
- {python_sendparcel-0.1.1 → python_sendparcel-0.3.0}/.gitignore +0 -0
- {python_sendparcel-0.1.1 → python_sendparcel-0.3.0}/.pre-commit-config.yaml +0 -0
- {python_sendparcel-0.1.1 → python_sendparcel-0.3.0}/CONTRIBUTING.md +0 -0
- {python_sendparcel-0.1.1 → python_sendparcel-0.3.0}/LICENSE +0 -0
- {python_sendparcel-0.1.1 → python_sendparcel-0.3.0}/docs/Makefile +0 -0
- {python_sendparcel-0.1.1 → python_sendparcel-0.3.0}/docs/compatibility-matrix.md +0 -0
- {python_sendparcel-0.1.1 → python_sendparcel-0.3.0}/docs/conf.py +0 -0
- {python_sendparcel-0.1.1 → python_sendparcel-0.3.0}/docs/getting-started.md +0 -0
- {python_sendparcel-0.1.1 → python_sendparcel-0.3.0}/docs/index.md +0 -0
- {python_sendparcel-0.1.1 → python_sendparcel-0.3.0}/docs/installation-matrix.md +0 -0
- {python_sendparcel-0.1.1 → python_sendparcel-0.3.0}/docs/plans/2026-02-17-order-optional.md +0 -0
- {python_sendparcel-0.1.1 → python_sendparcel-0.3.0}/docs/release-policy.md +0 -0
- {python_sendparcel-0.1.1 → python_sendparcel-0.3.0}/docs/requirements.txt +0 -0
- {python_sendparcel-0.1.1 → python_sendparcel-0.3.0}/src/sendparcel/exceptions.py +0 -0
- {python_sendparcel-0.1.1 → python_sendparcel-0.3.0}/src/sendparcel/providers/__init__.py +0 -0
- {python_sendparcel-0.1.1 → python_sendparcel-0.3.0}/src/sendparcel/py.typed +0 -0
- {python_sendparcel-0.1.1 → python_sendparcel-0.3.0}/tests/test_exceptions.py +0 -0
- {python_sendparcel-0.1.1 → python_sendparcel-0.3.0}/tests/test_provider.py +0 -0
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to this project will be documented in this file.
|
|
4
|
+
|
|
5
|
+
The format is based on [Keep a Changelog](https://keepachangelog.com/),
|
|
6
|
+
and this project adheres to [Semantic Versioning](https://semver.org/).
|
|
7
|
+
|
|
8
|
+
## [0.3.0] - 2026-07-04
|
|
9
|
+
|
|
10
|
+
### Changed (breaking)
|
|
11
|
+
|
|
12
|
+
- Unified the five capability trait ABCs (`LabelProvider`,
|
|
13
|
+
`PushCallbackProvider`, `PullStatusProvider`, `CancellableProvider`)
|
|
14
|
+
into a single `BaseProvider`; unsupported capabilities raise
|
|
15
|
+
`ProviderCapabilityError` by default
|
|
16
|
+
- Removed the validator framework; required config fields declared in
|
|
17
|
+
`config_schema` are now validated automatically at provider
|
|
18
|
+
construction
|
|
19
|
+
- Callbacks are handled through `CallbackContext`
|
|
20
|
+
(`verify_callback(ctx)` / `handle_callback(ctx)`)
|
|
21
|
+
- Providers receive their HTTP transport via `transport_factory`
|
|
22
|
+
injection (`create_provider` wires it)
|
|
23
|
+
|
|
24
|
+
### Fixed
|
|
25
|
+
|
|
26
|
+
- Missing `Any` import in `sendparcel.types` made
|
|
27
|
+
`CallbackContext` annotations unresolvable at runtime
|
|
28
|
+
(`typing.get_type_hints` raised `NameError`)
|
|
29
|
+
- `ShipmentBatch` created without an explicit registry now uses the
|
|
30
|
+
global registry singleton, consistent with `ShipmentFlow`
|
|
31
|
+
- `ConcurrentExecutor` now uses anyio primitives and works under the
|
|
32
|
+
trio backend (previously hardcoded asyncio)
|
|
33
|
+
- Lint and type-check regressions; `ruff check`, `ruff format --check`,
|
|
34
|
+
`ty check`, and `mypy --strict src` all pass again
|
|
35
|
+
- README quick-start example was missing the `update_fields` repository
|
|
36
|
+
method required by `create_label`
|
|
37
|
+
- Provider-authoring guide rewritten for the unified `BaseProvider`
|
|
38
|
+
API (previous examples raised `ImportError`)
|
|
39
|
+
|
|
40
|
+
### Added
|
|
41
|
+
|
|
42
|
+
- `PluginRegistry.slugs()` — public, thread-safe accessor for all
|
|
43
|
+
registered provider slugs
|
|
44
|
+
|
|
45
|
+
### Removed
|
|
46
|
+
|
|
47
|
+
- `fastapi` and `litestar` extras from the README (not published)
|
|
48
|
+
- Dead code: `ShipmentStatusResponse` alias, logging compat aliases
|
|
49
|
+
|
|
50
|
+
## [0.2.0] - 2025-06-05
|
|
51
|
+
|
|
52
|
+
### Added
|
|
53
|
+
|
|
54
|
+
- `CallbackContext` for webhook callback handling
|
|
55
|
+
- Transport injection via factory pattern
|
|
56
|
+
- `ConcurrentExecutor` generic concurrency primitive
|
|
57
|
+
- Structured JSON logging helpers
|
|
58
|
+
|
|
59
|
+
## [0.1.0] - 2025-02-16
|
|
60
|
+
|
|
61
|
+
### Added
|
|
62
|
+
|
|
63
|
+
- Provider protocol and plugin registry with entry-point discovery
|
|
64
|
+
- Shipment domain types (`AddressInfo`, `ParcelInfo`, `ShipmentCreateResult`, `LabelInfo`)
|
|
65
|
+
- Finite state machine for shipment lifecycle (`ShipmentStatus` enum with 9 states)
|
|
66
|
+
- Framework-agnostic `ShipmentFlow` orchestrator
|
|
67
|
+
- `BaseProvider` abstract class for provider plugins
|
|
68
|
+
- Built-in `DummyProvider` for testing and development
|
|
69
|
+
- Global and per-provider validator support
|
|
70
|
+
- Async-first runtime powered by `anyio`
|
|
71
|
+
- Full test suite (157 tests)
|
|
@@ -1,12 +1,11 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: python-sendparcel
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.3.0
|
|
4
4
|
Summary: Framework-agnostic parcel shipping core for Python.
|
|
5
|
-
Project-URL: Homepage, https://github.com/sendparcel/python-sendparcel
|
|
6
|
-
Project-URL:
|
|
7
|
-
Project-URL:
|
|
8
|
-
Project-URL:
|
|
9
|
-
Project-URL: Issue Tracker, https://github.com/sendparcel/python-sendparcel/issues
|
|
5
|
+
Project-URL: Homepage, https://github.com/python-sendparcel/python-sendparcel
|
|
6
|
+
Project-URL: Repository, https://github.com/python-sendparcel/python-sendparcel
|
|
7
|
+
Project-URL: Changelog, https://github.com/python-sendparcel/python-sendparcel/blob/main/CHANGELOG.md
|
|
8
|
+
Project-URL: Issue Tracker, https://github.com/python-sendparcel/python-sendparcel/issues
|
|
10
9
|
Author-email: Dominik Kozaczko <dominik@kozaczko.info>
|
|
11
10
|
License: MIT
|
|
12
11
|
License-File: LICENSE
|
|
@@ -24,14 +23,13 @@ Requires-Python: >=3.12
|
|
|
24
23
|
Requires-Dist: anyio>=4.0
|
|
25
24
|
Requires-Dist: httpx>=0.27.0
|
|
26
25
|
Provides-Extra: all
|
|
27
|
-
Requires-Dist: django-sendparcel>=0.
|
|
28
|
-
Requires-Dist:
|
|
29
|
-
Requires-Dist:
|
|
30
|
-
Requires-Dist: python-sendparcel-
|
|
31
|
-
Requires-Dist: python-sendparcel-
|
|
32
|
-
Requires-Dist: python-sendparcel-inpost>=0.1.0; extra == 'all'
|
|
26
|
+
Requires-Dist: django-sendparcel>=0.3.0; extra == 'all'
|
|
27
|
+
Requires-Dist: python-sendparcel-cli>=0.2.0; extra == 'all'
|
|
28
|
+
Requires-Dist: python-sendparcel-dpdpl>=0.1.1; extra == 'all'
|
|
29
|
+
Requires-Dist: python-sendparcel-inpost>=0.3.0; extra == 'all'
|
|
30
|
+
Requires-Dist: python-sendparcel-orlenpaczka>=0.0.1; extra == 'all'
|
|
33
31
|
Provides-Extra: cli
|
|
34
|
-
Requires-Dist: python-sendparcel-cli>=0.
|
|
32
|
+
Requires-Dist: python-sendparcel-cli>=0.2.0; extra == 'cli'
|
|
35
33
|
Provides-Extra: dev
|
|
36
34
|
Requires-Dist: pre-commit-hooks>=6.0.0; extra == 'dev'
|
|
37
35
|
Requires-Dist: pre-commit>=4.0; extra == 'dev'
|
|
@@ -39,32 +37,30 @@ Requires-Dist: pytest-asyncio>=0.24.0; extra == 'dev'
|
|
|
39
37
|
Requires-Dist: pytest-cov>=5.0; extra == 'dev'
|
|
40
38
|
Requires-Dist: pytest>=8.0; extra == 'dev'
|
|
41
39
|
Requires-Dist: ruff>=0.9.0; extra == 'dev'
|
|
40
|
+
Requires-Dist: trio>=0.27; extra == 'dev'
|
|
42
41
|
Requires-Dist: ty>=0.0.16; extra == 'dev'
|
|
43
42
|
Provides-Extra: django
|
|
44
|
-
Requires-Dist: django-sendparcel>=0.
|
|
43
|
+
Requires-Dist: django-sendparcel>=0.3.0; extra == 'django'
|
|
45
44
|
Provides-Extra: dpdpl
|
|
46
|
-
Requires-Dist: python-sendparcel-dpdpl>=0.1.
|
|
45
|
+
Requires-Dist: python-sendparcel-dpdpl>=0.1.1; extra == 'dpdpl'
|
|
47
46
|
Provides-Extra: dummy
|
|
48
|
-
Provides-Extra: fastapi
|
|
49
|
-
Requires-Dist: fastapi-sendparcel>=0.1.0; extra == 'fastapi'
|
|
50
47
|
Provides-Extra: frameworks
|
|
51
|
-
Requires-Dist: django-sendparcel>=0.
|
|
52
|
-
Requires-Dist: fastapi-sendparcel>=0.1.0; extra == 'frameworks'
|
|
53
|
-
Requires-Dist: litestar-sendparcel>=0.1.0; extra == 'frameworks'
|
|
48
|
+
Requires-Dist: django-sendparcel>=0.3.0; extra == 'frameworks'
|
|
54
49
|
Provides-Extra: inpost
|
|
55
|
-
Requires-Dist: python-sendparcel-inpost>=0.
|
|
56
|
-
Provides-Extra:
|
|
57
|
-
Requires-Dist:
|
|
50
|
+
Requires-Dist: python-sendparcel-inpost>=0.3.0; extra == 'inpost'
|
|
51
|
+
Provides-Extra: orlenpaczka
|
|
52
|
+
Requires-Dist: python-sendparcel-orlenpaczka>=0.0.1; extra == 'orlenpaczka'
|
|
58
53
|
Provides-Extra: providers
|
|
59
|
-
Requires-Dist: python-sendparcel-dpdpl>=0.1.
|
|
60
|
-
Requires-Dist: python-sendparcel-inpost>=0.
|
|
54
|
+
Requires-Dist: python-sendparcel-dpdpl>=0.1.1; extra == 'providers'
|
|
55
|
+
Requires-Dist: python-sendparcel-inpost>=0.3.0; extra == 'providers'
|
|
56
|
+
Requires-Dist: python-sendparcel-orlenpaczka>=0.0.1; extra == 'providers'
|
|
61
57
|
Description-Content-Type: text/markdown
|
|
62
58
|
|
|
63
59
|
# python-sendparcel
|
|
64
60
|
|
|
65
61
|
Framework-agnostic parcel shipping core for Python.
|
|
66
62
|
|
|
67
|
-
> Alpha notice: `0.
|
|
63
|
+
> Alpha notice: `0.3.0` is still unstable. The API can change fast because the
|
|
68
64
|
> ecosystem is still being cleaned up.
|
|
69
65
|
|
|
70
66
|
## What it is
|
|
@@ -129,6 +125,12 @@ class InMemoryRepository:
|
|
|
129
125
|
self._store[shipment.id] = shipment
|
|
130
126
|
return shipment
|
|
131
127
|
|
|
128
|
+
async def update_fields(self, shipment_id: str, **fields) -> MyShipment:
|
|
129
|
+
shipment = self._store[shipment_id]
|
|
130
|
+
for key, value in fields.items():
|
|
131
|
+
setattr(shipment, key, value)
|
|
132
|
+
return shipment
|
|
133
|
+
|
|
132
134
|
|
|
133
135
|
async def main() -> None:
|
|
134
136
|
flow = ShipmentFlow(repository=InMemoryRepository())
|
|
@@ -165,15 +167,20 @@ anyio.run(main)
|
|
|
165
167
|
|
|
166
168
|
## Provider model
|
|
167
169
|
|
|
168
|
-
|
|
170
|
+
All providers subclass `BaseProvider`; capability methods raise
|
|
171
|
+
`ProviderCapabilityError` unless overridden:
|
|
172
|
+
|
|
173
|
+
- `BaseProvider.create_shipment(...)` returns `ShipmentCreateResult` (required).
|
|
169
174
|
- `BaseProvider.confirmation_method` defaults to `ConfirmationMethod.NONE`.
|
|
170
|
-
- `
|
|
171
|
-
- `
|
|
172
|
-
|
|
173
|
-
- `
|
|
175
|
+
- `BaseProvider.create_label(...)` returns `LabelInfo`.
|
|
176
|
+
- `BaseProvider.verify_callback(ctx)` / `handle_callback(ctx)` return
|
|
177
|
+
`None` / `ShipmentUpdateResult`.
|
|
178
|
+
- `BaseProvider.fetch_shipment_status(...)` returns `ShipmentUpdateResult`.
|
|
179
|
+
- `BaseProvider.cancel_shipment(...)` returns `bool`.
|
|
174
180
|
|
|
175
|
-
Use `ConfirmationMethod.PUSH` only
|
|
176
|
-
`ConfirmationMethod.PULL` only
|
|
181
|
+
Use `ConfirmationMethod.PUSH` only when the callback methods are overridden
|
|
182
|
+
and `ConfirmationMethod.PULL` only when `fetch_shipment_status` is. See
|
|
183
|
+
`docs/provider-authoring.md` for the full guide.
|
|
177
184
|
|
|
178
185
|
The core owns shipment state transitions. Providers translate carrier responses
|
|
179
186
|
into normalized results.
|
|
@@ -193,10 +200,9 @@ uv add python-sendparcel
|
|
|
193
200
|
## Extras
|
|
194
201
|
|
|
195
202
|
- `django`
|
|
196
|
-
- `fastapi`
|
|
197
|
-
- `litestar`
|
|
198
203
|
- `inpost`
|
|
199
204
|
- `dpdpl`
|
|
205
|
+
- `orlenpaczka`
|
|
200
206
|
- `cli`
|
|
201
207
|
- `frameworks`
|
|
202
208
|
- `providers`
|
|
@@ -207,6 +213,8 @@ uv add python-sendparcel
|
|
|
207
213
|
```bash
|
|
208
214
|
uv sync --extra dev
|
|
209
215
|
uv run pytest
|
|
210
|
-
uv run ruff check
|
|
211
|
-
uv run
|
|
216
|
+
uv run ruff check .
|
|
217
|
+
uv run ruff format --check .
|
|
218
|
+
uv run ty check
|
|
219
|
+
uv run mypy src
|
|
212
220
|
```
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
Framework-agnostic parcel shipping core for Python.
|
|
4
4
|
|
|
5
|
-
> Alpha notice: `0.
|
|
5
|
+
> Alpha notice: `0.3.0` is still unstable. The API can change fast because the
|
|
6
6
|
> ecosystem is still being cleaned up.
|
|
7
7
|
|
|
8
8
|
## What it is
|
|
@@ -67,6 +67,12 @@ class InMemoryRepository:
|
|
|
67
67
|
self._store[shipment.id] = shipment
|
|
68
68
|
return shipment
|
|
69
69
|
|
|
70
|
+
async def update_fields(self, shipment_id: str, **fields) -> MyShipment:
|
|
71
|
+
shipment = self._store[shipment_id]
|
|
72
|
+
for key, value in fields.items():
|
|
73
|
+
setattr(shipment, key, value)
|
|
74
|
+
return shipment
|
|
75
|
+
|
|
70
76
|
|
|
71
77
|
async def main() -> None:
|
|
72
78
|
flow = ShipmentFlow(repository=InMemoryRepository())
|
|
@@ -103,15 +109,20 @@ anyio.run(main)
|
|
|
103
109
|
|
|
104
110
|
## Provider model
|
|
105
111
|
|
|
106
|
-
|
|
112
|
+
All providers subclass `BaseProvider`; capability methods raise
|
|
113
|
+
`ProviderCapabilityError` unless overridden:
|
|
114
|
+
|
|
115
|
+
- `BaseProvider.create_shipment(...)` returns `ShipmentCreateResult` (required).
|
|
107
116
|
- `BaseProvider.confirmation_method` defaults to `ConfirmationMethod.NONE`.
|
|
108
|
-
- `
|
|
109
|
-
- `
|
|
110
|
-
|
|
111
|
-
- `
|
|
117
|
+
- `BaseProvider.create_label(...)` returns `LabelInfo`.
|
|
118
|
+
- `BaseProvider.verify_callback(ctx)` / `handle_callback(ctx)` return
|
|
119
|
+
`None` / `ShipmentUpdateResult`.
|
|
120
|
+
- `BaseProvider.fetch_shipment_status(...)` returns `ShipmentUpdateResult`.
|
|
121
|
+
- `BaseProvider.cancel_shipment(...)` returns `bool`.
|
|
112
122
|
|
|
113
|
-
Use `ConfirmationMethod.PUSH` only
|
|
114
|
-
`ConfirmationMethod.PULL` only
|
|
123
|
+
Use `ConfirmationMethod.PUSH` only when the callback methods are overridden
|
|
124
|
+
and `ConfirmationMethod.PULL` only when `fetch_shipment_status` is. See
|
|
125
|
+
`docs/provider-authoring.md` for the full guide.
|
|
115
126
|
|
|
116
127
|
The core owns shipment state transitions. Providers translate carrier responses
|
|
117
128
|
into normalized results.
|
|
@@ -131,10 +142,9 @@ uv add python-sendparcel
|
|
|
131
142
|
## Extras
|
|
132
143
|
|
|
133
144
|
- `django`
|
|
134
|
-
- `fastapi`
|
|
135
|
-
- `litestar`
|
|
136
145
|
- `inpost`
|
|
137
146
|
- `dpdpl`
|
|
147
|
+
- `orlenpaczka`
|
|
138
148
|
- `cli`
|
|
139
149
|
- `frameworks`
|
|
140
150
|
- `providers`
|
|
@@ -145,6 +155,8 @@ uv add python-sendparcel
|
|
|
145
155
|
```bash
|
|
146
156
|
uv sync --extra dev
|
|
147
157
|
uv run pytest
|
|
148
|
-
uv run ruff check
|
|
149
|
-
uv run
|
|
158
|
+
uv run ruff check .
|
|
159
|
+
uv run ruff format --check .
|
|
160
|
+
uv run ty check
|
|
161
|
+
uv run mypy src
|
|
150
162
|
```
|
|
@@ -17,19 +17,35 @@ mycarrier = "sendparcel_mycarrier.provider:MyCarrierProvider"
|
|
|
17
17
|
## Provider contract
|
|
18
18
|
|
|
19
19
|
Every provider must subclass `sendparcel.provider.BaseProvider`.
|
|
20
|
+
There are no capability trait classes — `BaseProvider` declares every
|
|
21
|
+
capability method, and the unsupported ones raise
|
|
22
|
+
`ProviderCapabilityError` by default. Override only what the carrier
|
|
23
|
+
actually supports.
|
|
20
24
|
|
|
21
25
|
### Required
|
|
22
26
|
|
|
23
27
|
- `create_shipment(sender_address, receiver_address, parcels, **kwargs) -> ShipmentCreateResult`
|
|
24
28
|
|
|
25
|
-
### Optional
|
|
29
|
+
### Optional capability overrides
|
|
26
30
|
|
|
27
|
-
| Capability |
|
|
31
|
+
| Capability | Method to override | Return value |
|
|
28
32
|
|---|---|---|
|
|
29
|
-
| Labels | `
|
|
30
|
-
| Webhooks | `
|
|
31
|
-
| Polling | `
|
|
32
|
-
| Cancellation | `
|
|
33
|
+
| Labels | `create_label(**kwargs)` | `LabelInfo` |
|
|
34
|
+
| Webhooks | `verify_callback(ctx)` + `handle_callback(ctx)` | `None` / `ShipmentUpdateResult` |
|
|
35
|
+
| Polling | `fetch_shipment_status(**kwargs)` | `ShipmentUpdateResult` |
|
|
36
|
+
| Cancellation | `cancel_shipment(**kwargs)` | `bool` |
|
|
37
|
+
|
|
38
|
+
Webhook methods receive a `sendparcel.types.CallbackContext` carrying
|
|
39
|
+
`shipment_id`, `payload`, `headers`, `source_ip`, and `raw_body`.
|
|
40
|
+
|
|
41
|
+
### Transport
|
|
42
|
+
|
|
43
|
+
Providers that talk HTTP declare a `transport_factory` classvar
|
|
44
|
+
(`transport_factory(**config) -> transport`). The factory is called by
|
|
45
|
+
`sendparcel.factory.create_provider`, and the built transport is
|
|
46
|
+
available inside the provider via `self._get_client()`. Configuration
|
|
47
|
+
fields are declared in `config_schema`; required fields are validated
|
|
48
|
+
automatically at construction time.
|
|
33
49
|
|
|
34
50
|
## Important rule
|
|
35
51
|
|
|
@@ -48,18 +64,20 @@ That means callback and polling implementations should translate carrier payload
|
|
|
48
64
|
`BaseProvider.confirmation_method` defaults to `ConfirmationMethod.NONE`.
|
|
49
65
|
|
|
50
66
|
- `NONE` - the provider does not expose shipment updates.
|
|
51
|
-
- `PUSH` - the provider must
|
|
52
|
-
|
|
67
|
+
- `PUSH` - the provider must override `verify_callback` and
|
|
68
|
+
`handle_callback`.
|
|
69
|
+
- `PULL` - the provider must override `fetch_shipment_status`.
|
|
53
70
|
|
|
54
|
-
Do not declare `PUSH` or `PULL` unless the provider actually
|
|
55
|
-
matching capability
|
|
71
|
+
Do not declare `PUSH` or `PULL` unless the provider actually overrides the
|
|
72
|
+
matching capability methods.
|
|
56
73
|
|
|
57
74
|
## Minimal example
|
|
58
75
|
|
|
59
76
|
```python
|
|
60
77
|
from typing import Any, ClassVar
|
|
61
78
|
|
|
62
|
-
from sendparcel.
|
|
79
|
+
from sendparcel.enums import LabelFormat
|
|
80
|
+
from sendparcel.provider import BaseProvider
|
|
63
81
|
from sendparcel.types import (
|
|
64
82
|
AddressInfo,
|
|
65
83
|
LabelInfo,
|
|
@@ -68,7 +86,7 @@ from sendparcel.types import (
|
|
|
68
86
|
)
|
|
69
87
|
|
|
70
88
|
|
|
71
|
-
class MyCarrierProvider(BaseProvider
|
|
89
|
+
class MyCarrierProvider(BaseProvider):
|
|
72
90
|
slug: ClassVar[str] = "mycarrier"
|
|
73
91
|
display_name: ClassVar[str] = "My Carrier"
|
|
74
92
|
|
|
@@ -87,7 +105,7 @@ class MyCarrierProvider(BaseProvider, LabelProvider):
|
|
|
87
105
|
|
|
88
106
|
async def create_label(self, **kwargs: Any) -> LabelInfo:
|
|
89
107
|
return LabelInfo(
|
|
90
|
-
format=
|
|
108
|
+
format=LabelFormat.PDF,
|
|
91
109
|
url="https://carrier.example/labels/TRACK-123.pdf",
|
|
92
110
|
)
|
|
93
111
|
```
|
|
@@ -95,16 +113,18 @@ class MyCarrierProvider(BaseProvider, LabelProvider):
|
|
|
95
113
|
## Callback and polling example
|
|
96
114
|
|
|
97
115
|
```python
|
|
98
|
-
from sendparcel.
|
|
99
|
-
from sendparcel.types import ShipmentUpdateResult
|
|
116
|
+
from sendparcel.enums import ConfirmationMethod
|
|
117
|
+
from sendparcel.types import CallbackContext, ShipmentUpdateResult
|
|
118
|
+
|
|
100
119
|
|
|
120
|
+
class StatusProvider(MyCarrierProvider):
|
|
121
|
+
confirmation_method = ConfirmationMethod.PUSH
|
|
101
122
|
|
|
102
|
-
|
|
103
|
-
async def verify_callback(self, data, headers, **kwargs) -> None:
|
|
123
|
+
async def verify_callback(self, ctx: CallbackContext) -> None:
|
|
104
124
|
return None
|
|
105
125
|
|
|
106
126
|
async def handle_callback(
|
|
107
|
-
self,
|
|
127
|
+
self, ctx: CallbackContext
|
|
108
128
|
) -> ShipmentUpdateResult:
|
|
109
129
|
return ShipmentUpdateResult(status="in_transit")
|
|
110
130
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "python-sendparcel"
|
|
3
|
-
version = "0.
|
|
3
|
+
version = "0.3.0"
|
|
4
4
|
description = "Framework-agnostic parcel shipping core for Python."
|
|
5
5
|
readme = "README.md"
|
|
6
6
|
license = { text = "MIT" }
|
|
@@ -28,36 +28,39 @@ dev = [
|
|
|
28
28
|
"pytest-asyncio>=0.24.0",
|
|
29
29
|
"pytest-cov>=5.0",
|
|
30
30
|
"ruff>=0.9.0",
|
|
31
|
+
"trio>=0.27",
|
|
31
32
|
"ty>=0.0.16",
|
|
32
33
|
]
|
|
33
34
|
dummy = []
|
|
34
|
-
django = ["django-sendparcel>=0.
|
|
35
|
-
fastapi = ["fastapi-sendparcel>=0.1.0"]
|
|
36
|
-
litestar = ["litestar-sendparcel>=0.1.0"]
|
|
37
|
-
inpost = ["python-sendparcel-inpost>=0.
|
|
38
|
-
dpdpl = ["python-sendparcel-dpdpl>=0.1.
|
|
39
|
-
|
|
40
|
-
|
|
35
|
+
django = ["django-sendparcel>=0.3.0"]
|
|
36
|
+
# fastapi = ["fastapi-sendparcel>=0.1.0"]
|
|
37
|
+
# litestar = ["litestar-sendparcel>=0.1.0"]
|
|
38
|
+
inpost = ["python-sendparcel-inpost>=0.3.0"]
|
|
39
|
+
dpdpl = ["python-sendparcel-dpdpl>=0.1.1"]
|
|
40
|
+
orlenpaczka = ["python-sendparcel-orlenpaczka>=0.0.1"]
|
|
41
|
+
cli = ["python-sendparcel-cli>=0.2.0"]
|
|
42
|
+
providers = ["python-sendparcel-inpost>=0.3.0", "python-sendparcel-dpdpl>=0.1.1", "python-sendparcel-orlenpaczka>=0.0.1"]
|
|
41
43
|
frameworks = [
|
|
42
|
-
"django-sendparcel>=0.
|
|
43
|
-
"fastapi-sendparcel>=0.1.0",
|
|
44
|
-
"litestar-sendparcel>=0.1.0",
|
|
44
|
+
"django-sendparcel>=0.3.0",
|
|
45
|
+
# "fastapi-sendparcel>=0.1.0",
|
|
46
|
+
# "litestar-sendparcel>=0.1.0",
|
|
45
47
|
]
|
|
46
48
|
all = [
|
|
47
|
-
"django-sendparcel>=0.
|
|
48
|
-
"fastapi-sendparcel>=0.1.0",
|
|
49
|
-
"litestar-sendparcel>=0.1.0",
|
|
50
|
-
"python-sendparcel-inpost>=0.
|
|
51
|
-
"python-sendparcel-dpdpl>=0.1.
|
|
52
|
-
"python-sendparcel-
|
|
49
|
+
"django-sendparcel>=0.3.0",
|
|
50
|
+
# "fastapi-sendparcel>=0.1.0",
|
|
51
|
+
# "litestar-sendparcel>=0.1.0",
|
|
52
|
+
"python-sendparcel-inpost>=0.3.0",
|
|
53
|
+
"python-sendparcel-dpdpl>=0.1.1",
|
|
54
|
+
"python-sendparcel-orlenpaczka>=0.0.1",
|
|
55
|
+
"python-sendparcel-cli>=0.2.0",
|
|
53
56
|
]
|
|
54
57
|
|
|
55
58
|
[project.urls]
|
|
56
|
-
Homepage = "https://github.com/sendparcel/python-sendparcel"
|
|
57
|
-
Documentation = "https://python-sendparcel.readthedocs.io/"
|
|
58
|
-
Repository = "https://github.com/sendparcel/python-sendparcel"
|
|
59
|
-
Changelog = "https://github.com/sendparcel/python-sendparcel/blob/main/CHANGELOG.md"
|
|
60
|
-
"Issue Tracker" = "https://github.com/sendparcel/python-sendparcel/issues"
|
|
59
|
+
Homepage = "https://github.com/python-sendparcel/python-sendparcel"
|
|
60
|
+
# Documentation = "https://python-sendparcel.readthedocs.io/"
|
|
61
|
+
Repository = "https://github.com/python-sendparcel/python-sendparcel"
|
|
62
|
+
Changelog = "https://github.com/python-sendparcel/python-sendparcel/blob/main/CHANGELOG.md"
|
|
63
|
+
"Issue Tracker" = "https://github.com/python-sendparcel/python-sendparcel/issues"
|
|
61
64
|
|
|
62
65
|
[build-system]
|
|
63
66
|
requires = ["hatchling"]
|
|
@@ -82,6 +85,7 @@ ignore = ["N818", "B027"]
|
|
|
82
85
|
|
|
83
86
|
[tool.ruff.lint.per-file-ignores]
|
|
84
87
|
"tests/**" = ["RUF012"]
|
|
88
|
+
"docs/**" = ["A001"]
|
|
85
89
|
|
|
86
90
|
[tool.ty.environment]
|
|
87
91
|
python-version = "3.12"
|
|
@@ -98,10 +102,9 @@ invalid-return-type = "ignore"
|
|
|
98
102
|
|
|
99
103
|
[tool.uv.sources]
|
|
100
104
|
django-sendparcel = { path = "../django-sendparcel", editable = true }
|
|
101
|
-
fastapi-sendparcel = { path = "../fastapi-sendparcel", editable = true }
|
|
102
|
-
litestar-sendparcel = { path = "../litestar-sendparcel", editable = true }
|
|
103
105
|
python-sendparcel-inpost = { path = "../python-sendparcel-inpost", editable = true }
|
|
104
106
|
python-sendparcel-dpdpl = { path = "../python-sendparcel-dpdpl", editable = true }
|
|
107
|
+
python-sendparcel-orlenpaczka = { path = "../python-sendparcel-orlenpaczka", editable = true }
|
|
105
108
|
python-sendparcel-cli = { path = "../python-sendparcel-cli", editable = true }
|
|
106
109
|
|
|
107
110
|
[tool.coverage.run]
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
"""sendparcel core package."""
|
|
2
2
|
|
|
3
|
-
__version__ = "0.
|
|
3
|
+
__version__ = "0.3.0"
|
|
4
4
|
|
|
5
|
+
from sendparcel.batch import BatchCreateResult, BatchResult, ShipmentBatch
|
|
5
6
|
from sendparcel.enums import ConfirmationMethod, LabelFormat, ShipmentStatus
|
|
6
7
|
from sendparcel.exceptions import (
|
|
7
8
|
CommunicationError,
|
|
@@ -12,25 +13,25 @@ from sendparcel.exceptions import (
|
|
|
12
13
|
SendParcelException,
|
|
13
14
|
ShipmentNotFoundError,
|
|
14
15
|
)
|
|
16
|
+
from sendparcel.factory import create_provider
|
|
15
17
|
from sendparcel.flow import ShipmentFlow
|
|
16
|
-
from sendparcel.
|
|
17
|
-
|
|
18
|
-
CancellableProvider,
|
|
19
|
-
LabelProvider,
|
|
20
|
-
PullStatusProvider,
|
|
21
|
-
PushCallbackProvider,
|
|
22
|
-
)
|
|
18
|
+
from sendparcel.logging import configure_logging, get_logger
|
|
19
|
+
from sendparcel.provider import BaseProvider
|
|
23
20
|
from sendparcel.providers.dummy import DummyProvider
|
|
24
21
|
from sendparcel.registry import registry
|
|
25
22
|
from sendparcel.types import (
|
|
23
|
+
CallbackContext,
|
|
26
24
|
CreateLabelOutcome,
|
|
27
25
|
CreateShipmentOutcome,
|
|
28
26
|
ShipmentUpdateOutcome,
|
|
27
|
+
ShipmentUpdateResult,
|
|
29
28
|
)
|
|
30
29
|
|
|
31
30
|
__all__ = [
|
|
32
31
|
"BaseProvider",
|
|
33
|
-
"
|
|
32
|
+
"BatchCreateResult",
|
|
33
|
+
"BatchResult",
|
|
34
|
+
"CallbackContext",
|
|
34
35
|
"CommunicationError",
|
|
35
36
|
"ConfirmationMethod",
|
|
36
37
|
"CreateLabelOutcome",
|
|
@@ -39,16 +40,18 @@ __all__ = [
|
|
|
39
40
|
"InvalidCallbackError",
|
|
40
41
|
"InvalidTransitionError",
|
|
41
42
|
"LabelFormat",
|
|
42
|
-
"LabelProvider",
|
|
43
43
|
"ProviderCapabilityError",
|
|
44
44
|
"ProviderNotFoundError",
|
|
45
|
-
"PullStatusProvider",
|
|
46
|
-
"PushCallbackProvider",
|
|
47
45
|
"SendParcelException",
|
|
46
|
+
"ShipmentBatch",
|
|
48
47
|
"ShipmentFlow",
|
|
49
48
|
"ShipmentNotFoundError",
|
|
50
49
|
"ShipmentStatus",
|
|
51
50
|
"ShipmentUpdateOutcome",
|
|
51
|
+
"ShipmentUpdateResult",
|
|
52
52
|
"__version__",
|
|
53
|
+
"configure_logging",
|
|
54
|
+
"create_provider",
|
|
55
|
+
"get_logger",
|
|
53
56
|
"registry",
|
|
54
57
|
]
|