Framework-agnostic parcel shipping core for Python.
Project description
python-sendparcel
Framework-agnostic parcel shipping core for Python.
Alpha notice:
0.3.0is still unstable. The API can change fast because the ecosystem is still being cleaned up.
What it is
- Provider-agnostic shipment orchestration with a single core flow.
- Explicit shipment metadata persistence:
id,status,provider,external_id,tracking_number. - Label payloads are operation results, not persisted shipment fields.
- One normalized provider update contract for both callbacks and polling.
- Runtime-checkable
ShipmentandShipmentRepositoryprotocols so adapters can use their own models.
Core contract
ShipmentFlow.create_shipment(...) -> CreateShipmentOutcomeShipmentFlow.create_label(...) -> CreateLabelOutcomeShipmentFlow.handle_callback(...) -> ShipmentUpdateOutcomeShipmentFlow.fetch_and_update_status(...) -> ShipmentUpdateOutcomeShipmentFlow.cancel_shipment(...) -> bool
CreateShipmentOutcome and CreateLabelOutcome return label payloads when available.
The shipment object never stores label bytes or a persisted label_url in the core contract.
Quick start
from dataclasses import dataclass
from decimal import Decimal
import anyio
from sendparcel import ShipmentFlow
from sendparcel.types import AddressInfo, ParcelInfo
@dataclass
class MyShipment:
id: str
status: str = "new"
provider: str = ""
external_id: str = ""
tracking_number: str = ""
class InMemoryRepository:
def __init__(self) -> None:
self._store: dict[str, MyShipment] = {}
self._counter = 0
async def get_by_id(self, shipment_id: str) -> MyShipment:
return self._store[shipment_id]
async def create(self, **kwargs) -> MyShipment:
self._counter += 1
shipment = MyShipment(
id=str(self._counter),
status=str(kwargs.get("status", "new")),
provider=str(kwargs.get("provider", "")),
)
self._store[shipment.id] = shipment
return shipment
async def save(self, shipment: MyShipment) -> MyShipment:
self._store[shipment.id] = shipment
return shipment
async def update_fields(self, shipment_id: str, **fields) -> MyShipment:
shipment = self._store[shipment_id]
for key, value in fields.items():
setattr(shipment, key, value)
return shipment
async def main() -> None:
flow = ShipmentFlow(repository=InMemoryRepository())
created = await flow.create_shipment(
"dummy",
sender_address=AddressInfo(
name="Sender Co.",
line1="Marszalkowska 1",
city="Warsaw",
postal_code="00-001",
country_code="PL",
),
receiver_address=AddressInfo(
name="Jan Kowalski",
line1="Dluga 10",
city="Gdansk",
postal_code="80-001",
country_code="PL",
),
parcels=[ParcelInfo(weight_kg=Decimal("2.5"))],
)
print(created.shipment.status)
print(created.shipment.external_id)
print(created.shipment.tracking_number)
labelled = await flow.create_label(created.shipment)
print(labelled.label.get("url"))
anyio.run(main)
Provider model
All providers subclass BaseProvider; capability methods raise
ProviderCapabilityError unless overridden:
BaseProvider.create_shipment(...)returnsShipmentCreateResult(required).BaseProvider.confirmation_methoddefaults toConfirmationMethod.NONE.BaseProvider.create_label(...)returnsLabelInfo.BaseProvider.verify_callback(ctx)/handle_callback(ctx)returnNone/ShipmentUpdateResult.BaseProvider.fetch_shipment_status(...)returnsShipmentUpdateResult.BaseProvider.cancel_shipment(...)returnsbool.
Use ConfirmationMethod.PUSH only when the callback methods are overridden
and ConfirmationMethod.PULL only when fetch_shipment_status is. See
docs/provider-authoring.md for the full guide.
The core owns shipment state transitions. Providers translate carrier responses into normalized results.
Installation
pip install python-sendparcel
With uv:
uv add python-sendparcel
Extras
djangoinpostdpdplorlenpaczkacliframeworksprovidersall
Development
uv sync --extra dev
uv run pytest
uv run ruff check .
uv run ruff format --check .
uv run ty check
uv run mypy src
Project details
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 python_sendparcel-0.3.2.tar.gz.
File metadata
- Download URL: python_sendparcel-0.3.2.tar.gz
- Upload date:
- Size: 42.5 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
ccf1140e0b1840bb7465c0f279a5e3a6d9f834a3c99c68f2be3b65aac8efdab1
|
|
| MD5 |
c6af2cd0a190d750ebd8fe057a7924e4
|
|
| BLAKE2b-256 |
5dbd5adf1faf0b76910dbc6300f9ec4a2c34e025b469eefe290069aba4ba34ad
|
Provenance
The following attestation bundles were made for python_sendparcel-0.3.2.tar.gz:
Publisher:
release.yml on python-sendparcel/python-sendparcel
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
python_sendparcel-0.3.2.tar.gz -
Subject digest:
ccf1140e0b1840bb7465c0f279a5e3a6d9f834a3c99c68f2be3b65aac8efdab1 - Sigstore transparency entry: 2132101587
- Sigstore integration time:
-
Permalink:
python-sendparcel/python-sendparcel@a53586e979de689b2a5e0849375c0340349ab356 -
Branch / Tag:
refs/tags/v0.3.2 - Owner: https://github.com/python-sendparcel
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@a53586e979de689b2a5e0849375c0340349ab356 -
Trigger Event:
push
-
Statement type:
File details
Details for the file python_sendparcel-0.3.2-py3-none-any.whl.
File metadata
- Download URL: python_sendparcel-0.3.2-py3-none-any.whl
- Upload date:
- Size: 25.1 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 |
e87cb6aa58ae1343c0542610b61b9a6bd727f25007294e97bc868367448fe45e
|
|
| MD5 |
0d79c1c175b0fab5fd7cdc643f4e08d4
|
|
| BLAKE2b-256 |
14a1f930c77ed43f5a56c75951d801a26e3dbd10eff831aeac2b1b5a7c6e7af2
|
Provenance
The following attestation bundles were made for python_sendparcel-0.3.2-py3-none-any.whl:
Publisher:
release.yml on python-sendparcel/python-sendparcel
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
python_sendparcel-0.3.2-py3-none-any.whl -
Subject digest:
e87cb6aa58ae1343c0542610b61b9a6bd727f25007294e97bc868367448fe45e - Sigstore transparency entry: 2132101722
- Sigstore integration time:
-
Permalink:
python-sendparcel/python-sendparcel@a53586e979de689b2a5e0849375c0340349ab356 -
Branch / Tag:
refs/tags/v0.3.2 - Owner: https://github.com/python-sendparcel
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@a53586e979de689b2a5e0849375c0340349ab356 -
Trigger Event:
push
-
Statement type: