Clear all lru_cache in Python
Asked Answered
A

2

12

I have functions in python that have caches with lru_cache e.g.

 @lru_cache(maxsize=None)
 def my_function():
    ...

While i can individually clear the caches with e.g. my_function.cache_clear() is there any way of clearing the caches of every function at once? [I was thinking that maybe there is a way of returning all function names loaded in memory, and then loop over them clearing the cache from each].

I'm specifically looking to implement as part of a fall-back, for cases where say 90% of the memory on my machine gets used.

Adenitis answered 26/10, 2016 at 23:32 Comment(3)
Yes, the decorated function now has the method my_function.cache_clear(). You can also get stats with my_function.cache_info(). See lru_cacheVins
@Vins - sure, aware of both of those, question though is rather is there a way of applying those methods to every decorated functions (i.e. clear all the lru_cache's).Adenitis
No, there is no simple way to clear all decorated function caches, they are all independent. You could create a registry of all those functions and then iterate through them to clear.Vins
F
13

You can create a modified decorator which also takes note of the cached functions:

cached_functions = []

def clearable_lru_cache(*args, **kwargs):
    def decorator(func):
        func = lru_cache(*args, **kwargs)(func)
        cached_functions.append(func)
        return func

    return decorator

def clear_all_cached_functions():
    for func in cached_functions:
        func.cache_clear()

If you really want you can also use monkey patching to replace the original decorator.

Test:

@clearable_lru_cache()
def foo(x):
    print('foo', x)

@clearable_lru_cache()
def bar(x):
    print('bar', x)

for i in [1, 2]:
    for j in [1, 2]:
        print('Calling functions')
        for k in [1, 2, 3]:
            for f in [foo, bar]:
                f(k)
        print('Functions called - if you saw nothing they were cached')
    print('Clearing cache')
    clear_all_cached_functions()

Output:

Calling functions
foo 1
bar 1
foo 2
bar 2
foo 3
bar 3
Functions called - if you saw nothing they were cached
Calling functions
Functions called - if you saw nothing they were cached
Clearing cache
Calling functions
foo 1
bar 1
foo 2
bar 2
foo 3
bar 3
Functions called - if you saw nothing they were cached
Calling functions
Functions called - if you saw nothing they were cached
Clearing cache
Fondly answered 27/10, 2016 at 0:42 Comment(0)
C
19

maybe there is a way of returning all function names loaded in memory, and then loop over them clearing the cache from each

Yes that is possible as well:

import functools
import gc

gc.collect()
wrappers = [
    a for a in gc.get_objects() 
    if isinstance(a, functools._lru_cache_wrapper)]

for wrapper in wrappers:
    wrapper.cache_clear()

I'm specifically looking to implement as part of a fall-back, for cases where say 90% of the memory on my machine gets used.

This is code you will probably not use under normal circumstances, but can be useful for debugging.

In my particular case I wanted to clear some dangling file handles from matplotlib (in order to focus on my own loose file handles), so it was not possible to use the solution by Alex Hall.

Circumpolar answered 5/6, 2018 at 11:40 Comment(1)
Inheriting a codebase that was so hard to test because of the reliance on lru_caches that shared state between tests.Borg
F
13

You can create a modified decorator which also takes note of the cached functions:

cached_functions = []

def clearable_lru_cache(*args, **kwargs):
    def decorator(func):
        func = lru_cache(*args, **kwargs)(func)
        cached_functions.append(func)
        return func

    return decorator

def clear_all_cached_functions():
    for func in cached_functions:
        func.cache_clear()

If you really want you can also use monkey patching to replace the original decorator.

Test:

@clearable_lru_cache()
def foo(x):
    print('foo', x)

@clearable_lru_cache()
def bar(x):
    print('bar', x)

for i in [1, 2]:
    for j in [1, 2]:
        print('Calling functions')
        for k in [1, 2, 3]:
            for f in [foo, bar]:
                f(k)
        print('Functions called - if you saw nothing they were cached')
    print('Clearing cache')
    clear_all_cached_functions()

Output:

Calling functions
foo 1
bar 1
foo 2
bar 2
foo 3
bar 3
Functions called - if you saw nothing they were cached
Calling functions
Functions called - if you saw nothing they were cached
Clearing cache
Calling functions
foo 1
bar 1
foo 2
bar 2
foo 3
bar 3
Functions called - if you saw nothing they were cached
Calling functions
Functions called - if you saw nothing they were cached
Clearing cache
Fondly answered 27/10, 2016 at 0:42 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.