Source code for id_translation.offline._format_applier

from collections.abc import Sequence
from typing import TYPE_CHECKING, Any, Generic

from rics.misc import tname

from ..transform.types import Transformer
from ..types import ID, IdType, NameType, SourceType
from ._format import Format
from ._magic_dict import MagicDict
from .types import PlaceholdersTuple, PlaceholderTranslations, TranslatedIds

if TYPE_CHECKING:
    import pandas


[docs] class FormatApplier(Generic[NameType, SourceType, IdType]): """Application of :class:`.Format` specifications. This class converts raw translation data into ready-to-use dicts on the form ``{id: translation}``, where the translation is always a plain string. Args: translations: A :class:`~.PlaceholderTranslations` object returned by fetchers. transformer: Initialized :class:`.Transformer` instance. Raises: ValueError: If `default` is given and any placeholder names are missing. Examples: Basic usage. >>> data = {1999: "Sofia", 1991: "Richard", 1904: "Fred"} >>> translations = PlaceholderTranslations.from_dict("my-source", data) >>> applier = FormatApplier(translations) We used the simplified ``{id: name}`` to create the translation object above. Let's create the formats to use: >>> fmt = Format.parse("{id}:{name}") >>> default_fmt = Format.parse("<Failed: id={id!r}>") Using ``FormatApplier.__call__`` delegates to :meth:`apply`. >>> applier(fmt, default_fmt=default_fmt) {1999: '1999:Sofia', 1991: '1991:Richard', 1904: '1904:Fred'} The output may look like a regular ``dict``, but is actually a :class:`.MagicDict`. >>> magic_dict = applier(fmt, default_fmt=default_fmt) >>> type(magic_dict) <class 'id_translation.offline._magic_dict.MagicDict'> .. warning:: The :class:`.MagicDict` is does **not** behave like a regular dict. You can, for instance, use ``__getitem__`` on unknown keys: >>> magic_dict = applier(fmt, default_fmt=default_fmt) >>> magic_dict[-1] '<Failed: id=-1>' See the :class:`.MagicDict` class documentation for more information. """ def __init__( self, translations: PlaceholderTranslations[SourceType], *, transformer: Transformer[IdType] = None, ) -> None: self._translations = translations self._source = translations.source self._placeholder_names = translations.placeholders self._n_ids = len(translations.records) self._transformer = transformer
[docs] def apply( self, fmt: Format, *, default_fmt: Format, placeholders: PlaceholdersTuple | None = None, default_fmt_placeholders: dict[str, Any] | None = None, enable_uuid_heuristics: bool = True, ) -> MagicDict[IdType]: """Translate IDs. .. note:: This method does not accept strings. Use :meth:`.Format.parse` to convert raw formats. Args: fmt: Translation :class:`.Format` to use. placeholders: Tuple of placeholder names to include in the formatted output. If ``None``, use the intersection of :attr:`.placeholders` and :attr:`fmt.placeholders <.Format.placeholders>`. default_fmt: Alternative format for default translation. default_fmt_placeholders: Default placeholders, e.g. ``{'name': 'default name'}``. enable_uuid_heuristics: Improves matching when :py:class:`~uuid.UUID`-like IDs are in use. Returns: A dict ``{id: translation}``. Notes: This method is an alias of ``__call__``. """ assert isinstance(fmt, Format), f"invalid {fmt=}" # noqa: S101 assert isinstance(default_fmt, Format), f"invalid {default_fmt=}" # noqa: S101 if placeholders is None: # Use as many placeholders as possible. placeholders = tuple(filter(self._placeholder_names.__contains__, fmt.placeholders)) fstring = fmt.fstring(placeholders, positional=True) real_translations = self._apply(fstring, placeholders) if default_fmt_placeholders is None: default_fmt_placeholders = {} partial = default_fmt.partial(default_fmt_placeholders) try: default_fstring = partial.fstring(positional=True) except KeyError as e: raise ValueError( f"All required placeholders except {{{ID}}} must be provided for {default_fmt=}:" f" {default_fmt.required_placeholders}." ) from e return MagicDict(real_translations, default_fstring, enable_uuid_heuristics, self._transformer)
__call__ = apply
[docs] def to_dict(self) -> dict[str, Sequence[Any]]: """Get the underlying data used for translations as a dict. Returns: A dict ``{placeholder: [values...]}``. """ return self._translations.to_dict()
[docs] def to_pandas(self) -> "pandas.DataFrame": """Get the underlying data used for translations as a :class:`pandas.DataFrame`.""" from pandas import DataFrame return DataFrame(self.to_dict()).convert_dtypes()
@property def records(self) -> Sequence[Sequence[Any]]: """Records used by this instance; see :attr:`.PlaceholderTranslations.records`.""" return self._translations.records def _apply(self, fstring: str, placeholders: PlaceholdersTuple) -> TranslatedIds[IdType]: """Apply fstring to all IDs. Args: fstring: A format string. placeholders: Keys needed for the fstring, in the order in which they appear. Returns: A dict ``{id: translation}``. """ id_pos, records = self._translations.id_pos, self._translations.records if self._placeholder_names == placeholders: return {record[id_pos]: fstring.format(*record) for record in records} else: pos = tuple(map(self._placeholder_names.index, placeholders)) return {record[id_pos]: fstring.format(*(record[i] for i in pos)) for record in records} @property def source(self) -> SourceType: """Return translation source.""" return self._translations.source @property def placeholders(self) -> list[str]: """List of placeholder names; see :attr:`.PlaceholderTranslations.placeholders`.""" return list(self._translations.placeholders) @property def transformer(self) -> Transformer[IdType] | None: """Get the :class:`.Transformer` instance (or ``None``) used by this ``FormatApplier``.""" return self._transformer def __len__(self) -> int: return len(self._translations.records) def __repr__(self) -> str: placeholders = tuple(self._placeholder_names) source = self._source return f"{tname(self)}({len(self)} IDs, {placeholders=}, {source=})"