Source code for esrf_pathlib._schemas.fields.base
from enum import Enum
from typing import Any
from typing import Callable
from typing import List
from typing import Optional
from typing import Tuple
from typing import Type
from typing import Union
from typing import get_type_hints
from ..identifier import SchemaIdentifier
[docs]
class Field:
def __init__(
self,
name: str,
description: str,
schema_identifier: SchemaIdentifier,
value_generator: Union[Callable[..., Any], Type[Any]],
value_serializer: Union[Callable[..., Any], Type[Any], None] = None,
examples: Optional[List[str]] = None,
) -> None:
self._name = name
self._description = description
self._examples = examples
self._schema_identifier = schema_identifier
self._value_generator = value_generator
self._value_serializer = value_serializer
def __repr__(self) -> str:
return f"<{type(self).__name__} {self._name!r} schema={str(self._schema_identifier)!r}>"
@property
def name(self) -> str:
return self._name
@property
def description(self) -> str:
return self._description
@property
def docstring(self) -> str:
description = self._description or "Path concept"
description = _ensure_sentence(description)
category, values = self.value_info()
if category is None:
return description
return f"{description}\n\n{category}: ``{', '.join(values)}``"
@property
def schema_identifier(self) -> SchemaIdentifier:
return self._schema_identifier
[docs]
def annotation(self) -> Any:
"""More specific type annotation of the return value of ``deserialize``."""
return _get_return_annotation(self._value_generator)
[docs]
def value_info(self) -> Tuple[Optional[str], Optional[str]]:
enum_values = self._enum_values()
if enum_values:
return "Enumeration", enum_values
if self._examples:
return "Examples", list(map(repr, self._examples))
return None, None
def _enum_values(self) -> List[str]:
enum_type = None
if isinstance(self._value_generator, Enum):
enum_type = self._value_generator
else:
return_type = self.annotation()
if isinstance(return_type, type) and issubclass(return_type, Enum):
enum_type = return_type
if enum_type is None or self._value_serializer is None:
return []
values = []
for field in enum_type:
try:
value = repr(self._value_serializer(field))
except Exception:
continue
values.append(value)
return values
def _get_return_annotation(obj: Union[Callable[..., Any], Type[Any]]) -> Any:
"""Extract the return annotation of a function or a type."""
if isinstance(obj, type):
return obj
if callable(obj):
hints = get_type_hints(obj)
return hints.get("return", None)
raise TypeError(f"{obj!r} must be a type or callable")
def _ensure_sentence(text: str) -> str:
text = text.strip()
if not text:
return ""
if text[0].isalpha():
text = text[0].upper() + text[1:]
if text[-1] not in ".!?":
text += "."
return text