Ejemplo n.º 1
0
    def _compile_defined_serializer(
        self,
        annotation: "Annotation",
        ser: SerializerT,
    ) -> SerializerT:
        func_name = self._get_name(annotation)
        ser_name = "ser"
        ns = {ser_name: ser}
        with gen.Block(ns) as main:
            with main.f(
                    func_name,
                    main.param("o"),
                    main.param("lazy", default=False),
                    main.param("name", default=None),
            ) as func:
                self._check_add_null_check(func, annotation)
                self._add_type_check(func, annotation)
                line = f"{ser_name}(o)"
                if annotation.origin in (type(o)
                                         for o in self.resolver.OPTIONALS):
                    line = "None"
                func.l(f"{gen.Keyword.RET} {line}")

        serializer: SerializerT = main.compile(name=func_name, ns=ns)
        return serializer
Ejemplo n.º 2
0
    def validator(self) -> ValidatorT:
        ns = dict(__t=self.type, VT=VT, __values=(*self.type, ))
        func_name = self._get_validator_name()
        with gen.Block(ns) as main:
            with self.define(main, func_name) as f:
                if self.nullable:
                    with f.b(f"if value in {self.NULLABLES}:") as b:
                        b.l(f"{gen.Keyword.RET} True, value")
                # This is O(N), but so is casting to the enum
                # And handling a ValueError is an order of magnitude heavier
                f.l(f"{gen.Keyword.RET} value in __values, value")

        validator: ValidatorT = main.compile(name=func_name, ns=ns)
        return validator
Ejemplo n.º 3
0
    def validator(self) -> ValidatorT:
        ns = dict(__t=self.type, VT=VT, __values=frozenset(self.values))
        func_name = self._get_validator_name()
        with gen.Block(ns) as main:
            with self.define(main, func_name) as f:
                if self.nullable:
                    with f.b(f"if value in {self.NULLABLES}:") as b:
                        b.l(f"{gen.Keyword.RET} True, value")
                with f.b("try:") as b:
                    b.l(f"{gen.Keyword.RET} value in __values, value")
                with f.b("except TypeError:") as b:
                    b.l(f"{gen.Keyword.RET} False, value")

        validator: ValidatorT = main.compile(name=func_name, ns=ns)
        return validator
Ejemplo n.º 4
0
    def validator(self) -> ValidatorT:
        """Accessor for the generated multi-validator.

        Validators are keyed by the origin-type of :py:class:`BaseConstraints` inheritors.

        If a value does not match any origin-type, as reported by :py:func:`typic.origin`,
        then we will report the value as invalid.
        """
        func_name = self._get_validator_name()
        vmap = util.TypeMap({c.type: c for c in self.constraints})
        ns = {"tag": self.tag and self.tag.tag, "empty": util.empty}
        with gen.Block(ns) as main:
            with self.define(main, func_name) as f:
                if not vmap:
                    f.l(f"return True, {self.VALUE}")
                else:
                    f.l(f"{self.VALTNAME} = {self.VALUE}.__class__")
                    if self.nullable:
                        with f.b(f"if {self.VALUE} is None:") as b:
                            b.l(f"return True, {self.VALUE}")
                    if self.tag:
                        validators = {
                            value: vmap[t]
                            for value, t in self.tag.types_by_values
                        }
                        f.namespace.update(vmap=validators)
                        with f.b(
                                f"if issubclass({self.VALTNAME}, Mapping):",
                                Mapping=collections.abc.Mapping,
                        ) as b:
                            b.l(f"tag_value = {self.VALUE}.get(tag, empty)")
                        with f.b("else:") as b:
                            b.l(f"tag_value = getattr({self.VALUE}, tag, empty)"
                                )
                        f.l(f"valid, {self.VALUE} = "
                            f"(True, vmap[tag_value].validate({self.VALUE}, field=field)) "
                            f"if tag_value in vmap else (False, {self.VALUE})")
                    else:
                        vmap = util.TypeMap(
                            {util.origin(t): v
                             for t, v in vmap.items()})
                        f.namespace.update(vmap=vmap)
                        f.l(f"v = vmap.get_by_parent({self.VALTNAME}, None)")
                        f.l(f"valid, {self.VALUE} = (True, v.validate(value, field=field)) "
                            f"if v else (False, value)")
                    f.l(f"return valid, {self.VALUE}")
        validator = main.compile(name=func_name)
        return validator  # type: ignore
