def _check_n_shape(self, change): if change["previous"] != properties.undefined: # _n can only be set once if change["previous"] != change["value"]: raise AttributeError( "Cannot change n. Instead, create a new mesh") else: # check that if h has been set, sizes still agree if getattr(self, "h", None) is not None and len(self.h) > 0: for i in range(len(change["value"])): if len(self.h[i]) != change["value"][i]: raise properties.ValidationError( "Mismatched shape of n. Expected {}, len(h[{}]), got " "{}".format(len(self.h[i]), i, change["value"][i])) # check that if nodes have been set for curvi mesh, sizes still # agree if getattr(self, "node_list", None) is not None and len(self.node_list) > 0: for i in range(len(change["value"])): if self.node_list[0].shape[i] - 1 != change["value"][i]: raise properties.ValidationError( "Mismatched shape of n. Expected {}, len(node_list[{}]), " "got {}".format(self.node_list[0].shape[i] - 1, i, change["value"][i]))
def _validate_segments(self, change): """Ensure segments array is Mx2 and non-negative integers""" segments = change['value'] if (isinstance(segments, string_types) or segments is properties.undefined): return True if len(segments.shape) != 2 or segments.shape[1] != 2: raise properties.ValidationError( message='LineSet segments must be Mx2 array', reason='invalid', prop='segments', instance=self, ) if 'int' not in segments.dtype.lower(): raise properties.ValidationError( message='LineSet segments must be an integer array', reason='invalid', prop='segments', instance=self, ) if (getattr(segments, 'array', None) is not None and np.min(segments.array) < 0): raise properties.ValidationError( message='Segments may only have non-negative integers', reason='invalid', prop='segments', instance=self, ) return True
def _validate_geometry(self): """Ensure offset_w shape is consistent with tensor lengths""" if self.offset_w is None or isinstance(self.offset_w, string_types): return True if len(self.offset_w.shape) != 1: raise properties.ValidationError( message='offset_w must be 1D array, not of shape {}'.format( self.offset_w.shape), reason='invalid', prop='offset_w', instance=self, ) if self.offset_w.shape[0] != self.num_nodes: raise properties.ValidationError( message=( 'Length of offset_w, {zlen}, must equal number of nodes, ' '{nnode}'.format( zlen=self.offset_w.shape[0], nnode=self.num_nodes, )), reason='invalid', prop='offset_w', instance=self, ) return True
def _validate_triangles(self, change): """Ensure segments array is Mx2 and non-negative integers""" triangles = change['value'] if (isinstance(triangles, string_types) or triangles is properties.undefined): return True if len(triangles.shape) != 2 or triangles.shape[1] != 3: raise properties.ValidationError( message='Surface triangles must be Nx2 array', reason='invalid', prop='triangles', instance=self, ) if 'int' not in triangles.dtype.lower(): raise properties.ValidationError( message='Surface triangles must be an integer array', reason='invalid', prop='triangles', instance=self, ) if (getattr(triangles, 'array', None) is not None and np.min(triangles.array) < 0): raise properties.ValidationError( message='Triangles may only have positive integers', reason='invalid', prop='triangles', instance=self, ) return True
def _validate_data(self): """Check if element is built correctly""" if not self.data: return True for data in self.data: if isinstance(data, (string_types, TextureProjection)): continue if data.location not in self.location_lengths: raise properties.ValidationError( message='Invalid location {} - valid values: {}'.format( data.location, ', '.join(self.location_lengths)), reason='invalid', prop='data', instance=self, ) if isinstance(data.array, string_types): continue valid_length = self.location_lengths[data.location] if valid_length is None: continue if data.array.shape[0] != valid_length: raise properties.ValidationError( message=('data {index} length {datalen} does not match ' '{loc} length {meshlen}'.format( index=data.name, datalen=data.array.shape[0], loc=data.location, meshlen=valid_length, )), reason='invalid', prop='data', instance=self, ) return True
def _validate_controls(self): """Validate lengths of data_controls/gradient_controls/visibility""" if len(self.data_controls) != len(self.gradient_controls): raise properties.ValidationError( message='data and gradient controls must be equal length', reason='invalid', prop='data_controls', instance=self, ) if len(self.data_controls) != len(self.visibility) - 1: raise properties.ValidationError( message='visibility must be one longer than data controls', reason='invalid', prop='data_controls', instance=self, )
def _validate_lengths(self): """Validate lengths of values/indices/visibility""" if len(self.values) != len(self.indices): raise properties.ValidationError( message='values and indices must be equal length', reason='invalid', prop='indices', instance=self, ) if len(self.values) != len(self.visibility): raise properties.ValidationError( message='values and visibility must be equal length', reason='invalid', prop='visibility', instance=self, )
def _validate_mesh_type(self): """Check mesh type is one of two valid options: 'rect' or 'cylind'""" if self.coord not in ['rect', 'cylind']: raise properties.ValidationError( "Mesh type ('{}') not a valid mesh type. Only 'rect' and 'cylind' are suppurted" .format(self.coord)) return True
def _validate_data(self): """Ensure value or data/mapping are set""" if self.data and not self.mapping: raise properties.ValidationError( message=( 'Mapping must be specified on visualization options if ' 'data is present'), reason='missing', prop='mapping', instance=self, ) if not self.data and self.value is None: raise properties.ValidationError( message=('Value must be specified on visualization options if ' 'data is not'), reason='missing', prop='value', instance=self, )
def _validate_section(self): """Validate that position_b and color_b are set in section mode""" if (self.mode == 'section' and (self.position_b is None or self.color_b is None)): raise properties.ValidationError( message='Section mode requires position_b and color_b', reason='invalid', prop='mode', instance=self, )
def _validate_indices_unique(self, change): """Ensure indices are unique""" if change['value'] is properties.undefined: return if len(change['value']) != len(set(change['value'])): raise properties.ValidationError( message='indices must be unique: {}'.format(change['value']), reason='invalid', prop='indices', instance=self, )
def _validate_geometry(self): """Ensures triangle indices are valid in conjunction with vertices""" if (isinstance(self.vertices, string_types) or getattr(self.triangles, 'array', None) is None): return True if np.max(self.triangles.array) >= self.vertices.shape[0]: raise properties.ValidationError( message='Triangle indices are outside bounds for vertices', reason='invalid', prop='triangles', instance=self, ) return True
def _validate_array_int(self, change): """Ensure the array is only integers""" if (isinstance(change['value'], string_types) or change['value'] is properties.undefined): return if 'int' not in change['value'].dtype.lower(): raise properties.ValidationError( message='{} must use integer array'.format( self.__class__.__name__, ), reason='invalid', prop='array', instance=self, )
def _validate_array_1d(self, change): """Ensure the array is 1D""" if (isinstance(change['value'], string_types) or change['value'] is properties.undefined): return if not change['value'].is_1d(): raise properties.ValidationError( message='{} must use 1D array'.format( self.__class__.__name__, ), reason='invalid', prop='array', instance=self, )
def _validate_vertices(self, change): """Ensure vertices array is Nx3""" if (isinstance(change['value'], string_types) or change['value'] is properties.undefined): return True if len(change['value'].shape) != 2 or change['value'].shape[1] != 3: raise properties.ValidationError( message='Surface vertices must be Nx3 array', reason='invalid', prop='vertices', instance=self, ) return True
def _validate_lengths(self): """Validate lengths of values/end_points/end_inclusive/visibility""" if len(self.values) != len(self.visibility): raise properties.ValidationError( message='values and visibility must be equal length', reason='invalid', prop='visibility', instance=self, ) if len(self.values) != len(self.end_points) + 1: raise properties.ValidationError( message='values must be one longer than end points', reason='invalid', prop='end_points', instance=self, ) if len(self.values) != len(self.end_inclusive) + 1: raise properties.ValidationError( message='values must be one longer than end inclusive', reason='invalid', prop='end_inclusive', instance=self, )
def _validate_increasing(self, change): """Ensure end_points are increasing""" if change['value'] is properties.undefined: return diffs = np.array(change['value'][1:]) - np.array(change['value'][:-1]) if not np.all(diffs >= 0): raise properties.ValidationError( message='end points must not decrease: {}'.format( change['value'] ), reason='invalid', prop='end_points', instance=self, )
def extra_slide_validation(slide, element_list): """Perform extra validation of a Slide object Slide loading in the web app has limitations beyond the built-in Slide object validation. This includes: - references to elements, data, and textures must be URLs - all elements must have a corresponding view in the slide """ views = slide.scene.plots[0].views try: view_uids = set(view.element.split('/')[-1] for view in views) except AttributeError: raise properties.ValidationError( 'Elements specified in plot views must be URL strings' ) element_uids = set(elem.split('/')[-1] for elem in element_list) if view_uids != element_uids: raise properties.ValidationError( 'All Elements in the View must be placed in the plot; ' 'to hide the Element set visible=False' ) for view in views: for attr in 'color', 'opacity', 'radius': opts = getattr(view, attr, None) if not opts: continue if opts.data and not isinstance(opts.data, string_types): raise properties.ValidationError( 'Data specified in plot views must be URL strings' ) if getattr(view, 'textures', None): for tex in view.textures: if not isinstance(tex.data, string_types): raise properties.ValidationError( 'Texture data specified in plot views must be ' 'URL strings' )
def validate_uid(cls, value): """Hook called on Pointer validation to verify regex When a :code:`_BaseUIDModel` is specified as the instance class on a :class:`properties.extras.Pointer`, a string pointer can be assigned as the value (in place of an instance of the class) if it passes this validation. """ if not cls.pointer_regex.search(value): raise properties.ValidationError( 'Pointer must contain string matching {}'.format( cls.pointer_regex.pattern ) ) return True
def _validate_content_length(self, change): """Ensure content_length matches specified shape and dtype content_length may be smaller than the size expected from shape/dtype, if the data is compressed. However, it cannot be larger. """ if not self.dtype or not self.shape: return length = np.dtype(ARRAY_DTYPES[self.dtype][0]).itemsize for dim in self.shape: length *= dim if change['value'] > length: raise properties.ValidationError( message='content_length is too large for given shape/dtype', reason='invalid', prop='content_length', instance=self, )
def test_validation_error(self): with self.assertRaises(TypeError): properties.ValidationError( message='msg', reason=5, prop='a', instance=properties.HasProperties, ) with self.assertRaises(TypeError): properties.ValidationError( message='msg', reason='invalid', prop=5, instance=properties.HasProperties, ) with self.assertRaises(TypeError): properties.ValidationError( message='msg', reason='invalid', prop='a', instance=5, ) class Simple(properties.HasProperties): a = properties.List( doc='2 ints', prop=properties.Integer(''), min_length=2, max_length=2, ) s = Simple() with self.assertRaises(properties.ValidationError): s.a = 5 with self.assertRaises(properties.ValidationError): s.a = ['a', 'b'] s.a = [1] with self.assertRaises(properties.ValidationError): s.validate() with self.assertRaises(properties.ValidationError): Simple(a=5) class SeveralProps(properties.HasProperties): a = properties.Integer('') b = properties.Integer('') c = properties.Integer('') d = properties.Integer('') try: SeveralProps(a='a', b='b') except properties.ValidationError as err: assert hasattr(err, 'error_tuples') tup = err.error_tuples assert len(tup) == 2 assert tup[0].reason == 'invalid' assert tup[1].reason == 'invalid' assert set(t.prop for t in tup) == set(['a', 'b']) assert tup[0].instance.__class__ is SeveralProps assert tup[1].instance.__class__ is SeveralProps sp = SeveralProps() try: sp.a = 'a' except properties.ValidationError as err: assert hasattr(err, 'error_tuples') tup = err.error_tuples assert len(tup) == 1 assert tup[0].reason == 'invalid' assert tup[0].prop == 'a' assert tup[0].instance is sp try: sp.validate() except properties.ValidationError as err: assert hasattr(err, 'error_tuples') tup = err.error_tuples assert len(tup) == 4 assert set(t.prop for t in tup) == set(['a', 'b', 'c', 'd']) for t in tup: assert t.reason == 'missing' assert t.instance is sp try: sp._validate_props() except properties.ValidationError as err: assert hasattr(err, 'error_tuples') tup = err.error_tuples assert len(tup) == 1 assert tup[0].reason == 'missing' assert tup[0].instance is sp sp.a = sp.b = sp.c = 1 sp._backend['d'] = 'd' try: sp.validate() except properties.ValidationError as err: assert hasattr(err, 'error_tuples') tup = err.error_tuples assert len(tup) == 1 assert tup[0].reason == 'invalid' assert tup[0].prop == 'd' assert tup[0].instance is sp
def validate_uid(cls, value): if value.split(':')[0] != cls.__name__: raise properties.ValidationError('class not in uid')
def _validate_real_part(self, change): if not np.real(change['value']) > 0: raise properties.ValidationError("The real part of sigma must be positive")
def raise_error(self): raise properties.ValidationError('')