Created
December 8, 2018 17:37
Python module level property and cached property decorator
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
from mod_prop import mod_property, cached_mod_property | |
@mod_property | |
def my_prop(): | |
print('my_prop called') | |
return 42 | |
@cached_mod_property | |
def my_cached_prop(): | |
print('my_cached_property called') | |
return '24' |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import caching_mod | |
print(caching_mod) | |
print('p', caching_mod.my_prop) | |
print('q', caching_mod.my_prop) | |
from caching_mod import my_prop | |
print('r', caching_mod.my_prop) | |
print('s', caching_mod.my_prop) | |
from caching_mod import my_cached_prop | |
print('t') | |
print('u', my_cached_prop) | |
print('v', caching_mod.my_cached_prop) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
""" Module level property and cached property decorator """ | |
import sys | |
import types | |
import functools | |
def update_module_class(mod): | |
class CachingModule(types.ModuleType): | |
pass | |
mod.__class__ = CachingModule | |
def mod_property(func, cached=False): | |
func_name = func.__name__ | |
if '.' in func_name: | |
raise ValueError('mod_property only applicable to top-level module functions') | |
func_mod = sys.modules[func.__module__] | |
if func_mod.__class__ == types.ModuleType: | |
update_module_class(func_mod) | |
elif func_mod.__class__.__name__ != 'CachingModule': | |
raise RuntimeError(f'mod_property incompatible with module type: {func_mod.__name__}({func_mod.__class__.__qualname__})') | |
@functools.wraps(func) | |
def wrapper(mod): | |
value = func() | |
if cached: | |
setattr(func_mod.__class__, func_name, value) | |
delattr(func_mod, func_name) | |
return value | |
wrapper.__name__ = func_name | |
setattr(func_mod.__class__, func_name, property(wrapper)) | |
return wrapper | |
def cached_mod_property(func): | |
return mod_property(func, cached=True) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment