Beispiel #1
0
def _schema(
    builder: Type[SchemaBuilder],
    cls: AnyType,
    schema: Optional[Schema],
    conversions: Optional[Conversions],
    version: Optional[JsonSchemaVersion],
    aliaser: Optional[Aliaser],
    ref_factory: Optional[RefFactory],
    all_refs: Optional[bool],
    with_schema: bool,
) -> Mapping[str, Any]:
    add_defs = ref_factory is None
    if ref_factory is not None and all_refs is None:
        all_refs = True
    if aliaser is None:
        aliaser = settings.aliaser()
    version, ref_factory, all_refs = _default_version(version, ref_factory,
                                                      all_refs)
    refs = _extract_refs([(cls, conversions)], builder, all_refs)
    visitor = builder(aliaser, ref_factory, refs, False)
    with visitor._replace_conversions(conversions):
        json_schema = visitor.visit_with_schema(cls, schema)
    if add_defs:
        defs = _refs_schema(builder, aliaser, refs, ref_factory)
        if defs:
            json_schema["$defs"] = defs
    result = serialize(json_schema, conversions=version.conversions)
    if with_schema and version.schema is not None:
        result["$schema"] = version.schema
    return result
Beispiel #2
0
 def _field(self, field: ObjectField) -> Lazy[graphql.GraphQLInputField]:
     field_type = field.type
     field_default = graphql.Undefined if field.required else field.get_default(
     )
     default: Any = graphql.Undefined
     # Don't put `null` default + handle Undefined as None
     if field_default in {None, Undefined}:
         field_type = Optional[field_type]
     elif field_default is not graphql.Undefined:
         try:
             default = serialize(
                 field_type,
                 field_default,
                 aliaser=self.aliaser,
                 conversion=field.deserialization,
             )
         except Exception:
             field_type = Optional[field_type]
     factory = self.visit_with_conv(field_type, field.deserialization)
     return lambda: graphql.GraphQLInputField(
         factory.type,  # type: ignore
         default_value=default,
         description=get_description(field.schema, field.type),
         extensions={field.name: ""},
     )
Beispiel #3
0
def partial_serialize(
    obj: Any,
    *,
    conversions: Conversions = None,
    aliaser: Aliaser = None,
) -> Any:
    assert aliaser is not None
    cls = obj.__class__
    if cls in PRIMITIVE_TYPES_SET:
        return obj
    if cls in COLLECTION_TYPE_SET:
        return [
            partial_serialize(elt, conversions=conversions, aliaser=aliaser)
            for elt in obj
        ]
    if cls in MAPPING_TYPE_SET:
        return serialize(obj,
                         conversions=conversions,
                         aliaser=aliaser,
                         exclude_unset=False)
    target = None
    if conversions is not None:
        try:
            target = conversions[cls]
        except KeyError:
            pass
    conversion = SerializationVisitor._is_conversion(cls, target)
    if conversion is not None:
        _, (converter, sub_conversions) = conversion
        if isinstance(target, DataclassModelWrapper):
            return obj
        return partial_serialize(converter(obj),
                                 conversions=sub_conversions,
                                 aliaser=aliaser)
    if is_dataclass(cls):
        return obj
    if issubclass(cls, Enum):
        return serialize(obj.value, aliaser=aliaser, exclude_unset=False)
    if isinstance(obj, PRIMITIVE_TYPES):
        return obj
    if isinstance(obj, Mapping):
        return serialize(obj, aliaser=aliaser, exclude_unset=False)
    if isinstance(obj, Collection):
        return [partial_serialize(elt, aliaser=aliaser) for elt in obj]
    if issubclass(cls, tuple) and hasattr(cls, "_fields"):
        return obj
    raise Unsupported(cls)
