Esempio n. 1
1
            def method(data: Any) -> Any:
                if not isinstance(data, dict):
                    raise bad_type(data, dict)
                values: Dict[str, Any] = {}
                aliases: List[str] = []
                errors = list(
                    constraint_errors(data)) if constraint_errors else []
                field_errors: Dict[ErrorKey, ValidationError] = OrderedDict()
                for (
                        name,
                        alias,
                        field_method,
                        required,
                        fall_back_on_default,
                ) in normal_fields:
                    if alias in data:
                        aliases.append(alias)
                        try:
                            values[name] = field_method(data[alias])
                        except ValidationError as err:
                            if not fall_back_on_default:
                                field_errors[alias] = err
                    elif not required:
                        pass
                    elif required is True:
                        field_errors[alias] = MISSING_PROPERTY
                    else:
                        assert isinstance(required, AbstractSet)
                        requiring = required & data.keys()
                        if requiring:
                            msg = f"missing property (required by {sorted(requiring)})"
                            field_errors[alias] = ValidationError([msg])
                if has_aggregate_field:
                    for (
                            name,
                            flattened_alias,
                            field_method,
                            fall_back_on_default,
                    ) in flattened_fields:

                        flattened = {
                            alias: data[alias]
                            for alias in flattened_alias if alias in data
                        }
                        aliases.extend(flattened)
                        try:
                            values[name] = field_method(flattened)
                        except ValidationError as err:
                            if not fall_back_on_default:
                                errors.extend(err.messages)
                                field_errors.update(err.children)
                    if len(data) != len(aliases):
                        remain = data.keys() - set(aliases)
                    else:
                        remain = set()
                    for (
                            name,
                            pattern,
                            field_method,
                            fall_back_on_default,
                    ) in pattern_fields:
                        matched = {
                            key: data[key]
                            for key in remain if pattern.match(key)
                        }
                        remain -= matched.keys()
                        try:
                            values[name] = field_method(matched)
                        except ValidationError as err:
                            if not fall_back_on_default:
                                errors.extend(err.messages)
                                field_errors.update(err.children)
                    if additional_field is not None:
                        name, field_method, fall_back_on_default = additional_field
                        additional = {key: data[key] for key in remain}
                        try:
                            values[name] = field_method(additional)
                        except ValidationError as err:
                            if not fall_back_on_default:
                                errors.extend(err.messages)
                                field_errors.update(err.children)
                    elif remain and not additional_properties:
                        for key in remain:
                            field_errors[key] = UNEXPECTED_PROPERTY
                elif len(data) != len(aliases) and not additional_properties:
                    for key in data.keys() - set(aliases):
                        field_errors[key] = UNEXPECTED_PROPERTY

                validators2: Sequence[Validator]
                if validators:
                    init: Dict[str, Any] = {}
                    for name, default_factory in init_defaults:
                        if name in values:
                            init[name] = values[name]
                        elif name not in field_errors:
                            assert default_factory is not None
                            init[name] = default_factory()
                    # Don't keep validators when all dependencies are default
                    validators2 = [
                        v for v in validators
                        if v.dependencies & values.keys()
                    ]
                    if field_errors or errors:
                        error = ValidationError(errors, field_errors)
                        invalid_fields = field_errors.keys(
                        ) | post_init_modified
                        validators2 = [
                            v for v in validators2
                            if not v.dependencies & invalid_fields
                        ]
                        try:
                            validate(
                                ValidatorMock(cls, values),
                                validators2,
                                init,
                                aliaser=aliaser,
                            )
                        except ValidationError as err:
                            error = merge_errors(error, err)
                        raise error
                elif field_errors or errors:
                    raise ValidationError(errors, field_errors)
                else:
                    validators2, init = (
                    ), ...  # type: ignore # only for linter
                try:
                    res = cls(**values)
                except (AssertionError, ValidationError):
                    raise
                except TypeError as err:
                    if str(err).startswith("__init__() got"):
                        raise Unsupported(cls)
                    else:
                        raise ValidationError([str(err)])
                except Exception as err:
                    raise ValidationError([str(err)])
                if validators2:
                    validate(res, validators2, init, aliaser=aliaser)
                return res
