Recently we added a feature to dateutil (issue #635, PR #672) that involves holding a cache of weak references to objects so that only values that would otherwise still be alive remain in the cache. This is useful for when you aren't necessarily caching for performance but want an invariant like:

if x == y : assert cached_func ( x ) is cached_func ( y )

The problem with this was how to test such a thing. There should be two elements to the test: the function should satisfy the invariant and when the original strong reference expires, the item should be removed from the cache. The first part is simple enough, but how do you verify that the weak reference has expired without either digging into the implementation details or holding a strong reference to the original object? I was surprised to find that some cursory googling did not turn up a canonical reference for how to do this, and discussing it with a few people at a sprint did not turn up the answer I eventually landed on, so I thought I would write up my findings here.

Problem Imagine one wanted to test the following code: from weakref import WeakValueDictionary class Example : """An example class""" def __init__ ( self , x ): self . x = x def get_example ( x ): # Either return the cached value or populate the cache return get_example . __cache . setdefault ( x , Example ( x )) def __clear_cache (): get_example . __cache = WeakValueDictionary () get_example . clear_cache = __clear_cache # Create the initial empty cache get_example . clear_cache () I can fairly easily demonstrate that this works in the REPL: >>> # Get an Example, but don't store the result anywhere >>> print ( get_example ( 'Key' )) <__main__.Example object at 0x7f3cbe059908> >>> # Calling get_example a second time gets a new instance >>> print ( get_example ( 'Key' )) <__main__.Example object at 0x7f3cbe059978> >>> # Create a strong reference to an example retrieved this way >>> key_example = get_example ( 'Key' ) >>> get_example ( 'Key' ) is key_example True The problem with this approach is that the Python standard makes no guarantees about the reuse of object IDs, so any test that relies on the fact that the ID tends to be different for different objects is going to be flaky – particularly in a test that will almost by necessity involve deleting an object. A test that checks for the existence of the key in the cache would work, but the location of the cache is an implementation detail. We want to make guarantees about the properties of the code, not its structure.