Ejemplo n.º 5
0
    def validator(self) -> ValidatorT:
        ns = dict(__t=self.type, VT=VT)
        func_name = self._get_validator_name()
        with gen.Block(ns) as main:
            with self.define(main, func_name) as f:
                # Standard instancecheck is default.
                check = "isinstance(value, __t)"
                retval = "value"
                # Have to allow nulls if its nullable.
                if self.nullable:
                    check = "(value is None or isinstance(value, __t))"
                elif self.type is Any:
                    check = "True"
                f.l(f"{gen.Keyword.RET} {check}, {retval}")

        validator: ValidatorT = main.compile(name=func_name, ns=ns)
        return validator
Ejemplo n.º 6
0
    def _compile_defined_serializer(
        self,
        annotation: Annotation[Type[_T]],
        ser: SerializerT[_T],
    ) -> SerializerT[_T]:
        func_name = self._get_name(annotation)
        ser_name = "ser"
        ns = {ser_name: ser}
        with gen.Block(ns) as main:
            with self._define(main, func_name) as func:
                self._check_add_null_check(func, annotation)
                self._add_type_check(func, annotation)
                line = f"{ser_name}(o)"
                if annotation.origin in (type(o)
                                         for o in self.resolver.OPTIONALS):
                    line = "None"
                func.l(f"{gen.Keyword.RET} {line}")

        serializer: SerializerT = main.compile(name=func_name, ns=ns)
        return serializer
Ejemplo n.º 7
0
 def _build_des(  # noqa: C901
     self,
     annotation: Annotation[Type[ObjectT]],
     func_name: str,
     namespace: Type = None,
 ) -> DeserializerT[ObjectT]:
     args = annotation.args
     # Get the "origin" of the annotation.
     # For natives and their typing.* equivs, this will be a builtin type.
     # For SpecialForms (Union, mainly) this will be the un-subscripted type.
     # For custom types or classes, this will be the same as the annotation.
     origin = annotation.resolved_origin
     anno_name = get_unique_name(origin)
     ns = {
         anno_name: origin,
         "parent": getattr(origin, "__parent__", origin),
         "issubclass": cached_issubclass,
         **annotation.serde.asdict(),
     }
     if checks.isliteral(origin):
         return self._build_literal_des(annotation, func_name, namespace)
     with gen.Block(ns) as main:
         with main.f(func_name, main.param(f"{self.VNAME}")) as func:
             needs_return = None
             context = BuildContext(annotation, ns, anno_name, func,
                                    namespace)
             if origin not in self.UNRESOLVABLE:
                 # Set our top-level sanity checks.
                 self._set_checks(func, anno_name, annotation)
                 # Move through our queue.
                 for check, handler in self._HANDLERS.items():
                     # If this is a valid type for this handler,
                     #   write the deserializer.
                     if check(origin, args):
                         needs_return = handler(self, context)
                         break
             # If the deserializer doesn't contain a return statement, add one.
             if needs_return is not False:
                 func.l(f"{gen.Keyword.RET} {self.VNAME}")
     deserializer = main.compile(ns=ns, name=func_name)
     return deserializer
