def _validate(self, val: list, log: Optional[Logger] = None) -> Tuple[bool, List[str]]: """ Determine whether val is a valid instance of this array :returns: Success indicator and error list """ errors = [] if not isinstance(val, list): errors.append( f"{self._variable_name}: {repr(val)} is not an array") else: for i in range(0, len(val)): v = val[i] if not conforms(v, self._type, self._context.NAMESPACE): errors.append( f"{self._variable_name} element {i}: {v} is not a {self._type.__name__}" ) if len(val) < self._min: errors.append( f"{self._variable_name}: at least {self._min} value{'s' if self._min > 1 else ''} required - " f"element has {len(val) if len(val) else 'none'}") if self._max is not None and len(val) > self._max: errors.append( f"{self._variable_name}: no more than {self._max} values permitted - element has {len(val)}" ) if log: for error in errors: log.log(error) return not bool(errors), errors
def _map_jsg_type( self, name: str, element: Any, poss_types: Union[type, Tuple[type]]) -> Optional[JSGValidateable]: def _wrap(ty, el): # The first option is when we are assigning already loaded types # The second option addresses the optional situation return el if isinstance( el, JSGValidateable) or ty is type(None) else ty(el) for typ in poss_types if isinstance(poss_types, tuple) else (poss_types, ): if isinstance(typ, str): if typ in self._context.NAMESPACE: typ = self._context.NAMESPACE[typ] else: #raise ValueError(f"Unknown type: {typ}") raise ValueError("Unknown typr: " + typ) typ = proc_forward(typ, self._context.NAMESPACE) if is_union(typ): for t in typ.__args__: et = self._map_jsg_type(name, element, t) if et is not None: return et elif conforms(element, typ, self._context.NAMESPACE): return _wrap(typ, element) return None
def _is_valid_element(self, log: Logger, name: str, val: Type[JSGValidateable]) -> bool: if name not in self._members: return any(e._is_valid_element for e in self._reference_types) else: etype = self._members[name] if etype is JSGNull: return val is JSGNull or val is None if val is None and type(etype) is type(AnyType): return False if val is not None and val is not Empty and isinstance( val, JSGArray): if not val._validate(cast(list, val), log)[0] and not log.logging: return False elif not conforms(val, etype, self._context.NAMESPACE ): # Note: None and absent are equivalent if val is None or val is Empty: if log.log("{}: Missing required field: '{}'".format( self.__class__.__name__, name)): return False else: if log.log( "{}: Type mismatch for {}. Expecting: {} Got: {}". format(self.__class__.__name__, name, etype, type(val))): return False elif val is not None and not self._test( val, log): # Make sure that entry conforms to its own type return False return True
def __instancecheck__(self, instance: list) -> bool: if not isinstance(instance, list): return False for element in instance: if not conforms(element, self.typ, self.context.NAMESPACE): return False return len(instance) >= self.min and (not self.max or len(instance) <= self.max)
def __setattr__(self, key: str, value: Any): """ Screen attributes for name and type. Anything starting with underscore ('_') goes, anything in the IGNORE list and anything declared in the __init_ signature :param key: :param value: :return: """ if not key.startswith("_") and not self._context.unvalidated_parm(key): if self._name_filter is not None: if not isinstance(key, self._name_filter): raise ValueError(f"Illegal Object Map key: {key}={value}") if not conforms(value, self._value_type, self._context.NAMESPACE): raise ValueError("Illegal value type {} = {}".format(key, value)) if not isinstance(value, JSGValidateable): value = self._value_type('', self._context, value) \ if self._value_type is AnyType else self._value_type(value) self[key] = value
def __instancecheck__(self, element: object) -> bool: return conforms(element, JSGObject, self.context.NAMESPACE)