Beispiel #4
0
def _schema(
    builder: Type[SchemaBuilder],
    tp: AnyType,
    schema: Optional[Schema],
    conversion: Optional[AnyConversion],
    default_conversion: DefaultConversion,
    version: Optional[JsonSchemaVersion],
    aliaser: Optional[Aliaser],
    ref_factory: Optional[RefFactory],
    all_refs: Optional[bool],
    with_schema: bool,
    additional_properties: Optional[bool],
) -> Mapping[str, Any]:
    from apischema import settings

    add_defs = ref_factory is None
    if aliaser is None:
        aliaser = settings.aliaser
    if additional_properties is None:
        additional_properties = settings.deserialization.additional_properties
    version, ref_factory, all_refs = _default_version(version, ref_factory,
                                                      all_refs)
    refs = _extract_refs([(tp, conversion)], default_conversion, builder,
                         all_refs)
    json_schema = builder(additional_properties, aliaser, default_conversion,
                          False, ref_factory,
                          refs).visit_with_conv(tp, conversion)
    json_schema = full_schema(json_schema, schema)
    if add_defs:
        defs = _refs_schema(
            builder,
            aliaser,
            default_conversion,
            refs,
            ref_factory,
            additional_properties,
        )
        if defs:
            json_schema["$defs"] = defs
    result = serialize(
        JsonSchema,
        json_schema,
        aliaser=aliaser,
        fall_back_on_any=True,
        check_type=True,
        conversion=version.conversion,
        default_conversion=converters.default_serialization,
    )
    if with_schema and version.schema is not None:
        result["$schema"] = version.schema
    return result
Beispiel #5
0
 def visit_field(self, field: ObjectField) -> JsonSchema:
     result = full_schema(
         self.visit_with_conv(field.type, self._field_conversion(field)),
         field.schema,
     )
     if (not field.flattened and not field.pattern_properties
             and not field.additional_properties and not field.required
             and "default" not in result):
         result = JsonSchema(result)
         with suppress(Exception):
             result["default"] = serialize(
                 field.type,
                 field.get_default(),
                 fall_back_on_any=False,
                 check_type=True,
                 conversion=field.serialization,
             )
     return result
Beispiel #6
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
Beispiel #7
0
 def visit_field(self, field: Field, field_type: AnyType):
     field_type, conversions, _ = get_field_conversion(
         field, field_type, self.operation)
     schema: Optional[Schema] = None
     if SCHEMA_METADATA in field.metadata:
         schema = cast(Schema, field.metadata[SCHEMA_METADATA])
         if schema.annotations is not None and schema.annotations is ...:
             if not has_default(field):
                 raise TypeError("Invalid ... without field default")
             try:
                 default = serialize(get_default(field),
                                     conversions=conversions)
             except Exception:
                 pass
             else:
                 annotations = replace(schema.annotations, default=default)
                 schema = replace(schema, annotations=annotations)
     with self._replace_conversions(conversions):
         return self.visit_with_schema(field_type, schema)
Beispiel #8
0
def definitions_schema(
    *,
    deserialization: TypesWithConversions = (),
    serialization: TypesWithConversions = (),
    aliaser: Aliaser = None,
    version: JsonSchemaVersion = None,
    ref_factory: Optional[RefFactory] = None,
    all_refs: bool = None,
) -> Mapping[str, Mapping[str, Any]]:
    if aliaser is None:
        aliaser = settings.aliaser()
    version, ref_factory, all_refs = _default_version(version, ref_factory,
                                                      all_refs)
    deserialization_schemas = _defs_schema(deserialization,
                                           DeserializationSchemaBuilder,
                                           aliaser, ref_factory, all_refs)
    serialization_schemas = _defs_schema(serialization,
                                         SerializationSchemaBuilder, aliaser,
                                         ref_factory, all_refs)
    for duplicate in deserialization_schemas.keys(
    ) & serialization_schemas.keys():
        d_schema = deserialization_schemas[duplicate]
        s_schema = serialization_schemas[duplicate]
        _set_missing_properties(s_schema, d_schema.get("properties"),
                                "writeOnly")
        _set_missing_properties(d_schema, s_schema.get("properties"),
                                "readOnly")
        if "required" in d_schema and "required" in s_schema:
            s_schema["required"] = d_schema["required"]
        if d_schema != s_schema:
            raise TypeError(f"Reference {duplicate} has different schemas"
                            f" for deserialization and serialization")
    return {
        ref: serialize(schema, conversions=version.conversions)
        for ref, schema in chain(deserialization_schemas.items(),
                                 serialization_schemas.items())
    }
