Пример #1
0
 def convert_to_pydantic_field_info(cls, allow_null: bool = False) -> FieldInfo:
     base = cls.default_value()
     if base is None:
         base = (
             FieldInfo(default=None)
             if (cls.nullable or allow_null)
             else FieldInfo(default=pydantic.fields.Undefined)
         )
     for attr_name in FieldInfo.__dict__.keys():
         if cls.is_valid_field_info_field(attr_name):
             setattr(base, attr_name, cls.__dict__.get(attr_name))
     return base
Пример #2
0
def create_response_field(
    name: str,
    type_: Type[Any],
    class_validators: Optional[Dict[str, Validator]] = None,
    default: Optional[Any] = None,
    required: Union[bool, UndefinedType] = False,
    model_config: Type[BaseConfig] = BaseConfig,
    field_info: Optional[FieldInfo] = None,
    alias: Optional[str] = None,
) -> ModelField:
    """
    Create a new response field. Raises if type_ is invalid.
    """
    class_validators = class_validators or {}
    field_info = field_info or FieldInfo(None)

    response_field = functools.partial(
        ModelField,
        name=name,
        type_=type_,
        class_validators=class_validators,
        default=default,
        required=required,
        model_config=model_config,
        alias=alias,
    )

    try:
        return response_field(field_info=field_info)
    except RuntimeError:
        raise fastapi.exceptions.FastAPIError(
            f"Invalid args for response field! Hint: check that {type_} is a valid pydantic field type"
        )
Пример #3
0
def DjangoField(field):
    default = ...
    default_factory = None
    description = None
    title = None
    max_length = None
    # min_length = None
    if field.is_relation:
        internal_type = field.related_model._meta.pk.get_internal_type()
        if not field.concrete and field.auto_created:  # Reverse relation

            default = None
        elif field.null:
            default = None

        python_type = FIELD_TYPES.get(internal_type, int)

        if field.one_to_many or field.many_to_many:
            python_type = List[python_type]

        field = field.target_field

    else:
        internal_type = field.get_internal_type()
        if internal_type in STR_TYPES:
            python_type = str
            max_length = field.max_length

        elif internal_type in INT_TYPES:
            python_type = int
        else:
            python_type = FIELD_TYPES[internal_type]

        deconstructed = field.deconstruct()
        field_options = deconstructed[3] or {}
        blank = field_options.pop("blank", False)
        null = field_options.pop("null", False)
        if field.has_default():
            if callable(field.default):
                default_factory = field.default
            else:
                default = field.default
        elif field.primary_key or blank or null:
            default = None

    description = field.help_text
    title = field.verbose_name.title()

    return (
        python_type,
        FieldInfo(
            default,
            default_factory=default_factory,
            title=title,
            description=description,
            max_length=max_length,
            # **extra,
        ),
    )
Пример #4
0
def get_schema_field(field: Field, *, depth: int = 0) -> Tuple:
    alias = None
    default = ...
    default_factory = None
    description = None
    title = None
    max_length = None
    python_type = None

    if field.is_relation:
        if depth > 0:
            return get_related_field_schema(field, depth=depth)

        internal_type = field.related_model._meta.pk.get_internal_type()

        if not field.concrete and field.auto_created or field.null:
            default = None

        if hasattr(field, "get_attname"):
            alias = field.get_attname()

        pk_type = TYPES.get(internal_type, int)
        if field.one_to_many or field.many_to_many:
            m2m_type = create_m2m_link_type(pk_type)
            python_type = List[m2m_type]
        else:
            python_type = pk_type

    else:
        field_options = field.deconstruct()[3]  # 3 are the keywords
        blank = field_options.get("blank", False)
        null = field_options.get("null", False)
        max_length = field_options.get("max_length")

        internal_type = field.get_internal_type()
        python_type = TYPES[internal_type]

        if field.has_default():
            if callable(field.default):
                default_factory = field.default
            else:
                default = field.default
        elif field.primary_key or blank or null:
            default = None

    description = field.help_text
    title = field.verbose_name.title()

    return (
        python_type,
        FieldInfo(
            default,
            alias=alias,
            default_factory=default_factory,
            title=title,
            description=description,
            max_length=max_length,
        ),
    )
