Source code for httk.core.vectors.vector_native_backend

"""
Backend wrapping plain nested sequences (lists/tuples of numeric or string leaves).
"""

import decimal
import fractions
from typing import Any, Self

from .fracvector import FracVector, FracVectorBase, _fracvector_to_fractions
from .vector_api import Fractions
from .vector_backend import VectorBackend

_LEAF_TYPES = (int, float, decimal.Decimal, fractions.Fraction, str)


def _is_native(obj: Any) -> bool:
    """
    Conservatively validate that ``obj`` is a rectangular (possibly nested) list/tuple whose
    leaves are all int/float/Decimal/Fraction/str.
    """
    if not isinstance(obj, (list, tuple)):
        return False
    return _valid(obj)


def _valid(node: Any) -> bool:
    if isinstance(node, (list, tuple)):
        if len(node) == 0:
            return True
        child_is_seq = isinstance(node[0], (list, tuple))
        length = len(node[0]) if child_is_seq else None
        for c in node:
            if isinstance(c, (list, tuple)) != child_is_seq:
                return False
            if child_is_seq and len(c) != length:
                return False
            if not _valid(c):
                return False
        return True
    return isinstance(node, _LEAF_TYPES)


[docs] class VectorNativeBackend(VectorBackend): r""" Backend for a vector backed by plain nested sequences. The native representation is a (possibly nested) rectangular list or tuple whose leaves are ``int``, ``float``, :class:`decimal.Decimal`, :class:`fractions.Fraction`, or ``str``. Conversion into the exact ``fractions`` interchange goes through :class:`~httk.core.vectors.fracvector.FracVector`, so string-uncertainty parsing (e.g. ``"0.33342(10)"``) works here too. ``unwrap`` returns the original raw object. :param obj: The rectangular source data to wrap. :param \**hints: Optional backend-selection hints. """ _raw: Any _fracvector_cache: FracVector | None @classmethod def _backend_adopt(cls, obj: Any, **hints: Any) -> Self | None: r"""Adopt a native nested sequence when it matches this backend. :param obj: The object to adopt. :param \**hints: Backend-selection hints. :return: An initialized backend, or ``None`` when ``obj`` is not accepted. """ if hints and hints.get("kind", "native") != "native": return None if isinstance(obj, FracVectorBase): return None if not _is_native(obj): return None return cls(obj, **hints) def __init__(self, obj: Any, **hints: Any) -> None: self._raw = obj self._fracvector_cache = None @property def native(self) -> Any: """ The original nested list/tuple this backend wraps, leaves untouched. This is the same object returned by :meth:`unwrap`, exposed as a named accessor so the native view can present a natively-held vector's leaves *verbatim* (its preserve-original default) without reaching into private state. """ return self._raw def _as_fracvector(self) -> FracVector: if self._fracvector_cache is None: self._fracvector_cache = FracVector(self._raw) return self._fracvector_cache @property def fractions(self) -> Fractions: """Return the native value in the exact Fraction interchange format.""" return _fracvector_to_fractions(self._as_fracvector()) @property def dim(self) -> tuple[int, ...]: """Return the native value's shape.""" return self._as_fracvector().dim
[docs] def unwrap(self) -> Any: """Return the original nested list or tuple.""" return self._raw