IPP Software Navigation Tools IPP Links Communication Pan-STARRS Links

Changeset 40919


Ignore:
Timestamp:
Oct 8, 2019, 11:42:36 AM (7 years ago)
Author:
fairlamb
Message:

Finished schema parsing tests

Location:
branches/ipp-259_genericise_backups/tools/backups/utils
Files:
2 edited

Legend:

Unmodified
Added
Removed
  • branches/ipp-259_genericise_backups/tools/backups/utils/config_parse_helper.py

    r40915 r40919  
    33from backups.utils.errors import ConfigError, ValidationError
    44
    5 # The as configs should be parsed should be pretty uniform. There's a limited
    6 # amount that can be done with the actual loading.
    7 
    8 # Each backup file should be expecting particular lines from a config. So, this
    9 # means a schema can be made for what each backup expects.
    10 
    115
    126class 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
     8separate it out into list.
    149    """
    1510    commma_separated = 1
     
    1813
    1914class 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,
     16the type that the value should be processed as, and any special processing required
     17"""
    2218
    2319    def __init__(self, key: str, required: bool, expected_type, special_processing: SpecialSchemaProcessing=None):
     
    3026        self.special_processing = special_processing
    3127
     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
    3236
    3337class ConfigSchema():
    34     """Defines the schema to be used"""
     38    """Defines a schema to be used. Consists of a single section name and
     39multiple config lines"""
    3540
    3641    def __init__(self, section_name, options: [ConfigSchemaLine]):
     
    5257        self.options[line.key] = line
    5358
     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
    5471
    5572def _split_string_to_list(items: str, separator=',') -> [str]:
     
    6077
    6178def 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
     80and return them as a dictionary of the keys and the assoicated value as the
     81type defined by the schema
     82"""
    6383    parsed_items = {}
    6484
    6585    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)
    6789
    68         if config.has_option(section, csl.key):
    69 
     90        if config.has_option(section, csl_key):
    7091            if csl.expected_type is bool:
    7192                parsed_items[csl.key] = config.getboolean(section, csl.key)
    72             elif csl.expected is int:
     93            elif csl.expected_type is int:
    7394                parsed_items[csl.key] = config.getint(section, csl.key)
    7495            elif csl.expected_type is float:
     
    79100                if csl.special_processing == SpecialSchemaProcessing.commma_separated:
    80101                    items = config[section][csl.key]
    81                     parsed_items[csl.key] = _split_string_to_list(items, ' ')
    82                     # parse comma_separated stuff
     102                    splitup = _split_string_to_list(items, ',')
     103                    parsed_items[csl.key] = splitup
    83104                elif csl.special_processing == SpecialSchemaProcessing.space_separated:
    84105                    items = config[section][csl.key]
    85                     parsed_items[csl.key] = _split_string_to_list(items, ',')
    86                     # space separated processing
     106                    splitup = _split_string_to_list(items, ' ')
     107                    parsed_items[csl.key] = splitup
    87108                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}")
    89110            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__}")
    91112
    92113        else:
    93114            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")
    95116
    96117    return parsed_items
  • branches/ipp-259_genericise_backups/tools/backups/utils/config_parse_helper_test.py

    r40915 r40919  
    134134
    135135
    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:
     136class 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.