Ejemplo n.º 1
0
def get_schema_compatible_field(*, field: ModelField) -> ModelField:
    out_field = field
    if lenient_issubclass(field.type_, UploadFile):
        use_type: type = bytes
        if field.shape in sequence_shapes:
            use_type = List[bytes]
        if PYDANTIC_1:
            out_field = ModelField(
                name=field.name,
                type_=use_type,
                class_validators=field.class_validators,
                model_config=field.model_config,
                default=field.default,
                required=field.required,
                alias=field.alias,
                field_info=field.field_info,
            )
        else:  # pragma: nocover
            out_field = ModelField(  # type: ignore
                name=field.name,
                type_=use_type,
                class_validators=field.class_validators,
                model_config=field.model_config,
                default=field.default,
                required=field.required,
                alias=field.alias,
                schema=field.schema,  # type: ignore
            )

    return out_field
Ejemplo n.º 2
0
def get_body_field(*, dependant: Dependant, name: str) -> Optional[ModelField]:
    flat_dependant = get_flat_dependant(dependant)
    if not flat_dependant.body_params:
        return None
    first_param = flat_dependant.body_params[0]
    field_info = get_field_info(first_param)
    embed = getattr(field_info, "embed", None)
    if len(flat_dependant.body_params) == 1 and not embed:
        return get_schema_compatible_field(field=first_param)
    model_name = "Body_" + name
    BodyModel = create_model(model_name)
    for f in flat_dependant.body_params:
        BodyModel.__fields__[f.name] = get_schema_compatible_field(field=f)
    required = any(True for f in flat_dependant.body_params if f.required)

    BodyFieldInfo_kwargs: Dict[str, Any] = dict(default=None)
    if any(
            isinstance(get_field_info(f), params.File)
            for f in flat_dependant.body_params):
        BodyFieldInfo: Type[params.Body] = params.File
    elif any(
            isinstance(get_field_info(f), params.Form)
            for f in flat_dependant.body_params):
        BodyFieldInfo = params.Form
    else:
        BodyFieldInfo = params.Body

        body_param_media_types = [
            getattr(get_field_info(f), "media_type")
            for f in flat_dependant.body_params
            if isinstance(get_field_info(f), params.Body)
        ]
        if len(set(body_param_media_types)) == 1:
            BodyFieldInfo_kwargs["media_type"] = body_param_media_types[0]
    if PYDANTIC_1:
        field = ModelField(
            name="body",
            type_=BodyModel,
            default=None,
            required=required,
            model_config=BaseConfig,
            class_validators={},
            alias="body",
            field_info=BodyFieldInfo(**BodyFieldInfo_kwargs),
        )
    else:  # pragma: nocover
        field = ModelField(  # type: ignore
            name="body",
            type_=BodyModel,
            default=None,
            required=required,
            model_config=BaseConfig,
            class_validators={},
            alias="body",
            schema=BodyFieldInfo(**BodyFieldInfo_kwargs),
        )
    return field
Ejemplo n.º 3
0
    def test_valid_url_avatar(self):
        f = Field(
            ...,
            regex=url_regex,
        )
        field = ModelField(
            name="name",
            type_=str,
            class_validators={},
            field_info=f,
            model_config=BaseConfig,
        )
        with pytest.raises(ValueError):
            bad_urls_format = [
                "http://www.example.com/,,main.html",
                "http://www.example.com/ main.html",
                "http:www.example.com/main.html",
            ]
            for url in bad_urls_format:
                PartialUser.valid_url_avatar(url, field=field)

        good_urls_format = [
            "https://www.eliam-lotonga.fr/about",
            "http://www.john-doe.com/",
            "http://www.example.com/main.html",
        ]
        for url in good_urls_format:
            assert PartialUser.valid_url_avatar(url, field=field) == url
Ejemplo n.º 4
0
def get_pydantic_field(field_name: str, model: Type["Model"]) -> "ModelField":
    return ModelField(
        name=field_name,
        type_=model.Meta.model_fields[field_name].__type__,  # type: ignore
        model_config=model.__config__,
        required=not model.Meta.model_fields[field_name].nullable,
        class_validators={},
    )