Beispiel #9
0
def definitions_schema(
    *,
    deserialization: TypesWithConversion = (),
    serialization: TypesWithConversion = (),
    default_deserialization: DefaultConversion = None,
    default_serialization: DefaultConversion = None,
    aliaser: Aliaser = None,
    version: JsonSchemaVersion = None,
    ref_factory: Optional[RefFactory] = None,
    all_refs: bool = None,
    additional_properties: bool = None,
) -> Mapping[str, Mapping[str, Any]]:
    from apischema import settings

    if additional_properties is None:
        additional_properties = settings.deserialization.additional_properties
    if aliaser is None:
        aliaser = settings.aliaser
    if default_deserialization is None:
        default_deserialization = settings.deserialization.default_conversion
    if default_serialization is None:
        default_serialization = settings.serialization.default_conversion
    version, ref_factory, all_refs = _default_version(version, ref_factory,
                                                      all_refs)
    deserialization_schemas = _defs_schema(
        deserialization,
        default_deserialization,
        DeserializationSchemaBuilder,
        aliaser,
        ref_factory,
        all_refs,
        additional_properties,
    )
    serialization_schemas = _defs_schema(
        serialization,
        default_serialization,
        SerializationSchemaBuilder,
        aliaser,
        ref_factory,
        all_refs,
        additional_properties,
    )
    schemas = {}
    for ref in deserialization_schemas.keys() | serialization_schemas.keys():
        if ref in deserialization_schemas and ref in serialization_schemas:
            try:
                schemas[ref] = compare_schemas(deserialization_schemas[ref],
                                               serialization_schemas[ref])
            except ValueError:
                raise TypeError(f"Reference {ref} has different schemas"
                                f" for deserialization and serialization")
        else:
            schemas[ref] = deserialization_schemas.get(
                ref, serialization_schemas.get(ref))
    return {
        ref: serialize(
            JsonSchema,
            schema,
            aliaser=aliaser,
            fall_back_on_any=True,
            check_type=True,
            conversion=version.conversion,
            default_conversion=converters.default_serialization,
        )
        for ref, schema in schemas.items()
    }
Beispiel #10
0
    def _resolver(self, field: ResolverField) -> Lazy[graphql.GraphQLField]:
        resolve = self._wrap_resolve(
            resolver_resolve(
                field.resolver,
                field.types,
                self.aliaser,
                self.input_builder.default_conversion,
                self.default_conversion,
            ))
        args = None
        if field.parameters is not None:
            args = {}
            for param in field.parameters:
                default: Any = graphql.Undefined
                param_type = field.types[param.name]
                if is_union_of(param_type, graphql.GraphQLResolveInfo):
                    break
                param_field = ObjectField(
                    param.name,
                    param_type,
                    param.default is Parameter.empty,
                    field.metadata.get(param.name, empty_dict),
                    default=param.default,
                )
                if param_field.required:
                    pass
                # Don't put `null` default + handle Undefined as None
                # also https://github.com/python/typing/issues/775
                elif param.default in {None, Undefined}:
                    param_type = Optional[param_type]
                # param.default == graphql.Undefined means the parameter is required
                # even if it has a default
                elif param.default not in {Parameter.empty, graphql.Undefined}:
                    try:
                        default = serialize(
                            param_type,
                            param.default,
                            fall_back_on_any=False,
                            check_type=True,
                        )
                    except Exception:
                        param_type = Optional[param_type]
                arg_factory = self.input_builder.visit_with_conv(
                    param_type, param_field.deserialization)
                description = get_description(param_field.schema,
                                              param_field.type)

                def arg_thunk(
                        arg_factory=arg_factory,
                        default=default,
                        description=description) -> graphql.GraphQLArgument:
                    return graphql.GraphQLArgument(arg_factory.type, default,
                                                   description)

                args[self.aliaser(param_field.alias)] = arg_thunk
        factory = self.visit_with_conv(field.type, field.resolver.conversion)
        return lambda: graphql.GraphQLField(
            factory.type,  # type: ignore
            {name: arg()
             for name, arg in args.items()} if args else None,
            resolve,
            field.subscribe,
            field.description,
            field.deprecated,
        )
Beispiel #11
0
def bijection(cls, data, expected):
    obj = deserialize(cls, data)
    assert obj == expected
    assert serialize(cls, obj) == data