Exemple #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)
            })
Exemple #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())}
        is_date = _is_date
        is_decimal = _is_decimal

        # 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.optionalset = frozenset(options.defaults)

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

        for field, typ in fields.items():
            is_model, concrete_type = _is_model(typ)
            if is_model:
                # Extract all model fields
                options.models[field] = typ
                # Create mapping of model fields to concrete types if available
                modelattrs[field] = concrete_type

        # extract all fields that are not built-in types,
        # e.g. List[datetime]
        options.converse = {}
        if options.isodates:
            options.converse.update({
                field: Converter(typ, cls._parse_iso8601)
                for field, typ in fields.items()
                if field not in modelattrs and is_date(typ)
            })
        if options.decimals:
            options.converse.update({
                field: Converter(typ, cls._parse_decimal)
                for field, typ in fields.items()
                if field not in modelattrs and is_decimal(typ)
            })
Exemple #3
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)
        }

        # 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():
            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)
Exemple #4
0
    def _init_subclass(
        cls,
        serializer: str = None,
        namespace: str = None,
        include_metadata: bool = None,
        isodates: bool = None,
        abstract: bool = False,
        allow_blessed_key: bool = None,
        decimals: bool = None,
        coerce: bool = None,
        coercions: CoercionMapping = None,
        polymorphic_fields: bool = None,
        validation: bool = None,
        date_parser: Callable[[Any], datetime] = None,
    ) -> None:
        # Can set serializer/namespace/etc. using:
        #    class X(Record, serializer='json', namespace='com.vandelay.X'):
        #        ...
        try:
            custom_options = cls.Options
        except AttributeError:
            custom_options = None
        else:
            delattr(cls, "Options")
        options = getattr(cls, "_options", None)
        if options is None:
            options = ModelOptions()
            options.coercions = {}
            options.defaults = {}
        else:
            options = options.clone_defaults()
        if custom_options:
            options.__dict__.update(custom_options.__dict__)
        if coerce is not None:
            options.coerce = coerce
        if coercions is not None:
            options.coercions.update(coercions)
        if serializer is not None:
            options.serializer = serializer
        if include_metadata is not None:
            options.include_metadata = include_metadata
        if isodates is not None:
            options.isodates = isodates
        if decimals is not None:
            options.decimals = decimals
        if allow_blessed_key is not None:
            options.allow_blessed_key = allow_blessed_key
        if polymorphic_fields is not None:
            options.polymorphic_fields = polymorphic_fields
        if validation is not None:
            options.validation = validation
            options.coerce = True  # validation implies coerce
        if date_parser is not None:
            options.date_parser = date_parser

        options.namespace = namespace or canoname(cls)

        if abstract:
            # Custom base classes can set this to skip class initialization.
            cls.__is_abstract__ = True
            cls._options = options
            cls.__init__ = cls.__abstract_init__  # type: ignore
            return
        cls.__is_abstract__ = False

        # Add introspection capabilities
        cls._contribute_to_options(options)
        # Add FieldDescriptors for every field.
        options.descriptors = cls._contribute_field_descriptors(cls, options)

        # Store options on new subclass.
        cls._options = options

        cls._contribute_methods()

        # Register in the global registry, so we can look up
        # models by namespace.
        registry[options.namespace] = cls

        codegens = [
            ("__init__", cls._BUILD_init, "_model_init"),
            ("__hash__", cls._BUILD_hash, "_model_hash"),
            ("__eq__", cls._BUILD_eq, "_model_eq"),
            ("__ne__", cls._BUILD_ne, "_model_ne"),
            ("__gt__", cls._BUILD_gt, "_model_gt"),
            ("__ge__", cls._BUILD_ge, "_model_ge"),
            ("__lt__", cls._BUILD_lt, "_model_lt"),
            ("__le__", cls._BUILD_le, "_model_le"),
        ]

        for meth_name, meth_gen, attr_name in codegens:
            # self._model_init = cls._BUILD_init()
            # if '__init__' not in cls.__dict__:
            #     cls.__init__ = self._model_init
            meth = meth_gen()
            setattr(cls, attr_name, meth)
            if meth_name not in cls.__dict__:
                setattr(cls, meth_name, meth)
Exemple #5
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))
            })
Exemple #6
0
    def _init_subclass(cls,
                       serializer: str = None,
                       namespace: str = None,
                       include_metadata: bool = None,
                       isodates: bool = None,
                       abstract: bool = False,
                       allow_blessed_key: bool = None,
                       decimals: bool = None,
                       coercions: CoercionMapping = None) -> None:
        # Can set serializer/namespace/etc. using:
        #    class X(Record, serializer='json', namespace='com.vandelay.X'):
        #        ...
        try:
            custom_options = cls.Options
        except AttributeError:
            custom_options = None
        else:
            delattr(cls, 'Options')
        options = getattr(cls, '_options', None)
        if options is None:
            options = ModelOptions()
            options.coercions = {}
            options.defaults = {}
        else:
            options = options.clone_defaults()
        if custom_options:
            options.__dict__.update(custom_options.__dict__)
        if coercions is not None:
            options.coercions.update(coercions)
        if serializer is not None:
            options.serializer = serializer
        if include_metadata is not None:
            options.include_metadata = include_metadata
        if isodates is not None:
            options.isodates = isodates
        if decimals is not None:
            options.decimals = decimals
        if allow_blessed_key is not None:
            options.allow_blessed_key = allow_blessed_key

        options.namespace = namespace or canoname(cls)

        if abstract:
            # Custom base classes can set this to skip class initialization.
            cls.__is_abstract__ = True
            cls._options = options
            cls.__init__ = cls.__abstract_init__  # type: ignore
            return
        cls.__is_abstract__ = False

        # Add introspection capabilities
        cls._contribute_to_options(options)
        # Add FieldDescriptors for every field.
        cls._contribute_field_descriptors(cls, options)

        # Store options on new subclass.
        cls._options = options

        cls._contribute_methods()

        # Register in the global registry, so we can look up
        # models by namespace.
        registry[options.namespace] = cls

        cls._model_init = cls._BUILD_init()
        if '__init__' not in cls.__dict__:
            cls.__init__ = cls._model_init  # type: ignore
        cls._model_hash = cls._BUILD_hash()
        if '__hash__' not in cls.__dict__:
            cls.__hash__ = cls._model_hash  # type: ignore
        cls._model_eq = cls._BUILD_eq()
        if '__eq__' not in cls.__dict__:
            cls.__eq__ = cls._model_eq  # type: ignore