Exemplo n.º 1
0
def self_deserialization_wrapper(cls: Type) -> Type:
    wrapper = new_class(
        f"{cls.__name__}SelfDeserializer",
        (cls[cls.__parameters__] if has_type_vars(cls) else cls, ),
        exec_body=lambda ns: ns.update({
            "__new__":
            lambda _, *args, **kwargs: cls(*args, **kwargs)
        }),
    )
    return type_name(None)(wrapper)
Exemplo n.º 2
0
def as_names(cls: EnumCls, aliaser: Callable[[str], str] = lambda s: s) -> EnumCls:
    # Enum requires to call namespace __setitem__
    def exec_body(namespace: dict):
        for elt in cls:  # type: ignore
            namespace[elt.name] = aliaser(elt.name)

    if not issubclass(cls, Enum):
        raise TypeError("as_names must be called with Enum subclass")
    name_cls = type_name(None)(
        new_class(cls.__name__, (str, Enum), exec_body=exec_body)
    )
    deserializer(Conversion(partial(getattr, cls), source=name_cls, target=cls))
    serializer(
        Conversion(lambda obj: getattr(name_cls, obj.name), source=cls, target=name_cls)
    )
    return cls
Exemplo n.º 3
0
def object_deserialization(
    func: Callable[..., T],
    *input_class_modifiers: Callable[[type], Any],
    parameters_metadata: Mapping[str, Mapping] = None,
) -> Any:
    fields = parameters_as_fields(func, parameters_metadata)
    types = get_type_hints(func, include_extras=True)
    if "return" not in types:
        raise TypeError("Object deserialization must be typed")
    return_type = types["return"]
    bases = ()
    if getattr(return_type, "__parameters__", ()):
        bases = (Generic[return_type.__parameters__], )  # type: ignore
    elif func.__name__ != "<lambda>":
        input_class_modifiers = (
            type_name(to_pascal_case(func.__name__)),
            *input_class_modifiers,
        )

    def __init__(self, **kwargs):
        self.kwargs = kwargs

    input_cls = new_class(
        to_pascal_case(func.__name__),
        bases,
        exec_body=lambda ns: ns.update({"__init__": __init__}),
    )
    for modifier in input_class_modifiers:
        modifier(input_cls)
    set_object_fields(input_cls, fields)
    if any(f.additional_properties for f in fields):
        kwargs_param = next(f.name for f in fields if f.additional_properties)

        def wrapper(input):
            kwargs = input.kwargs.copy()
            kwargs.update(kwargs.pop(kwargs_param))
            return func(**kwargs)

    else:

        def wrapper(input):
            return func(**input.kwargs)

    wrapper.__annotations__["input"] = input_cls
    wrapper.__annotations__["return"] = return_type
    return wrapper
Exemplo n.º 4
0
def object_serialization(
    cls: Type[T],
    fields_and_methods: Union[Iterable[Any], Callable[[], Iterable[Any]]],
    *output_class_modifiers: Callable[[type], Any],
) -> Callable[[T], Any]:

    generic, bases = cls, ()
    if getattr(cls, "__parameters__", ()):
        generic = cls[cls.__parameters__]  # type: ignore
        bases = Generic[cls.__parameters__]  # type: ignore
    elif (callable(fields_and_methods)
          and fields_and_methods.__name__ != "<lambda>"
          and not getattr(cls, "__parameters__", ())):
        output_class_modifiers = (
            type_name(to_pascal_case(fields_and_methods.__name__)),
            *output_class_modifiers,
        )

    def __init__(self, obj):
        _, new_init = _fields_and_init(cls, fields_and_methods)
        new_init.__annotations__ = {"obj": generic}
        output_cls.__init__ = new_init
        new_init(self, obj)

    __init__.__annotations__ = {"obj": generic}
    output_cls = new_class(
        f"{cls.__name__}Serialization",
        bases,
        exec_body=lambda ns: ns.update({"__init__": __init__}),
    )
    for modifier in output_class_modifiers:
        modifier(output_cls)
    set_object_fields(output_cls,
                      lambda: _fields_and_init(cls, fields_and_methods)[0])

    return output_cls
Exemplo n.º 5
0
 def __init_subclass__(cls, **kwargs):
     super().__init_subclass__(**kwargs)
     type_name(graphql=connection_name)(cls)
Exemplo n.º 6
0
 def __init_subclass__(cls, **kwargs):
     super().__init_subclass__(**kwargs)
     type_name(graphql=edge_name)(cls)
