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:22 +0000

1import functools 

2import inspect 

3 

4 

5class ProxyAttr: 

6 """ 

7 Descriptor class for wrapping attributes of the wrapped instance. 

8 

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 

14 

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) 

24 

25 def setter(self, value): 

26 return setattr(self._wrapped__, name, value) 

27 

28 setattr(owner, name, property(getter, setter)) 

29 

30 

31class ProxyFunc: 

32 """ 

33 Descriptor class for wrapping functions of the wrapped instance. 

34 

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 

40 

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__ 

45 

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) 

57 

58 functools.update_wrapper(wrap_func, func) 

59 wrap_func = staticmethod(wrap_func) 

60 

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) 

72 

73 functools.update_wrapper(wrap_func, func) 

74 wrap_func = classmethod(wrap_func) 

75 

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) 

87 

88 functools.update_wrapper(wrap_func, func) 

89 

90 setattr(owner, name, wrap_func) 

91 

92 

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 

102 

103 

104class Proxy(metaclass=ProxyMeta): 

105 """ 

106 A proxy class implementing the Facade pattern. 

107 

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 

112 

113 def __init__(self, instance): 

114 """ 

115 Initializes the proxy by setting the wrapped instance. 

116 

117 :param instance: The instance of the class to be wrapped. 

118 """ 

119 object.__setattr__(self, "_wrapped__", instance) 

120 

121 @property 

122 def __class__(self): 

123 return type(self)._wrapped__