from dataclasses import dataclass from typing import Any from typing import cast from typing import Dict from typing import Generic from typing import Type from typing import TypeVar T = TypeVar("T") @dataclass(frozen=True) class FooKey(Generic[T]): """Key-type of a `FooMap`. Note that `FooKey` is hashed by identity; even if the fields of two `FooKey`s are identical, they'll have different hashes, so that they can be used in the same map. :param name: The developer-facing name of the key, for debugging. :param value_type: The type of the value associated with this key. """ name: str value_type: Type[T] def __hash__(self) -> int: return super().__hash__() class FooMap(Dict[FooKey[Any], object]): """A `dict` keyed by `FooKey`s. Useful for sharing a `dict` between modules; by importing the same `FooKey` instance, any number of modules can predictably access and modify the same slot in the `dict`. There are several benefits of this design over picking string key names to agree on: * Collisions aren't possible, even if two `FooKey`s have the same ``name`` field, so there aren't namespacing concerns when sharing a `FooMap` across several (possibly uncooperating) modules. * Static analysis tools can analyze the keys, so their definitions are easy to find, refactoring is painless, etc. * A `FooKey` stores its associated value type, enabling static typechecking (e.g. with ``mypy``) of heterogeneous `dict`s with dynamic keys. (Compare with `typing.TypedDict`, which can only support a fixed set of keys.) Example:: UBOOT_VERSION_CTX = FooKey("UBOOT_VERSION_CTX", str) context = FooMap() context[UBOOT_VERSION_CTX] = get_uboot_version() # In another module: print('uboot version:', context[UBOOT_VERSION_CTX]) """ def __getitem__(self, k: FooKey[T]) -> T: return cast(T, super().__getitem__(k)) def __setitem__(self, k: FooKey[T], v: T) -> None: return super().__setitem__(k, v)