Пример #5
0
 def create_field_info(cls,
                       annotation: dict[str, Any],
                       value: Any = Undefined):
     if isinstance(value, FieldInfo):
         # Annotations override the existing FieldInfo. Use deepcopy in case the FieldInfo is
         # used multiple times with different annotations.
         field_info, value = deepcopy(value), Undefined
     else:
         field_info = FieldInfo()
     # Populate the FieldInfo instance with our FieldAnnotations
     field_annotations = cls.collect_from_annotation(annotation)
     for field_annotation in field_annotations:
         field_annotation.populate_field_info(field_info)
     # If the value is set, use that as the default over any Default annotation.
     if value is not Undefined:
         field_info.default = value
     if (field_info.default is not Undefined
             and field_info.default_factory is not None):
         raise ValueError("cannot specify both default and default_factory")
     return field_info
Пример #6
0
    def get_base_pydantic_field_info(cls, allow_null: bool) -> FieldInfo:
        """
        Generates base pydantic.FieldInfo with only default and optionally
        required to fix pydantic Json field being set to required=False.
        Used in an ormar Model Metaclass.

        :param allow_null: flag if the default value can be None
        or if it should be populated by pydantic Undefined
        :type allow_null: bool
        :return: instance of base pydantic.FieldInfo
        :rtype: pydantic.FieldInfo
        """
        base = cls.default_value()
        if base is None:
            base = (FieldInfo(default=None) if
                    (cls.nullable or allow_null) else FieldInfo(
                        default=Undefined))
        if cls.__type__ == Json and base.default is Undefined:
            base.default = Required
        return base
Пример #7
0
    def convert_to_pydantic_field_info(cls,
                                       allow_null: bool = False) -> FieldInfo:
        """
        Converts a BaseField into pydantic.FieldInfo
        that is later easily processed by pydantic.
        Used in an ormar Model Metaclass.

        :param allow_null: flag if the default value can be None
        or if it should be populated by pydantic Undefined
        :type allow_null: bool
        :return: actual instance of pydantic.FieldInfo with all needed fields populated
        :rtype: pydantic.FieldInfo
        """
        base = cls.default_value()
        if base is None:
            base = (FieldInfo(default=None) if
                    (cls.nullable or allow_null) else FieldInfo(
                        default=pydantic.fields.Undefined))
        for attr_name in FieldInfo.__dict__.keys():
            if cls.is_valid_field_info_field(attr_name):
                setattr(base, attr_name, cls.__dict__.get(attr_name))
        return base
Пример #8
0
def _extension_model_factory(
    stac_extensions: Tuple[str], base_class: Type[Item], skip_remote_refs: bool = False
) -> Tuple[Type[BaseModel], FieldInfo]:
    """
    Create a stac item properties model for a set of stac extensions
    """
    fields = decompose_model(base_class.__fields__["properties"].type_)
    for ext in stac_extensions:
        if skip_remote_refs and ext.startswith("http"):
            continue
        if ext == "checksum":
            continue
        fields.update(decompose_model(Extensions.get(ext)))
    return (
        create_model("CustomItemProperties", __base__=ItemProperties, **fields),
        FieldInfo(...),
    )
Пример #9
0
def get_related_field_schema(field: Field, *, depth: int):
    from ninja.orm import create_schema

    model = field.related_model
    schema = create_schema(model, depth=depth - 1)
    default = ...
    if not field.concrete and field.auto_created or field.null:
        default = None
    if isinstance(field, ManyToManyField):
        schema = List[schema]

    return (
        schema,
        FieldInfo(
            default=default,
            description=field.help_text,
            title=field.verbose_name.title(),
        ),
    )
Пример #10
0
def MSONable_to_pydantic(monty_cls: type, pydantic_model=None):
    monty_props = {
        "@class": (
            str,
            Field(
                default=monty_cls.__name__,
                title="MSONable Class",
                description="The formal class name for serialization lookup",
            ),
        ),
        "@module": (
            str,
            Field(
                default=monty_cls.__module__,
                title="Python Module",
                description="The module this class is defined in",
            ),
        ),
    }
    if pydantic_model:
        props = {
            name: (field.type_, field.field_info)
            for name, field in pydantic_model.__fields__.items()
        }
    else:
        _type_hints = get_type_hints(
            monty_cls.__init__).items()  # type: ignore
        props = {
            field_name: (__make_pydantic(field_type), FieldInfo(...))
            for field_name, field_type in _type_hints
        }

    return create_model(monty_cls.__name__,
                        field_definitions={
                            **monty_props,
                            **props
                        })
