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) })
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) })
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)
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)) })