Packaging & a production capstone
Build a typed, tested, packaged order service that integrates the whole track.
Packaging: the last mile
Your code only creates value once someone else can run it. Packaging is how you hand a colleague pip install orders instead of a folder and a prayer. A published package pins your version, declares its dependencies, and installs the same way on every machine, which is exactly what CI, Docker images, and teammates depend on.
What a wheel actually is
A wheel (.whl) is a zip of your importable code plus metadata, named to a fixed convention. pyproject.toml is the single source of truth: the [project] table declares name, version, requires-python, and dependencies, plus a dev extra for pytest, ruff, and mypy. A [build-system] table names the build backend that turns the project into artifacts.
uv build # writes dist/orders-1.0.0.tar.gz and dist/orders-1.0.0-py3-none-any.whl
uv publish # uploads those artifacts to a package index (PyPI)
The py3-none-any tag means pure Python, any interpreter, any OS. Nothing to compile.
The production checklist
A shippable library is structured (a clean package with clear entry points), typed (hints on the public API so callers and mypy know the contract), validated (untrusted input parsed into typed values at the boundary), and tested (pytest over the real cases, run in CI). Your capstone hits all four.
Parsing at the boundary
Raw input is not your model. Each raw order arrives as a dict whose "amount" is a string, so summarize must parse it into a number before it can do arithmetic:
def summarize(raw_orders):
paid = [o for o in raw_orders if o["paid"]]
revenue = round(sum(float(o["amount"]) for o in paid), 2)
return {"count": len(raw_orders), "paid": len(paid), "revenue": revenue}
rows = [{"amount": "10.0", "paid": True}, {"amount": "5.0", "paid": False}]
print(summarize(rows))
# {'count': 2, 'paid': 1, 'revenue': 10.0}
count is every order, paid is how many cleared, and revenue sums only the paid amounts. In the capstone, a read-only parse_order does this coercion for you, turning each raw dict into a typed Order.
Pitfalls
int("10.0")raisesValueErrorbecause"10.0"is not an integer literal. Parse decimal strings withfloat("10.0").- Round once, at the very end. Sum first, then
round(total, 2). Floats cannot hold most decimals exactly, so0.1 + 0.2 == 0.3isFalse(it is0.30000000000000004), and rounding after each addition compounds that error.
Interview nuance: float cannot represent most base-10 fractions exactly, so a long chain of price additions drifts. round(total, 2) cleans up the display but not the stored value, and round uses banker's rounding, so round(0.125, 2) gives 0.12 (rounded to even), not 0.13. Production money code stores integer cents or uses Decimal; reach for float only when a tiny rounding error is acceptable.
from dataclasses import dataclass
@dataclass
class Order:
id: int
amount: float
paid: bool
orders = [Order(1, 10.0, True), Order(2, 5.0, False)]
paid = [o for o in orders if o.paid]
print({"count": len(orders), "paid": len(paid), "revenue": round(sum(o.amount for o in paid), 2)})Apply
Your turn
The task this lesson builds to.
Warm-up (one file): implement summarize(raw_orders). Each raw order is a dict with
"amount" (a numeric string) and "paid" (a bool). Return
{"count": <all>, "paid": <paid>, "revenue": <sum of paid amounts, rounded to 2>}.
For one paid "10.0" and one unpaid "5.0", revenue is 10.0.
3 hints and 3 automated checks are waiting in the workspace.
Practice
Make it stick
A second problem on the same idea, so it survives past today.
Capstone: implement summarize(raw_orders) in orders/report.py. Parse each raw order with
the read-only parse_order (into a typed Order), then return
{"count", "paid", "revenue"} where revenue sums paid orders' amounts, rounded to 2 decimals.
Some tests are hidden.
3 hints and 2 automated checks are waiting in the workspace.