degal/utils.py
author Tero Marttila <terom@fixme.fi>
Fri, 03 Jul 2009 00:00:51 +0300
changeset 147 283a15412709
parent 139 d3167c40e7b9
permissions -rw-r--r--
remove obsolete shorturl.py
"""
    Miscellaneous utilities
"""

import functools

class LazyProperty (object) :
    """
        Lazy-loaded cached properties.

        This functions by overriding the class's property attribute with the actual value attribute in the instance's
        dict, causing all future attribute lookups to return that directly.

        >>> class Foo (object) :
        ...     counter = 0
        ...     @lazy_load
        ...     def foo (self) :
        ...             self.counter += 1
        ...             return self.counter
        ... 
        >>> foo = Foo(); foo.foo, foo.foo, foo.counter
        (1, 1, 1)

    """

    def __init__ (self, func) :
        """
            Initialize with no value
        """
        
        # the getter function
        self.func = func

        # the attribute name
        self.name = func.__name__

    def run (self, obj) :
        """
            Run the background func and return the to-be-cached value.

            `obj` is the object instance
        """

        return self.func(obj)
 
    def store (self, obj, value) :
        """
            Store the cached value, overring ourself
        """

#        print "[%x] %r.__dict__[%r] = %r" % (id(obj), obj, self.name, value)

        obj.__dict__[self.name] = value

    def __get__ (self, obj, owner) :
        """
            Generate the value, store it as the attribute and return it.
        """

        # compute value
        value = self.run(obj)

        # store it
        self.store(obj, value)
        
        # return it
        return value
    
class LazyIteratorProperty (LazyProperty) :
    """
        A lazy-loaded property that automatically converts an iterator/genexp into a non-mutable tuple.
    """

    def run (self, obj) :
        return tuple(self.func(obj))

lazy_load = LazyProperty
lazy_load_iter = LazyIteratorProperty

def unload (obj, *attrs) :
    """
        Un-load the named attributes from the given object.
    """
    
    for attr in attrs :
        if attr in obj.__dict__ :
            # this will drop refcounts and free resources, so it may take some execution time
            del obj.__dict__[attr]

def first (iterable) :
    """
        Returns the first item from the iterable that evaluates to True, otherwise None.

        >>> first((0, 1))
        1
        >>> first("abc")
        'a'
        >>> first(('', list(), (), False))
    """

    for item in iterable :
        if item :
            return item

    else :
        return None


# testing
if __name__ == '__main__' :
    import doctest

    doctest.testmod()