Ejemplo n.º 5
0
    def parse(
        cls: Type[T],
        *,
        call: Callable[..., Any],
        parameterless: Optional[List[Any]] = None,
        allow_types: Optional[List[Type[Param]]] = None,
    ) -> T:
        signature = get_typed_signature(call)
        params = signature.parameters
        dependent = cls(
            call=call,
            allow_types=allow_types,
        )

        for param_name, param in params.items():
            default_value = Required
            if param.default != param.empty:
                default_value = param.default

            if isinstance(default_value, Param):
                field_info = default_value
                default_value = field_info.default
            else:
                field_info = dependent.parse_param(param_name, param)
                default_value = field_info.default

            annotation: Any = Any
            required = default_value == Required
            if param.annotation != param.empty:
                annotation = param.annotation
            annotation = get_annotation_from_field_info(
                annotation, field_info, param_name
            )
            dependent.params.append(
                ModelField(
                    name=param_name,
                    type_=annotation,
                    class_validators=None,
                    model_config=CustomConfig,
                    default=None if required else default_value,
                    required=required,
                    field_info=field_info,
                )
            )

        parameterless_params = [
            dependent.parse_parameterless(param) for param in (parameterless or [])
        ]
        dependent.parameterless.extend(parameterless_params)

        logger.trace(
            f"Parsed dependent with call={call}, "
            f"params={[param.field_info for param in dependent.params]}, "
            f"parameterless={dependent.parameterless}"
        )

        return dependent
Ejemplo n.º 6
0
def create_pydantic_field(field_name: str, model: Type["Model"],
                          model_field: Type[ManyToManyField]) -> None:
    model_field.through.__fields__[field_name] = ModelField(
        name=field_name,
        type_=model,
        model_config=model.__config__,
        required=False,
        class_validators={},
    )
Ejemplo n.º 7
0
def parse_type(type_: Type):
    class Config(BaseConfig):
        arbitrary_types_allowed = True

    parsed = ModelField(name='',
                        type_=type_,
                        model_config=Config,
                        class_validators=None)
    return parsed.outer_type_, parsed.required
Ejemplo n.º 8
0
 def _get_field(default: Any = UNSET,
                default_factory: Any = UNSET) -> ModelField:
     return ModelField(
         name="a",
         type_=str,
         class_validators={},
         model_config=BaseConfig,
         default=default,
         default_factory=default_factory,
     )
Ejemplo n.º 9
0
 def batch_decorator(cls: ModelMetaclass) -> ModelMetaclass:
     cls.__annotations__ = {arg_name: list for arg_name in model_args_names}
     cls.__fields__ = {
         arg_name: ModelField(name=arg_name,
                              type_=list,
                              class_validators=None,
                              model_config=BaseConfig,
                              required=False,
                              field_info=Field(None))
         for arg_name in model_args_names
     }
     return cls
Ejemplo n.º 10
0
 def annotation(self,
                type,
                class_validators=None,
                default=None,
                required=False):
     self.cls.__fields__[self.name] = ModelField(
         name=self.name,
         type_=type,
         class_validators=class_validators,
         model_config=self.cls.Config,
         default=default,
         required=required)
Ejemplo n.º 11
0
def make_request_model(name, module, body_params: List[ModelField]):
    whole_params_list = [
        p for p in body_params if isinstance(p.field_info, Params)
    ]
    if len(whole_params_list):
        if len(whole_params_list) > 1:
            raise RuntimeError(f"Only one 'Params' allowed: "
                               f"params={whole_params_list}")
        body_params_list = [
            p for p in body_params if not isinstance(p.field_info, Params)
        ]
        if body_params_list:
            raise RuntimeError(
                f"No other params allowed when 'Params' used: "
                f"params={whole_params_list}, other={body_params_list}")

    if whole_params_list:
        assert whole_params_list[0].alias == 'params'
        params_field = whole_params_list[0]
    else:
        _JsonRpcRequestParams = ModelMetaclass.__new__(
            ModelMetaclass, '_JsonRpcRequestParams', (BaseModel, ), {})

        for f in body_params:
            _JsonRpcRequestParams.__fields__[f.name] = f

        _JsonRpcRequestParams = component_name(f'_Params[{name}]',
                                               module)(_JsonRpcRequestParams)

        params_field = ModelField(
            name='params',
            type_=_JsonRpcRequestParams,
            class_validators={},
            default=None,
            required=True,
            model_config=BaseConfig,
            field_info=Field(...),
        )

    class _Request(BaseModel):
        jsonrpc: StrictStr = Field('2.0', const=True, example='2.0')
        id: Union[StrictStr, int] = Field(None, example=0)
        method: StrictStr = Field(name, const=True, example=name)

        class Config:
            extra = 'forbid'

    _Request.__fields__[params_field.name] = params_field

    _Request = component_name(f'_Request[{name}]', module)(_Request)

    return _Request
