Coverage for lynceus/utils/lynceus_dict.py: 100%

27 statements  

« prev     ^ index     » next       coverage.py v7.10.0, created at 2025-07-29 08:46 +0000

1class LynceusDict(dict): 

2 """ 

3 Little dict enhancement allowing [.] attribute access, in addition to usual [key] ones. 

4 """ 

5 

6 def __getattr__(self, key): 

7 """ 

8 Enable attribute-style access to dictionary keys. 

9 

10 Allows accessing dictionary items using dot notation (e.g., obj.key) 

11 in addition to standard bracket notation (e.g., obj['key']). 

12 

13 Parameters 

14 ---------- 

15 key : str 

16 The dictionary key to access as an attribute. 

17 

18 Returns 

19 ------- 

20 Any 

21 The value associated with the key. 

22 

23 Raises 

24 ------ 

25 AttributeError 

26 If the key does not exist in the dictionary. 

27 """ 

28 try: 

29 return self[key] 

30 except KeyError as key_error: 

31 raise AttributeError from key_error 

32 

33 def __setattr__(self, key, value): 

34 """ 

35 Enable attribute-style assignment to dictionary keys. 

36 

37 Allows setting dictionary items using dot notation (e.g., obj.key = value) 

38 in addition to standard bracket notation (e.g., obj['key'] = value). 

39 

40 Parameters 

41 ---------- 

42 key : str 

43 The dictionary key to set as an attribute. 

44 value : Any 

45 The value to assign to the key. 

46 """ 

47 self[key] = value 

48 

49 def __delattr__(self, key): 

50 """ 

51 Enable attribute-style deletion of dictionary keys. 

52 

53 Allows deleting dictionary items using dot notation (e.g., del obj.key) 

54 in addition to standard bracket notation (e.g., del obj['key']). 

55 

56 Parameters 

57 ---------- 

58 key : str 

59 The dictionary key to delete as an attribute. 

60 

61 Raises 

62 ------ 

63 AttributeError 

64 If the key does not exist in the dictionary. 

65 """ 

66 try: 

67 del self[key] 

68 except KeyError as key_error: 

69 raise AttributeError from key_error 

70 

71 @staticmethod 

72 def _do_to_lynceus_dict(obj, *, max_depth: int, depth: int = 0): 

73 """ 

74 Internal recursive method to convert nested objects to LynceusDict instances. 

75 

76 Recursively traverses an object structure and converts dictionaries and 

77 objects with __dict__ attributes to LynceusDict instances up to a specified 

78 maximum depth to prevent infinite recursion. 

79 

80 Parameters 

81 ---------- 

82 obj : Any 

83 The object to convert. 

84 max_depth : int 

85 Maximum recursion depth allowed. 

86 depth : int, optional 

87 Current recursion depth. Defaults to 0. 

88 

89 Returns 

90 ------- 

91 Any 

92 The converted object with nested dictionaries as LynceusDict instances, 

93 or the original object if max_depth is reached or no conversion is needed. 

94 """ 

95 if depth >= max_depth: 

96 return obj 

97 

98 if isinstance(obj, dict): 

99 return LynceusDict( 

100 { 

101 k: LynceusDict._do_to_lynceus_dict( 

102 v, max_depth=max_depth, depth=depth + 1 

103 ) 

104 for k, v in obj.items() 

105 } 

106 ) 

107 

108 if hasattr(obj, "__dict__"): 

109 return LynceusDict( 

110 { 

111 k: LynceusDict._do_to_lynceus_dict( 

112 v, max_depth=max_depth, depth=depth + 1 

113 ) 

114 for k, v in obj.__dict__.items() 

115 } 

116 ) 

117 

118 if isinstance(obj, list): 

119 return [ 

120 LynceusDict._do_to_lynceus_dict( 

121 elem, max_depth=max_depth, depth=depth + 1 

122 ) 

123 for elem in obj 

124 ] 

125 

126 return obj 

127 

128 @staticmethod 

129 def to_lynceus_dict(obj, *, max_depth: int = 4): 

130 """ 

131 Convert an object to a LynceusDict with nested dictionary conversion. 

132 

133 Converts dictionaries, objects with __dict__ attributes, and lists containing 

134 such objects to LynceusDict instances recursively up to the specified depth. 

135 This enables dot notation access throughout the nested structure. 

136 

137 Parameters 

138 ---------- 

139 obj : Any 

140 The object to convert to LynceusDict. 

141 max_depth : int, optional 

142 Maximum recursion depth to prevent infinite 

143 recursion. Defaults to 4. 

144 

145 Returns 

146 ------- 

147 LynceusDict | Any 

148 A LynceusDict instance if the object is convertible, 

149 otherwise returns the original object. 

150 

151 Examples 

152 -------- 

153 >>> data = {'user': {'name': 'John', 'age': 30}} 

154 >>> lynceus_data = LynceusDict.to_lynceus_dict(data) 

155 >>> lynceus_data.user.name # Access using dot notation 

156 'John' 

157 """ 

158 return LynceusDict._do_to_lynceus_dict(obj, max_depth=max_depth)