def get_declared_error_types(response_type): # When there is only one error type, reqrep.make_annotations # would not generate Optional[T]. fields = dataclasses.fields(response_type.Error) if len(fields) == 1: return {ASSERT.issubclass(fields[0].type, Exception): fields[0].name} else: return { ASSERT( typings.is_recursive_type(field.type) and typings.is_union_type(field.type) and typings.match_optional_type(field.type), 'expect typing.Optional[T]: {!r}', field, ): field.name for field in fields }
def _encode_value(self, value_type, value): """Encode a value into a raw value. This and ``_decode_raw_value`` complement each other. """ if typings.is_recursive_type(value_type): if value_type.__origin__ in (list, set, frozenset): element_type = value_type.__args__[0] return [ self._encode_value(element_type, element) for element in value ] elif value_type.__origin__ is tuple: ASSERT.equal(len(value), len(value_type.__args__)) return tuple( self._encode_value(element_type, element) for element_type, element in zip( value_type.__args__, value, )) elif typings.is_union_type(value_type): # Make a special case for ``None``. if value is None: ASSERT.in_(NoneType, value_type.__args__) return None # Make a special case for ``Optional[T]``. type_ = typings.match_optional_type(value_type) if type_: return self._encode_value(type_, value) for type_ in value_type.__args__: if typings.is_recursive_type(type_): if _match_recursive_type(type_, value): return { str(type_): self._encode_value(type_, value) } elif isinstance(value, type_): return { type_.__name__: self._encode_value(type_, value) } return ASSERT.unreachable( 'value is not any union element type: {!r} {!r}', value_type, value, ) else: return ASSERT.unreachable('unsupported generic: {!r}', value_type) elif wiredata.is_message(value): ASSERT.predicate(value_type, wiredata.is_message_type) return { f.name: self._encode_value(f.type, getattr(value, f.name)) for f in dataclasses.fields(value) } elif isinstance(value, datetime.datetime): ASSERT.issubclass(value_type, datetime.datetime) return value.isoformat() elif isinstance(value, enum.Enum): ASSERT.issubclass(value_type, enum.Enum) return value.name # JSON does not support binary type; so it has to be encoded. elif isinstance(value, bytes): ASSERT.issubclass(value_type, bytes) return base64.standard_b64encode(value).decode('ascii') elif isinstance(value, Exception): ASSERT.issubclass(value_type, Exception) return { type(value).__name__: [ ASSERT.isinstance(arg, _DIRECTLY_SERIALIZABLE_TYPES) for arg in value.args ] } elif isinstance(value, _DIRECTLY_SERIALIZABLE_TYPES): ASSERT.issubclass(value_type, _DIRECTLY_SERIALIZABLE_TYPES) return value else: return ASSERT.unreachable('unsupported value type: {!r} {!r}', value_type, value)