Ejemplo n.º 8
0
 def _compile_validator(self) -> ValidatorT:
     func_name = self._get_validator_name()
     origin = util.origin(self.type)
     type_name = self.type_name
     self._check_syntax()
     assertions = self._get_assertions()
     context: ContextT = {type_name: self.type}
     with gen.Block() as main:
         with self.define(main, func_name) as f:
             # This is a signal that -*-anything can happen...-*-
             if origin in {Any, Signature.empty}:
                 f.l(f"return True, {self.VALUE}")
                 return main.compile(name=func_name)
             f.l(f"{self.VALTNAME} = {type_name!r}")
             f.l(f"{self.FNAME} = {self.VALTNAME} if field is None else field"
                 )
             # Short-circuit validation if the value isn't the correct type.
             if self.instancecheck == InstanceCheck.IS:
                 line = f"if isinstance({self.VALUE}, {type_name}):"
                 if self.nullable:
                     line = (f"if {self.VALUE} in {self.NULLABLES} "
                             f"or isinstance({self.VALUE}, {type_name}):")
                 with f.b(line, **context) as b:  # type: ignore
                     b.l(f"return True, {self.VALUE}")
             else:
                 if self.nullable:
                     with f.b(f"if {self.VALUE} in {self.NULLABLES}:") as b:
                         b.l(f"return True, {self.VALUE}")
                 line = f"if not isinstance({self.VALUE}, {type_name}):"
                 with f.b(line, **context) as b:  # type: ignore
                     b.l(f"return False, {self.VALUE}")
             context = self._build_validator(f,
                                             context=context,
                                             assertions=assertions)
             f.namespace.update(context)
             f.localize_context(*context)
             f.l(f"return True, {self.VALUE}")
     return main.compile(name=func_name)
Ejemplo n.º 9
0
 def _build_key_serializer(self, name: str, kser: SerializerT,
                           annotation: "Annotation") -> SerializerT:
     kser_name = util.get_name(kser)
     # Build the namespace
     ns: Dict[str, Any] = {
         kser_name: kser,
     }
     kvar = "k_"
     with gen.Block(ns) as main:
         with main.f(name, main.param(kvar),
                     main.param("lazy", default=False)) as kf:
             k = f"{kser_name}({kvar})"
             # If there are args & field mapping, get the correct field name
             # AND serialize the key.
             if annotation.serde.fields_out:
                 ns["fields_out"] = annotation.serde.fields_out
                 k = f"{kser_name}(fields_out.get({kvar}, {kvar}))"
             # If there are only serializers, get the serialized value
             if annotation.serde.flags.case:
                 ns.update(case=annotation.serde.flags.case.transformer)
                 k = f"case({k})"
             kf.l(f"{gen.Keyword.RET} {k}")
     return main.compile(name=name, ns=ns)
