def default_namedtuple_deserializer(obj: Union[list, dict], cls: type, **kwargs) -> object: """ Deserialize a (JSON) list or dict into a named tuple by deserializing all items of that list/dict. :param obj: the tuple that needs deserializing. :param cls: the NamedTuple. :param kwargs: any keyword arguments. :return: a deserialized named tuple (i.e. an instance of a class). """ is_dict = isinstance(obj, dict) args = [] for index, field_name in enumerate(cls._fields): if index < len(obj): key = field_name if is_dict else index field = obj[key] else: field = cls._field_defaults.get(field_name, None) if field is None: msg = ('No value present in {} for argument "{}"'.format( obj, field_name)) raise UnfulfilledArgumentError(msg, field_name, obj, cls) field_types = getattr(cls, '_field_types', None) cls_ = field_types.get(field_name) if field_types else None loaded_field = load(field, cls_, **kwargs) args.append(loaded_field) inst = cls(*args) return inst
def load(cls: type, json_obj: object, **kwargs) -> object: """ See ``jsons.load``. :param kwargs: the keyword args are passed on to the serializer function. :param json_obj: the object that is loaded into an instance of `cls`. :return: this instance in a JSON representation (dict). """ return load(json_obj, cls, fork_inst=cls, **kwargs)
def default_list_deserializer(obj: list, cls: type = None, **kwargs) -> list: """ Deserialize a list by deserializing all items of that list. :param obj: the list that needs deserializing. :param cls: the type optionally with a generic (e.g. List[str]). :param kwargs: any keyword arguments. :return: a deserialized list instance. """ cls_ = None if cls and hasattr(cls, '__args__'): cls_ = cls.__args__[0] return [load(x, cls_, **kwargs) for x in obj]
def _set_remaining_attrs(instance, remaining_attrs, attr_getters=None, **kwargs): # Set any remaining attributes on the newly created instance. attr_getters = attr_getters or {} for attr_name in remaining_attrs: loaded_attr = load(remaining_attrs[attr_name], type(remaining_attrs[attr_name]), **kwargs) try: setattr(instance, attr_name, loaded_attr) except AttributeError: pass # This is raised when a @property does not have a setter. for attr_name, getter in attr_getters.items(): setattr(instance, attr_name, getter())
def default_string_deserializer(obj: str, cls: Optional[type] = None, **kwargs) -> object: """ Deserialize a string. If the given ``obj`` can be parsed to a date, a ``datetime`` instance is returned. :param obj: the string that is to be deserialized. :param cls: not used. :param kwargs: any keyword arguments. :return: the deserialized obj. """ try: result = load(obj, datetime, **kwargs) except DeserializationError: result = obj return result
def default_list_deserializer(obj: list, cls: type = None, **kwargs) -> list: """ Deserialize a list by deserializing all items of that list. :param obj: the list that needs deserializing. :param cls: the type optionally with a generic (e.g. List[str]). :param kwargs: any keyword arguments. :return: a deserialized list instance. """ cls_ = None kwargs_ = {**kwargs} if cls and hasattr(cls, '__args__'): cls_ = cls.__args__[0] # Mark the cls as 'inferred' so that later it is known where cls came # from and the precedence of classes can be determined. kwargs_['_inferred_cls'] = True return [load(x, cls_, **kwargs_) for x in obj]
def _get_value_from_obj(obj, sig, sig_key, meta_hints, **kwargs): # Obtain the value for the attribute with the given signature from the # given obj. Try to obtain the class of this attribute from the meta info # or from type hints. cls_key = '/{}'.format(sig_key) cls_from_meta = meta_hints.get(cls_key, None) new_hints = meta_hints arg_cls = None if cls_from_meta: arg_cls = get_cls_from_str(cls_from_meta, obj, kwargs['fork_inst']) # Rebuild the class hints: cls_key becomes the new root. new_hints = { key.replace(cls_key, '/'): meta_hints[key] for key in meta_hints if key != '/' } elif sig.annotation != inspect.Parameter.empty: arg_cls = sig.annotation value = load(obj[sig_key], arg_cls, meta_hints=new_hints, **kwargs) return value
def default_tuple_deserializer(obj: list, cls: type = None, **kwargs) -> object: """ Deserialize a (JSON) list into a tuple by deserializing all items of that list. :param obj: the tuple that needs deserializing. :param cls: the type optionally with a generic (e.g. Tuple[str, int]). :param kwargs: any keyword arguments. :return: a deserialized tuple instance. """ if hasattr(cls, '_fields'): return default_namedtuple_deserializer(obj, cls, **kwargs) tuple_types = getattr(cls, '__tuple_params__', cls.__args__) if len(tuple_types) > 1 and tuple_types[1] is ...: tuple_types = [tuple_types[0]] * len(obj) list_ = [load(value, tuple_types[i], **kwargs) for i, value in enumerate(obj)] return tuple(list_)
def default_dict_deserializer(obj: dict, cls: type, key_transformer: Optional[Callable[[str], str]] = None, **kwargs) -> dict: """ Deserialize a dict by deserializing all instances of that dict. :param obj: the dict that needs deserializing. :param key_transformer: a function that transforms the keys to a different style (e.g. PascalCase). :param cls: not used. :param kwargs: any keyword arguments. :return: a deserialized dict instance. """ key_transformer = key_transformer or (lambda key: key) kwargs_ = {**{'key_transformer': key_transformer}, **kwargs} if hasattr(cls, '__args__') and len(cls.__args__) > 1: sub_cls = cls.__args__[1] kwargs_['cls'] = sub_cls return {key_transformer(key): load(obj[key], **kwargs_) for key in obj}
def default_union_deserializer(obj: object, cls: Union, **kwargs) -> object: """ Deserialize an object to any matching type of the given union. The first successful deserialization is returned. :param obj: The object that needs deserializing. :param cls: The Union type with a generic (e.g. Union[str, int]). :param kwargs: Any keyword arguments that are passed through the deserialization process. :return: An object of the first type of the Union that could be deserialized successfully. """ for sub_type in cls.__args__: try: return load(obj, sub_type, **kwargs) except JsonsError: pass # Try the next one. else: args_msg = ', '.join([get_class_name(cls_) for cls_ in cls.__args__]) err_msg = ('Could not match the object of type "{}" to any type of ' 'the Union: {}'.format(str(cls), args_msg)) raise DeserializationError(err_msg, obj, cls)
def _get_value_from_obj(obj, cls, sig, sig_key, meta_hints, **kwargs): # Obtain the value for the attribute with the given signature from the # given obj. Try to obtain the class of this attribute from the meta info # or from type hints. cls_key = '/{}'.format(sig_key) cls_str_from_meta = meta_hints.get(cls_key, None) new_hints = meta_hints cls_from_meta = None if cls_str_from_meta: cls_from_meta = get_cls_from_str(cls_str_from_meta, obj, kwargs['fork_inst']) # Rebuild the class hints: cls_key becomes the new root. new_hints = { key.replace(cls_key, '/'): meta_hints[key] for key in meta_hints if key != '/' } cls_ = determine_precedence(cls=cls, cls_from_meta=cls_from_meta, cls_from_type=None, inferred_cls=True) value = load(obj[sig_key], cls_, meta_hints=new_hints, **kwargs) return value
def _get_value_for_attr(obj, cls, sig_key, sig, attr_getters, **kwargs): result = None, None if obj and sig_key in obj: # This argument is in obj. arg_cls = None if sig.annotation != inspect.Parameter.empty: arg_cls = sig.annotation value = load(obj[sig_key], arg_cls, **kwargs) result = sig_key, value elif sig_key in attr_getters: # There exists an attr_getter for this argument. attr_getter = attr_getters.pop(sig_key) result = sig_key, attr_getter() elif sig.default != inspect.Parameter.empty: # There is a default value for this argument. result = sig_key, sig.default elif sig.kind not in (inspect.Parameter.VAR_POSITIONAL, inspect.Parameter.VAR_KEYWORD): # This argument is no *args or **kwargs and has no value. raise UnfulfilledArgumentError( 'No value found for "{}"'.format(sig_key), sig_key, obj, cls) return result
def default_namedtuple_deserializer(obj: list, cls: type, **kwargs) -> object: """ Deserialize a (JSON) list into a named tuple by deserializing all items of that list. :param obj: the tuple that needs deserializing. :param cls: the NamedTuple. :param kwargs: any keyword arguments. :return: a deserialized named tuple (i.e. an instance of a class). """ args = [] for index, field_name in enumerate(cls._fields): if index < len(obj): field = obj[index] else: field = cls._field_defaults.get(field_name, None) if not field: msg = ('No value present in {} for argument "{}"' .format(obj, field_name)) raise UnfulfilledArgumentError(msg, field_name, obj, cls) cls_ = cls._field_types.get(field_name, None) loaded_field = load(field, cls_, **kwargs) args.append(loaded_field) inst = cls(*args) return inst
def _wrapper(cls_, inst, **kwargs_): return load(inst, cls_, **{**kwargs_, **kwargs})