Esempio n. 2
0
def validate(
    obj: T,
    validators: Iterable[Validator] = None,
    kwargs: Mapping[str, Any] = None,
    *,
    aliaser: Aliaser = lambda s: s,
) -> T:
    if validators is None:
        validators = get_validators(type(obj))
    error: Optional[ValidationError] = None
    validators = iter(validators)
    for validator in validators:
        try:
            if not kwargs:
                validator.validate(obj)
            elif validator.params == kwargs.keys():
                validator.validate(obj, **kwargs)
            else:
                if any(k not in kwargs for k in validator.params):
                    raise RuntimeError(
                        f"Missing parameters {kwargs.keys() - validator.params}"
                        f" for validator {validator.func}")
                validator.validate(obj,
                                   **{k: kwargs[k]
                                      for k in validator.params})
        except ValidationError as e:
            err = apply_aliaser(e, aliaser)
        except NonTrivialDependency as exc:
            exc.validator = validator
            raise
        except AssertionError:
            raise
        except Exception as e:
            err = ValidationError([str(e)])
        else:
            continue
        if validator.field is not None:
            alias = getattr(get_alias(validator.owner),
                            get_field_name(validator.field))
            err = ValidationError(children={aliaser(alias): err})
        error = merge_errors(error, err)
        if validator.discard:
            try:
                discarded = set(map(get_field_name, validator.discard))
                next_validators = (v for v in validators
                                   if not discarded & v.dependencies)
                validate(obj, next_validators, kwargs, aliaser=aliaser)
            except ValidationError as err:
                error = merge_errors(error, err)
    if error is not None:
        raise error
    return obj
Esempio n. 3
0
    def resolve(__self, __info, **kwargs):
        values = {}
        errors: Dict[str, ValidationError] = {}
        for alias, param_name, deserializer, opt_param, required in parameters:
            if alias in kwargs:
                # It is possible for the parameter to be non-optional in Python
                # type hints but optional in the generated schema. In this case
                # we should ignore it.
                # See: https://github.com/wyfo/apischema/pull/130#issuecomment-845497392
                if not opt_param and kwargs[alias] is None:
                    assert not required
                    continue
                try:
                    values[param_name] = deserializer(kwargs[alias])
                except ValidationError as err:
                    errors[aliaser(param_name)] = err
            elif opt_param and required:
                values[param_name] = None

        if errors:
            raise ValueError(ValidationError(children=errors).errors)
        if info_parameter:
            values[info_parameter] = __info
        try:
            return serialize_result(func(__self, **values))
        except Exception as error:
            if error_handler is None:
                raise
            assert serialize_error is not None
            return serialize_error(error_handler(error, __self, __info, **kwargs))
Esempio n. 4
0
 def method(data: Any) -> Any:
     try:
         result = values[primitive_values.index(data)]
     except ValueError:
         raise ValidationError([f"not one of {primitive_values}"])
     deserialize_any(data)  # for validation
     return result
Esempio n. 5
0
 def method(data: Any) -> Any:
     try:
         return converter(
             conv_deserializer(data))  # type: ignore
     except (ValidationError, AssertionError):
         raise
     except Exception as err:
         raise ValidationError([str(err)])
Esempio n. 6
0
def no_coercion(expected: Type[T], data: Any) -> T:
    if not isinstance(data, expected):
        if expected is float and isinstance(data, int):
            return float(data)  # type: ignore
        msg = (f"expected type {JsonType.from_type(expected)},"
               f" found {JsonType.from_type(type(data))}")
        raise ValidationError([msg])
    return data
Esempio n. 7
0
 def method(data: Any) -> Any:
     if constraints is not None:
         cls = type(data)
         if cls in constraints.errors_by_type:
             errors = constraints.errors_by_type[cls](data)
             if errors:
                 raise ValidationError(errors)
     return data
Esempio n. 8
0
 def method(data: Any) -> Any:
     if not isinstance(data, cls):
         if cls == float and isinstance(data, int):
             data = float(data)
         else:
             raise bad_type(data, cls)
     if data is not None:
         errors = constraint_errors(data)
         if errors:
             raise ValidationError(errors)
     return data
Esempio n. 9
0
 def method(data: Any) -> Any:
     if not isinstance(data, dict):
         raise bad_type(data, dict)
     items = {}
     item_errors: Dict[ErrorKey, ValidationError] = {}
     for key, value in data.items():
         assert isinstance(key, str)
         try:
             items[deserialize_key(key)] = deserialize_value(value)
         except ValidationError as err:
             item_errors[key] = err
     errors = constraint_errors(data) if constraint_errors else ()
     if item_errors or errors:
         raise ValidationError(errors, item_errors)
     return items if cls is DICT_TYPE else MAPPING_TYPES[cls](items)
Esempio n. 10
0
 def method(data: Any) -> Any:
     if not isinstance(data, list):
         raise bad_type(data, list)
     elts: List[Any] = []
     elt_errors: Dict[ErrorKey, ValidationError] = {}
     for i, (deserialize_elt,
             elt) in enumerate(zip(elt_deserializers, data)):
         try:
             elts.append(deserialize_elt(elt))
         except ValidationError as err:
             elt_errors[i] = err
     errors = constraint_errors(data) if constraint_errors else ()
     if elt_errors or errors:
         raise ValidationError(errors, elt_errors)
     return tuple(elts)
