Пример #1
0
def _typeddict(codec_type, python_type):

    python_type, _ = split_annotated(python_type)

    if not is_typeddict(python_type):
        return

    if codec_type is JSON:

        if c := _building.get((codec_type, python_type)):
            return c  # return the (incomplete) outer one still being built

        hints = get_type_hints(python_type, include_extras=True)

        for key in hints:
            if type(key) is not str:
                raise TypeError("codec only supports TypedDict with str keys")

        @affix_type_hints(localns=locals())
        class _TypedDict_JSON(JSON[python_type]):

            json_type = dict[str, Any]  # will be replaced below

            def _process(self, value, method):
                result = {}
                for key in hints:
                    codec = get_codec(JSON, hints[key])
                    try:
                        with CodecError.path_on_error(key):
                            result[key] = getattr(codec, method)(value[key])
                    except KeyError:
                        continue
                return result

            def encode(self, value: python_type) -> Any:
                if not isinstance(value, dict):
                    raise EncodeError
                return self._process(value, "encode")

            def decode(self, value: Any) -> python_type:
                return self._process(value, "decode")

        result = _TypedDict_JSON()
        _building[(codec_type, python_type)] = result

        try:
            json_type = TypedDict(
                "_TypedDict",
                {key: get_codec(JSON, hints[key]).json_type
                 for key in hints},
                total=python_type.__total__,
            )
            json_type.__required_keys__ = python_type.__required_keys__
            json_type.__optional_keys__ = python_type.__optional_keys__
            result.json_type = result.__class__.json_type = json_type

        finally:
            del _building[(codec_type, python_type)]

        return result
Пример #2
0
def get_body_type(operation):
    """Return the type of the request body for the specified operation."""
    signature = inspect.signature(operation)
    type_hints = typing.get_type_hints(operation, include_extras=True)
    as_body_param = None
    in_body_params = {}
    required_keys = set()
    for name, hint in type_hints.items():
        if name == "return":
            continue
        param_in = get_param_in(operation, name, hint)
        if not isinstance(param_in, (AsBody, InBody)):
            continue
        is_required = signature.parameters[
            name].default is inspect.Parameter.empty
        if isinstance(param_in, InBody):
            in_body_params[param_in.name] = hint
            if is_required:
                required_keys.add(param_in.name)
        elif isinstance(param_in, AsBody):
            if as_body_param:
                raise TypeError("multiple AsBody annotated parameters")
            as_body_param = name
    if as_body_param and in_body_params:
        raise TypeError("mixed AsBody and InBody annotated parameters")
    if as_body_param:
        return type_hints[as_body_param]
    if not in_body_params:
        return None
    rb = TypedDict("RequestBody", in_body_params, total=False)
    rb.__required_keys__ = frozenset(required_keys)
    rb.__optional_keys__ = frozenset(k for k in in_body_params
                                     if k not in required_keys)
    return rb
Пример #3
0
def dataclass_codec(codec_type, python_type):

    dc_type, _ = split_annotated(python_type)

    if not dataclasses.is_dataclass(dc_type):
        return

    fields = dataclasses.fields(dc_type)

    if codec_type is JSON:

        if c := _building.get((codec_type, python_type)):
            return c  # return the (incomplete) outer one still being built

        hints = get_type_hints(dc_type, include_extras=True)

        @affix_type_hints(localns=locals())
        class _Dataclass_JSON(JSON[python_type]):

            json_type = dict[str, Any]  # will be replaced below

            def encode(self, value: python_type) -> Any:
                if not isinstance(value, dc_type):
                    raise EncodeError
                result = {}
                for field in fields:
                    v = getattr(value, field.name, None)
                    if v is not None:
                        with CodecError.path_on_error(field.name):
                            result[_dc_kw.get(field.name,
                                              field.name)] = get_codec(
                                                  JSON, field.type).encode(v)
                return result

            def decode(self, value: Any) -> python_type:
                if not isinstance(value, dict):
                    raise DecodeError
                kwargs = {}
                for field in fields:
                    codec = get_codec(JSON, field.type)
                    try:
                        with CodecError.path_on_error(field.name):
                            kwargs[field.name] = codec.decode(value[_dc_kw.get(
                                field.name, field.name)])
                    except KeyError:
                        if (is_optional(field.type)
                                and field.default is dataclasses.MISSING and
                                field.default_factory is dataclasses.MISSING):
                            kwargs[field.name] = None
                try:
                    return python_type(**kwargs)
                except Exception as e:
                    raise DecodeError from e

        result = _Dataclass_JSON()
        _building[(codec_type, python_type)] = result

        try:
            json_type = TypedDict(
                "_TypedDict",
                {key: get_codec(JSON, hints[key]).json_type
                 for key in hints},
                total=False,
            )

            # workaround for https://bugs.python.org/issue42059
            json_type.__required_keys__ = frozenset()
            json_type.__optional_keys__ = frozenset(hints)

            result.json_type = result.__class__.json_type = json_type

        finally:
            del _building[(codec_type, python_type)]

        return result