Ejemplo n.º 10
0
    def _compile_serializer(
            self, annotation: Annotation[Type[_T]]) -> SerializerT[_T]:
        # Check for an optional and extract the type if possible.
        func_name = self._get_name(annotation)
        # We've been here before...
        if func_name in self._serializer_cache:
            return self._serializer_cache[func_name]

        serializer: SerializerT
        origin = annotation.resolved_origin
        # Lazy shortcut for messy paths (Union, Any, ...)
        if (origin in self._DYNAMIC or not annotation.static
                or checks.isuniontype(origin)):
            serializer = cast(SerializerT, self.resolver.primitive)
        # Routines (functions or methods) can't be serialized...
        elif issubclass(
                origin,
                abc.Callable) or inspect.isroutine(origin):  # type: ignore
            name = util.get_qualname(origin)
            with gen.Block() as main:
                with self._define(main, func_name) as func:
                    func.l(
                        f'raise TypeError("Routines are not serializable. ({name!r}).")'
                    )

            serializer = main.compile(name=func_name)
            self._serializer_cache[func_name] = serializer
        # Enums are special
        elif checks.isenumtype(annotation.resolved):
            serializer = self._compile_enum_serializer(annotation)
        # Primitives don't require further processing.
        # Just check for nullable and the correct type.
        elif origin in self._PRIMITIVES:
            ns: dict = {}
            with gen.Block(ns) as main:
                with self._define(main, func_name) as func:
                    self._check_add_null_check(func, annotation)
                    self._add_type_check(func, annotation)
                    line = "o"
                    if annotation.origin in (type(o)
                                             for o in self.resolver.OPTIONALS):
                        line = "None"
                    func.l(f"{gen.Keyword.RET} {line}")

            serializer = main.compile(name=func_name, ns=ns)
            self._serializer_cache[func_name] = serializer

        # Defined cases are pre-compiled, but we have to check for optionals.
        elif origin in self._DEFINED:
            serializer = self._compile_defined_serializer(
                annotation, self._DEFINED[origin])
        elif issubclass(origin, (*self._DEFINED, )):
            serializer = self._compile_defined_subclass_serializer(
                origin, annotation)
        elif issubclass(origin, self._PRIMITIVES):
            serializer = self._compile_primitive_subclass_serializer(
                origin, annotation)
        else:
            # Build the function namespace
            anno_name = f"{func_name}_anno"
            ns = {anno_name: origin, **annotation.serde.asdict()}
            with gen.Block(ns) as main:
                with self._define(main, func_name) as func:
                    # Mapping types need special nested processing as well
                    istypeddict = checks.istypeddict(origin)
                    istypedtuple = checks.istypedtuple(origin)
                    istypicklass = checks.istypicklass(origin)
                    if not istypeddict and issubclass(origin, self._DICTITER):
                        self._build_dict_serializer(func, annotation)
                    # Array types need nested processing.
                    elif (not istypedtuple and not istypeddict
                          and not istypicklass
                          and issubclass(origin, self._LISTITER)):
                        self._build_list_serializer(func, annotation)
                    # Build a serializer for a structured class.
                    else:
                        self._build_class_serializer(func, annotation)
            serializer = main.compile(name=func_name, ns=ns)
            self._serializer_cache[func_name] = serializer
        return serializer
Ejemplo n.º 11
0
 def _build_des(  # noqa: C901
         self,
         annotation: "Annotation",
         func_name: str,
         namespace: Type = None) -> Callable:
     args = annotation.args
     # Get the "origin" of the annotation.
     # For natives and their typing.* equivs, this will be a builtin type.
     # For SpecialForms (Union, mainly) this will be the un-subscripted type.
     # For custom types or classes, this will be the same as the annotation.
     origin = annotation.resolved_origin
     anno_name = get_unique_name(origin)
     ns = {
         anno_name: origin,
         "parent": getattr(origin, "__parent__", origin),
         "issubclass": cached_issubclass,
         **annotation.serde.asdict(),
     }
     if checks.isliteral(origin):
         return self._build_literal_des(annotation, func_name, namespace)
     with gen.Block(ns) as main:
         with main.f(func_name, main.param(f"{self.VNAME}")) as func:
             if origin not in self.UNRESOLVABLE:
                 self._set_checks(func, anno_name, annotation)
                 if origin is Union:
                     self._build_union_des(func, annotation, namespace)
                 elif checks.isdatetype(origin):
                     self._build_date_des(func, anno_name, annotation)
                 elif checks.istimetype(origin):
                     self._build_time_des(func, anno_name, annotation)
                 elif checks.istimedeltatype(origin):
                     self._build_timedelta_des(func, anno_name, annotation)
                 elif checks.isuuidtype(origin):
                     self._build_uuid_des(func, anno_name, annotation)
                 elif origin in {Pattern, re.Pattern}:  # type: ignore
                     self._build_pattern_des(func, anno_name)
                 elif issubclass(origin, pathlib.Path):
                     self._build_path_des(func, anno_name)
                 elif not args and checks.isbuiltintype(origin):
                     self._build_builtin_des(func, anno_name, annotation)
                 elif checks.isfromdictclass(origin):
                     self._build_fromdict_des(func, anno_name)
                 elif checks.isenumtype(origin):
                     self._build_builtin_des(func, anno_name, annotation)
                 elif checks.istypeddict(origin):
                     self._build_typeddict_des(
                         func,
                         anno_name,
                         annotation,
                         total=origin.__total__,  # type: ignore
                         namespace=namespace,
                     )
                 elif checks.istypedtuple(origin) or checks.isnamedtuple(
                         origin):
                     self._build_typedtuple_des(func,
                                                anno_name,
                                                annotation,
                                                namespace=namespace)
                 elif not args and checks.isbuiltinsubtype(origin):
                     self._build_builtin_des(func, anno_name, annotation)
                 elif checks.ismappingtype(origin):
                     self._build_mapping_des(func,
                                             anno_name,
                                             annotation,
                                             namespace=namespace)
                 elif checks.istupletype(origin):
                     self._build_tuple_des(func,
                                           anno_name,
                                           annotation,
                                           namespace=namespace)
                 elif checks.iscollectiontype(origin):
                     self._build_collection_des(func,
                                                anno_name,
                                                annotation,
                                                namespace=namespace)
                 else:
                     self._build_generic_des(func,
                                             anno_name,
                                             annotation,
                                             namespace=namespace)
             func.l(f"{gen.Keyword.RET} {self.VNAME}")
     deserializer = main.compile(ns=ns, name=func_name)
     return deserializer