Esempio n. 11
0
 def method(data: Any) -> Any:
     if not isinstance(data, list):
         raise bad_type(data, list)
     elts = []
     elt_errors: Dict[ErrorKey, ValidationError] = {}
     for i, elt in enumerate(data):
         try:
             elts.append(deserialize_value(elt))
         except ValidationError as err:
             elt_errors[i] = err
     errors = constraint_errors(data) if constraint_errors else ()
     if elt_errors or errors:
         raise ValidationError(errors, elt_errors)
     return elts if cls is LIST_TYPE else COLLECTION_TYPES[cls](
         elts)
Esempio n. 12
0
 def method(data: Any) -> Any:
     error: Optional[ValidationError] = None
     for deserialize_conv, converter in conv_deserializers:
         try:
             value = deserialize_conv(data)
             break
         except ValidationError as err:
             error = merge_errors(error, err)
     else:
         assert error is not None
         raise error
     try:
         return converter(value)  # type: ignore
     except (ValidationError, AssertionError):
         raise
     except Exception as err:
         raise ValidationError([str(err)])
Esempio n. 13
0
 def resolve(self, info, **kwargs):
     errors: Dict[str, ValidationError] = {}
     for arg_name, arg_type in arg_types:
         if arg_name in kwargs:
             try:
                 kwargs[arg_name] = deserialize(arg_type, kwargs[arg_name])
             except ValidationError as err:
                 errors[aliaser(arg_name)] = err
     if errors:
         raise TypeError(serialize(ValidationError(children=errors)))
     if info_param:
         kwargs[info_param] = info
     try:
         return serialize_result(func(self, **kwargs))
     except Exception:
         if error_as_null:
             return None
         else:
             raise
Esempio n. 14
0
def validate(__obj: T, __validators: Iterable[Validator] = None, **kwargs) -> T:
    if __validators is None:
        __validators = get_validators(__obj)
    if not __validators:
        return __obj
    error: Optional[ValidationError] = None
    __validators = iter(__validators)
    while True:
        for validator in __validators:
            try:
                if kwargs and validator.params != kwargs.keys():
                    assert all(k in kwargs for k in validator.params)
                    validator.validate(
                        __obj, **{k: kwargs[k] for k in validator.params}
                    )
                else:
                    validator.validate(__obj, **kwargs)
            except ValidationError as err:
                error = merge_errors(error, err)
            except Discard as err:
                error = merge_errors(error, err.error)
                discarded = {f.name for f in err.fields}
                __validators = iter(
                    v for v in __validators if not discarded & v.dependencies
                )
                break
            except NonTrivialDependency as exc:
                exc.validator = validator
                raise
            except AssertionError:
                raise
            except Exception as err:
                error = merge_errors(error, ValidationError([str(err)]))
        else:
            break
    if error is not None:
        raise error
    return __obj
Esempio n. 15
0
def bad_type(data: Any, expected: type) -> ValidationError:
    msg = (f"expected type {JsonType.from_type(expected)},"
           f" found {JsonType.from_type(data.__class__)}")
    return ValidationError([msg])
Esempio n. 16
0
 def validate(self, data: T) -> T:
     errors = self.errors(data)
     if errors:
         raise ValidationError(errors)
     return data
Esempio n. 17
0
def coercion_error(cls: Type, data) -> ValidationError:
    msg = (f"cannot coerce {JsonType.from_type(cls)}"
           f" from {JsonType.from_type(type(data))}")
    return ValidationError([msg])
Esempio n. 18
0
    deprecate_kwargs,
    get_origin_or_type,
    identity,
    literal_values,
    opt_or,
)
from apischema.validation import get_validators
from apischema.validation.errors import ErrorKey, ValidationError, merge_errors
from apischema.validation.mock import ValidatorMock
from apischema.validation.validators import Validator, validate
from apischema.visitor import Unsupported

DICT_TYPE = get_origin(Dict[Any, Any])
LIST_TYPE = get_origin(List[Any])

MISSING_PROPERTY = ValidationError(["missing property"])
UNEXPECTED_PROPERTY = ValidationError(["unexpected property"])

NOT_NONE = object()

INIT_VARS_ATTR = f"{PREFIX}_init_vars"

T = TypeVar("T")

DeserializationMethod = Callable[[Any], T]


@dataclass(frozen=True)
class DeserializationMethodFactory:
    factory: Callable[[Optional[Constraints], Sequence[Validator]],
                      DeserializationMethod]
Esempio n. 19
0
 def validate(__obj, **kwargs):
     try:
         wrapped_field(__obj, **kwargs)
     except ValidationError as err:
         raise ValidationError(children={FieldPath(field): err})