1# -*- coding: utf-8 -*-
2# Part of Odoo. See LICENSE file for full copyright and licensing details.
3
4__all__ = ['synchronized', 'lazy_classproperty', 'lazy_property',
5           'classproperty', 'conditional', 'lazy']
6
7from functools import wraps
8from inspect import getsourcefile
9from json import JSONEncoder
10
11
12class lazy_property(object):
13    """ Decorator for a lazy property of an object, i.e., an object attribute
14        that is determined by the result of a method call evaluated once. To
15        reevaluate the property, simply delete the attribute on the object, and
16        get it again.
17    """
18    def __init__(self, fget):
19        assert not fget.__name__.startswith('__'),\
20            "lazy_property does not support mangled names"
21        self.fget = fget
22
23    def __get__(self, obj, cls):
24        if obj is None:
25            return self
26        value = self.fget(obj)
27        setattr(obj, self.fget.__name__, value)
28        return value
29
30    @property
31    def __doc__(self):
32        return self.fget.__doc__
33
34    @staticmethod
35    def reset_all(obj):
36        """ Reset all lazy properties on the instance `obj`. """
37        cls = type(obj)
38        obj_dict = vars(obj)
39        for name in list(obj_dict):
40            if isinstance(getattr(cls, name, None), lazy_property):
41                obj_dict.pop(name)
42
43class lazy_classproperty(lazy_property):
44    """ Similar to :class:`lazy_property`, but for classes. """
45    def __get__(self, obj, cls):
46        val = self.fget(cls)
47        setattr(cls, self.fget.__name__, val)
48        return val
49
50def conditional(condition, decorator):
51    """ Decorator for a conditionally applied decorator.
52
53        Example:
54
55           @conditional(get_config('use_cache'), ormcache)
56           def fn():
57               pass
58    """
59    if condition:
60        return decorator
61    else:
62        return lambda fn: fn
63
64def synchronized(lock_attr='_lock'):
65    def decorator(func):
66        @wraps(func)
67        def wrapper(self, *args, **kwargs):
68            lock = getattr(self, lock_attr)
69            try:
70                lock.acquire()
71                return func(self, *args, **kwargs)
72            finally:
73                lock.release()
74        return wrapper
75    return decorator
76
77def frame_codeinfo(fframe, back=0):
78    """ Return a (filename, line) pair for a previous frame .
79        @return (filename, lineno) where lineno is either int or string==''
80    """
81
82    try:
83        if not fframe:
84            return "<unknown>", ''
85        for i in range(back):
86            fframe = fframe.f_back
87        try:
88            fname = getsourcefile(fframe)
89        except TypeError:
90            fname = '<builtin>'
91        lineno = fframe.f_lineno or ''
92        return fname, lineno
93    except Exception:
94        return "<unknown>", ''
95
96def compose(a, b):
97    """ Composes the callables ``a`` and ``b``. ``compose(a, b)(*args)`` is
98    equivalent to ``a(b(*args))``.
99
100    Can be used as a decorator by partially applying ``a``::
101
102         @partial(compose, a)
103         def b():
104            ...
105    """
106    @wraps(b)
107    def wrapper(*args, **kwargs):
108        return a(b(*args, **kwargs))
109    return wrapper
110
111
112class _ClassProperty(property):
113    def __get__(self, cls, owner):
114        return self.fget.__get__(None, owner)()
115
116def classproperty(func):
117    return _ClassProperty(classmethod(func))
118
119
120class lazy(object):
121    """ A proxy to the (memoized) result of a lazy evaluation::
122
123            foo = lazy(func, arg)           # func(arg) is not called yet
124            bar = foo + 1                   # eval func(arg) and add 1
125            baz = foo + 2                   # use result of func(arg) and add 2
126
127    """
128    __slots__ = ['_func', '_args', '_kwargs', '_cached_value']
129
130    def __init__(self, func, *args, **kwargs):
131        # bypass own __setattr__
132        object.__setattr__(self, '_func', func)
133        object.__setattr__(self, '_args', args)
134        object.__setattr__(self, '_kwargs', kwargs)
135
136    @property
137    def _value(self):
138        if self._func is not None:
139            value = self._func(*self._args, **self._kwargs)
140            object.__setattr__(self, '_func', None)
141            object.__setattr__(self, '_args', None)
142            object.__setattr__(self, '_kwargs', None)
143            object.__setattr__(self, '_cached_value', value)
144        return self._cached_value
145
146    def __getattr__(self, name): return getattr(self._value, name)
147    def __setattr__(self, name, value): return setattr(self._value, name, value)
148    def __delattr__(self, name): return delattr(self._value, name)
149
150    def __repr__(self):
151        return repr(self._value) if self._func is None else object.__repr__(self)
152    def __str__(self): return str(self._value)
153    def __bytes__(self): return bytes(self._value)
154    def __format__(self, format_spec): return format(self._value, format_spec)
155
156    def __lt__(self, other): return self._value < other
157    def __le__(self, other): return self._value <= other
158    def __eq__(self, other): return self._value == other
159    def __ne__(self, other): return self._value != other
160    def __gt__(self, other): return self._value > other
161    def __ge__(self, other): return self._value >= other
162
163    def __hash__(self): return hash(self._value)
164    def __bool__(self): return bool(self._value)
165
166    def __call__(self, *args, **kwargs): return self._value(*args, **kwargs)
167
168    def __len__(self): return len(self._value)
169    def __getitem__(self, key): return self._value[key]
170    def __missing__(self, key): return self._value.__missing__(key)
171    def __setitem__(self, key, value): self._value[key] = value
172    def __delitem__(self, key): del self._value[key]
173    def __iter__(self): return iter(self._value)
174    def __reversed__(self): return reversed(self._value)
175    def __contains__(self, key): return key in self._value
176
177    def __add__(self, other): return self._value.__add__(other)
178    def __sub__(self, other): return self._value.__sub__(other)
179    def __mul__(self, other): return self._value.__mul__(other)
180    def __matmul__(self, other): return self._value.__matmul__(other)
181    def __truediv__(self, other): return self._value.__truediv__(other)
182    def __floordiv__(self, other): return self._value.__floordiv__(other)
183    def __mod__(self, other): return self._value.__mod__(other)
184    def __divmod__(self, other): return self._value.__divmod__(other)
185    def __pow__(self, other): return self._value.__pow__(other)
186    def __lshift__(self, other): return self._value.__lshift__(other)
187    def __rshift__(self, other): return self._value.__rshift__(other)
188    def __and__(self, other): return self._value.__and__(other)
189    def __xor__(self, other): return self._value.__xor__(other)
190    def __or__(self, other): return self._value.__or__(other)
191
192    def __radd__(self, other): return self._value.__radd__(other)
193    def __rsub__(self, other): return self._value.__rsub__(other)
194    def __rmul__(self, other): return self._value.__rmul__(other)
195    def __rmatmul__(self, other): return self._value.__rmatmul__(other)
196    def __rtruediv__(self, other): return self._value.__rtruediv__(other)
197    def __rfloordiv__(self, other): return self._value.__rfloordiv__(other)
198    def __rmod__(self, other): return self._value.__rmod__(other)
199    def __rdivmod__(self, other): return self._value.__rdivmod__(other)
200    def __rpow__(self, other): return self._value.__rpow__(other)
201    def __rlshift__(self, other): return self._value.__rlshift__(other)
202    def __rrshift__(self, other): return self._value.__rrshift__(other)
203    def __rand__(self, other): return self._value.__rand__(other)
204    def __rxor__(self, other): return self._value.__rxor__(other)
205    def __ror__(self, other): return self._value.__ror__(other)
206
207    def __iadd__(self, other): return self._value.__iadd__(other)
208    def __isub__(self, other): return self._value.__isub__(other)
209    def __imul__(self, other): return self._value.__imul__(other)
210    def __imatmul__(self, other): return self._value.__imatmul__(other)
211    def __itruediv__(self, other): return self._value.__itruediv__(other)
212    def __ifloordiv__(self, other): return self._value.__ifloordiv__(other)
213    def __imod__(self, other): return self._value.__imod__(other)
214    def __ipow__(self, other): return self._value.__ipow__(other)
215    def __ilshift__(self, other): return self._value.__ilshift__(other)
216    def __irshift__(self, other): return self._value.__irshift__(other)
217    def __iand__(self, other): return self._value.__iand__(other)
218    def __ixor__(self, other): return self._value.__ixor__(other)
219    def __ior__(self, other): return self._value.__ior__(other)
220
221    def __neg__(self): return self._value.__neg__()
222    def __pos__(self): return self._value.__pos__()
223    def __abs__(self): return self._value.__abs__()
224    def __invert__(self): return self._value.__invert__()
225
226    def __complex__(self): return complex(self._value)
227    def __int__(self): return int(self._value)
228    def __float__(self): return float(self._value)
229
230    def __index__(self): return self._value.__index__()
231
232    def __round__(self): return self._value.__round__()
233    def __trunc__(self): return self._value.__trunc__()
234    def __floor__(self): return self._value.__floor__()
235    def __ceil__(self): return self._value.__ceil__()
236
237    def __enter__(self): return self._value.__enter__()
238    def __exit__(self, exc_type, exc_value, traceback):
239        return self._value.__exit__(exc_type, exc_value, traceback)
240
241    def __await__(self): return self._value.__await__()
242    def __aiter__(self): return self._value.__aiter__()
243    def __anext__(self): return self._value.__anext__()
244    def __aenter__(self): return self._value.__aenter__()
245    def __aexit__(self, exc_type, exc_value, traceback):
246        return self._value.__aexit__(exc_type, exc_value, traceback)
247