Пример #11
0
def create_cloned_field(field: ModelField) -> ModelField:
    original_type = field.type_
    if is_dataclass(original_type) and hasattr(original_type, "__pydantic_model__"):
        original_type = original_type.__pydantic_model__  # type: ignore
    use_type = original_type
    if lenient_issubclass(original_type, BaseModel):
        original_type = cast(Type[BaseModel], original_type)
        use_type = create_model(
            original_type.__name__, __config__=original_type.__config__
        )
        for f in original_type.__fields__.values():
            use_type.__fields__[f.name] = f
        use_type.__validators__ = original_type.__validators__
    if PYDANTIC_1:
        new_field = ModelField(
            name=field.name,
            type_=use_type,
            class_validators={},
            default=None,
            required=False,
            model_config=BaseConfig,
            field_info=FieldInfo(None),
        )
    else:  # pragma: nocover
        new_field = ModelField(  # type: ignore
            name=field.name,
            type_=use_type,
            class_validators={},
            default=None,
            required=False,
            model_config=BaseConfig,
            schema=FieldInfo(None),
        )
    new_field.has_alias = field.has_alias
    new_field.alias = field.alias
    new_field.class_validators = field.class_validators
    new_field.default = field.default
    new_field.required = field.required
    new_field.model_config = field.model_config
    if PYDANTIC_1:
        new_field.field_info = field.field_info
    else:  # pragma: nocover
        new_field.schema = field.schema  # type: ignore
    new_field.allow_none = field.allow_none
    new_field.validate_always = field.validate_always
    if field.sub_fields:
        new_field.sub_fields = [
            create_cloned_field(sub_field) for sub_field in field.sub_fields
        ]
    if field.key_field:
        new_field.key_field = create_cloned_field(field.key_field)
    new_field.validators = field.validators
    if PYDANTIC_1:
        new_field.pre_validators = field.pre_validators
        new_field.post_validators = field.post_validators
    else:  # pragma: nocover
        new_field.whole_pre_validators = field.whole_pre_validators  # type: ignore
        new_field.whole_post_validators = field.whole_post_validators  # type: ignore
    new_field.parse_json = field.parse_json
    new_field.shape = field.shape
    try:
        new_field.populate_validators()
    except AttributeError:  # pragma: nocover
        # TODO: remove when removing support for Pydantic < 1.0.0
        new_field._populate_validators()  # type: ignore
    return new_field