Ejemplo n.º 12
0
def copy_model_field(field: ModelField, type_: Any) -> ModelField:
    """Copy a model field and assign a new type, e.g. to accept an Any type
    even though the original value is typed differently.
    """
    return ModelField(
        name=field.name,
        type_=type_,
        class_validators=field.class_validators,
        model_config=field.model_config,
        default=field.default,
        default_factory=field.default_factory,
        required=field.required,
    )
Ejemplo n.º 13
0
    def test_valid_email(self):
        field = ModelField(
            name="name",
            type_=str,
            class_validators={},
            model_config=BaseConfig,
        )
        with pytest.raises(ValueError):
            bad_email_format = ["johndoe.com" "[email protected]" "*****@*****.**"]
            for email in bad_email_format:
                PartialUser.valid_email(email, field=field)

        good_email_format = ["*****@*****.**", "*****@*****.**"]
        for email in good_email_format:
            assert PartialUser.valid_email(email, field=field) == email
Ejemplo n.º 14
0
 def _():
     klass = int()
     fields = {}
     annotations = {}
     for key, annot in int.__init__.__annotations__.items():
         fields[key] = ModelField(name=key, type_=)
         annotations[key] = annot
     klass.Property = type(
         'Property', BaseModel,
         {
             '__fields__': fields,
             '__annotations__': annotations,
         }
     )
     return klass
    def test_as_least_1_character(self):
        f = Field(..., min_length=1)
        field = ModelField(
            name="content",
            type_=str,
            class_validators={},
            field_info=f,
            model_config=BaseConfig,
        )
        with pytest.raises(ValueError):
            Comment.at_least_1_character("        ", field=field)
        with pytest.raises(ValueError):
            Comment.at_least_1_character("", field=field)

        assert Comment.at_least_1_character("First comment") == "First comment"
Ejemplo n.º 16
0
    def test_between_3_and_50_characters(self):
        f = Field(..., min_length=3, max_length=50)
        field = ModelField(
            name="name",
            type_=str,
            class_validators={},
            field_info=f,
            model_config=BaseConfig,
        )
        with pytest.raises(ValueError):
            long_text = "Lorem Ipsum is simply dummy text of\
                the printing and typesetting industry. "

            PartialUser.between_3_and_50_characters("He", field=field)
            PartialUser.between_3_and_50_characters(
                long_text,
                field=field,
            )
        assert PartialUser.between_3_and_50_characters("Hello") == "Hello"
Ejemplo n.º 17
0
def get_pydantic_field(field_name: str, model: Type["Model"]) -> "ModelField":
    """
    Extracts field type and if it's required from Model model_fields by passed
    field_name. Returns a pydantic field with type of field_name field type.

    :param field_name: field name to fetch from Model and name of pydantic field
    :type field_name: str
    :param model: type of field to register
    :type model: Model class
    :return: newly created pydantic field
    :rtype: pydantic.ModelField
    """
    return ModelField(
        name=field_name,
        type_=model.Meta.model_fields[field_name].__type__,  # type: ignore
        model_config=model.__config__,
        required=not model.Meta.model_fields[field_name].nullable,
        class_validators={},
    )
Ejemplo n.º 18
0
 def _check_param(cls, dependent: Dependent, name: str,
                  param: inspect.Parameter) -> Optional["EventParam"]:
     if param.default == param.empty:
         if generic_check_issubclass(param.annotation, Event):
             if param.annotation is not Event:
                 dependent.pre_checkers.append(
                     _EventChecker(
                         Required,
                         field=ModelField(
                             name=name,
                             type_=param.annotation,
                             class_validators=None,
                             model_config=CustomConfig,
                             default=None,
                             required=True,
                         ),
                     ))
             return cls(Required)
         elif param.annotation == param.empty and name == "event":
             return cls(Required)
Ejemplo n.º 19
0
def create_pydantic_field(field_name: str, model: Type["Model"],
                          model_field: Type[ManyToManyField]) -> None:
    """
    Registers pydantic field on through model that leads to passed model
    and is registered as field_name passed.

    Through model is fetched from through attributed on passed model_field.

    :param field_name: field name to register
    :type field_name: str
    :param model: type of field to register
    :type model: Model class
    :param model_field: relation field from which through model is extracted
    :type model_field: ManyToManyField class
    """
    model_field.through.__fields__[field_name] = ModelField(
        name=field_name,
        type_=model,
        model_config=model.__config__,
        required=False,
        class_validators={},
    )
Ejemplo n.º 20
0
def infer_request_from_annotations(func: Callable) -> Optional[Type[BaseModel]]:
    """Infer an OpenAPI request body from function annotations.

    The annotations are converted to a `BaseModel` and schema is extracted from
    it.

    Args:
        func: The function whose annotations are to be inferred.

    Returns:
        A `BaseModel` generated from the annotation's return value

    """
    annot = {x: y for x, y in func.__annotations__.items() if x != "return"}
    if annot:
        class Annot(BaseModel):
            pass
        for x, y in annot.items():
            Annot.__fields__[x] = ModelField(name=x, type_=y, class_validators={},
                                             model_config=BaseConfig,
                                             required=True)
        return Annot
    else:
        return None
Ejemplo n.º 21
0
def get_param_field(
    *,
    param: inspect.Parameter,
    param_name: str,
    default_field_info: Type[params.Param] = params.Param,
    force_type: params.ParamTypes = None,
    ignore_default: bool = False,
) -> ModelField:
    default_value = Required
    had_schema = False
    if not param.default == param.empty and ignore_default is False:
        default_value = param.default
    if isinstance(default_value, FieldInfo):
        had_schema = True
        field_info = default_value
        default_value = field_info.default
        if (isinstance(field_info, params.Param)
                and getattr(field_info, "in_", None) is None):
            field_info.in_ = default_field_info.in_
        if force_type:
            field_info.in_ = force_type  # type: ignore
    else:
        field_info = default_field_info(default_value)
    required = default_value == Required
    annotation: Any = Any
    if not param.annotation == param.empty:
        annotation = param.annotation
    annotation = get_annotation_from_field_info(annotation, field_info,
                                                param_name)
    if not field_info.alias and getattr(field_info, "convert_underscores",
                                        None):
        alias = param.name.replace("_", "-")
    else:
        alias = field_info.alias or param.name
    if PYDANTIC_1:
        field = ModelField(
            name=param.name,
            type_=annotation,
            default=None if required else default_value,
            alias=alias,
            required=required,
            model_config=BaseConfig,
            class_validators={},
            field_info=field_info,
        )
        # TODO: remove when removing support for Pydantic < 1.2.0
        field.required = required
    else:  # pragma: nocover
        field = ModelField(  # type: ignore
            name=param.name,
            type_=annotation,
            default=None if required else default_value,
            alias=alias,
            required=required,
            model_config=BaseConfig,
            class_validators={},
            schema=field_info,
        )
        field.required = required
    if not had_schema and not is_scalar_field(field=field):
        if PYDANTIC_1:
            field.field_info = params.Body(field_info.default)
        else:
            field.schema = params.Body(
                field_info.default)  # type: ignore  # pragma: nocover

    return field
Ejemplo n.º 22
0
DEFAULT_DATA = [
    {
        "__ejsorm__": "generated",
        "__temp__": []
    },
    {
        "__table__": "__other",
        "__data__": {}
    },
]

PK_FIELD = ModelField(
    name=EJ_OBJECT_ID_FIELD,
    type_=int,
    required=True,
    model_config=BaseConfig,
    class_validators=None,
)


class Ejsorm:
    def __init__(self, path: str):
        if not path.endswith(".json"):
            raise EJError(f"Path must has .json file extension, got {path}")
        self.path = path
        self._data: typing.Optional[EJData] = None

    @property
    def data(self) -> EJData:
        if self._data is None:
Ejemplo n.º 23
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
Ejemplo n.º 24
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())