Source code for dolor.types.util

"""Type utilities."""

import collections
import functools

from ..util import arg_annotations
from .type import Type
from .version import VersionSwitchedType

[docs]def prepare_type(obj): """Ensures an object is a :class:`~.Type`. Parameters ---------- obj : subclass of :class:`~.Type` or :class:`collections.abc.Mapping` If a subclass of :class:`~.Type`, then that is simply returned. If a :class:`collections.abc.Mapping` (e.g. a :class:`dict`), then a :class:`~.VersionSwitchedType` is returned with ``obj`` as the switch. Otherwise an error is raised. Returns ------- subclass of :class:`~.Type` The corresponding type. Raises ------ :exc:`ValueError` If ``obj`` cannot be converted to a :class:`~.Type`. Examples -------- >>> import dolor >>> dolor.types.prepare_type(dolor.types.VarInt) <class 'dolor.types.numeric.VarInt'> >>> dolor.types.prepare_type({}) <class 'dolor.types.version.VersionSwitchedType'> """ if isinstance(obj, collections.abc.Mapping): return VersionSwitchedType(obj) if isinstance(obj, type) and issubclass(obj, Type): return obj raise TypeError(f"Object cannot be converted to a Type: {obj}")
[docs]def prepare_types(func): """A decorator that passes certain arguments are passed through :func:`prepare_type`. Arguments annotated with :class:`~.Type` are passed through :func:`prepare_type` before being forwarded onto the function. This works for ``*args`` and ``**kwargs`` as well. """ @functools.wraps(func) def wrapper(*args, **kwargs): args_annotations, kwargs_annotations = arg_annotations(func, *args, **kwargs) new_args = [ prepare_type(x) if y is Type else x for x, y in args_annotations ] new_kwargs = {} for name, (value, annotation) in kwargs_annotations.items(): if annotation is Type: value = prepare_type(value) new_kwargs[name] = value return func(*new_args, **new_kwargs) return wrapper