Source code for id_translation.offline._format

from collections.abc import Iterable, Mapping
from typing import Any, Literal, Self

from rics.misc import tname

from . import parse_format_string
from .types import PlaceholdersTuple


[docs] class Format: """Format specification for translations strings. Translation formats are similar to regular f-strings, with two important exceptions: 1. Positional placeholders (``'{}'``) may not be used; correct form is ``'{placeholder-name}'``. 2. Placeholders surrounded by ``'[]'`` denote an optional element. Optional elements are rendered... * Only if `all` of its placeholders are defined. * Without delimiting brackets. * As literal text (with brackets) if there is no placeholder in the block. .. hint:: Double the wanted bracket character to render as a literal, analogous to ``'{{'`` and ``'}}'`` in plain Python f-strings. See the example below for a demonstration. Args: fmt: A translation fstring. Examples: **Basic usage** Formats are created by passing a single ``str`` arguments, as described above. >>> Format(Format.DEFAULT) "Format('{id}:{name}')" >>> Format(Format.DEFAULT).fstring().format(id=1, name="First") '1:First' Using :meth:`Format.fstring` and :py:meth:`str.format` is flexible but verbose. Formats can be applied either through :meth:`Format.format`... >>> fmt = Format(Format.DEFAULT_FAILED) >>> fmt.format(id=1, name="First") ...or just ``Format.__call__()``. >>> fmt(id=1, name="First") '<Failed: id=1>' Using either convenience method will use as many placeholders as possible. >>> fmt = Format(Format.DEFAULT) >>> fmt.placeholders "('id', 'name')" >>> fmt(id=1, name="First") '1:First' >>> fmt(id=1, name="First", unknown=20.19) '1:First' Unknown placeholders are simply ignored. **Optional placeholders** A format string using literal angle brackets and an optional element. >>> from id_translation.offline import Format >>> fmt = Format("{id}:[[{name}]][, nice={is_nice}]") The ``Format`` class when used directly only returns required placeholders by default... >>> fmt.fstring() '{id}:[{name}]' >>> fmt(id=0, name="Tarzan") '0:[Tarzan]' ...but the :attr:`placeholders` attribute can be used to retrieve all placeholders, required and optional: >>> fmt.placeholders ('id', 'name', 'is_nice') >>> fmt(id=1, name="Morris", is_nice=True) '1:[Morris], nice=True' The :class:`.Translator` will automatically add optional placeholders, if they are present in the source. .. note:: Python format specifications and conversions are preserved. This is especially useful for long values such as UUIDs. >>> from uuid import UUID >>> uuid = UUID("550e8400-e29b-41d4-a716-446655440000") Convert to string and truncate to eight characters. >>> Format("{id!s:.8}:{name!r}").format(id=uuid, name="Sofia") "550e8400:'Sofia'" See the official :py:ref:`formatspec` documentation for details. """ DEFAULT: Literal["{id}:{name}"] = "{id}:{name}" """Default translation format.""" DEFAULT_FAILED: Literal["<Failed: id={id!r}>"] = "<Failed: id={id!r}>" """Default format for missing IDs.""" def __init__(self, fmt: str) -> None: self._fmt = fmt self._elements: list[parse_format_string.Element] = parse_format_string.get_elements(fmt)
[docs] def format(self, **placeholders: Any) -> str: """Apply the format. Args: **placeholders: Formats to use in the finals string. Returns: Formatting using `placeholders`. """ return self.fstring(placeholders, positional=False).format_map(placeholders)
__call__ = format
[docs] def fstring(self, placeholders: Iterable[str] | None = None, *, positional: bool = False) -> str: """Create a format string for the given placeholders. Args: placeholders: Keys to keep. Passing ``None`` is equivalent to passing :attr:`required_placeholders`. positional: If ``True``, remove names to return a positional fstring. Returns: An fstring with optional elements removed unless included in `placeholders`. Raises: KeyError: If required placeholders are missing. """ placeholders = placeholders or self.required_placeholders missing_required_placeholders = set(self.required_placeholders).difference(placeholders) if missing_required_placeholders: raise KeyError(f"Required key(s) {missing_required_placeholders} missing from {placeholders=}.") return self._make_fstring(placeholders, positional=positional)
def _make_fstring(self, placeholders: Iterable[str], positional: bool) -> str: def predicate(e: parse_format_string.Element) -> bool: return e.required or set(placeholders).issuperset(e.placeholders) return "".join(e.positional_part if positional else e.part for e in filter(predicate, self._elements))
[docs] def partial(self, defaults: Mapping[str, Any]) -> Self: """Get a partially formatted :meth:`fstring`. Args: defaults: Keys which should be replaced with real values. Keys which are **not** part of `defaults` will be left as-is. Returns: A partially formatted fstring. """ new_fmt, _placeholders = parse_format_string.Element.parse_block(self._fmt, defaults=defaults) cls = type(self) return cls(new_fmt)
[docs] @classmethod def parse(cls, fmt: str | Self) -> Self: """Parse a format. Args: fmt: A ``str`` or ``Format`` instance. Returns: A ``Format`` instance. """ return cls(fmt) if isinstance(fmt, str) else fmt
@property def placeholders(self) -> PlaceholdersTuple: """All placeholders in the order in which they appear.""" return self._extract_placeholders(self._elements) @property def required_placeholders(self) -> PlaceholdersTuple: """All required placeholders in the order in which they appear.""" return self._extract_placeholders(filter(lambda e: e.required, self._elements)) @property def optional_placeholders(self) -> PlaceholdersTuple: # pragma: no cover """All optional placeholders in the order in which they appear.""" return self._extract_placeholders(filter(lambda e: not e.required, self._elements)) @staticmethod def _extract_placeholders(elements: Iterable[parse_format_string.Element]) -> PlaceholdersTuple: ans = [] for e in elements: ans.extend(e.placeholders) return tuple(ans) def __repr__(self) -> str: return f"{tname(self)}({self._fmt!r})"