def make_output_factory_args_kwargs(self, mapping_items): # type: (NamespaceMappingOrIterable) -> Tuple[Iterable, Mapping[Name, Value]] try: if isinstance(mapping_items, (str, bytes, bytearray)): raise DataConversionError('got a `str`/`bytes`/`bytearray`') self.verify_isinstance(mapping_items, (Mapping, Iterable)) mapping = dict(**dict(mapping_items)) except (TypeError, DataConversionError): raise DataConversionError( 'expected a name-to-value mapping or an equivalent ' 'collection of name-value pairs') kwargs = dict(self.output_factory_default_kwargs) kwargs.update(mapping) return (), kwargs
def _adjust_server_secret(value): # type: (...) -> str if not isinstance(value, (unicode, bytes)): #3: `unicode` -> `str` raise DataConversionError('not a `str` or `bytes` - its type is `{}`' .format(ascii_str(type(value).__name__))) #3: `__name__` -> `__qualname__` try: value = as_unicode(value) except UnicodeDecodeError: # We don't want to reveal the value in the traceback etc. raise DataConversionError('contains non-UTF-8 binary data') #3: add: `from None` if not value.strip(): raise DataConversionError('an empty or whitespace-only value') value = value.encode('utf-8') #3-- assert isinstance(value, str) yield value
def _verify_length(self, value): value_length = len(value) if self.max_length is not None and value_length > self.max_length: raise DataConversionError( 'too long string (its length: {} characters; ' 'maximum valid length: {} characters)'.format( value_length, self.max_length))
def generate_output_items(self): # type: () -> Iterator[NameValuePair] for (output_name, converted_value_cell ) in self._output_name_to_converted_value_cell.items(): possible_input_names = self.iter_input_names_for_output_name( output_name) with DataConversionError.sublocation(possible_input_names): yield output_name, converted_value_cell.output
def _adjust_token_max_age(value): # type: (...) -> int orig_value = value value = int(value) if value == orig_value and not isinstance(orig_value, bool): yield value else: raise DataConversionError('{!r} is not an integer number'.format(orig_value))
def _convert_flag_string(self, value): word = value.lower() if not self._true_flag_strings and not self._false_flag_strings: raise DataConversionError( '{!a} is an illegal value (for this item, strings ' 'cannot be converted to Boolean values)'.format(word)) if word in self._true_flag_strings: word = True elif word in self._false_flag_strings: word = False else: raise DataConversionError( '{!a} is an illegal value (for this item these are the only ' 'strings that can be converted to Boolean values: {})'.format( word, ', '.join( sorted(map(ascii, self._true_flag_strings)) + sorted(map(ascii, self._false_flag_strings))))) return word
def verify_required_input_items_collected(self, required_input_names): # type: (AbstractSet) -> None uncollected = required_input_names - self._collected_input_names if uncollected: raise DataConversionError( 'the following required items are missing or have ' 'been skipped as effectively NULL-like (i.e., not ' 'carrying any meaningful data): {}'.format(', '.join( sorted( repr(ascii_str(input_name)) for input_name in uncollected))))
def verify_is_regular_iterable_collection(cls, value): # type: (Value) -> None """ A helper that can to be used in subclasses: verify that for the given `value` -- * `isinstance(value, typing.Iterable)` is true, **but** * `isinstance(value, typing.Mapping)` is *not* true, and * `isinstance(value, str)` is *not* true, and * `isinstance(value, (bytes, bytearray, memoryview))` is *not* true. If the result of the above verification is negative, a `DataConversionError` with an appropriate message is raised. """ cls.verify_isinstance(value, Iterable, 'expected a collection') if isinstance(value, Mapping): raise DataConversionError( 'expected a collection but *not* a mapping') if isinstance(value, str): raise DataConversionError('expected a collection, *not* a string') if isinstance(value, (bytes, bytearray, memoryview)): raise DataConversionError( 'expected a collection, *not* a binary data object')
def verify_in(value, container, error_message=None): # type: (Value, Container, Optional[String]) -> None """ A helper that can to be used in subclasses: verify that `value in container` is true. If not, raise `DataConversionError(error_message)` or -- if `error_message` is unspecified or specified as `None` -- a `DataConversionError` with an automatically generated `"... is not in ..."` message. """ if value not in container: if error_message is None: error_message = '{!a} is not in {!a}'.format(value, container) raise DataConversionError(error_message)
def validate_input_names(self, data): # type: (Iterator) -> (Sequence[NameValuePair]) """ Note: this method works in an eager (not lazy) manner (in particular, is not a generator and does not return an iterator, but a sequence) -- because we want to emphasize that we do *not* want to defer the validation, but to have it performed immediately. """ validated = [] for input_name, input_value in data: if not isinstance(input_name, str): raise DataConversionError( 'unexpected non-string key ({!a}) in ' 'the mapping'.format(input_name)) validated.append((input_name, input_value)) return validated
def verify_isinstance(value, type_spec, error_message=None): # type: (Value, TypeSpec, Optional[String]) -> None """ A helper that can to be used in subclasses: verify that `isinstance(value, type_spec)` is true. If not, raise `DataConversionError(error_message)` or -- if `error_message` is unspecified or specified as `None` -- a `DataConversionError` with an automatically generated `"unexpected type of..."`-like message. """ if not isinstance(value, type_spec): if error_message is None: if isinstance(type_spec, tuple): error_message = 'unexpected type of {!a}'.format(value) else: safe_class_name = ascii_str(type_spec.__qualname__) error_message = 'unexpected type of {!a} (should be a {})'.format( value, safe_class_name) raise DataConversionError(error_message)
def __call__(self, data): # type: (Value) -> Iterator with self.state_bookkeeper_context() as bookkeeper: input_name_value_pairs = self.generate_input_name_value_pairs(data) input_name_value_pairs = self.validate_input_names( input_name_value_pairs) input_name_value_pairs = bookkeeper.preprocess_input_items( input_name_value_pairs) for input_name, input_value in input_name_value_pairs: with DataConversionError.sublocation(input_name): item_conv = self._input_name_to_converter.get( input_name, self._free_item_converter) for output_name, converted_value_cell in item_conv( (input_name, input_value)): bookkeeper.collect_converted_item( input_name, output_name, converted_value_cell) bookkeeper.verify_required_input_items_collected( self._required_input_names) output_name_value_pairs = bookkeeper.generate_output_items() for assembled_data in self._assembling_converter( output_name_value_pairs): yield assembled_data
def __add__(self, other): # type: (Cell) -> Cell if isinstance(other, StemCell): return self raise DataConversionError('data duplication detected')
def illegal_item_error_factory(_data): return DataConversionError('illegal item name')
def generate_output_elements(self, input_elements): # type: (Iterator[Value]) -> Iterator[Value] for index, value in enumerate(input_elements): with DataConversionError.sublocation(index): for converted_value in self._element_converter(value): yield converted_value
def default_error_factory(_data): # type: (Value) -> BaseException return DataConversionError('unspecified error')