summaryrefslogtreecommitdiffstats
path: root/python/knot_resolver_manager/utils/modeling/renaming.py
diff options
context:
space:
mode:
Diffstat (limited to 'python/knot_resolver_manager/utils/modeling/renaming.py')
-rw-r--r--python/knot_resolver_manager/utils/modeling/renaming.py90
1 files changed, 90 insertions, 0 deletions
diff --git a/python/knot_resolver_manager/utils/modeling/renaming.py b/python/knot_resolver_manager/utils/modeling/renaming.py
new file mode 100644
index 00000000..2420ed04
--- /dev/null
+++ b/python/knot_resolver_manager/utils/modeling/renaming.py
@@ -0,0 +1,90 @@
+"""
+This module implements a standard dict and list alternatives, which can dynamically rename its keys replacing `-` with `_`.
+They persist in nested data structes, meaning that if you try to obtain a dict from Renamed variant, you will actually
+get RenamedDict back instead.
+
+Usage:
+
+d = dict()
+l = list()
+
+rd = renamed(d)
+rl = renamed(l)
+
+assert isinstance(rd, Renamed) == True
+assert l = rl.original()
+"""
+
+from abc import ABC, abstractmethod # pylint: disable=[no-name-in-module]
+from typing import Any, Dict, List, TypeVar
+
+
+class Renamed(ABC):
+ @abstractmethod
+ def original(self) -> Any:
+ """
+ Returns a data structure, which is the source without dynamic renamings
+ """
+
+ @staticmethod
+ def map_public_to_private(name: Any) -> Any:
+ if isinstance(name, str):
+ return name.replace("_", "-")
+ return name
+
+ @staticmethod
+ def map_private_to_public(name: Any) -> Any:
+ if isinstance(name, str):
+ return name.replace("-", "_")
+ return name
+
+
+K = TypeVar("K")
+V = TypeVar("V")
+
+
+class RenamedDict(Dict[K, V], Renamed):
+ def keys(self) -> Any:
+ keys = super().keys()
+ return {Renamed.map_private_to_public(key) for key in keys}
+
+ def __getitem__(self, key: K) -> V:
+ key = Renamed.map_public_to_private(key)
+ res = super().__getitem__(key)
+ return renamed(res)
+
+ def __setitem__(self, key: K, value: V) -> None:
+ key = Renamed.map_public_to_private(key)
+ return super().__setitem__(key, value)
+
+ def __contains__(self, key: object) -> bool:
+ key = Renamed.map_public_to_private(key)
+ return super().__contains__(key)
+
+ def items(self) -> Any:
+ for k, v in super().items():
+ yield Renamed.map_private_to_public(k), renamed(v)
+
+ def original(self) -> Dict[K, V]:
+ return dict(super().items())
+
+
+class RenamedList(List[V], Renamed): # type: ignore
+ def __getitem__(self, key: Any) -> Any:
+ res = super().__getitem__(key)
+ return renamed(res)
+
+ def original(self) -> Any:
+ return list(super().__iter__())
+
+
+def renamed(obj: Any) -> Any:
+ if isinstance(obj, dict):
+ return RenamedDict(**obj)
+ elif isinstance(obj, list):
+ return RenamedList(obj)
+ else:
+ return obj
+
+
+__all__ = ["renamed", "Renamed"]