Coverage for adhoc-cicd-odoo-odoo / odoo / tools / facade.py: 64%
71 statements
« prev ^ index » next coverage.py v7.13.4, created at 2026-03-09 18:15 +0000
« prev ^ index » next coverage.py v7.13.4, created at 2026-03-09 18:15 +0000
1import functools
2import inspect
5class ProxyAttr:
6 """
7 Descriptor class for wrapping attributes of the wrapped instance.
9 Used with the `Proxy` class, this class is used to set exposed attributes of the wrapped instance while providing
10 optional type casting.
11 """
12 def __init__(self, cast=False):
13 self._cast__ = cast
15 def __set_name__(self, owner, name):
16 cast = self._cast__
17 if cast:
18 def getter(self):
19 value = getattr(self._wrapped__, name)
20 return cast(value) if value is not None else None
21 else:
22 def getter(self):
23 return getattr(self._wrapped__, name)
25 def setter(self, value):
26 return setattr(self._wrapped__, name, value)
28 setattr(owner, name, property(getter, setter))
31class ProxyFunc:
32 """
33 Descriptor class for wrapping functions of the wrapped instance.
35 Used with the `Proxy` class, this class is used to set exposed functions of the wrapped instance
36 while also allowing optional type casting on return values.
37 """
38 def __init__(self, cast=False):
39 self._cast__ = cast
41 def __set_name__(self, owner, name):
42 func = getattr(owner._wrapped__, name)
43 descriptor = inspect.getattr_static(owner._wrapped__, name)
44 cast = self._cast__
46 if isinstance(descriptor, staticmethod): 46 ↛ 47line 46 didn't jump to line 47 because the condition on line 46 was never true
47 if cast:
48 def wrap_func(*args, **kwargs):
49 result = func(*args, **kwargs)
50 return cast(result) if result is not None else None
51 elif cast is None:
52 def wrap_func(*args, **kwargs):
53 func(*args, **kwargs)
54 else:
55 def wrap_func(*args, **kwargs):
56 return func(*args, **kwargs)
58 functools.update_wrapper(wrap_func, func)
59 wrap_func = staticmethod(wrap_func)
61 elif isinstance(descriptor, classmethod):
62 if cast:
63 def wrap_func(cls, *args, **kwargs):
64 result = func(*args, **kwargs)
65 return cast(result) if result is not None else None
66 elif cast is None: 66 ↛ 67line 66 didn't jump to line 67 because the condition on line 66 was never true
67 def wrap_func(cls, *args, **kwargs):
68 func(*args, **kwargs)
69 else:
70 def wrap_func(cls, *args, **kwargs):
71 return func(*args, **kwargs)
73 functools.update_wrapper(wrap_func, func)
74 wrap_func = classmethod(wrap_func)
76 else:
77 if cast:
78 def wrap_func(self, *args, **kwargs):
79 result = func(self._wrapped__, *args, **kwargs)
80 return cast(result) if result is not None else None
81 elif cast is None:
82 def wrap_func(self, *args, **kwargs):
83 func(self._wrapped__, *args, **kwargs)
84 else:
85 def wrap_func(self, *args, **kwargs):
86 return func(self._wrapped__, *args, **kwargs)
88 functools.update_wrapper(wrap_func, func)
90 setattr(owner, name, wrap_func)
93class ProxyMeta(type):
94 def __new__(cls, clsname, bases, attrs):
95 attrs.update({func: ProxyFunc() for func in ("__repr__", "__str__") if func not in attrs})
96 proxy_class = super().__new__(cls, clsname, bases, attrs)
97 # To preserve the docstring, signature, code of the wrapped class
98 # `updated` to an emtpy list so it doesn't copy the `__dict__`
99 # See `functools.WRAPPER_ASSIGNMENTS` and `functools.WRAPPER_UPDATES`
100 functools.update_wrapper(proxy_class, proxy_class._wrapped__, updated=[])
101 return proxy_class
104class Proxy(metaclass=ProxyMeta):
105 """
106 A proxy class implementing the Facade pattern.
108 This class delegates to an underlying instance while exposing a curated subset of its attributes and methods.
109 Useful for controlling access, simplifying interfaces, or adding cross-cutting concerns.
110 """
111 _wrapped__ = object
113 def __init__(self, instance):
114 """
115 Initializes the proxy by setting the wrapped instance.
117 :param instance: The instance of the class to be wrapped.
118 """
119 object.__setattr__(self, "_wrapped__", instance)
121 @property
122 def __class__(self):
123 return type(self)._wrapped__