示例#1
0
    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
示例#3
0
    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