Changeset 456

Show
Ignore:
Timestamp:
08/25/10 17:28:08 (21 months ago)
Author:
emuller
Message:

ParameterSchema? and basic validation implemented.

Location:
branches/parameter_set_schema_validation
Files:
4 modified

Legend:

Unmodified
Added
Removed
  • branches/parameter_set_schema_validation/src/parameters/__init__.py

    r450 r456  
    193193            global_dict.update(update_namespace) 
    194194 
     195         
     196        D=None 
    195197        try: 
    196198            D = eval(s, global_dict) 
    197         except SyntaxError, e: 
     199        except SyntaxError as e: 
    198200            raise SyntaxError("Invalid string for ParameterSet definition: %s\n%s" % (s,e)) 
     201             
    199202        return D or {} 
    200203     
     
    229232                pstr = f.read() 
    230233                self._url = initialiser 
     234 
     235                 
    231236            except IOError: 
    232237                pstr = initialiser 
     
    235240                f.close() 
    236241 
    237             initialiser = ParameterSet.read_from_str(pstr,update_namespace) 
     242 
     243            # is it a yaml url? 
     244            if self._url: 
     245                import urlparse, os.path 
     246                o = urlparse.urlparse(self._url) 
     247                base,ext = os.path.splitext(o.path) 
     248                if ext in ['.yaml','.yml']: 
     249                    import yaml 
     250                    initialiser = yaml.load(pstr) 
     251                else: 
     252                    initialiser = ParameterSet.read_from_str(pstr,update_namespace) 
     253            else: 
     254                initialiser = ParameterSet.read_from_str(pstr,update_namespace) 
     255 
    238256         
    239257        # By this stage, `initialiser` should be a dict. Iterate through it, 
     
    297315        return dict.__getitem__(self,split[0])[split[1]] 
    298316 
     317    def flat_add(self,name,value): 
     318        """ Like __setitem__, but it will add ParametSet({}) objects 
     319        into the namespace tree if needed. """ 
     320 
     321        split = name.split('.',1) 
     322        if len(split)==1: 
     323            dict.__setitem__(self,name,value) 
     324        else: 
     325            # nested set 
     326            try: 
     327                ps = dict.__getitem__(self,split[0]) 
     328            except KeyError: 
     329                # setting nested name without parent existing 
     330                # create parent 
     331                ps = ParameterSet({}) 
     332                dict.__setitem__(self,split[0],ps) 
     333                # and try again 
     334            ps.flat_add(split[1],value) 
     335 
    299336    def __setitem__(self,name,value): 
    300337        """ Modified set that detects dots '.' in the names and goes down the 
     
    306343        else: 
    307344            # nested set 
    308             try: 
    309                 dict.__getitem__(self,split[0])[split[1]]=value 
    310             except KeyError: 
    311                 # setting nested name without parent existing 
    312                 # create parent 
    313                 dict.__setitem__(self,split[0],{}) 
    314                 # and try again 
    315                 dict.__getitem__(self,split[0])[split[1]]=value 
     345            dict.__getitem__(self,split[0])[split[1]]=value 
     346 
    316347    
    317348    # should __len__() be the usual dict length, or the flattened length? Probably the former for consistency with dicts 
  • branches/parameter_set_schema_validation/src/parameters/validators.py

    r450 r456  
    2424 
    2525    def validate(self, leaf): 
    26         return issubclass(leaf, type) 
     26        return isinstance(leaf, self.type) 
    2727 
    2828    def __repr__(self): 
     
    7777            value = x[1] 
    7878            if isinstance(value, SchemaBase): 
    79                 self[key] = value 
     79                self.flat_add(key,value) 
    8080            else: 
    81                 self[key] = Subclass(type=type(value)) 
    82  
    83  
    84  
    85 # Schema ParameterSet types 
    86  
    87  
    88  
    89  
    90 class CongruencyValidator: 
    91     pass 
    92  
    93  
    94  
     81                self.flat_add(key,Subclass(type=type(value))) 
     82 
     83 
     84 
     85 
     86class ValidationError(Exception): 
     87    def __init__(self, path='',schema_base=None, parameter=None ): 
     88        self.path = path 
     89        self.schema_base = schema_base 
     90        self.parameter = parameter 
     91    def __str__(self): 
     92        return 'validation error @ %s: parameter "%s" failed against schema: %s' % (self.path, self.parameter, self.schema_base) 
     93     
     94 
     95 
     96class CongruencyValidator(object): 
     97    """ 
     98    A CongruencyValidator validates a ParameterSet against a ParameterSchema 
     99    either returning True, or raising a ValidationError with the path, SchemaBase subclass 
     100    and parameter value for which the validation failed. 
     101 
     102    The CongruencyValidator expects all names defined in the schema to be present in the parameter set 
     103    and vice-versa, and will run validation for each item in the namespace tree. 
     104     
     105    The validation functionality is available via the "validate" member 
     106    CongruencyValidator.validate(parameter_set, parameter_schema) 
     107 
     108    example: 
     109 
     110    validator = CongruencyValidator() 
     111 
     112    try: 
     113       validator.validate(parameter_set,parameter_schema) 
     114    except ValidationError, e: 
     115        
     116 
     117    """ 
     118 
     119    def __init__(self): 
     120        pass 
     121 
     122 
     123    def validate(self, parameter_set, parameter_schema): 
     124        """ 
     125        CongruencyValidator.validate(parameter_set, parameter_schema) 
     126         
     127        validates a ParameterSet against a ParameterSchema 
     128        either returning True, or raising a ValidationError with the path and SchemaBase subclass 
     129        for which validation failed. 
     130 
     131 
     132        expects all names defined in the schema to be present in the parameter set 
     133        and vice-versa, and will run validation for each item in the namespace tree. 
     134 
     135        See als0: CongruencyValidator docstring. 
     136 
     137        """ 
     138 
     139        ps = parameter_set 
     140        schema = parameter_schema 
     141 
     142        ps_keys = set() 
     143        schema_keys = set() 
     144 
     145        for path,sb in schema.flat(): 
     146            try: 
     147                val = ps[path] 
     148            except KeyError: 
     149                e = ValidationError(path=path,schema_base=sb,parameter='<MISSING>') 
     150                raise e 
     151            if not sb.validate(val): 
     152                e = ValidationError(path=path,schema_base=sb,parameter=val) 
     153                raise e 
     154 
     155         
     156        for path,val in ps.flat(): 
     157            try: 
     158                sb = schema[path] 
     159            except KeyError: 
     160                e = ValidationError(path=path,schema_base='<MISSING>',parameter=val) 
     161                raise e 
     162             
     163        return True 
     164         
     165             
    95166 
    96167def congruent_dicts(template, candidate, subset=False,parent_path=''): 
     
    149220NeuroTools.parameters.Subclass = Subclass 
    150221NeuroTools.parameters.CongruencyValidator = CongruencyValidator 
    151  
    152  
    153      
     222NeuroTools.parameters.ValidationError = ValidationError 
     223 
     224 
     225 
     226 
     227     
  • branches/parameter_set_schema_validation/test/test_parameters.py

    r355 r456  
    8686        ps2 = ParameterSet({'a': 1, 'b':2}) 
    8787        self.assertEqual(ps1, ps2) 
     88 
     89    def test_create_from_flat_iterator(self): 
     90        ps = ParameterSet({'a':1, 'b':2}, label="PS1") 
     91        ps2 = ParameterSet({'ps':ps, 'c':19}, label="PS2") 
     92        ps3 = ParameterSet({'hello': 'world', 'ps2': ps2, 'null': None, 
     93                            'true': False, 'mylist': [1,2,3,4], 
     94                            'mydict': {'c': 3, 'd':4}, 'yourlist': [1,2,{'e':5, 'f':6}], 
     95                            }, label="PS3") 
     96        ps4 = ParameterSet({}) 
     97        for x in ps3.flat(): 
     98            ps4.flat_add(x[0],x[1]) 
     99        self.assertEqual(ps4, ps3) 
     100 
    88101         
    89102    def test_create_with_syntax_error(self): 
     
    95108    def test_create_with_invalid_initialiser(self): 
    96109        self.assertRaises(TypeError, ParameterSet, object) 
     110 
     111    def test_create_yaml_url(self): 
     112        import tempfile, yaml 
     113 
     114        conf1_str = """ 
     115        # user info 
     116        username: joe 
     117        email:  joe@example.com 
     118 
     119        # recipes 
     120        recipes: 
     121           all: /somewhere1/file1.xml 
     122           specific: /somewhere2/file2.xml 
     123        """ 
     124 
     125        ps = ParameterSet 
     126         
     127        tf = tempfile.NamedTemporaryFile(suffix='.yaml') 
     128        tf.file.writelines(conf1_str) 
     129         
     130        tf.file.flush() 
     131        tf.file.seek(0) 
     132 
     133        ps = ParameterSet("file://"+tf.name) 
     134 
     135        tf.close() 
     136 
     137        ps1 = ParameterSet(yaml.load(conf1_str)) 
     138        assert ps1 == ps 
     139         
     140         
     141         
     142 
    97143     
    98144class ParameterSetSaveLoadTest(unittest.TestCase): 
  • branches/parameter_set_schema_validation/test/test_validators.py

    r454 r456  
    4848                           'true': NeuroTools.parameters.validators.Subclass(type=bool),  
    4949                           'yourlist': NeuroTools.parameters.validators.Subclass(type=list),  
    50                            'ps2': {'ps.a': NeuroTools.parameters.validators.Subclass(type=int),  
    51                                    'c': NeuroTools.parameters.validators.Subclass(type=int),  
    52                                    'ps.b': NeuroTools.parameters.validators.Subclass(type=int)}, 'null': NeuroTools.parameters.validators.Subclass(type=NoneType), 'mydict': {'c': NeuroTools.parameters.validators.Subclass(type=int), 'd': NeuroTools.parameters.validators.Subclass(type=int)}, 'hello': NeuroTools.parameters.validators.Subclass(type=str)} 
     50                           'ps2': {'ps': {'a':NeuroTools.parameters.validators.Subclass(type=int), 
     51                                          'b':NeuroTools.parameters.validators.Subclass(type=int)}, 
     52                                   'c': NeuroTools.parameters.validators.Subclass(type=int)},  
     53                           'null': NeuroTools.parameters.validators.Subclass(type=type(None)), 
     54                           'mydict': {'c': NeuroTools.parameters.validators.Subclass(type=int), 
     55                                      'd': NeuroTools.parameters.validators.Subclass(type=int)}, 
     56                           'hello': NeuroTools.parameters.validators.Subclass(type=str)}) 
    5357 
    5458 
     
    6367 
    6468 
     69    def test_validate_generated_schema(self): 
     70        s1 = ParameterSchema(ps3) 
     71 
     72        v = CongruencyValidator() 
     73        assert v.validate(ps3,s1)==True 
     74         
    6575    def test_simple_validate(self): 
     76        v = CongruencyValidator() 
     77        assert v.validate(ps3,schema3)==True 
     78 
     79    def test_validation_failure_info(self): 
     80        """ Test the error output for an failed validation against a schema""" 
     81 
    6682        s1 = ParameterSchema(ps3) 
    67         print s1 
    68         #print ps3.flatten() 
    6983         
     84        s1.ps2.ps.a = Subclass(type=float) 
     85        v = CongruencyValidator() 
     86        r = False 
     87        try: 
     88            r = v.validate(ps3,s1) 
     89        except Exception as e: 
     90            assert isinstance(e,ValidationError) 
     91            assert e.path=='ps2.ps.a' 
     92            assert e.parameter==ps3.ps2.ps.a 
     93            assert e.schema_base==s1.ps2.ps.a 
     94        assert r==False 
    7095 
    7196    def test_congruent_dicts(self):