We can't find the internet
Attempting to reconnect
Something went wrong!
Attempting to reconnect
Python roadmap
Ordered runs of problems, and nothing else: no reading, no progress bar, just the sequence. For a syllabus that interleaves written explainers and can be worked through with friends, see tracks.
Python: the full path
Every python track in course order, grouped into the syllabus's four waves. Foundations first, then the design-heavy tracks, then systems, then depth.
195 problems
1
Reading a mypy error: codes, reveal_type, assert_type
Easy
2
Placement 1/6: generics and variance
Medium
3
Placement 2/6: Protocol vs ABC
Easy
4
Placement 3/6: exception boundary design
Medium
5
Placement 4/6: generators are consumed once
Easy
6
Placement 5/6: async cancellation
Hard
7
Placement 6/6: import-cycle detection
Medium
8
Modernise legacy annotations
Easy
9
Flatten a union at runtime
Easy
10
Coerce to int at an untyped boundary
Medium
11
assert_never and the event that was never handled
Medium
12
Literal fit modes and the Final default
Medium
13
A @final rate limiter
Easy
14
ClassVar, field(), and the counter that must reset
Easy
15
Amortise cents without mixing up the units
Medium
16
A recursive Json alias, depth and flatten
Medium
17
Generic partition and chunk
Medium
18
A generic Result[T, E]
Medium
19
Bounds versus constraints: clamp and dedupe
Hard
20
Why list[Dog] is not a list[Animal]
Medium
21
Producer, Consumer, Cell: variance by member set
Hard
22
Accept broad, return narrow: group_by and merge_counts
Medium
23
Retry, and the predicate that reads backwards
Hard
24
Self and the fluent query builder
Medium
25
@override and the notifier that stopped notifying
Easy
26
Narrowing without the truthiness trap
Medium
27
TypeIs, TypeGuard, and a predicate that lies
Hard
28
runtime_checkable is presence, not a signature
Hard
29
Modernisation kata: inventory.py, 2019 to 2026
Hard
30
Counter: top n-grams with deterministic ties
Easy
31
defaultdict: group rows, hand back a plain dict
Easy
32
deque: rolling median and the maxlen narrowing
Medium
33
ChainMap: resolve config and keep the provenance
Medium
34
itertools: take n across chunks without over-consuming
Easy
35
groupby: summarise rows without losing half of them
Medium
36
batched: page a stream, and validate before you yield
Easy
37
accumulate and pairwise: max drawdown in one pass
Easy
38
cache: memoised edit distance behind a validated wrapper
Medium
39
lru_cache on a method: fix the unbounded instance leak
Hard
40
cached_property: compute once, release with the instance, invalidate on demand
Medium
41
singledispatch: a JSON encoder callers can extend from outside
Hard
42
partial: a signature-preserving retrier, and the 3.14 descriptor change
Medium
43
total_ordering: SemVer, where pre-release sorts before release
Medium
44
contextmanager: temporarily patch an attribute, and put it back
Medium
45
ExitStack: all-or-nothing acquisition of N resources
Hard
46
nullcontext and suppress: use the stream you were given, close only what you opened
Easy
47
aclosing: release the connection on break, error and cancellation
Hard
48
pathlib: safe_join, or how to not serve /etc/passwd
Easy
49
walk: prune the subtree, not just the results
Medium
50
StrEnum: migrate a module of string constants without breaking callers
Medium
51
Flag: a permission mask that prints, iterates and refuses bad bits
Medium
52
tomllib: turn dict[str, Any] into a typed config, or a named error
Medium
53
datetime: reject naive input, and find the next local wall clock
Hard
54
heapq: a priority queue that is FIFO among equal priorities
Medium
55
Exception hierarchies as API design
Medium
56
Exception chaining: raise ... from
Medium
57
Bare except, blind except Exception, and where to catch
Medium
58
ExceptionGroup, split() and except*
Hard
59
Failure aggregation over batches
Medium
60
Retry, backoff, and retryable vs terminal
Medium
61
Deprecating public API: @warnings.deprecated
Medium
62
Typing the error path: Result, attempt and Never
Hard
63
Dataclasses: slots, frozen, order
Easy
64
asdict() and astuple(): the deep-copy tax
Hard
65
Serializing dataclasses properly: round-trips, paths and schema versions
Hard
66
Converters and validate-on-assignment: what attrs buys you
Medium
67
dataclass_transform (PEP 681): making your own decorator type-checkable
Hard
68
@dataclass is a code generator: what it emits
Easy
69
eq, hash, and the eq/frozen/unsafe_hash truth table
Hard
70
replace(), copy.replace(), and the mypy gap between them
Hard
71
field(): the seven knobs that change generated behaviour
Medium
72
frozen=True: what it buys, what it costs, and what it does not do
Medium
73
Writing generic code over dataclasses under --strict
Hard
74
kw_only, KW_ONLY and the inheritance ordering wall
Medium
75
The mutable-default trap — and why the guard is weaker than you think
Easy
76
NamedTuple: when a record should be a tuple
Medium
77
Parse at the boundary, dataclasses in the core
Hard
78
__post_init__ and InitVar: parameters that are consumed, not stored
Medium
79
Runtime introspection after PEP 649: Field.type is not a type
Hard
80
Generic dataclasses, Self, and the covariance question
Hard
81
order=True, the 3.13 __eq__ change, and orderings that are not tuples
Hard
82
__slots__, part 2: the eight things it breaks
Hard
83
Protocols: what structural subtyping actually asserts
Easy
84
The protocol attribute invariance trap
Hard
85
abc.ABC: abstract properties and classmethods, in the right order
Easy
86
@runtime_checkable checks names, not types
Medium
87
Subclassing a Protocol: what Python will and will not stop you doing
Medium
88
Callback protocols: closing the Callable[..., Any] hole
Medium
89
Variance in generic protocols
Hard
90
@overload: one implementation, several honest signatures
Hard
91
ParamSpec and Concatenate: decorators that keep the signature
Hard
92
TypeVarTuple: generics over an arbitrary number of types
Hard
93
TypedDict: Required, NotRequired and the ReadOnly unlock
Medium
94
Unpack[TypedDict]: typing **kwargs precisely
Medium
95
Annotated: metadata the type checker deliberately ignores
Medium
96
Self-types, and restricting a method to one parameterisation
Medium
97
Auditing the Any surface of a module
Medium
98
Replacing cast() with a TypeIs predicate
Medium
99
Suppression hygiene: planning the minimum set of type: ignore
Easy
100
__all__, re-export rules, and the accidental public API
Medium
101
Writing a minimal .pyi for an untyped dependency
Hard
102
Fixture setup and teardown order
Medium
103
A rate limiter with an injected clock
Medium
104
Reimplementing pytest's parametrize expansion
Medium
105
Four properties for one codec
Hard
106
Minimal counterexamples for an LRU cache
Hard
107
Mutation testing: scoring a test suite
Hard
108
A typed factory fixture
Hard
109
Detecting leaked tasks and forcing a race
Medium
110
Async iterators: the paginator you can actually close
Medium
111
Batched async stream: never yield inside a cancel scope
Hard
112
Bounded concurrency and backpressure
Hard
113
Lazy record batches: what laziness moves
Medium
114
Iterator, Iterable, Generator, Sequence — and what the annotation promises
Medium
115
Streaming pipelines end to end
Hard
116
The streaming sort-merge join
Hard
117
The single-consumption contract
Medium
118
zip(strict=True) and the silent-truncation bug
Easy
119
BoundedCounter: check-then-act under 64 threads
Medium
120
BoundedBuffer: a blocking queue from one Condition
Medium
121
run_workers: results, errors and a join deadline
Medium
122
map_with_failures: partial success on a thread pool
Medium
123
run_sync: the sync boundary a library should expose
Easy
124
A Fetcher Protocol three implementations can satisfy
Medium
125
BackgroundTasks: strong references, and a drain that raises
Medium
126
fetch_all: structured fan-out with TaskGroup
Medium
127
with_cleanup: bounded cleanup on every exit path
Hard
128
Deadline: one budget, many nested scopes
Medium
129
process_stream: a bounded pipeline that cannot hang
Medium
130
asyncify: a ParamSpec wrapper that does not block the loop
Medium
131
Service.serve: a shutdown that is bounded and returns
Hard
132
request_context: an id that survives tasks and threads
Medium
133
Capstone: a typed, cancellable, observable worker pool
Hard
134
Which copy of the package wins?
Easy
135
What goes on sys.path first
Medium
136
What does this module actually export?
Hard
137
Every import cycle, deterministically
Hard
138
The layering contract, as a test
Hard
139
A rate limiter you can test without sleeping
Hard
140
Own what you create, borrow what you are given
Medium
141
Forbidden import chains, with the route
Hard
142
Does this version satisfy this specifier?
Medium
143
Flattening a dependency group
Medium
144
Parsing an entry-point object reference
Medium
145
Auditing a library for import-time side effects
Hard
146
The __exit__ contract: return the resource, propagate the exception
Medium
147
Walking an object graph with gc.get_referents
Medium
148
A registry that does not prevent collection
Medium
149
Identity vs equality: fix the planted `is`
Easy
150
Selective deep copy: one memo, and the sharing you meant to keep
Medium
151
deep_sizeof: count every distinct object exactly once
Medium
152
find_growth: measure retention, and stop the tracer on every path
Medium
153
Order-preserving dedupe and a real sliding window
Easy
154
render_report: join for str, bytearray for bytes
Medium
155
Accidental quadratics: an index-backed join and a streaming top-k
Medium
156
timeit: take the minimum, and put the collector back
Medium
157
hot_functions: the column you sort by is the answer you get
Medium
158
Moving average twice: naive, cumulative-sum, and knowing which to use
Medium
159
Sizing the Pool: process_cpu_count, cgroups, Oversubscription
Medium
160
Process Is a Typing Black Hole — Prefer Executors
Easy
161
Migrating a Fork-Dependent Codebase to Forkserver
Hard
162
Free-Threading: Writing Code That Survives It
Hard
163
Typing Multiprocessing: Where the Annotations Lie to You
Medium
164
Zero-Copy IPC With Pickle Protocol 5 Out-of-Band Buffers
Hard
165
Graceful Shutdown of a Process Pool
Hard
166
What Actually Crosses a Process Boundary
Medium
167
Queue Mechanics: The Feeder Thread and the Join Deadlock
Hard
168
Manager and Proxies: Convenience at 275x the Price
Medium
169
shared_memory, the Resource Tracker, and track=
Hard
170
The Three Start Methods, and What 3.14 Changed
Medium
171
Subinterpreters: PEP 734 and the Typed Queue
Medium
172
Worker Recycling: max_tasks_per_child Without the Silent Downgrade
Medium
173
Parse, don't validate: the boundary returns a narrower type
Medium
174
Typed settings: parse the environment once, report every problem
Medium
175
Injection: the list form, and a SQL type your checker enforces
Hard
176
The configuration boundary in practice: file, environment, argv, one Config
Medium
177
Logging in libraries: the hierarchy, the effective level, and whose configuration it is
Medium
178
Structured logging: events, not sentences
Hard
179
PEP 686: UTF-8 by default, and the migration hazard
Hard
180
The f-string format specification mini-language
Easy
181
PEP 701: what f-strings can finally do in 3.12
Medium
182
Self-documenting f-strings: f"{x=}"
Easy
183
match/case fundamentals: literal, capture, wildcard
Medium
184
Class patterns, __match_args__ and an expression simplifier
Medium
185
Exhaustive matching with assert_never
Hard
186
Guards, OR patterns, AS patterns and the binding rules
Medium
187
Sequence and mapping patterns: the two rules everyone gets wrong
Medium
188
Positional-only `/` and keyword-only `*`
Medium
189
Template strings (t-strings) and interpolation safety
Hard
190
The walrus operator, tastefully
Medium
191
Capstone: the upload that cannot be misused
Hard
192
Capstone: break the monolith, then enforce the layering
Hard
193
Capstone: an immutable domain model that survives review
Hard
194
Capstone: a fully typed, cancellable, observable async worker pool
Hard
195
Capstone: five defects, and which tool would have found each one
Hard