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.
Files changed (59) hide show
  1. python_sendparcel-0.3.0/CHANGELOG.md +71 -0
  2. {python_sendparcel-0.1.1 → python_sendparcel-0.3.0}/PKG-INFO +45 -37
  3. {python_sendparcel-0.1.1 → python_sendparcel-0.3.0}/README.md +24 -12
  4. {python_sendparcel-0.1.1 → python_sendparcel-0.3.0}/docs/provider-authoring.md +38 -18
  5. {python_sendparcel-0.1.1 → python_sendparcel-0.3.0}/pyproject.toml +27 -24
  6. {python_sendparcel-0.1.1 → python_sendparcel-0.3.0}/src/sendparcel/__init__.py +15 -12
  7. python_sendparcel-0.3.0/src/sendparcel/batch.py +305 -0
  8. python_sendparcel-0.3.0/src/sendparcel/concurrent.py +71 -0
  9. {python_sendparcel-0.1.1 → python_sendparcel-0.3.0}/src/sendparcel/enums.py +1 -0
  10. python_sendparcel-0.3.0/src/sendparcel/factory.py +33 -0
  11. python_sendparcel-0.3.0/src/sendparcel/flow.py +268 -0
  12. python_sendparcel-0.3.0/src/sendparcel/fsm.py +106 -0
  13. python_sendparcel-0.3.0/src/sendparcel/logging.py +137 -0
  14. python_sendparcel-0.3.0/src/sendparcel/protocols.py +75 -0
  15. python_sendparcel-0.3.0/src/sendparcel/provider.py +242 -0
  16. {python_sendparcel-0.1.1 → python_sendparcel-0.3.0}/src/sendparcel/providers/dummy.py +7 -20
  17. {python_sendparcel-0.1.1 → python_sendparcel-0.3.0}/src/sendparcel/registry.py +69 -28
  18. {python_sendparcel-0.1.1 → python_sendparcel-0.3.0}/src/sendparcel/types.py +31 -4
  19. {python_sendparcel-0.1.1 → python_sendparcel-0.3.0}/tests/conftest.py +54 -1
  20. python_sendparcel-0.3.0/tests/test_batch.py +464 -0
  21. {python_sendparcel-0.1.1 → python_sendparcel-0.3.0}/tests/test_builtin_dummy_provider.py +32 -14
  22. python_sendparcel-0.3.0/tests/test_concurrent.py +167 -0
  23. {python_sendparcel-0.1.1 → python_sendparcel-0.3.0}/tests/test_enums.py +19 -0
  24. {python_sendparcel-0.1.1 → python_sendparcel-0.3.0}/tests/test_flow.py +106 -53
  25. {python_sendparcel-0.1.1 → python_sendparcel-0.3.0}/tests/test_fsm.py +5 -5
  26. {python_sendparcel-0.1.1 → python_sendparcel-0.3.0}/tests/test_integration.py +22 -24
  27. {python_sendparcel-0.1.1 → python_sendparcel-0.3.0}/tests/test_protocols.py +32 -0
  28. python_sendparcel-0.3.0/tests/test_provider_enhancements.py +376 -0
  29. {python_sendparcel-0.1.1 → python_sendparcel-0.3.0}/tests/test_public_api.py +8 -4
  30. {python_sendparcel-0.1.1 → python_sendparcel-0.3.0}/tests/test_registry.py +11 -0
  31. {python_sendparcel-0.1.1 → python_sendparcel-0.3.0}/tests/test_types.py +10 -0
  32. python_sendparcel-0.1.1/CHANGELOG.md +0 -20
  33. python_sendparcel-0.1.1/src/sendparcel/flow.py +0 -193
  34. python_sendparcel-0.1.1/src/sendparcel/fsm.py +0 -99
  35. python_sendparcel-0.1.1/src/sendparcel/protocols.py +0 -26
  36. python_sendparcel-0.1.1/src/sendparcel/provider.py +0 -92
  37. python_sendparcel-0.1.1/src/sendparcel/validators.py +0 -21
  38. python_sendparcel-0.1.1/tests/test_validators.py +0 -66
  39. {python_sendparcel-0.1.1 → python_sendparcel-0.3.0}/.github/workflows/ci.yml +0 -0
  40. {python_sendparcel-0.1.1 → python_sendparcel-0.3.0}/.github/workflows/ecosystem-smoke.yml +0 -0
  41. {python_sendparcel-0.1.1 → python_sendparcel-0.3.0}/.github/workflows/release.yml +0 -0
  42. {python_sendparcel-0.1.1 → python_sendparcel-0.3.0}/.gitignore +0 -0
  43. {python_sendparcel-0.1.1 → python_sendparcel-0.3.0}/.pre-commit-config.yaml +0 -0
  44. {python_sendparcel-0.1.1 → python_sendparcel-0.3.0}/CONTRIBUTING.md +0 -0
  45. {python_sendparcel-0.1.1 → python_sendparcel-0.3.0}/LICENSE +0 -0
  46. {python_sendparcel-0.1.1 → python_sendparcel-0.3.0}/docs/Makefile +0 -0
  47. {python_sendparcel-0.1.1 → python_sendparcel-0.3.0}/docs/compatibility-matrix.md +0 -0
  48. {python_sendparcel-0.1.1 → python_sendparcel-0.3.0}/docs/conf.py +0 -0
  49. {python_sendparcel-0.1.1 → python_sendparcel-0.3.0}/docs/getting-started.md +0 -0
  50. {python_sendparcel-0.1.1 → python_sendparcel-0.3.0}/docs/index.md +0 -0
  51. {python_sendparcel-0.1.1 → python_sendparcel-0.3.0}/docs/installation-matrix.md +0 -0
  52. {python_sendparcel-0.1.1 → python_sendparcel-0.3.0}/docs/plans/2026-02-17-order-optional.md +0 -0
  53. {python_sendparcel-0.1.1 → python_sendparcel-0.3.0}/docs/release-policy.md +0 -0
  54. {python_sendparcel-0.1.1 → python_sendparcel-0.3.0}/docs/requirements.txt +0 -0
  55. {python_sendparcel-0.1.1 → python_sendparcel-0.3.0}/src/sendparcel/exceptions.py +0 -0
  56. {python_sendparcel-0.1.1 → python_sendparcel-0.3.0}/src/sendparcel/providers/__init__.py +0 -0
  57. {python_sendparcel-0.1.1 → python_sendparcel-0.3.0}/src/sendparcel/py.typed +0 -0
  58. {python_sendparcel-0.1.1 → python_sendparcel-0.3.0}/tests/test_exceptions.py +0 -0
  59. {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.1.1
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: Documentation, https://python-sendparcel.readthedocs.io/
7
- Project-URL: Repository, https://github.com/sendparcel/python-sendparcel
8
- Project-URL: Changelog, https://github.com/sendparcel/python-sendparcel/blob/main/CHANGELOG.md
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.1.0; extra == 'all'
28
- Requires-Dist: fastapi-sendparcel>=0.1.0; extra == 'all'
29
- Requires-Dist: litestar-sendparcel>=0.1.0; extra == 'all'
30
- Requires-Dist: python-sendparcel-cli>=0.1.0; extra == 'all'
31
- Requires-Dist: python-sendparcel-dpdpl>=0.1.0; extra == 'all'
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.1.0; extra == 'cli'
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.1.0; extra == 'django'
43
+ Requires-Dist: django-sendparcel>=0.3.0; extra == 'django'
45
44
  Provides-Extra: dpdpl
46
- Requires-Dist: python-sendparcel-dpdpl>=0.1.0; extra == 'dpdpl'
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.1.0; extra == 'frameworks'
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.1.0; extra == 'inpost'
56
- Provides-Extra: litestar
57
- Requires-Dist: litestar-sendparcel>=0.1.0; extra == 'litestar'
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.0; extra == 'providers'
60
- Requires-Dist: python-sendparcel-inpost>=0.1.0; extra == 'providers'
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.1.1` is still unstable. The API can change fast because the
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
- - `BaseProvider.create_shipment(...)` returns `ShipmentCreateResult`.
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
- - `LabelProvider.create_label(...)` returns `LabelInfo`.
171
- - `PushCallbackProvider.handle_callback(...)` returns `ShipmentUpdateResult`.
172
- - `PullStatusProvider.fetch_shipment_status(...)` returns `ShipmentUpdateResult`.
173
- - `CancellableProvider.cancel_shipment(...)` returns `bool`.
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 with `PushCallbackProvider` and
176
- `ConfirmationMethod.PULL` only with `PullStatusProvider`.
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 src tests
211
- uv run mypy src tests
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.1.1` is still unstable. The API can change fast because the
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
- - `BaseProvider.create_shipment(...)` returns `ShipmentCreateResult`.
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
- - `LabelProvider.create_label(...)` returns `LabelInfo`.
109
- - `PushCallbackProvider.handle_callback(...)` returns `ShipmentUpdateResult`.
110
- - `PullStatusProvider.fetch_shipment_status(...)` returns `ShipmentUpdateResult`.
111
- - `CancellableProvider.cancel_shipment(...)` returns `bool`.
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 with `PushCallbackProvider` and
114
- `ConfirmationMethod.PULL` only with `PullStatusProvider`.
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 src tests
149
- uv run mypy src tests
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 traits
29
+ ### Optional capability overrides
26
30
 
27
- | Capability | Trait | Return value |
31
+ | Capability | Method to override | Return value |
28
32
  |---|---|---|
29
- | Labels | `LabelProvider` | `LabelInfo` |
30
- | Webhooks | `PushCallbackProvider` | `ShipmentUpdateResult` |
31
- | Polling | `PullStatusProvider` | `ShipmentUpdateResult` |
32
- | Cancellation | `CancellableProvider` | `bool` |
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 implement `PushCallbackProvider`.
52
- - `PULL` - the provider must implement `PullStatusProvider`.
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 implements the
55
- matching capability trait.
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.provider import BaseProvider, LabelProvider
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, LabelProvider):
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="PDF",
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.provider import PullStatusProvider, PushCallbackProvider
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
- class StatusProvider(BaseProvider, PushCallbackProvider, PullStatusProvider):
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, data, headers, **kwargs
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.1.1"
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.1.0"]
35
- fastapi = ["fastapi-sendparcel>=0.1.0"]
36
- litestar = ["litestar-sendparcel>=0.1.0"]
37
- inpost = ["python-sendparcel-inpost>=0.1.0"]
38
- dpdpl = ["python-sendparcel-dpdpl>=0.1.0"]
39
- cli = ["python-sendparcel-cli>=0.1.0"]
40
- providers = ["python-sendparcel-inpost>=0.1.0", "python-sendparcel-dpdpl>=0.1.0"]
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.1.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.1.0",
48
- "fastapi-sendparcel>=0.1.0",
49
- "litestar-sendparcel>=0.1.0",
50
- "python-sendparcel-inpost>=0.1.0",
51
- "python-sendparcel-dpdpl>=0.1.0",
52
- "python-sendparcel-cli>=0.1.0",
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.1.1"
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.provider import (
17
- BaseProvider,
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
- "CancellableProvider",
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
  ]