Exemplo n.º 7
0
    def __init_subclass__(cls, **kwargs):
        super().__init_subclass__(**kwargs)
        if not hasattr(cls, "mutate"):
            return
        if not isinstance(cls.__dict__["mutate"], (classmethod, staticmethod)):
            raise TypeError(
                f"{cls.__name__}.mutate must be a classmethod/staticmethod")
        mutate = getattr(cls, "mutate")
        type_name(f"{cls.__name__}Payload")(cls)
        types = get_type_hints(mutate,
                               localns={cls.__name__: cls},
                               include_extras=True)
        async_mutate = is_async(mutate, types)
        fields: List[Tuple[str, AnyType, Field]] = []
        cmi_param = None
        for param_name, param in signature(mutate).parameters.items():
            if param.kind is Parameter.POSITIONAL_ONLY:
                raise TypeError("Positional only parameters are not supported")
            if param.kind in {
                    Parameter.POSITIONAL_OR_KEYWORD, Parameter.KEYWORD_ONLY
            }:
                if param_name not in types:
                    raise TypeError("Mutation parameters must be typed")
                field_type = types[param_name]
                field_ = MISSING if param.default is Parameter.empty else param.default
                if is_union_of(field_type, ClientMutationId):
                    cmi_param = param_name
                    if cls._client_mutation_id is False:
                        if field_ is MISSING:
                            raise TypeError(
                                "Cannot have a ClientMutationId parameter"
                                " when _client_mutation_id = False")
                        continue
                    elif cls._client_mutation_id is True:
                        field_ = MISSING
                    field_ = field(default=field_,
                                   metadata=alias(CLIENT_MUTATION_ID))
                fields.append((param_name, field_type, field_))
        field_names = [name for (name, _, _) in fields]
        if cmi_param is None and cls._client_mutation_id is not False:
            fields.append((
                CLIENT_MUTATION_ID,
                ClientMutationId
                if cls._client_mutation_id else Optional[ClientMutationId],
                MISSING if cls._client_mutation_id else None,
            ))
            cmi_param = CLIENT_MUTATION_ID
        input_cls = make_dataclass(f"{cls.__name__}Input", fields)

        def wrapper(input):
            return mutate(
                **{name: getattr(input, name)
                   for name in field_names})

        wrapper.__annotations__["input"] = input_cls
        wrapper.__annotations__[
            "return"] = Awaitable[cls] if async_mutate else cls
        if cls._client_mutation_id is not False:
            cls.__annotations__[
                CLIENT_MUTATION_ID] = input_cls.__annotations__[cmi_param]
            setattr(cls, CLIENT_MUTATION_ID, field(init=False))
            wrapped = wrapper

            if async_mutate:

                async def wrapper(input):
                    result = await wrapped(input)
                    setattr(result, CLIENT_MUTATION_ID,
                            getattr(input, cmi_param))
                    return result

            else:

                def wrapper(input):
                    result = wrapped(input)
                    setattr(result, CLIENT_MUTATION_ID,
                            getattr(input, cmi_param))
                    return result

            wrapper = wraps(wrapped)(wrapper)

        cls._mutation = Mutation_(
            function=wrapper,
            alias=camel_to_snake(cls.__name__),
            schema=cls._schema,
            error_handler=cls._error_handler,
        )
Exemplo n.º 8
0
    TypeVar,
)

from graphql.pyutils import camel_to_snake

from apischema.aliases import alias
from apischema.graphql.schema import Mutation as Mutation_
from apischema.schemas import Schema
from apischema.serialization.serialized_methods import ErrorHandler
from apischema.type_names import type_name
from apischema.types import AnyType, Undefined
from apischema.typing import get_type_hints
from apischema.utils import is_async, is_union_of, wrap_generic_init_subclass

ClientMutationId = NewType("ClientMutationId", str)
type_name(None)(ClientMutationId)
CLIENT_MUTATION_ID = "client_mutation_id"
M = TypeVar("M", bound="Mutation")


class Mutation:
    _error_handler: ErrorHandler = Undefined
    _schema: Optional[Schema] = None
    _client_mutation_id: Optional[bool] = None
    _mutation: Mutation_  # set in __init_subclass__

    # Mutate is not defined to prevent Mypy warning about signature of superclass
    mutate: Callable

    @wrap_generic_init_subclass
    def __init_subclass__(cls, **kwargs):