Пример #12
0
    def __init__(
        self,
        path: str,
        endpoint: Callable,
        *,
        response_model: Type[Any] = None,
        status_code: int = 200,
        tags: List[str] = None,
        dependencies: Sequence[params.Depends] = None,
        summary: str = None,
        description: str = None,
        response_description: str = "Successful Response",
        responses: Dict[Union[int, str], Dict[str, Any]] = None,
        deprecated: bool = None,
        name: str = None,
        methods: Optional[Union[Set[str], List[str]]] = None,
        operation_id: str = None,
        response_model_include: Union[SetIntStr, DictIntStrAny] = None,
        response_model_exclude: Union[SetIntStr, DictIntStrAny] = set(),
        response_model_by_alias: bool = True,
        response_model_exclude_unset: bool = False,
        include_in_schema: bool = True,
        response_class: Optional[Type[Response]] = None,
        dependency_overrides_provider: Any = None,
    ) -> None:
        self.path = path
        self.endpoint = endpoint
        self.name = get_name(endpoint) if name is None else name
        self.path_regex, self.path_format, self.param_convertors = compile_path(
            path)
        if methods is None:
            methods = ["GET"]
        self.methods = set([method.upper() for method in methods])
        self.unique_id = generate_operation_id_for_path(
            name=self.name, path=self.path_format, method=list(methods)[0])
        self.response_model = response_model
        if self.response_model:
            assert (
                status_code not in STATUS_CODES_WITH_NO_BODY
            ), f"Status code {status_code} must not have a response body"
            response_name = "Response_" + self.unique_id
            if PYDANTIC_1:
                self.response_field: Optional[ModelField] = ModelField(
                    name=response_name,
                    type_=self.response_model,
                    class_validators={},
                    default=None,
                    required=False,
                    model_config=BaseConfig,
                    field_info=FieldInfo(None),
                )
            else:
                self.response_field: Optional[
                    ModelField] = ModelField(  # type: ignore  # pragma: nocover
                        name=response_name,
                        type_=self.response_model,
                        class_validators={},
                        default=None,
                        required=False,
                        model_config=BaseConfig,
                        schema=FieldInfo(None),
                    )
            # Create a clone of the field, so that a Pydantic submodel is not returned
            # as is just because it's an instance of a subclass of a more limited class
            # e.g. UserInDB (containing hashed_password) could be a subclass of User
            # that doesn't have the hashed_password. But because it's a subclass, it
            # would pass the validation and be returned as is.
            # By being a new field, no inheritance will be passed as is. A new model
            # will be always created.
            self.secure_cloned_response_field: Optional[
                ModelField] = create_cloned_field(self.response_field)
        else:
            self.response_field = None
            self.secure_cloned_response_field = None
        self.status_code = status_code
        self.tags = tags or []
        if dependencies:
            self.dependencies = list(dependencies)
        else:
            self.dependencies = []
        self.summary = summary
        self.description = description or inspect.cleandoc(
            self.endpoint.__doc__ or "")
        # if a "form feed" character (page break) is found in the description text,
        # truncate description text to the content preceding the first "form feed"
        self.description = self.description.split("\f")[0]
        self.response_description = response_description
        self.responses = responses or {}
        response_fields = {}
        for additional_status_code, response in self.responses.items():
            assert isinstance(response,
                              dict), "An additional response must be a dict"
            model = response.get("model")
            if model:
                assert (
                    additional_status_code not in STATUS_CODES_WITH_NO_BODY
                ), f"Status code {additional_status_code} must not have a response body"
                assert lenient_issubclass(
                    model,
                    BaseModel), "A response model must be a Pydantic model"
                response_name = f"Response_{additional_status_code}_{self.unique_id}"
                if PYDANTIC_1:
                    response_field = ModelField(
                        name=response_name,
                        type_=model,
                        class_validators=None,
                        default=None,
                        required=False,
                        model_config=BaseConfig,
                        field_info=FieldInfo(None),
                    )
                else:
                    response_field = ModelField(  # type: ignore  # pragma: nocover
                        name=response_name,
                        type_=model,
                        class_validators=None,
                        default=None,
                        required=False,
                        model_config=BaseConfig,
                        schema=FieldInfo(None),
                    )
                response_fields[additional_status_code] = response_field
        if response_fields:
            self.response_fields: Dict[Union[int, str],
                                       ModelField] = response_fields
        else:
            self.response_fields = {}
        self.deprecated = deprecated
        self.operation_id = operation_id
        self.response_model_include = response_model_include
        self.response_model_exclude = response_model_exclude
        self.response_model_by_alias = response_model_by_alias
        self.response_model_exclude_unset = response_model_exclude_unset
        self.include_in_schema = include_in_schema
        self.response_class = response_class

        assert inspect.isfunction(endpoint) or inspect.ismethod(
            endpoint), f"An endpoint must be a function or method"
        self.dependant = get_dependant(path=self.path_format,
                                       call=self.endpoint)
        for depends in self.dependencies[::-1]:
            self.dependant.dependencies.insert(
                0,
                get_parameterless_sub_dependant(depends=depends,
                                                path=self.path_format),
            )
        self.body_field = get_body_field(dependant=self.dependant,
                                         name=self.unique_id)
        self.dependency_overrides_provider = dependency_overrides_provider
        self.app = request_response(self.get_route_handler())
Пример #13
0
 def BField(default: Any) -> Any:
     return FieldInfo(
         default,
         description="B Field",
     )
