def test_warning_logged(): ''' warning is logged if we try to get a class from 'obj_type' that is not in the gnome namespace ''' with LogCapture() as lc: with pytest.raises(AttributeError): class_from_objtype('os.path') lc.check(('gnome.gnomeobject', 'WARNING', 'os.path is not part of gnome namespace'))
def test_warning_logged(): ''' warning is logged if we try to get a class from 'obj_type' that is not in the gnome namespace ''' with LogCapture() as lc: with pytest.raises(AttributeError): class_from_objtype('os.path') lc.check(('gnome.gnomeobject', 'WARNING', 'os.path is not part of gnome namespace'))
def _deser(self, node, value, refs): if value is None: return None if type(value) is dict and 'obj_type' in value: id_ = value.get('id', None) if id_ not in refs or id_ is None: obj_type = class_from_objtype(value['obj_type']) obj = obj_type.new_from_dict(value) log.info('Created new {} object {}' .format(obj.obj_type, obj.name)) node.register_refs(node, obj, refs) if id_ is None: id_ = obj.id refs[id_] = obj return refs[id_] else: raise TypeError('{} is not dictionary, ' 'or does not have an obj_type' .format(value))
def _deser(self, node, value, refs): if value is None: return None if type(value) is dict and 'obj_type' in value: id_ = value.get('id', None) if id_ not in refs or id_ is None: obj_type = class_from_objtype(value['obj_type']) obj = obj_type.new_from_dict(value) log.info('Created new {} object {}' .format(obj.obj_type, obj.name)) node.register_refs(node, obj, refs) if id_ is None: id_ = obj.id refs[id_] = obj return refs[id_] else: raise TypeError('{} is not dictionary, ' 'or does not have an obj_type' .format(value))
def validate_input_schema(self, obj_or_json): ''' Takes an object or json dict and determines if it can be represented by this schema instance. Returns an instance of the schema of the object, or raises an error if the object cannot be used with this schema. ''' if type(obj_or_json) is dict: json_ = obj_or_json obj_type = class_from_objtype(json_['obj_type']) schema = obj_type._schema for s in self.acceptable_schemas: if schema is s or issubclass(schema, s): return schema() else: raise TypeError('This type of json is not supported {}' .format(schema)) else: obj = obj_or_json schema = obj.__class__._schema for s in self.acceptable_schemas: if schema is s or issubclass(schema, s): return schema() raise TypeError('This type of object {} is not supported. ' 'Schema: {}' .format(obj, schema))
def load(self, obj_json, saveloc=None, refs=None): if obj_json is None: raise ValueError(self.__class__.__name__ + ': Cannot load a None') cls = class_from_objtype(obj_json['obj_type']) if cls._schema is not self.__class__: raise TypeError('A {0} cannot save a {1}'.format(self.__class__.__name__, cls.__name__)) if zipfile is None: raise ValueError('Must pass an open zipfile.Zipfile in append mode to saveloc') obj = self.typ.load(self, obj_json, saveloc, refs) return obj
def load(self, obj_json, saveloc=None, refs=None): if obj_json is None: raise ValueError('{}: Cannot load a None' .format(self.__class__.__name__)) cls = class_from_objtype(obj_json['obj_type']) if cls._schema is not self.__class__: raise TypeError('A {} cannot save a {}' .format(self.__class__.__name__, cls.__name__)) if zipfile is None: raise ValueError('Must pass an open zipfile.Zipfile ' 'in append mode to saveloc') return self.typ.load(self, obj_json, saveloc, refs)
def validate_save_json(json_, zipfile_, orig_obj): ''' validates the json_ and zipfile_ of an object. In particular: class_from_objtype must return the original object's class when provided json_['obj_type'] All save_reference attributes have a .json file referenced, and such files also exist in the zipfile_ All missing=drop attributes that are None on the original object do not appear. No GnomeId python objects exist in the json Note that this should not be used to validate cases where the object may be doing custom save or using a custom to_dict. If an object does not do this however, it should be able to pass these tests ''' assert class_from_objtype(json_['obj_type']) is orig_obj.__class__ schema = orig_obj._schema() save_refs = schema.get_nodes_by_attr('save_reference') for n in save_refs: if getattr(orig_obj, n) is not None: if isinstance(getattr(orig_obj, n), collections.Iterable): for i, ref in enumerate(getattr(orig_obj, n)): assert json_[n][i] == ref.name + '.json' assert json_[n][i] in zipfile_.namelist() else: ref = getattr(orig_obj, n) assert json_[n] == ref.name + '.json' assert json_[n] in zipfile_.namelist() # potential_missing = schema.get_nodes_by_attr('missing') # for n in potential_missing: # if getattr(orig_obj,n) is None: # assert n not in json_ for v in json_.values(): assert not issubclass(v.__class__, GnomeId) return True
def validate_save_json(json_, zipfile_, orig_obj): ''' validates the json_ and zipfile_ of an object. In particular: class_from_objtype must return the original object's class when provided json_['obj_type'] All save_reference attributes have a .json file referenced, and such files also exist in the zipfile_ All missing=drop attributes that are None on the original object do not appear. No GnomeId python objects exist in the json Note that this should not be used to validate cases where the object may be doing custom save or using a custom to_dict. If an object does not do this however, it should be able to pass these tests ''' assert class_from_objtype(json_['obj_type']) is orig_obj.__class__ schema = orig_obj._schema() save_refs = schema.get_nodes_by_attr('save_reference') for n in save_refs: if getattr(orig_obj, n) is not None: if isinstance(getattr(orig_obj, n), collections.Iterable): for i, ref in enumerate(getattr(orig_obj, n)): assert json_[n][i] == ref.name + '.json' assert json_[n][i] in zipfile_.namelist() else: ref = getattr(orig_obj, n) assert json_[n] == ref.name + '.json' assert json_[n] in zipfile_.namelist() # potential_missing = schema.get_nodes_by_attr('missing') # for n in potential_missing: # if getattr(orig_obj,n) is None: # assert n not in json_ for v in json_.values(): assert not issubclass(v.__class__, GnomeId) return True
def test_serialize_from_blob_old(self): # this one uses the "old" name, before moving the map module. json_data = { 'approximate_raster_interval': 53.9608870724, 'filename': u'/Users/chris.barker/Hazmat/GitLab/pygnome/py_gnome/tests/unit_tests/sample_data/florida_with_lake_small.bna', 'id': u'b3590b7d-aab1-11ea-8899-1e00b098d304', 'map_bounds': [(-82.8609915978, 24.5472415066), (-82.8609915978, 28.1117673335), (-80.0313642811, 28.1117673335), (-80.0313642811, 24.5472415066)], 'name': u'MapFromBNA_8', 'obj_type': u'gnome.map.MapFromBNA', 'raster_size': 16777216.0, 'spillable_area': None, 'refloat_halflife': 1.0 } cls = class_from_objtype(json_data['obj_type']) # obj = cls.load(saveloc, fname, references) print "found class:", cls map = cls.deserialize(json_data) # when we go to Python3 :-( # assert map.__class__.__qualname__ == "gnome.maps.map.MapFromBNA" assert map.__class__.__name__ == "MapFromBNA" assert map.__class__.__module__ == "gnome.maps.map" print map.spillable_area print map.land_polys assert map.spillable_area is None assert len(map.map_bounds) == 4
def validate_serialize_json(json_, orig_obj): ''' Takes the json_ from a gnome object's serialize function, and verifies that it fits the schema. In particular: class_from_objtype must return the original object's class when provided json_['obj_type'] all schema nodes set to missing=drop and are None on the original object do not appear in the json_ No GnomeId python objects exist in the json Note that this should not be used to validate cases where the object may be doing custom serialization or using a custom to_dict. If an object does not do this however, it should be able to pass these tests ''' assert class_from_objtype(json_['obj_type']) is orig_obj.__class__ _schema = orig_obj._schema() for v in json_.values(): assert not issubclass(v.__class__, GnomeId) return True
def validate_input_schema(self, obj_or_json): ''' Takes an object or json dict and determines if it can be represented by this schema instance. Returns an instance of the schema of the object, or raises an error if the object cannot be used with this schema. ''' if type(obj_or_json) is dict: json_ = obj_or_json obj_type = class_from_objtype(json_['obj_type']) schema = obj_type._schema else: obj = obj_or_json obj_type = obj.__class__ schema = obj.__class__._schema for s in self.acceptable_schemas: if schema is s or issubclass(schema, s): return schema() raise TypeError('This type of object {} is not supported. ' 'Schema: {}' .format(obj_type, schema))
def validate_serialize_json(json_, orig_obj): ''' Takes the json_ from a gnome object's serialize function, and verifies that it fits the schema. In particular: class_from_objtype must return the original object's class when provided json_['obj_type'] all schema nodes set to missing=drop and are None on the original object do not appear in the json_ No GnomeId python objects exist in the json Note that this should not be used to validate cases where the object may be doing custom serialization or using a custom to_dict. If an object does not do this however, it should be able to pass these tests ''' assert class_from_objtype(json_['obj_type']) is orig_obj.__class__ _schema = orig_obj._schema() for v in json_.values(): assert not issubclass(v.__class__, GnomeId) return True
def load(saveloc, fname='Model.json', references=None): ''' read json from file and load the appropriate object This is a general purpose load method that looks at the json['obj_type'] and invokes json['obj_type'].loads(json) method :param saveloc: path to zipfile that contains data files and json files. It could also be a directory containing files - keep original support for location files. :param fname: .json file to load. Default is 'Model.json'. zipfile/dir must contain 'fname' :param references=None: References object that keeps track of objects in a dict as they are constructed, using the filename as the key :returns: object constructed from the json .. note:: Function first assumes saveloc is a directory and looks for saveloc/fname. If this fails, it checks if saveloc is a zipfile. If this fails, it checks if saveloc is a file and loads this assuming its json for a gnome object. If none of these work, it just returns None. ''' if zipfile.is_zipfile(saveloc): with zipfile.ZipFile(saveloc, 'r') as z: saveloc_dir = os.path.dirname(saveloc) folders = zipfile_folders(z) if len(folders) == 1: # we allow our model content to be in a single top-level folder prefix = folders[0] extract_zipfile(z, saveloc_dir, prefix) elif len(folders) == 0: # all datafiles are at the top-level, which is fine extract_zipfile(z, saveloc_dir) else: # nothing to do, zipfile does not have a good structure return saveloc = saveloc_dir if os.path.isdir(saveloc): # is a directory, look for our fname in directory fd = open(os.path.join(saveloc, fname), 'r') elif os.path.isfile(saveloc): fd = open(saveloc, 'r') saveloc, fname = os.path.split(saveloc) else: # nothing to do, saveloc is not a file or a directory return # load json data from file descriptor json_data = json.loads("".join([l.rstrip('\n') for l in fd])) fd.close() # create a reference to the object being loaded cls = class_from_objtype(json_data.pop('obj_type')) obj = cls.load(saveloc, fname, references) if obj is None: # object failed to load - look in log messages for clues return # after loading, add the object to references if references: references.reference(obj, fname) return obj
def test_class_from_objtype(): ''' test that correct class is returned by class_from_objtype ''' cls = class_from_objtype('gnome.movers.WindMover') assert cls is WindMover
def test_class_from_objtype(): ''' test that correct class is returned by class_from_objtype ''' cls = class_from_objtype('gnome.movers.WindMover') assert cls is WindMover
def load(saveloc, fname='Model.json', references=None): ''' read json from file and load the appropriate object This is a general purpose load method that looks at the json['obj_type'] and invokes json['obj_type'].loads(json) method :param saveloc: path to zipfile that contains data files and json files. It could also be a directory containing files - keep original support for location files. :param fname: .json file to load. Default is 'Model.json'. zipfile/dir must contain 'fname' :param references=None: References object that keeps track of objects in a dict as they are constructed, using the filename as the key :returns: object constructed from the json .. note:: Function first assumes saveloc is a directory and looks for saveloc/fname. If this fails, it checks if saveloc is a zipfile. If this fails, it checks if saveloc is a file and loads this assuming its json for a gnome object. If none of these work, it just returns None. ''' if zipfile.is_zipfile(saveloc): with zipfile.ZipFile(saveloc, 'r') as z: saveloc_dir = os.path.dirname(saveloc) folders = zipfile_folders(z) if len(folders) == 1: # we allow our model content to be in a single top-level folder prefix = folders[0] extract_zipfile(z, saveloc_dir, prefix) elif len(folders) == 0: # all datafiles are at the top-level, which is fine extract_zipfile(z, saveloc_dir) else: # nothing to do, zipfile does not have a good structure return saveloc = saveloc_dir if os.path.isdir(saveloc): # is a directory, look for our fname in directory fd = open(os.path.join(saveloc, fname), 'r') elif os.path.isfile(saveloc): fd = open(saveloc, 'r') saveloc, fname = os.path.split(saveloc) else: # nothing to do, saveloc is not a file or a directory return # load json data from file descriptor json_data = json.loads("".join([l.rstrip('\n') for l in fd])) fd.close() # create a reference to the object being loaded cls = class_from_objtype(json_data.pop('obj_type')) obj = cls.load(saveloc, fname, references) if obj is None: # object failed to load - look in log messages for clues return # after loading, add the object to references if references: references.reference(obj, fname) return obj