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 5import functools 6import logging 7 8 9def in_context(context_name): 10 """ 11 Call a method in the context of member variable 'context_name.' 12 13 You can use this like: 14 class Foo(object): 15 def __init__(self): 16 self._mutex = threading.RLock() 17 18 @in_context('_mutex') 19 def bar(self): 20 # Two threads can call Foo.bar safely without 21 # any other synchronization. 22 print 'Locked up tight.' 23 24 def contextless_bar(self): 25 with self._mutex: 26 print 'Locked up tight.' 27 28 With the in_context decorator, self.bar is equivalent to 29 self.contextless_bar. You can use this this to declare synchronized 30 methods in the style of Java. Similar to other locking methods, this 31 can land you in deadlock in a hurry if you're not aware of what you're 32 doing. 33 34 @param context_name string name of the context manager to look up in self. 35 36 """ 37 def wrap(func): 38 """ 39 This function will get called with the instance method pulled off 40 of self. It does not get the self object though, so we wrap yet 41 another nested function. 42 43 @param func Function object that we'll eventually call. 44 45 """ 46 @functools.wraps(func) 47 def wrapped_manager(self, *args, **kwargs): 48 """ Do the actual work of acquiring the context. 49 50 We need this layer of indirection so that we can get at self. 51 We use functools.wraps does some magic so that the function 52 names and docs are set correctly on the wrapped function. 53 54 """ 55 context = getattr(self, context_name) 56 with context: 57 return func(self, *args, **kwargs) 58 return wrapped_manager 59 return wrap 60 61 62class _CachedProperty(object): 63 64 65 def __init__(self, func, name=None): 66 self._func = func 67 self._name = name if name is not None else func.__name__ 68 69 def __get__(self, instance, owner): 70 value = self._func(instance) 71 setattr(instance, self._name, value) 72 return value 73 74 75def cached_property(func): 76 """ 77 A read-only property that is only run the first time the attribute is 78 accessed, and then the result is saved and returned on each future 79 reference. 80 81 @param func: The function to calculate the property value. 82 @returns: An object that abides by the descriptor protocol. 83 """ 84 return _CachedProperty(func) 85 86 87def test_module_available(module, raise_error=False): 88 """A decorator to test if the given module is available first before 89 calling a function. 90 91 @param module: Module object. The value should be None if the module is 92 failed to be imported. 93 @param raise_error: If true an import error will be raised on call if module 94 is not imported. 95 """ 96 97 def decorator(f): 98 """The actual decorator. 99 100 @param f: The function to call. 101 102 @return: The function to call based on the value of `module` 103 """ 104 105 def dummy_func(*args, **kargs): 106 """A dummy function silently pass.""" 107 logging.debug('Module %s is not found. Call %s is skipped.', module, 108 f) 109 if raise_error: 110 raise ImportError('Module %s is not found.' % module) 111 112 return f if module else dummy_func 113 114 return decorator 115