terom@44: """ terom@44: Miscellaneous utilities terom@44: """ terom@44: terom@59: import functools terom@23: terom@59: class LazyProperty (property) : terom@12: """ terom@59: Lazy-loaded properties terom@12: """ terom@12: terom@75: ATTRNAME = '_LazyProperty_values' terom@75: terom@59: def __init__ (self, func) : terom@59: """ terom@59: Initialize with no value terom@59: """ terom@12: terom@59: super(LazyProperty, self).__init__(func) terom@75: terom@75: self.func = func terom@75: self.name = func.__name__ terom@19: terom@59: self.value = None terom@26: terom@73: def run (self, obj) : terom@59: """ terom@59: Run the background func and return the to-be-cached value terom@59: """ terom@28: terom@75: return self.func(obj) terom@73: terom@75: def obj_dict (self, obj): terom@75: """ terom@75: Get the lazy-property dict of the given obj terom@75: """ terom@75: terom@75: dict = getattr(obj, self.ATTRNAME, None) terom@75: terom@75: if dict is None : terom@75: dict = {} terom@75: terom@75: setattr(obj, self.ATTRNAME, dict) terom@75: terom@75: return dict terom@75: terom@73: def get (self, obj) : terom@73: """ terom@73: Return the cached value terom@73: """ terom@59: terom@75: return self.obj_dict(obj)[self.name] terom@73: terom@73: def set (self, obj, value) : terom@73: """ terom@73: Set the cached value terom@73: """ terom@73: terom@75: self.obj_dict(obj)[self.name] = value terom@73: terom@73: def test (self, obj) : terom@73: """ terom@73: Tests if a value is set terom@73: """ terom@73: terom@75: return self.name in self.obj_dict(obj) terom@73: terom@75: def __get__ (self, obj, owner) : terom@59: """ terom@59: Return the cached value if it exists, otherwise, call the func terom@59: """ terom@59: terom@75: if not obj : terom@75: return self terom@75: terom@75: if not self.test(obj) : terom@59: # generate it terom@73: self.set(obj, self.run(obj)) terom@59: terom@73: return self.get(obj) terom@59: terom@59: class LazyIteratorProperty (LazyProperty) : terom@59: """ terom@59: A lazy-loaded property that automatically converts an iterator/genexp into a list. terom@59: """ terom@59: terom@73: def run (self, obj) : terom@59: """ terom@59: Wrap LazyProperty.run to return a list terom@59: """ terom@59: terom@73: return list(super(LazyIteratorProperty, self).run(obj)) terom@59: terom@59: lazy_load = LazyProperty terom@59: lazy_load_iter = LazyIteratorProperty terom@59: