| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263 | import refrom typing import Tuplefrom pydantic import field_validatorfrom clean_python import DomainServicefrom clean_python import Id__all__ = ["KeyMapper"]def _maybe_coerce_int(x: str) -> Id:    try:        return int(x)    except ValueError:        return xclass KeyMapper(DomainService):    """Maps one or multiple ids to a string and vice versa.    The mapping is configured using a python formatting string with standard    {} placeholders. Additionally, the key can be prefixed with a tenant id    when multitenant=True.    """    pattern: str = "{}"    @field_validator("pattern")    @classmethod    def validate_pattern(cls, v):        if isinstance(v, str):            assert not v.startswith("/"), "pattern should not start with '/'"            assert v.endswith("{}"), "pattern cannot have a suffix"            try:                v.format(*((2,) * v.count("{}")))            except KeyError:                raise ValueError("invalid pattern")        return v    @property    def n_placeholders(self) -> int:        return self.pattern.count("{}")    def get_named_pattern(self, *names: str) -> str:        return self.pattern.format(*[f"{{{x}}}" for x in names])    @property    def regex(self) -> str:        return "^" + self.pattern.replace("{}", "(.+)") + "$"    def to_key(self, *args: Id) -> str:        assert len(args) == self.n_placeholders        return self.pattern.format(*args)    def to_key_prefix(self, *args: Id) -> str:        return self.to_key(*(args + ("",)))    def from_key(self, key: str) -> Tuple[Id, ...]:        match = re.fullmatch(self.regex, key)        if match is None:            raise ValueError("key does not match expected pattern")        return tuple(_maybe_coerce_int(x) for x in match.groups())
 |