medium +20 pts

Resource Cleanup Manager

Build a context manager that ensures resources are always released, even on failure.

Implement a class `ResourceCleanup` that acts as a context manager to manage a set of named resources (strings). When entering the context (`with ResourceCleanup() as rc:`), it initializes an internal registry. The class must provide methods: - `acquire(name)` – records the resource name as acquired and returns `name`. - `release(name)` – marks the resource as released. If the resource was not previously acquired, raise a `KeyError`. If the resource was already fully released (i.e., its outstanding count is zero or it is not currently pending), raise a `RuntimeError`. - `close()` – releases all acquired but not yet fully released resources, and returns a list of resource names that were released during this call (in the order they were first acquired). If `close()` is called more than once, the second and subsequent calls return an empty list and do not raise. During `__exit__`, the context manager must call `close()` automatically. If the body of the `with` block raised an exception, `__exit__` must still call `close()`, and the exception must propagate unchanged (i.e., do not suppress it). If `close()` itself raises an unexpected exception, it should be allowed to propagate (but in normal usage it should not). The context manager must work correctly if `acquire` is called multiple times on the same resource – each call re-acquires it, meaning it must be released that many times before considered fully released (the resource appears once in registry but with a count). The `release` method decrements the count; when the count reaches zero, the resource is considered fully released and removed from the pending set. Implement the class with the following signature: ```python class ResourceCleanup: def __enter__(self): ... def __exit__(self, exc_type, exc_val, exc_tb): ... def acquire(self, name: str) -> str: ... def release(self, name: str) -> None: ... def close(self) -> list: ... ``` All methods should be deterministic. The `close()` method should release resources in the order they were first acquired (FIFO). The list returned by `close()` should contain the names of resources that were released during that call. If a resource has been acquired multiple times and released partially, it is still pending, and `close()` must release all remaining occurrences (but the returned list should contain the resource name only once per close call, regardless of how many counts were outstanding). Constraints: names are non-empty strings. Acquire/release counts can grow large, but the test inputs are small. The class should not require any external imports.

Constraints

Name strings are non-empty and may contain any characters. The number of distinct resources and operations is at most 1000. Time complexity should be O(n) per operation on average. No external libraries.

Example

>>> rc = ResourceCleanup()
>>> with rc:
...     rc.acquire('file1')
...     rc.acquire('file2')
>>> rc.close()
[]

>>> rc = ResourceCleanup()
>>> rc.acquire('a')
'a'
>>> rc.acquire('b')
'b'
>>> rc.release('a')
>>> rc.close()
['b']

>>> rc = ResourceCleanup()
>>> rc.acquire('x')
'x'
>>> rc.acquire('x')
'x'
>>> rc.release('x')
>>> rc.close()
['x']
>>> rc.close()
[]
20 points ~20 min

Recent Submissions

No submissions yet — hit Run Tests to try!

Hints

Use a dictionary mapping resource names to their current outstanding count, and a list (or ordered set) to remember the order of first acquisition.
In `__exit__`, always call `self.close()` and then return `False` (or `None` implicitly) to let exceptions propagate.
For `release`, decrement the count; if it reaches zero, remove from the pending set. If not found, raise `KeyError`; if count is already zero (i.e., not pending), raise `RuntimeError`.
In `close`, iterate over the pending resources in the order they were acquired, release each one, and return the list of names released this call. After that, clear the order list and reset counts.
Python 3
All tests passed!
Test Results
Press Ctrl+Enter or click Run Tests to execute your code.