We can't find the internet
Attempting to reconnect
Something went wrong!
Attempting to reconnect
← Memory, Copies and Slow Code step 11 of 38
Fix a Nested Context Manager That Hides Errors
PooledResource borrows a named resource from a pool for the length of a
with block. The starter has two bugs. Both pass mypy, and both show up only
when something goes wrong:
__exit__returnsTrue, so an error raised in the block disappears.- Entering the same wrapper twice checks out two resources, and the inner exit gives a resource back while the outer block still uses it.
Fix PooledResource. Keep ResourcePool and solve as they are.
What to write
- Creating a
PooledResourceborrows nothing. Do not use__del__for cleanup. __enter__returns the wrapper itself, annotated-> Self. The first entry checks out one resource.- An entry while the wrapper is already entered uses the resource it holds. Only the outermost exit gives the resource back.
- On the outermost exit,
__exit__gives the resource back, whether the block finished or raised. It returnsNone, so it never hides the block's exception. Keep its three parameters, and change its return annotation frombooltoNone. - The
nameproperty returns the held name while the wrapper is entered. At any other time it raisesRuntimeError("not checked out"). - After the outermost exit, you can enter the wrapper again. If a checkout fails, the wrapper stays unentered, so a later attempt can succeed once a resource is free.
The tests use balanced with blocks in one thread. You do not need to
handle threads, or a call to __exit__ without a matching __enter__.
The pool
ResourcePool(n) starts with the free names r0 to r<n-1>. checkout()
takes the first free name, logs out:<name> and returns the name. With no
free name, it raises RuntimeError("pool exhausted"). checkin(name) adds
the name to the end of the free list and logs in:<name>. available is
the number of free names.
The driver
solve(pool_size, actions) creates the pool and runs the actions in order.
pool_size is 0 or more.
| Action | What it does |
|---|---|
"ok" |
Enters a new wrapper, logs use:<name>, and exits normally. |
"raise" |
Enters a new wrapper, logs use:<name>, raises ValueError("body failed"), and catches it outside the block. |
"nested" |
Enters one wrapper, then enters it again inside the first block. Logs use:<name> in the inner block and still:<name> after the inner block ends. |
"orphan" |
Creates a wrapper without entering it, and logs orphan. |
It returns {"log": pool.log, "available": pool.available, "escaped": escaped}.
escaped holds one "ValueError:body failed" for each "raise" action whose
error got out of the block. Any other error reaches the caller of solve,
such as the RuntimeError from an empty pool. An unknown action raises
ValueError.
Each action gives its resource back before the next one starts, so the free list keeps its order. A nested action needs only one free resource. With a pool of size 0, an empty action list or a list of orphan actions still completes.
Your code, saved here
Sign in to access your accepted solutions and saved draft history. You can keep reading the lesson without an account.
Sign in to your accountStuck?
Python reference solution
Sign in to track your attempts toward unlocking the reference solution.