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
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
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