Source code for pynxxas.models.units
import pint
import pydantic
from pydantic_core import core_schema
from pydantic.json_schema import JsonSchemaValue
from typing import Any, Sequence, Union, List, Annotated
_REGISTRY = pint.UnitRegistry()
_REGISTRY.formatter.default_format = "~" # unit symbols instead of full unit names
[docs]
def as_quantity(value: Union[str, pint.Quantity, Sequence]) -> pint.Quantity:
if isinstance(value, pint.Quantity):
return value
if (
isinstance(value, Sequence)
and len(value) == 2
and (isinstance(value[1], str) or value[1] is None)
):
value, units = value
else:
units = None
return _REGISTRY.Quantity(value, units)
[docs]
def as_units(value: Union[str, pint.Unit]) -> pint.Unit:
if isinstance(value, pint.Unit):
return value
return _REGISTRY.parse_units(value)
class _QuantityPydanticAnnotation:
# https://docs.pydantic.dev/latest/concepts/types/#handling-third-party-types
@classmethod
def __get_pydantic_core_schema__(
cls,
_source_type: Any,
_handler: pydantic.GetCoreSchemaHandler,
) -> core_schema.CoreSchema:
def serialize(value: Any) -> List:
value = as_quantity(value)
return [value.magnitude.tolist(), str(value.units)]
json_schema = core_schema.chain_schema(
[
core_schema.no_info_plain_validator_function(as_quantity),
]
)
return core_schema.json_or_python_schema(
json_schema=json_schema,
python_schema=core_schema.union_schema(
[
# check if it's an instance first before doing any further work
core_schema.is_instance_schema(pint.Quantity),
json_schema,
]
),
serialization=core_schema.plain_serializer_function_ser_schema(serialize),
)
@classmethod
def __get_pydantic_json_schema__(
cls,
_core_schema: core_schema.CoreSchema,
handler: pydantic.GetJsonSchemaHandler,
) -> JsonSchemaValue:
return handler(
core_schema.union_schema(
[
core_schema.float_schema(),
core_schema.list_schema(core_schema.float_schema()),
]
)
)
PydanticQuantity = Annotated[pint.Quantity, _QuantityPydanticAnnotation]