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, ) long_text = "Lorem Ipsum is simply dummy text of\ the printing and typesetting industry. " with pytest.raises(ValueError): PartialUser.between_3_and_50_characters("He", field=field) with pytest.raises(ValueError): PartialUser.between_3_and_50_characters( long_text, field=field, ) assert PartialUser.between_3_and_50_characters("Hello") == "Hello"
async def serialize_response( *, field: ModelField = None, response: Response, include: Union[SetIntStr, DictIntStrAny] = None, exclude: Union[SetIntStr, DictIntStrAny] = set(), by_alias: bool = True, exclude_unset: bool = False, is_coroutine: bool = True, ) -> Any: if field: errors = [] if exclude_unset and isinstance(response, BaseModel): if PYDANTIC_1: response = response.dict(exclude_unset=exclude_unset) else: response = response.dict( skip_defaults=exclude_unset) # pragma: nocover if is_coroutine: value, errors_ = field.validate(response, {}, loc=("response", )) else: value, errors_ = await run_in_threadpool(field.validate, response, {}, loc=("response", )) if isinstance(errors_, ErrorWrapper): errors.append(errors_) elif isinstance(errors_, list): errors.extend(errors_) if errors: raise ValidationError(errors, field.type_) return jsonable_encoder( value, include=include, exclude=exclude, by_alias=by_alias, exclude_unset=exclude_unset, ) else: return jsonable_encoder(response)
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={}, )
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
async def serialize_response( *, field: ModelField = None, response_content: Any, include: Union[SetIntStr, DictIntStrAny] = None, exclude: Union[SetIntStr, DictIntStrAny] = set(), by_alias: bool = True, exclude_unset: bool = False, is_coroutine: bool = True, ) -> Any: if field: errors = [] response_content = _prepare_response_content( response_content, by_alias=by_alias, exclude_unset=exclude_unset ) if is_coroutine: value, errors_ = field.validate(response_content, {}, loc=("response",)) else: value, errors_ = await run_in_threadpool( field.validate, response_content, {}, loc=("response",) ) if isinstance(errors_, ErrorWrapper): errors.append(errors_) elif isinstance(errors_, list): errors.extend(errors_) if errors: raise ValidationError(errors, field.type_) return jsonable_encoder( value, include=include, exclude=exclude, by_alias=by_alias, exclude_unset=exclude_unset, ) else: return jsonable_encoder(response_content)
def _check_param( cls, dependent: Dependent, name: str, param: inspect.Parameter ) -> Optional["EventParam"]: from nonebot.adapters import Event 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)
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
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
def __new__(mcs, name, bases, namespace, **kwargs): from pydantic.fields import Undefined from pydantic.class_validators import extract_validators, inherit_validators from pydantic.types import PyObject from pydantic.typing import is_classvar, resolve_annotations from pydantic.utils import lenient_issubclass, validate_field_name from pydantic.main import inherit_config, prepare_config, UNTOUCHED_TYPES fields: Dict[str, ModelField] = {} config = BaseConfig validators: Dict[str, List[Validator]] = {} for base in reversed(bases): if issubclass(base, AbstractCheckedSession) and base != AbstractCheckedSession: config = inherit_config(base.__config__, config) fields.update(deepcopy(base.__fields__)) validators = inherit_validators(base.__validators__, validators) config = inherit_config(namespace.get('Config'), config) validators = inherit_validators(extract_validators(namespace), validators) # update fields inherited from base classes for field in fields.values(): field.set_config(config) extra_validators = validators.get(field.name, []) if extra_validators: field.class_validators.update(extra_validators) # re-run prepare to add extra validators field.populate_validators() prepare_config(config, name) # extract and build fields class_vars = set() if (namespace.get('__module__'), namespace.get('__qualname__')) != \ ('larray.core.checked', 'CheckedSession'): untouched_types = UNTOUCHED_TYPES + config.keep_untouched # annotation only fields need to come first in fields annotations = resolve_annotations(namespace.get('__annotations__', {}), namespace.get('__module__', None)) for ann_name, ann_type in annotations.items(): if is_classvar(ann_type): class_vars.add(ann_name) elif not ann_name.startswith('_'): validate_field_name(bases, ann_name) value = namespace.get(ann_name, Undefined) if (isinstance(value, untouched_types) and ann_type != PyObject and not lenient_issubclass(getattr(ann_type, '__origin__', None), Type)): continue fields[ann_name] = ModelField.infer(name=ann_name, value=value, annotation=ann_type, class_validators=validators.get(ann_name, []), config=config) for var_name, value in namespace.items(): # 'var_name not in annotations' because namespace.items() contains annotated fields # with default values # 'var_name not in class_vars' to avoid to update a field if it was redeclared (by mistake) if (var_name not in annotations and not var_name.startswith('_') and not isinstance(value, untouched_types) and var_name not in class_vars): validate_field_name(bases, var_name) # the method ModelField.infer() fails to infer the type of Group objects # (which are interpreted as ndarray objects) annotation = type(value) if isinstance(value, Group) else annotations.get(var_name) inferred = ModelField.infer(name=var_name, value=value, annotation=annotation, class_validators=validators.get(var_name, []), config=config) if var_name in fields and inferred.type_ != fields[var_name].type_: raise TypeError(f'The type of {name}.{var_name} differs from the new default value; ' f'if you wish to change the type of this field, please use a type ' f'annotation') fields[var_name] = inferred new_namespace = { '__config__': config, '__fields__': fields, '__field_defaults__': {n: f.default for n, f in fields.items() if not f.required}, '__validators__': validators, **{n: v for n, v in namespace.items() if n not in fields}, } return super().__new__(mcs, name, bases, new_namespace, **kwargs)
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:
def check_field_type(field: ModelField, value: V) -> V: _, errs_ = field.validate(value, {}, loc=()) if errs_: raise TypeMisMatch(field, value) return value
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())
async def serialize_response( *, field: ModelField = None, response_content: Any, include: Union[SetIntStr, DictIntStrAny] = None, exclude: Union[SetIntStr, DictIntStrAny] = set(), by_alias: bool = True, exclude_unset: bool = False, exclude_defaults: bool = False, exclude_none: bool = False, is_coroutine: bool = True, skip_jsonable_encoder: bool = False, skip_validation: bool = False, ) -> Any: if field: errors = [] response_content = _prepare_response_content( response_content, by_alias=by_alias, exclude_unset=exclude_unset, exclude_defaults=exclude_defaults, exclude_none=exclude_none, ) if skip_validation: # If include is explicitly set, then never skip jsonable_encoder # # _prepare_response_content emits a decoded dict/list python datastructure. # It however doesn't complete or coerce any types, so can only be used when # explicitly specifying that one knows what one is doing, and disabling validation # # If you trust the encoder to handle all your types then we can just return # the output of _prepare_response_content here if include is None and skip_jsonable_encoder is True: return response_content value = response_content else: if is_coroutine: value, errors_ = field.validate(response_content, {}, loc=("response", )) else: value, errors_ = await run_in_threadpool(field.validate, response_content, {}, loc=("response", )) if isinstance(errors_, ErrorWrapper): errors.append(errors_) elif isinstance(errors_, list): errors.extend(errors_) if errors: raise ValidationError(errors, field.type_) return jsonable_encoder( value, include=include, exclude=exclude, by_alias=by_alias, exclude_unset=exclude_unset, exclude_defaults=exclude_defaults, exclude_none=exclude_none, ) else: # If you trust the encoder to handle all your types then we can just return # the output of _prepare_response_content here return (response_content if skip_jsonable_encoder else jsonable_encoder(response_content))