[IMP] don't alter call-assets's template_attributes in-place, create fake one for...
[odoo/odoo.git] / openerp / tools / func.py
1 # -*- coding: utf-8 -*-
2 ##############################################################################
3 #
4 #    OpenERP, Open Source Management Solution
5 #    Copyright (C) 2004-2009 Tiny SPRL (<http://tiny.be>).
6 #    Copyright (C) 2010, 2014 OpenERP s.a. (<http://openerp.com>).
7 #
8 #    This program is free software: you can redistribute it and/or modify
9 #    it under the terms of the GNU Affero General Public License as
10 #    published by the Free Software Foundation, either version 3 of the
11 #    License, or (at your option) any later version.
12 #
13 #    This program is distributed in the hope that it will be useful,
14 #    but WITHOUT ANY WARRANTY; without even the implied warranty of
15 #    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16 #    GNU Affero General Public License for more details.
17 #
18 #    You should have received a copy of the GNU Affero General Public License
19 #    along with this program.  If not, see <http://www.gnu.org/licenses/>.
20 #
21 ##############################################################################
22
23 __all__ = ['synchronized', 'lazy_property']
24
25 from functools import wraps
26 from inspect import getsourcefile
27
28
29 class lazy_property(object):
30     """ Decorator for a lazy property of an object, i.e., an object attribute
31         that is determined by the result of a method call evaluated once. To
32         reevaluate the property, simply delete the attribute on the object, and
33         get it again.
34     """
35     def __init__(self, fget):
36         self.fget = fget
37         self.name = fget.__name__
38
39     def __get__(self, obj, cls):
40         if obj is None:
41             return self
42         value = self.fget(obj)
43         setattr(obj, self.name, value)
44         return value
45
46     @staticmethod
47     def reset_all(obj):
48         """ Reset all lazy properties on the instance `obj`. """
49         cls = type(obj)
50         obj_dict = obj.__dict__
51         for name in obj_dict.keys():
52             if isinstance(getattr(cls, name, None), lazy_property):
53                 obj_dict.pop(name)
54
55
56 def synchronized(lock_attr='_lock'):
57     def decorator(func):
58         @wraps(func)
59         def wrapper(self, *args, **kwargs):
60             lock = getattr(self, lock_attr)
61             try:
62                 lock.acquire()
63                 return func(self, *args, **kwargs)
64             finally:
65                 lock.release()
66         return wrapper
67     return decorator
68
69 def frame_codeinfo(fframe, back=0):
70     """ Return a (filename, line) pair for a previous frame .
71         @return (filename, lineno) where lineno is either int or string==''
72     """
73     
74     try:
75         if not fframe:
76             return "<unknown>", ''
77         for i in range(back):
78             fframe = fframe.f_back
79         try:
80             fname = getsourcefile(fframe)
81         except TypeError:
82             fname = '<builtin>'
83         lineno = fframe.f_lineno or ''
84         return fname, lineno
85     except Exception:
86         return "<unknown>", ''
87
88 def compose(a, b):
89     """ Composes the callables ``a`` and ``b``. ``compose(a, b)(*args)`` is
90     equivalent to ``a(b(*args))``.
91
92     Can be used as a decorator by partially applying ``a``::
93
94          @partial(compose, a)
95          def b():
96             ...
97     """
98     @wraps(b)
99     def wrapper(*args, **kwargs):
100         return a(b(*args, **kwargs))
101     return wrapper
102
103 # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: