Ejemplo n.º 1
0
    def _contribute_to_options(cls, options: ModelOptions) -> None:
        # Find attributes and their types, and create indexes for these.
        # This only happens once when the class is created, so Faust
        # models are fast at runtime.
        fields, defaults = annotations(
            cls,
            stop=Record,
            skip_classvar=True,
            alias_types=ALIAS_FIELD_TYPES,
            localns={cls.__name__: cls},
        )
        options.fields = cast(Mapping, fields)
        options.fieldset = frozenset(fields)
        options.fieldpos = {i: k for i, k in enumerate(fields.keys())}
        is_concretely = _is_concretely

        # extract all default values, but only for actual fields.
        options.defaults = {
            k: v.default if isinstance(v, FieldDescriptor) else v
            for k, v in defaults.items() if k in fields
            and not (isinstance(v, FieldDescriptor) and v.required)
        }

        options.models = {}
        modelattrs = options.modelattrs = {}

        for field, typ in fields.items():
            is_model, polymorphic_type = _is_model(typ)
            if is_model:
                # Extract all model fields
                options.models[field] = typ
                # Create mapping of model fields to polymorphic types if
                # available
                modelattrs[field] = polymorphic_type
            if is_optional(typ):
                # Optional[X] also needs to be added to defaults mapping.
                options.defaults.setdefault(field, None)

        # Create frozenset index of default fields.
        options.optionalset = frozenset(options.defaults)

        # extract all fields that we want to coerce to a different type
        # (decimals=True, isodates=True, coercions={MyClass: converter})
        # Then move them to options.field_coerce, which is what the
        # model.__init__ method uses to coerce any fields that need to
        # be coerced.
        options.field_coerce = {}
        if options.isodates:
            options.coercions.setdefault(DATE_TYPES, iso8601.parse)
        if options.decimals:
            options.coercions.setdefault(DECIMAL_TYPES, str_to_decimal)

        for coerce_types, coerce_handler in options.coercions.items():
            options.field_coerce.update({
                field: TypeCoerce(typ, coerce_handler)
                for field, typ in fields.items() if field not in modelattrs
                and is_concretely(coerce_types, typ)
            })
Ejemplo n.º 2
0
    def _contribute_to_options(cls, options: ModelOptions) -> None:
        # Find attributes and their types, and create indexes for these.
        # This only happens once when the class is created, so Faust
        # models are fast at runtime.

        fields, defaults = annotations(
            cls,
            stop=Record,
            skip_classvar=True,
            alias_types=ALIAS_FIELD_TYPES,
            localns={cls.__name__: cls},
        )
        options.fields = cast(Mapping, fields)
        options.fieldset = frozenset(fields)
        options.fieldpos = {i: k for i, k in enumerate(fields.keys())}

        # extract all default values, but only for actual fields.
        options.defaults = {
            k: v.default if isinstance(v, FieldDescriptor) else v
            for k, v in defaults.items() if k in fields
            and not (isinstance(v, FieldDescriptor) and v.required)
        }

        options.models = {}
        options.polyindex = {}
        modelattrs = options.modelattrs = {}

        def _is_concrete_type(field: str, wanted: IsInstanceArgT) -> bool:
            typeinfo = options.polyindex[field]
            try:
                return issubclass(typeinfo.member_type, wanted)
            except TypeError:
                return False

        # Raise error if non-defaults are mixed in with defaults
        # like namedtuple/dataclasses do.
        local_defaults = []
        for attr_name in cls.__annotations__:
            if attr_name in cls.__dict__:
                default_value = cls.__dict__[attr_name]
                if isinstance(default_value, FieldDescriptorT):
                    if not default_value.required:
                        local_defaults.append(attr_name)
                else:
                    local_defaults.append(attr_name)
            else:
                if local_defaults:
                    raise TypeError(
                        E_NON_DEFAULT_FOLLOWS_DEFAULT.format(
                            cls_name=cls.__name__,
                            field_name=attr_name,
                            fields=pluralize(len(local_defaults), 'field'),
                            default_names=', '.join(local_defaults),
                        ))

        for field, typ in fields.items():
            is_model, member_type, generic_type = _is_model(typ)
            options.polyindex[field] = TypeInfo(generic_type, member_type)
            if is_model:
                # Extract all model fields
                options.models[field] = typ
                # Create mapping of model fields to polymorphic types if
                # available
                modelattrs[field] = generic_type
            if is_optional(typ):
                # Optional[X] also needs to be added to defaults mapping.
                options.defaults.setdefault(field, None)

        # Create frozenset index of default fields.
        options.optionalset = frozenset(options.defaults)

        # extract all fields that we want to coerce to a different type
        # (decimals=True, isodates=True, coercions={MyClass: converter})
        # Then move them to options.field_coerce, which is what the
        # model.__init__ method uses to coerce any fields that need to
        # be coerced.
        options.field_coerce = {}
        if options.isodates:
            options.coercions.setdefault(DATE_TYPES, iso8601.parse)
        if options.decimals:
            options.coercions.setdefault(DECIMAL_TYPES, str_to_decimal)

        for coerce_types, coerce_handler in options.coercions.items():
            options.field_coerce.update({
                field: TypeCoerce(typ, coerce_handler)
                for field, typ in fields.items()
                if (field not in modelattrs
                    and _is_concrete_type(field, coerce_types))
            })