Changeset 40919
- Timestamp:
- Oct 8, 2019, 11:42:36 AM (7 years ago)
- Location:
- branches/ipp-259_genericise_backups/tools/backups/utils
- Files:
-
- 2 edited
-
config_parse_helper.py (modified) (6 diffs)
-
config_parse_helper_test.py (modified) (1 diff)
Legend:
- Unmodified
- Added
- Removed
-
branches/ipp-259_genericise_backups/tools/backups/utils/config_parse_helper.py
r40915 r40919 3 3 from backups.utils.errors import ConfigError, ValidationError 4 4 5 # The as configs should be parsed should be pretty uniform. There's a limited6 # amount that can be done with the actual loading.7 8 # Each backup file should be expecting particular lines from a config. So, this9 # means a schema can be made for what each backup expects.10 11 5 12 6 class SpecialSchemaProcessing(Enum): 13 """These enums define how a config line should be processed if it is a list 7 """These enums define how processing should be applied to a string to 8 separate it out into list. 14 9 """ 15 10 commma_separated = 1 … … 18 13 19 14 class ConfigSchemaLine(): 20 """Details the name of key, whether the option is required, 21 and type expected""" 15 """Defines a config line by what the key is, if it is required or optional, 16 the type that the value should be processed as, and any special processing required 17 """ 22 18 23 19 def __init__(self, key: str, required: bool, expected_type, special_processing: SpecialSchemaProcessing=None): … … 30 26 self.special_processing = special_processing 31 27 28 def __str__(self): 29 return (f'<{self.__class__.__name__} - ' 30 f'Key:{self.key}, ' 31 f'Required:{self.required}, ' 32 f'Type:{self.expected_type}, ' 33 f'Processing: {self.special_processing}' 34 f'>') 35 32 36 33 37 class ConfigSchema(): 34 """Defines the schema to be used""" 38 """Defines a schema to be used. Consists of a single section name and 39 multiple config lines""" 35 40 36 41 def __init__(self, section_name, options: [ConfigSchemaLine]): … … 52 57 self.options[line.key] = line 53 58 59 def key_in_options(self, key: str) -> bool: 60 return key in self.options.keys() 61 62 def get_schema_line(self, key: str) -> ConfigSchemaLine: 63 if not self.key_in_options(key): 64 raise KeyError(f"KeyError: '{key}' is not present in schema") 65 return self.options[key] 66 67 def __str__(self): 68 options_strs = [ o.__str__() for o in self.options] 69 return (f'{self.__class__.__name__} with {options_strs}') 70 54 71 55 72 def _split_string_to_list(items: str, separator=',') -> [str]: … … 60 77 61 78 def parse_config(config: ConfigParser(), schema: ConfigSchema) -> dict: 62 # attempt to parse everything as intended. 79 """For a given config it will extract all values defined by the schema 80 and return them as a dictionary of the keys and the assoicated value as the 81 type defined by the schema 82 """ 63 83 parsed_items = {} 64 84 65 85 section = schema.section_name 66 for csl in schema.options: 86 for csl_key in schema.options: 87 csl = schema.get_schema_line(csl_key) 88 print(csl) 67 89 68 if config.has_option(section, csl.key): 69 90 if config.has_option(section, csl_key): 70 91 if csl.expected_type is bool: 71 92 parsed_items[csl.key] = config.getboolean(section, csl.key) 72 elif csl.expected is int:93 elif csl.expected_type is int: 73 94 parsed_items[csl.key] = config.getint(section, csl.key) 74 95 elif csl.expected_type is float: … … 79 100 if csl.special_processing == SpecialSchemaProcessing.commma_separated: 80 101 items = config[section][csl.key] 81 parsed_items[csl.key] = _split_string_to_list(items, '')82 # parse comma_separated stuff102 splitup = _split_string_to_list(items, ',') 103 parsed_items[csl.key] = splitup 83 104 elif csl.special_processing == SpecialSchemaProcessing.space_separated: 84 105 items = config[section][csl.key] 85 parsed_items[csl.key] = _split_string_to_list(items, ',')86 # space separated processing106 splitup = _split_string_to_list(items, ' ') 107 parsed_items[csl.key] = splitup 87 108 else: 88 raise ConfigError( f"ConfigError: Don't know how to process special schema {csl.special_processing}")109 raise ConfigError(config, f"ConfigError: Don't know how to process special schema {csl.special_processing}") 89 110 else: 90 raise ConfigError( f"ConfigError: Type '{csl.expected_type}' cannot be processed - need to modify {__name__}")111 raise ConfigError(config, f"ConfigError: Type '{csl.expected_type}' cannot be processed - need to modify {__name__}") 91 112 92 113 else: 93 114 if csl.required: 94 raise ConfigError( f"ConfigError: Key '{csl.key}' is required but was not found in config")115 raise ConfigError(config, f"ConfigError: Key '{csl.key}' is required but was not found in config") 95 116 96 117 return parsed_items -
branches/ipp-259_genericise_backups/tools/backups/utils/config_parse_helper_test.py
r40915 r40919 134 134 135 135 136 # class TestParsingConfig(): 137 138 # def test_parsing_fails_with_missing_required_key(self): 139 # assert False is True 140 141 # def test_parsing_schema_with_ints(self): 142 # assert False is True 143 144 # def test_parsing_schema_with_floats(self): 145 # assert False is True 146 147 # def test_parsing_schema_with_bools(self): 148 # assert False is True 149 150 # def test_parsing_schema_with_strs(self): 151 # assert False is True 152 153 # def test_parsing_schema_with_lists_comma_separated(self): 154 # assert False is True 155 156 # def test_parsing_schema_with_lists_space_separated(self): 157 # assert False is True 158 159 # def test_parsing_schema_fails_with_invalid_key(self): 160 # assert False is True 161 162 # def test_parsing_schema_fails_with_unknown_type(self): 163 # assert False is True 164 165 # def test_parsing_schema_fails_with_unknown_special_enum(self): 166 # assert False is True 167 168 # def parse_config(config: ConfigParser(), schema: ConfigSchema) -> dict: 136 class TestParsingConfig(): 137 138 def make_simple_config(self, section_name='DEFAULT') -> helper.ConfigParser: 139 config = helper.ConfigParser() 140 config[section_name] = \ 141 { 'inty' : '3' 142 , 'floaty' : '3.141' 143 , 'mr_bool' : 'True' 144 , 'stringer' : "bell" 145 , 'listo_comma' : "i,am,comma,separated" 146 , 'listo_space' : "yo it's time for space!" 147 } 148 return config 149 150 def test_parsing_fails_with_missing_required_key(self): 151 config = helper.ConfigParser() 152 config['DEFAULT'] = { 'not_the_required_key' : False } 153 154 csl1 = helper.ConfigSchemaLine('Required_key', True, bool) 155 schema = helper.ConfigSchema('DEFAULT', [csl1] ) 156 157 with pytest.raises(ConfigError): 158 helper.parse_config(config, schema) 159 160 def test_schema_of_only_optional_values_is_ok(self): 161 csl1 = helper.ConfigSchemaLine('not_required', False, bool) 162 csl2 = helper.ConfigSchemaLine('not_required_either', False, float) 163 schema = helper.ConfigSchema('DEFAULT', [csl1, csl2] ) 164 config = self.make_simple_config() 165 result = helper.parse_config(config, schema) 166 assert result == {} 167 168 def test_parsing_schema_with_ints(self): 169 csl1 = helper.ConfigSchemaLine('inty', False, int) 170 schema = helper.ConfigSchema('DEFAULT', [csl1] ) 171 config = self.make_simple_config() 172 result = helper.parse_config(config, schema) 173 assert result == { 'inty' : 3} 174 175 def test_parsing_schema_with_floats(self): 176 csl1 = helper.ConfigSchemaLine('floaty', False, float) 177 schema = helper.ConfigSchema('DEFAULT', [csl1] ) 178 config = self.make_simple_config() 179 result = helper.parse_config(config, schema) 180 assert result == { 'floaty' : 3.141 } 181 182 def test_parsing_schema_with_bools(self): 183 csl1 = helper.ConfigSchemaLine('mr_bool', True, bool) 184 schema = helper.ConfigSchema('DEFAULT', [csl1] ) 185 config = self.make_simple_config() 186 result = helper.parse_config(config, schema) 187 assert result == { 'mr_bool' : True } 188 189 def test_parsing_schema_with_strs(self): 190 csl1 = helper.ConfigSchemaLine('stringer', True, str) 191 schema = helper.ConfigSchema('DEFAULT', [csl1] ) 192 config = self.make_simple_config() 193 result = helper.parse_config(config, schema) 194 assert result == { 'stringer' : 'bell' } 195 196 def test_parsing_schema_with_lists_comma_separated(self): 197 csl1 = helper.ConfigSchemaLine('listo_comma', False, list, 198 helper.SpecialSchemaProcessing.commma_separated) 199 schema = helper.ConfigSchema('DEFAULT', [csl1] ) 200 config = self.make_simple_config() 201 result = helper.parse_config(config, schema) 202 assert result == { 'listo_comma' : [ "i" 203 , "am" 204 , "comma" 205 , "separated" 206 ] 207 } 208 209 def test_parsing_schema_with_lists_space_separated(self): 210 csl1 = helper.ConfigSchemaLine('listo_space', False, list, 211 helper.SpecialSchemaProcessing.space_separated) 212 schema = helper.ConfigSchema('DEFAULT', [csl1] ) 213 config = self.make_simple_config() 214 result = helper.parse_config(config, schema) 215 assert result == { 'listo_space' : [ "yo" 216 , "it's" 217 , "time" 218 , "for" 219 , "space!" 220 ] 221 } 222 223 def test_parsing_as_different_types_always_ok_for_str_and_list(self): 224 config = self.make_simple_config() 225 226 csl_float = helper.ConfigSchemaLine('floaty', True, float) 227 csl_int = helper.ConfigSchemaLine('floaty', True, int) 228 csl_bool = helper.ConfigSchemaLine('floaty', True, bool) 229 csl_str = helper.ConfigSchemaLine('floaty', True, str) 230 csl_list = helper.ConfigSchemaLine('floaty', True, list, 231 helper.SpecialSchemaProcessing.commma_separated) 232 233 schema = helper.ConfigSchema('DEFAULT', [csl_float]) 234 result = helper.parse_config(config, schema) 235 assert result == { 'floaty' : 3.141 } 236 assert type(result['floaty']) is float 237 238 schema = helper.ConfigSchema('DEFAULT', [csl_int]) 239 with pytest.raises(ValueError): 240 helper.parse_config(config, schema) 241 242 schema = helper.ConfigSchema('DEFAULT', [csl_bool]) 243 with pytest.raises(ValueError): 244 helper.parse_config(config, schema) 245 246 schema = helper.ConfigSchema('DEFAULT', [csl_str]) 247 result = helper.parse_config(config, schema) 248 assert result == { 'floaty' : '3.141' } 249 assert type(result['floaty']) is str 250 251 schema = helper.ConfigSchema('DEFAULT', [csl_list]) 252 result = helper.parse_config(config, schema) 253 assert result == { 'floaty' : ['3.141'] } 254 assert type(result['floaty']) is list 255 256 def test_parsing_schema_fails_with_unknown_type(self): 257 csl1 = helper.ConfigSchemaLine('floaty', True, dict) 258 schema = helper.ConfigSchema('DEFAULT', [csl1] ) 259 config = self.make_simple_config() 260 with pytest.raises(ConfigError): 261 helper.parse_config(config, schema) 262 263 def test_all_special_processing_options_are_handled(self): 264 config = self.make_simple_config() 265 config['DEFAULT'] = {'enum_checker' : "comma, and spaces"} 266 267 for e in helper.SpecialSchemaProcessing: 268 csl = helper.ConfigSchemaLine('enum_checker', False, list, e) 269 schema = helper.ConfigSchema('DEFAULT', [csl]) 270 271 result = helper.parse_config(config, schema) 272 assert type(result['enum_checker']) is list 273 274 def test_parsing_all(self): 275 config = self.make_simple_config('FULL_TEST') 276 277 csl1 = helper.ConfigSchemaLine('inty', False, int) 278 csl2 = helper.ConfigSchemaLine('floaty', False, float) 279 csl3 = helper.ConfigSchemaLine('mr_bool', True, bool) 280 csl4 = helper.ConfigSchemaLine('stringer', True, str) 281 csl5 = helper.ConfigSchemaLine('listo_comma', False, list, 282 helper.SpecialSchemaProcessing.commma_separated) 283 csl6 = helper.ConfigSchemaLine('listo_space', False, list, 284 helper.SpecialSchemaProcessing.space_separated) 285 schema = helper.ConfigSchema('FULL_TEST', [csl1, csl2, csl3, csl4, csl5, csl6]) 286 287 result = helper.parse_config(config, schema) 288 assert result == \ 289 { 'inty' : 3 290 , 'floaty' : 3.141 291 , 'mr_bool' : True 292 , 'stringer' : "bell" 293 , 'listo_comma' : ["i", "am", "comma", "separated"] 294 , 'listo_space' : ["yo", "it's", "time", "for", "space!"] 295 }
Note:
See TracChangeset
for help on using the changeset viewer.
