mirror of
https://github.com/YunoHost/yunohost.git
synced 2024-09-03 20:06:10 +02:00
use pydantic Field as base info defition
This commit is contained in:
parent
c118383a1c
commit
669bccff57
2 changed files with 192 additions and 10 deletions
|
@ -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
15
src/interface/types.py
Normal 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]]
|
Loading…
Add table
Reference in a new issue