Build a cache that does not own the objects it indexes. Cached values are weak-referenceable user-defined objects.
Prototype: observe(obj) returns a weakref.ref for that exact object. Calling the returned reference yields the live object, or None after it has been collected. The observer must not keep an extra strong reference.
Final: implement WeakCache() using weakref.WeakValueDictionary. put(key, obj) stores or replaces the weakly held object and returns None; keys are strings. get(key) returns the live object or None when missing/collected. discard(key) removes an entry if present, returns None, and is harmless when absent. len(cache) reports the currently live entries. Store no extra strong reference to a cached value. Different caches are independent. Tests retain strong references when checking live values and explicitly call gc.collect() when checking controlled unreachable cycles. Do not add destructors, tune collection thresholds, assert collection counts, or require exact allocation sizes. Both stages run in native Python and Pyodide.
Complete the starter code, then run the checks.
Results and errors will appear here.