Learn / Python and FastAPI Backend Engineering
Python Runtime and Async Boundaries
A consolidated foundations lesson preserving 2 focused PTesting lessons without duplicating an unrelated authored PTLearn topic.
Course: Python and FastAPI Backend Engineering. Level: Intermediate. Topic: Backend craft.
Stage: intermediate - Practice - Language and runtime foundations. Connect python runtime and async boundaries to the professional workflow for Python & FastAPI.
Outcomes
- Describe Python bytecode execution at a high level.
- Explain reference counting and garbage collection.
- Understand why the GIL affects CPU-bound threading.
- Reduce avoidable allocations in hot paths.
- Describe what an event loop does.
- Distinguish coroutine functions, coroutine objects, and tasks.
- Avoid blocking the event loop with synchronous I/O.
- Use cancellation-aware patterns for request-scoped work.
Concepts
- Runtime and Memory
- Memory model in practice
- GIL and concurrency
- Python + FastAPI foundations
- Guided practice
- Async and the Event Loop
- Await points yield control
- Blocking calls are contagious
Concept flow
Show how language and runtime foundations moves from trigger to implementation outcome in Python & FastAPI.
- Language model
- Runtime behavior
- Engineering decision
- Verification evidence
Session flow
- Model Runtime and Memory (concept, 68 min) — Name the decisions behind Runtime and Memory before writing code.
- Describe Python bytecode execution at a high level.
- Explain where Runtime and Memory belongs in subscription billing API.
- Build the vertical slice (walkthrough, 122 min) — Implement the smallest useful slice in legacy/python-fastapi/python-runtime-and-async-boundaries.txt.
- Explain reference counting and garbage collection.
- Connect Memory model in practice to a working example.
- Verify and harden (exercise, 81 min) — Hint: One call blocks the thread immediately.
- Understand why the GIL affects CPU-bound threading.
- Record one risk or follow-up before moving on.
- Objects, Garbage Collection, and the GIL: Memory model in practice (concept, 60 min) — CPython stores objects on a managed heap and primarily frees them through reference counting. A cyclic garbage collector handles reference cycles. You rarely manage memory manually, but object lifetime and allocation patterns still affect latency and memory use.
- Retained source example: Names and object lifetime
items = [1, 2, 3]
alias = items
items.append(4)
print(alias)
Expected output: [1, 2, 3, 4]
- Objects, Garbage Collection, and the GIL: GIL and concurrency (walkthrough, 60 min) — The Global Interpreter Lock lets one thread execute Python bytecode in a process at a time in standard CPython. Threads are still useful for I/O waits, but CPU-heavy Python loops usually need multiprocessing, native extensions, vectorized libraries, or external workers.
- Retained source example: Generator avoids building a list
def squares(limit: int):
for number in range(limit):
yield number * number
print(sum(squares(5)))
Expected output: 30
- Objects, Garbage Collection, and the GIL: practices and mistakes (review, 5 min) — Retained guidance from the legacy PTesting lesson.
- Practice: Measure memory and latency before optimizing.
- Practice: Use generators for streaming large sequences.
- Practice: Keep CPU-heavy work outside request handlers when latency matters.
- Avoid: Expecting async to make CPU-bound code faster.
- Avoid: Holding onto large global caches without eviction.
- Avoid: Creating large intermediate lists when streaming would work.
- Objects, Garbage Collection, and the GIL: references (review, 2 min) — Original references retained from the legacy library.
- Python tutorial: https://docs.python.org/3/tutorial/
- Python standard library: https://docs.python.org/3/library/
- gc module: https://docs.python.org/3/library/gc.html
- concurrent.futures: https://docs.python.org/3/library/concurrent.futures.html
- Async/Await for Web Workloads: Await points yield control (concept, 75 min) — An async function returns a coroutine object. The event loop drives that coroutine and can switch to other work at await points. This is powerful for many concurrent I/O waits, but it requires compatible non-blocking libraries.
- Retained source example: Concurrent I/O-shaped work
import asyncio
async def fetch_label(label: str) -> str:
await asyncio.sleep(0.01)
return label.upper()
async def main() -> None:
results = await asyncio.gather(fetch_label('a'), fetch_label('b'))
print(results)
asyncio.run(main())
Expected output: ['A', 'B']
- Async/Await for Web Workloads: Blocking calls are contagious (walkthrough, 75 min) — Calling time.sleep, requests.get, or a blocking database client inside an async route can freeze the loop for other requests. Use async libraries, run blocking work in a thread pool when appropriate, or keep the route synchronous so the server can place it on a worker thread.
- Retained source example: Use the async sleep in async code
import asyncio
async def polite_wait() -> str:
await asyncio.sleep(1)
return 'done'
- Async/Await for Web Workloads: practices and mistakes (review, 5 min) — Retained guidance from the legacy PTesting lesson.
- Practice: Use async clients with async routes.
- Practice: Keep CPU-heavy work out of the event loop.
- Practice: Set timeouts and handle cancellation for outbound calls.
- Avoid: Marking every function async even when no await is needed.
- Avoid: Calling blocking libraries from async routes.
- Avoid: Creating background tasks without observing errors or shutdown behavior.
- Async/Await for Web Workloads: references (review, 2 min) — Original references retained from the legacy library.
- asyncio: https://docs.python.org/3/library/asyncio.html
- FastAPI async docs: https://fastapi.tiangolo.com/async/
Code example
python in legacy/python-fastapi/python-runtime-and-async-boundaries.txt.
def main() -> None:
print("PTLearn foundation")
if __name__ == "__main__":
main()
Walkthrough examples
- Python Runtime and Async Boundaries in a subscription billing API — A team is extending a production-style FastAPI service and needs this lesson's pattern to be clear enough for review, testing, and future maintenance.
- File: legacy/python-fastapi/python-runtime-and-async-boundaries.txt
- File: tests/python-runtime-and-async-boundaries.spec
- File: docs/python-fastapi/python-runtime-and-async-boundaries.md
- Start from the provided python snippet and make the intent visible in names and boundaries.
- Apply the checklist item "Explain the language or runtime rule in your own words" before adding extra behavior.
- Write down how the implementation changes when Memory model in practice fails or becomes slow.
- Names and object lifetime — Additional example retained from the legacy lesson.
- Retained source code:
items = [1, 2, 3]
alias = items
items.append(4)
print(alias)
- Expected output: [1, 2, 3, 4]
- Compare the example with the canonical PTLearn implementation.
- Generator avoids building a list — Additional example retained from the legacy lesson.
- Retained source code:
def squares(limit: int):
for number in range(limit):
yield number * number
print(sum(squares(5)))
- Expected output: 30
- Compare the example with the canonical PTLearn implementation.
- Avoid repeated string concatenation in loops — Additional example retained from the legacy lesson.
- Retained source code:
parts = ['fast', 'api', 'service']
name = '-'.join(parts)
print(name)
- Expected output: fast-api-service
- Compare the example with the canonical PTLearn implementation.
- Concurrent I/O-shaped work — Additional example retained from the legacy lesson.
- Retained source code:
import asyncio
async def fetch_label(label: str) -> str:
await asyncio.sleep(0.01)
return label.upper()
async def main() -> None:
results = await asyncio.gather(fetch_label('a'), fetch_label('b'))
print(results)
asyncio.run(main())
- Expected output: ['A', 'B']
- Compare the example with the canonical PTLearn implementation.
- Use the async sleep in async code — Additional example retained from the legacy lesson.
- Retained source code:
import asyncio
async def polite_wait() -> str:
await asyncio.sleep(1)
return 'done'
- Explain the expected behavior.
- Compare the example with the canonical PTLearn implementation.
- FastAPI can use async or sync routes — Additional example retained from the legacy lesson.
- Retained source code:
from fastapi import FastAPI
app = FastAPI()
@app.get('/async-health')
async def async_health() -> dict[str, str]:
return {'status': 'ok'}
@app.get('/sync-health')
def sync_health() -> dict[str, str]:
return {'status': 'ok'}
- Explain the expected behavior.
- Compare the example with the canonical PTLearn implementation.
Practice
- Pick a CPU-bound strategy: For a CPU-heavy image conversion task, choose a better option than an async route doing all work inline.
- Hint: Async helps while waiting on I/O, not while burning CPU.
- Reference solution: Move it to a process pool or external worker.
- Accepted answers: Move it to a process pool or external worker. | process pool | external worker
- Find the blocking call: In an async route, which call is usually a red flag: time.sleep(1) or await asyncio.sleep(1)?
- Hint: One call blocks the thread immediately.
- Reference solution: time.sleep(1)
- Accepted answers: time.sleep(1) | time.sleep
Checklist
- Explain the language or runtime rule in your own words
- Run one focused example and record its output
- Name one failure mode or tradeoff
- Keep the verification evidence with the lesson
Quiz prompts
- A teammate wants to hide Runtime and Memory inside a convenient helper. What should you check first? — Place Runtime and Memory at the boundary that keeps subscription billing API behavior explicit, testable, and reviewable.
- Which artifact best proves this Python & FastAPI lesson is ready for review? — Production-ready learning needs evidence: a test, trace, command, screenshot, or log that catches the risk again.
- What does the GIL mainly limit in CPython? — The GIL is about Python bytecode execution, not whether I/O can overlap.
- What happens when async code performs blocking I/O on the event loop thread? — The loop can switch tasks only when code yields control.
Flashcards
- In Python & FastAPI, what should you remember about Runtime and Memory? Runtime and Memory matters here because it supports "Describe Python bytecode execution at a high level.".
- In Python & FastAPI, what should you remember about Memory model in practice? Memory model in practice matters here because it supports "Explain reference counting and garbage collection.".
- In Python & FastAPI, what should you remember about GIL and concurrency? GIL and concurrency matters here because it supports "Understand why the GIL affects CPU-bound threading.".
- In Python & FastAPI, what should you remember about Python + FastAPI foundations? Python + FastAPI foundations matters here because it supports "Reduce avoidable allocations in hot paths.".
Labs
- Ship a python runtime and async boundaries slice — Extend a production-style FastAPI service with a small but reviewable feature that proves the lesson's architecture in code.
- Pick a CPU-bound strategy: For a CPU-heavy image conversion task, choose a better option than an async route doing all work inline.
- Hint: Async helps while waiting on I/O, not while burning CPU.
- Reference solution: Move it to a process pool or external worker.
- Accepted answers: Move it to a process pool or external worker. | process pool | external worker
- Find the blocking call: In an async route, which call is usually a red flag: time.sleep(1) or await asyncio.sleep(1)?
- Hint: One call blocks the thread immediately.
- The implementation demonstrates Runtime and Memory without hidden global state.
- At least one test or verification step covers the main success path.
- The review notes explain the tradeoff behind Memory model in practice.
Challenge
- Review-ready python runtime and async boundaries (Stretch) — Turn the lesson work into a pull-request-sized change for subscription billing API. Include the code, verification notes, one explicit tradeoff, and an updated concept diagram that names the riskiest handoff.
- All checklist items are either implemented or documented with a reason.
- The change can be understood from legacy/python-fastapi/python-runtime-and-async-boundaries.txt plus one short note.
- The concept diagram names ownership, failure handling, and verification points.
- A teammate could run the verification steps without asking for hidden context.
Canonical lesson URL