Coverage for backpack/config/serde.py: 74%

54 statements  

« prev     ^ index     » next       coverage.py v7.2.2, created at 2023-03-30 23:12 +0000

1''' Serializers/Deserializers (SerDe) convert between configuration strings (or numbers) and 

2different Python structures. 

3 

4You should subclass :class:`ConfigSerDeBase` and override the following static fields: 

5 

6- ``name``: the description of the structure this serde can convert, for example "Comma-separated 

7 list of integers". This text will be used in the documentation generated by the ConfigBase CLI. 

8- ``serialize``: Take the Python structure and returns a string representation. 

9- ``deserialize``: Take a string representation of the value and return a Python structure. 

10''' 

11 

12from typing import Any, List, Sequence, Mapping 

13from abc import ABC, abstractmethod 

14import json 

15import datetime 

16 

17class ConfigSerDeBase(ABC): 

18 ''' Defines a serializer / deserializer interface. ''' 

19 

20 description : str = 'The base serde encodes and decodes strings to themselves.' 

21 ''' A textual description how the object is encoded in the string. Will be used in docs. ''' 

22 

23 example : str = 'abcdefgh' 

24 ''' Provide a textual example of the encoded string. Will be used in docs. ''' 

25 

26 @staticmethod 

27 @abstractmethod 

28 def serialize(value: Any, metadata: Mapping[str, Any]={}) -> str: 

29 ''' Serializes a config value to a string. 

30 

31 Args: 

32 value (Any): a Python object to be serialized. 

33 metadata (Mapping[str, Any]): Additional metadata to be passed to SerDe implementations. 

34 

35 Returns: 

36 The object serialized into a string. 

37 ''' 

38 return str(value) 

39 

40 @staticmethod 

41 @abstractmethod 

42 def deserialize(value: str, metadata: Mapping[str, Any]={}) -> Any: 

43 ''' Deserializes a string to a config value. 

44 

45 Args: 

46 value (str): a Python object serialized into a string 

47 metadata (Mapping[str, Any]): Additional metadata to be passed to SerDe implementations. 

48 

49 Returns: 

50 The the restored Python object. 

51 ''' 

52 return value 

53 

54 

55class IntegerListSerDe(ConfigSerDeBase): 

56 ''' De/serializes a string containing a comma-separated list of integers.''' 

57 

58 description : str = 'Comma-separated list of integers' 

59 example: str = '0, 1, 2' 

60 

61 @staticmethod 

62 def serialize(value: Sequence[int], metadata: Mapping[str, Any]={}) -> str: 

63 ''' Serializes a list of integers into a string. 

64 

65 Args: 

66 value (Sequence[int]): The list of integers. 

67 

68 Returns: 

69 The list of integers serialized into a string. 

70 ''' 

71 sep = metadata.get('separator', ', ') 

72 return sep.join(str(e) for e in value) 

73 

74 @staticmethod 

75 def deserialize(value: str, metadata: Mapping[str, Any]={}) -> List[int]: 

76 '''Restores a list of integers from a string. 

77 

78 Args: 

79 value (str): A string containing a serialized list of integers. 

80 

81 Returns: 

82 The list of integers restored from the string. 

83 

84 Raises: 

85 Exception: exceptions related to invalid string format. 

86 ''' 

87 sep = metadata.get('separator', ',') 

88 return [int(e) for e in value.split(sep)] 

89 

90 

91class StringListSerDe(ConfigSerDeBase): 

92 ''' De/serializes a string containing a comma-separated list of strings.''' 

93 

94 description : str = 'Comma-separated list of strings' 

95 example: str = 'apple, pear, banana' 

96 

97 @staticmethod 

98 def serialize(value: Sequence[str], metadata: Mapping[str, Any]={}) -> str: 

99 ''' Serializes a list of strings into a string. 

100 

101 Args: 

102 value (Sequence[str]): The list of strings. 

103 

104 Returns: 

105 The list of strings serialized into a string. 

106 ''' 

107 sep = metadata.get('separator', ', ') 

108 return sep.join(str(e) for e in value) 

109 

110 @staticmethod 

111 def deserialize(value: str, metadata: Mapping[str, Any]={}) -> List[str]: 

112 '''Restores a list of strings from a string. 

113 

114 Args: 

115 value (str): A string containing a serialized list of strings. 

116 

117 Returns: 

118 The list of strings restored from the string. 

119 

120 Raises: 

121 Exception: exceptions related to invalid string format. 

122 ''' 

123 sep = metadata.get('separator', ',') 

124 return [str(e).strip() for e in value.split(sep)] 

125 

126 

127class JsonSerDe(ConfigSerDeBase): 

128 ''' De/serializes a string containing a JSON-encoded object.''' 

129 

130 description : str = 'JSON-encoded object' 

131 example: str = '{"foo": "bar", "list": [0, 1, 2]}' 

132 

133 @staticmethod 

134 def serialize(value: Any, metadata: Mapping[str, Any]={}) -> str: 

135 ''' Serializes a JSON object into a string. 

136 

137 Args: 

138 value (Any): A JSON-serializable object. 

139 

140 Returns: 

141 The JSON object serialized into a string. 

142 ''' 

143 return json.dumps(value) 

144 

145 @staticmethod 

146 def deserialize(value: str, metadata: Mapping[str, Any]={}) -> Any: 

147 ''' Deserializes JSON object from a string. 

148 

149 Args: 

150 value (str): A string containing a serialized JSON object. 

151 

152 Returns: 

153 The JSON object restored from the string. 

154 

155 Raises: 

156 Exception: exceptions related to invalid string format. 

157 ''' 

158 return json.loads(value) 

159 

160 

161class TimeDeltaSecondsSerDe(ConfigSerDeBase): 

162 ''' De/serializes a timedelta instance into a float number. 

163 

164 The time interval is expressed in seconds. 

165 ''' 

166 

167 name : str = 'float32 value interpreted as seconds' 

168 

169 @staticmethod 

170 def serialize(value: datetime.timedelta, metadata: Mapping[str, Any]={}) -> float: 

171 ''' Serializes a timedelta instance as a float number. 

172 

173 The time interval is expressed in seconds. ''' 

174 return value.total_seconds() 

175 

176 

177 @staticmethod 

178 def deserialize(value: float, metadata: Mapping[str, Any]={}) -> datetime.timedelta: 

179 ''' Deserializes a float value into a time interval. 

180 

181 The value is interpreted in seconds. 

182 ''' 

183 return datetime.timedelta(seconds=value)