Home | History | Annotate | Download | only in common_lib
      1 # Copyright (c) 2013 The Chromium OS Authors. All rights reserved.
      2 # Use of this source code is governed by a BSD-style license that can be
      3 # found in the LICENSE file.
      4 
      5 import functools
      6 
      7 
      8 def in_context(context_name):
      9     """
     10     Call a method in the context of member variable 'context_name.'
     11 
     12     You can use this like:
     13     class Foo(object):
     14         def __init__(self):
     15             self._mutex = threading.RLock()
     16 
     17         @in_context('_mutex')
     18         def bar(self):
     19             # Two threads can call Foo.bar safely without
     20             # any other synchronization.
     21             print 'Locked up tight.'
     22 
     23         def contextless_bar(self):
     24             with self._mutex:
     25                 print 'Locked up tight.'
     26 
     27     With the in_context decorator, self.bar is equivalent to
     28     self.contextless_bar.  You can use this this to declare synchronized
     29     methods in the style of Java.  Similar to other locking methods, this
     30     can land you in deadlock in a hurry if you're not aware of what you're
     31     doing.
     32 
     33     @param context_name string name of the context manager to look up in self.
     34 
     35     """
     36     def wrap(func):
     37         """
     38         This function will get called with the instance method pulled off
     39         of self.  It does not get the self object though, so we wrap yet
     40         another nested function.
     41 
     42         @param func Function object that we'll eventually call.
     43 
     44         """
     45         @functools.wraps(func)
     46         def wrapped_manager(self, *args, **kwargs):
     47             """ Do the actual work of acquiring the context.
     48 
     49             We need this layer of indirection so that we can get at self.
     50             We use functools.wraps does some magic so that the function
     51             names and docs are set correctly on the wrapped function.
     52 
     53             """
     54             context = getattr(self, context_name)
     55             with context:
     56                 return func(self, *args, **kwargs)
     57         return wrapped_manager
     58     return wrap
     59 
     60 
     61 class _Property(object):
     62     def __init__(self, func):
     63         self._func = func
     64 
     65     def __get__(self, obj, type=None):
     66         if not hasattr(obj, '_property_cache'):
     67             obj._property_cache = {}
     68         if self._func not in obj._property_cache:
     69             obj._property_cache[self._func] = self._func(obj)
     70         return obj._property_cache[self._func]
     71 
     72 
     73 def cached_property(func):
     74     """
     75     A read-only property that is only run the first time the attribute is
     76     accessed, and then the result is saved and returned on each future
     77     reference.
     78 
     79     @param func: The function to calculate the property value.
     80     @returns: An object that abides by the descriptor protocol.
     81     """
     82     return _Property(func)
     83 
     84