Ejemplo n.º 12
0
    def _compile_serializer(self, annotation: "Annotation") -> SerializerT:
        # Check for an optional and extract the type if possible.
        func_name = self._get_name(annotation)
        # We've been here before...
        if func_name in self._serializer_cache:
            return self._serializer_cache[func_name]

        serializer: SerializerT
        origin = annotation.resolved_origin
        # Lazy shortcut for messy paths (Union, Any, ...)
        if origin in self._DYNAMIC or not annotation.static:
            serializer = self.resolver.primitive
        # Enums are special
        elif checks.isenumtype(annotation.resolved):
            serializer = self._compile_enum_serializer(annotation)
        # Primitives don't require further processing.
        # Just check for nullable and the correct type.
        elif origin in self._PRIMITIVES:
            ns: dict = {}
            with gen.Block(ns) as main:
                with main.f(
                        func_name,
                        main.param("o"),
                        main.param("lazy", default=False),
                        main.param("name", default=None),
                ) as func:
                    self._check_add_null_check(func, annotation)
                    self._add_type_check(func, annotation)
                    line = "o"
                    if annotation.origin in (type(o)
                                             for o in self.resolver.OPTIONALS):
                        line = "None"
                    func.l(f"{gen.Keyword.RET} {line}")

            serializer = main.compile(name=func_name, ns=ns)
            self._serializer_cache[func_name] = serializer

        # Defined cases are pre-compiled, but we have to check for optionals.
        elif origin in self._DEFINED:
            serializer = self._compile_defined_serializer(
                annotation, self._DEFINED[origin])
        elif issubclass(origin, (*self._DEFINED, )):
            serializer = self._compile_defined_subclass_serializer(
                origin, annotation)
        elif issubclass(origin, self._PRIMITIVES):
            serializer = self._compile_primitive_subclass_serializer(
                origin, annotation)
        else:
            # Build the function namespace
            anno_name = f"{func_name}_anno"
            ns = {anno_name: origin, **annotation.serde.asdict()}
            with gen.Block(ns) as main:
                with main.f(
                        func_name,
                        main.param("o"),
                        main.param("lazy", default=False),
                        main.param("name", default=None),
                ) as func:
                    # Mapping types need special nested processing as well
                    if not checks.istypeddict(origin) and issubclass(
                            origin, self._DICTITER):
                        self._build_dict_serializer(func, annotation)
                    # Array types need nested processing.
                    elif not checks.istypedtuple(origin) and issubclass(
                            origin, self._LISTITER):
                        self._build_list_serializer(func, annotation)
                    # Build a serializer for a structured class.
                    else:
                        self._build_class_serializer(func, annotation)
            serializer = main.compile(name=func_name, ns=ns)
            self._serializer_cache[func_name] = serializer
        return serializer