terom@44: """ terom@44: Miscellaneous utilities terom@44: """ terom@44: terom@59: import functools terom@23: terom@59: class LazyProperty (property) : terom@12: """ terom@84: Lazy-loaded cached properties. terom@84: terom@84: This functions by overriding the class's property attribute with the actual value attribute in the instance's terom@84: dict, causing all future attribute lookups to return that directly. terom@12: """ terom@12: 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@84: # the getter function terom@75: self.func = func terom@84: terom@84: # the attribute name terom@75: self.name = func.__name__ terom@19: terom@73: def run (self, obj) : terom@59: """ terom@84: Run the background func and return the to-be-cached value. terom@84: terom@84: `obj` is the object instance terom@59: """ terom@28: terom@75: return self.func(obj) terom@73: terom@84: def store (self, obj, value) : terom@73: """ terom@84: Store the cached value, overring ourself terom@73: """ terom@84: terom@84: obj.__dict__[self.name] = value terom@73: terom@75: def __get__ (self, obj, owner) : terom@59: """ terom@84: Generate the value, store it as the attribute and return it. terom@59: """ terom@84: terom@84: # compute value terom@84: value = self.run(obj) terom@59: terom@84: # store it terom@84: self.store(obj, value) terom@84: terom@84: # return it terom@84: return value 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: