use pydantic Field as base info defition

This commit is contained in:
axolotle 2023-01-17 18:48:03 +01:00
parent c118383a1c
commit 669bccff57
2 changed files with 192 additions and 10 deletions

View file

@ -1,9 +1,29 @@
from __future__ import (
annotations,
) # Enable self reference a class in its own method arguments
import inspect import inspect
import types import types
import re import re
from enum import Enum from enum import Enum
from typing import Any, Callable, Optional, TypeVar from typing import (
Annotated,
Any,
Callable,
Iterable,
Iterator,
Optional,
TypeVar,
Union,
get_origin,
get_args,
)
import pydantic
from yunohost.interface.types import PrivateParam
ViewFunc = TypeVar("ViewFunc") ViewFunc = TypeVar("ViewFunc")
@ -42,9 +62,12 @@ def get_params_doc(docstring: Optional[str]) -> dict[str, str]:
} }
def validate_pattern(pattern: str, value: str, name: str): def validate_pattern(pattern: str, value: str, name: Optional[str] = None):
if not re.match(pattern, value, re.UNICODE): if not re.match(pattern, value, re.UNICODE):
raise ValueError(f"'{value}' does'nt match pattern '{pattern}'") error = name if name else "'{value}' does'nt match pattern '{pattern}'"
raise ValueError(error.format(value=value, pattern=pattern))
return value return value
@ -65,7 +88,7 @@ def override_function(
) )
returned_func.__name__ = name or func.__name__ returned_func.__name__ = name or func.__name__
returned_func.__doc__ = doc or func.__doc__ returned_func.__doc__ = doc or func.__doc__
returned_func.__signature__ = func_signature.replace(parameters=tuple(new_params)) returned_func.__signature__ = func_signature.replace(parameters=tuple(new_params)) # type: ignore
return returned_func return returned_func
@ -92,16 +115,160 @@ class BaseInterface:
def clear_local_data(self): def clear_local_data(self):
self.local_data = {} self.local_data = {}
def filter_params(self, params: list[inspect.Parameter]) -> list[inspect.Parameter]: @staticmethod
private = self.local_data.get("private", []) def build_fields(
cls,
params: Iterable[inspect.Parameter],
annotations: dict[str, Any],
doc: dict[str, str],
positional_params: list[str],
) -> Iterator[tuple[inspect.Parameter, Optional[pydantic.fields.FieldInfo]]]:
return [ for param in params:
param for param in params annotation = annotations[param.name]
if param.name not in private and param.name != "operation_logger" field: Optional[pydantic.fields.FieldInfo] = None
] description = doc.get(param.name, None)
if get_origin(annotation) is Annotated:
annotation, field = get_args(annotation)
if get_origin(field) is PrivateParam:
field = None
elif isinstance(field, pydantic.fields.FieldInfo):
update_field_from_annotation(
field,
param.default,
name=param.name,
description=description,
positional=param.name in positional_params,
)
else:
raise Exception(
"Views function paramaters can only be 'Annotated[Any, PrivateParam | Param]' but found '{new_param}'"
)
else:
field = Field(
param.default,
name=param.name,
description=description,
positional=param.name in positional_params,
)
param = param.replace(annotation=annotation)
yield param, field
def api(self, *args, **kwargs): def api(self, *args, **kwargs):
return pass_func return pass_func
def cli(self, *args, **kwargs): def cli(self, *args, **kwargs):
return pass_func return pass_func
def Field(
default: Any = ...,
*,
name: Optional[str] = None,
positional: bool = False,
param_decls: Optional[list[str]] = None,
deprecated: bool = False,
description: Optional[str] = None,
hidden: bool = False,
pattern: Optional[Union[str, tuple[str, str]]] = None,
ask: Union[str, bool] = False,
confirm: bool = False,
redac: bool = False,
file: bool = False,
panel: Optional[str] = None,
example: Optional[str] = None,
# default_factory: Optional[NoArgAnyCallable] = None,
# alias: str = None,
# title: str = None,
# exclude: Union['AbstractSetIntStr', 'MappingIntStrAny', Any] = None,
# include: Union['AbstractSetIntStr', 'MappingIntStrAny', Any] = None,
# const: bool = None,
# gt: float = None,
# ge: float = None,
# lt: float = None,
# le: float = None,
# multiple_of: float = None,
# allow_inf_nan: bool = None,
# max_digits: int = None,
# decimal_places: int = None,
# min_items: int = None,
# max_items: int = None,
# unique_items: bool = None,
# min_length: int = None,
# max_length: int = None,
# allow_mutation: bool = True,
# discriminator: str = None,
# repr: bool = True,
**kwargs: Any,
) -> pydantic.fields.FieldInfo:
pattern_name = None
if isinstance(pattern, tuple):
pattern_name, pattern = pattern
return pydantic.fields.Field(
default=... if default is inspect.Parameter.empty else default,
# default_factory=default_factory,
# alias=alias,
# title=title,
description=description, # type: ignore
# exclude=exclude,
# include=include,
# const=const,
# gt=gt,
# ge=ge,
# lt=lt,
# le=le,
# multiple_of=multiple_of,
# allow_inf_nan=allow_inf_nan,
# max_digits=max_digits,
# decimal_places=decimal_places,
# min_items=min_items,
# max_items=max_items,
# unique_items=unique_items,
# min_length=min_length,
# max_length=max_length,
# allow_mutation=allow_mutation,
regex=pattern, # type: ignore
# discriminator=discriminator,
# repr=repr,
# Yunohost custom
name=name,
param_decls=param_decls,
positional=positional,
deprecated=deprecated,
hidden=hidden,
# Typer Option only
ask=False if deprecated else ask,
confirm=confirm,
redac=redac,
# Type
file=file,
# Rich
panel=panel,
pattern_name=pattern_name,
example=example,
# **kwargs,
)
def update_field_from_annotation(
field: pydantic.fields.FieldInfo,
default: Any,
name: Optional[str] = None,
description: Optional[str] = None,
positional: bool = False,
):
field.default = ... if default is inspect.Parameter.empty else default
if name:
field.extra["name"] = name
if description:
field.description = description
if positional:
field.extra["positional"] = positional
field.extra["ask"] = False

15
src/interface/types.py Normal file
View file

@ -0,0 +1,15 @@
from __future__ import (
annotations,
) # Enable self reference a class in its own method arguments
from typing import Annotated, Generic, TypeVar
T = TypeVar("T")
class PrivateParam(Generic[T]):
...
Private = Annotated[T, PrivateParam[T]]