Пример #14
0
def ModelSchemaField(field: Any) -> tuple:
    default: Optional[Required] = Required
    default_factory = None
    description = None
    title = None
    max_length = None

    if field.is_relation:
        if not field.related_model:
            internal_type = field.model._meta.pk.get_internal_type()
        else:
            internal_type = field.related_model._meta.pk.get_internal_type()
            if not field.concrete and field.auto_created or field.null:
                default = None

        pk_type = FIELD_TYPES.get(internal_type, int)
        if field.one_to_many or field.many_to_many:
            python_type = List[Dict[str, pk_type]]
        else:
            python_type = pk_type

        if field.related_model:
            field = field.target_field

    else:
        if field.choices:
            enum_choices = {v: k for k, v in field.choices}
            python_type = Enum(  # type: ignore
                f"{field.name.title().replace('_', '')}Enum",
                enum_choices,
                module=__name__,
            )
        else:
            internal_type = field.get_internal_type()
            if internal_type in STR_TYPES:
                python_type = str
                if not field.choices:
                    max_length = field.max_length

            elif internal_type in INT_TYPES:
                python_type = int

            elif internal_type in FIELD_TYPES:
                python_type = FIELD_TYPES[internal_type]

            else:  # pragma: nocover

                # Only required for 2.2 to support custom field subclasses
                for field_class in type(field).__mro__:
                    get_internal_type = getattr(field_class,
                                                "get_internal_type", None)
                    if get_internal_type:
                        _internal_type = get_internal_type(field_class())
                        if _internal_type in FIELD_TYPES:
                            python_type = FIELD_TYPES[_internal_type]
                            break

        deconstructed = field.deconstruct()
        field_options = deconstructed[3] or {}
        blank = field_options.pop("blank", False)
        null = field_options.pop("null", False)

        if field.has_default():
            if callable(field.default):
                default_factory = field.default
            else:
                default = field.default
        elif field.primary_key or blank or null:
            default = None

        description = field.help_text
        title = field.verbose_name.title()

    if not description:
        description = field.name

    return (
        python_type,
        FieldInfo(
            default,
            default_factory=default_factory,
            title=title,
            description=str(description),
            max_length=max_length,
        ),
    )
Пример #15
0
class Item(BaseModel):
    id: int
    name: str
    price: float
    desc: str = FieldInfo(default="", requierd=False)
    city: Optional[str] = None
Пример #16
0
def ModelSchemaField(field: Any, schema_name: str) -> tuple:
    default = Required
    default_factory = None
    description = None
    title = None
    max_length = None
    python_type = None

    if field.is_relation:
        if not field.related_model:
            internal_type = field.model._meta.pk.get_internal_type()
        else:
            internal_type = field.related_model._meta.pk.get_internal_type()
            if not field.concrete and field.auto_created or field.null:
                default = None

        pk_type = FIELD_TYPES.get(internal_type, int)
        if field.one_to_many or field.many_to_many:
            python_type = List[Dict[str, pk_type]]
        else:
            python_type = pk_type

        if field.related_model:
            field = field.target_field

    else:
        if field.choices:
            enum_choices = {}
            for k, v in field.choices:
                if Promise in type(v).__mro__:
                    v = str(v)
                enum_choices[v] = k
            if field.blank:
                enum_choices['_blank'] = ''

            enum_prefix = (
                f"{schema_name.replace('_', '')}{field.name.title().replace('_', '')}"
            )
            python_type = Enum(  # type: ignore
                f"{enum_prefix}Enum",
                enum_choices,
                module=__name__,
            )

            if field.has_default() and isinstance(field.default, Enum):
                default = field.default.value
        else:
            internal_type = field.get_internal_type()
            if internal_type in STR_TYPES:
                python_type = str
                if not field.choices:
                    max_length = field.max_length

            elif internal_type in INT_TYPES:
                python_type = int

            elif internal_type in FIELD_TYPES:
                python_type = FIELD_TYPES[internal_type]

            else:  # pragma: nocover
                for field_class in type(field).__mro__:
                    get_internal_type = getattr(field_class,
                                                "get_internal_type", None)
                    if get_internal_type:
                        _internal_type = get_internal_type(field_class())
                        if _internal_type in FIELD_TYPES:
                            python_type = FIELD_TYPES[_internal_type]
                            break

        if python_type is None:
            logger.warning("%s is currently unhandled, defaulting to str.",
                           field.__class__)
            python_type = str

        deconstructed = field.deconstruct()
        field_options = deconstructed[3] or {}
        blank = field_options.pop("blank", False)
        null = field_options.pop("null", False)

        if default is Required and field.has_default():
            if callable(field.default):
                default_factory = field.default
                default = Undefined
            else:
                default = field.default
        elif field.primary_key or blank or null:
            default = None

        if default is not None and field.null:
            python_type = Union[python_type, None]

        description = field.help_text
        title = field.verbose_name.title()

    if not description:
        description = field.name

    return (
        python_type,
        FieldInfo(
            default,
            default_factory=default_factory,
            title=title,
            description=str(description),
            max_length=max_length,
        ),
    )