def _get_configuration(self, origin: Type, flags: SerdeFlags) -> SerdeConfig: if hasattr(origin, SERDE_FLAGS_ATTR): flags = getattr(origin, SERDE_FLAGS_ATTR) # Get all the annotated fields params = util.safe_get_params(origin) # This is probably a builtin and has no signature fields: Dict[str, Annotation] = {} hints = util.cached_type_hints(origin) for name, t in hints.items(): fields[name] = self.annotation( t, flags=dataclasses.replace(flags, fields={}), default=getattr(origin, name, EMPTY), namespace=origin, ) # Filter out any annotations which aren't part of the object's signature. if flags.signature_only: fields = {x: fields[x] for x in fields.keys() & params.keys()} # Create a field-to-field mapping fields_out = {x: x for x in fields} # Make sure to include any fields explicitly listed include = flags.fields if include: if isinstance(include, Mapping): fields_out.update(include) else: fields_out.update({x: x for x in include}) # Transform the output fields to the correct case. if flags.case: case = Case(flags.case) fields_out = {x: case.transformer(y) for x, y in fields_out.items()}
def get_fields( self, type: Type, as_source: bool = False ) -> Optional[Mapping[str, inspect.Parameter]]: """Get the fields for the given type. Notes ----- We want this to be the type's signature, we really do. But if for some reason we can't make that happen, we fallback to a few known, semi-reliable methods for making this happen. """ # Try first with the signature of the target if this is the target type params = safe_get_params(type) undefined = self.sig_is_undef(params) if not as_source and not undefined: return params # Now we start building a fake signature k: ParameterKind = inspect.Parameter.POSITIONAL_OR_KEYWORD # **kwargs if self.kw_only(params): k = inspect.Parameter.KEYWORD_ONLY # *args elif self.pos_only(params): k = inspect.Parameter.POSITIONAL_ONLY # Fetch any type hints and try to use those. hints = cached_type_hints(type) if hints: return self._fields_from_hints(k, hints) # Fallback to the target object's defined attributes # This will basically work for ORM models, Pydantic models... # Anything that defines the instance using the class body. attrs = cached_simple_attributes(type) if attrs: return self._fields_from_attrs(k, attrs) # Can't be done. return None if undefined else params
def protocols(self, obj, *, strict: bool = False) -> SerdeProtocolsT: """Get a mapping of param/attr name -> :py:class:`SerdeProtocol` Parameters ---------- obj The class or callable object you wish to extract resolved annotations from. strict Whether to validate instead of coerce. Examples -------- >>> import typic >>> >>> @typic.klass ... class Foo: ... bar: str ... >>> protocols = typic.protocols(Foo) See Also -------- :py:class:`SerdeProtocol` """ if not any((inspect.ismethod(obj), inspect.isfunction(obj), inspect.isclass(obj))): obj = obj.__class__ hints = util.cached_type_hints(obj) params = util.safe_get_params(obj) fields: Mapping[str, dataclasses.Field] = {} if dataclasses.is_dataclass(obj): fields = {f.name: f for f in dataclasses.fields(obj)} ann = {} for name in params.keys() | hints.keys(): param = params.get(name) hint = hints.get(name) field = fields.get(name) annotation = hint or param.annotation # type: ignore annotation = util.resolve_supertype(annotation) param = param or inspect.Parameter( name, inspect.Parameter.POSITIONAL_OR_KEYWORD, default=EMPTY, annotation=hint or annotation, ) if repr(param.default) == "<factory>": param = param.replace(default=EMPTY) if checks.isclassvartype(annotation): val = getattr(obj, name) if annotation is ClassVar: annotation = annotation[type(val)] default = val param = param.replace(default=default) if (field and field.default is not dataclasses.MISSING and param.default is EMPTY): if field.init is False and util.origin( annotation) is not ReadOnly: annotation = ReadOnly[annotation] # type: ignore param = param.replace(default=field.default) if not checks.ishashable(param.default): param = param.replace(default=...) resolved = self.resolve( annotation, parameter=param, name=name, is_strict=strict, namespace=obj, ) ann[name] = resolved try: setattr(obj, TYPIC_ANNOS_NAME, ann) # We wrapped a bound method, or # are wrapping a static-/classmethod # after they were wrapped with @static/class except (AttributeError, TypeError): pass return ann