in HowTo/gRPC/Linux/OpenAI/LangChain/PyServer/venv/Lib/pydantic/v1/schema.py [0:0]
def get_annotation_with_constraints(annotation: Any, field_info: FieldInfo) -> Tuple[Type[Any], Set[str]]: # noqa: C901
"""
Get an annotation with used constraints implemented for numbers and strings based on the field_info.
:param annotation: an annotation from a field specification, as ``str``, ``ConstrainedStr``
:param field_info: an instance of FieldInfo, possibly with declarations for validations and JSON Schema
:return: the same ``annotation`` if unmodified or a new annotation along with the used constraints.
"""
used_constraints: Set[str] = set()
def go(type_: Any) -> Type[Any]:
if (
is_literal_type(type_)
or isinstance(type_, ForwardRef)
or lenient_issubclass(type_, (ConstrainedList, ConstrainedSet, ConstrainedFrozenSet))
):
return type_
origin = get_origin(type_)
if origin is not None:
args: Tuple[Any, ...] = get_args(type_)
if any(isinstance(a, ForwardRef) for a in args):
# forward refs cause infinite recursion below
return type_
if origin is Annotated:
return go(args[0])
if is_union(origin):
return Union[tuple(go(a) for a in args)] # type: ignore
if issubclass(origin, List) and (
field_info.min_items is not None
or field_info.max_items is not None
or field_info.unique_items is not None
):
used_constraints.update({'min_items', 'max_items', 'unique_items'})
return conlist(
go(args[0]),
min_items=field_info.min_items,
max_items=field_info.max_items,
unique_items=field_info.unique_items,
)
if issubclass(origin, Set) and (field_info.min_items is not None or field_info.max_items is not None):
used_constraints.update({'min_items', 'max_items'})
return conset(go(args[0]), min_items=field_info.min_items, max_items=field_info.max_items)
if issubclass(origin, FrozenSet) and (field_info.min_items is not None or field_info.max_items is not None):
used_constraints.update({'min_items', 'max_items'})
return confrozenset(go(args[0]), min_items=field_info.min_items, max_items=field_info.max_items)
for t in (Tuple, List, Set, FrozenSet, Sequence):
if issubclass(origin, t): # type: ignore
return t[tuple(go(a) for a in args)] # type: ignore
if issubclass(origin, Dict):
return Dict[args[0], go(args[1])] # type: ignore
attrs: Optional[Tuple[str, ...]] = None
constraint_func: Optional[Callable[..., type]] = None
if isinstance(type_, type):
if issubclass(type_, (SecretStr, SecretBytes)):
attrs = ('max_length', 'min_length')
def constraint_func(**kw: Any) -> Type[Any]:
return type(type_.__name__, (type_,), kw)
elif issubclass(type_, str) and not issubclass(type_, (EmailStr, AnyUrl)):
attrs = ('max_length', 'min_length', 'regex')
if issubclass(type_, StrictStr):
def constraint_func(**kw: Any) -> Type[Any]:
return type(type_.__name__, (type_,), kw)
else:
constraint_func = constr
elif issubclass(type_, bytes):
attrs = ('max_length', 'min_length', 'regex')
if issubclass(type_, StrictBytes):
def constraint_func(**kw: Any) -> Type[Any]:
return type(type_.__name__, (type_,), kw)
else:
constraint_func = conbytes
elif issubclass(type_, numeric_types) and not issubclass(
type_,
(
ConstrainedInt,
ConstrainedFloat,
ConstrainedDecimal,
ConstrainedList,
ConstrainedSet,
ConstrainedFrozenSet,
bool,
),
):
# Is numeric type
attrs = ('gt', 'lt', 'ge', 'le', 'multiple_of')
if issubclass(type_, float):
attrs += ('allow_inf_nan',)
if issubclass(type_, Decimal):
attrs += ('max_digits', 'decimal_places')
numeric_type = next(t for t in numeric_types if issubclass(type_, t)) # pragma: no branch
constraint_func = _map_types_constraint[numeric_type]
if attrs:
used_constraints.update(set(attrs))
kwargs = {
attr_name: attr
for attr_name, attr in ((attr_name, getattr(field_info, attr_name)) for attr_name in attrs)
if attr is not None
}
if kwargs:
constraint_func = cast(Callable[..., type], constraint_func)
return constraint_func(**kwargs)
return type_
return go(annotation), used_constraints