stash.py 3.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112
  1. from typing import Any
  2. from typing import cast
  3. from typing import Dict
  4. from typing import Generic
  5. from typing import TypeVar
  6. from typing import Union
  7. __all__ = ["Stash", "StashKey"]
  8. T = TypeVar("T")
  9. D = TypeVar("D")
  10. class StashKey(Generic[T]):
  11. """``StashKey`` is an object used as a key to a :class:`Stash`.
  12. A ``StashKey`` is associated with the type ``T`` of the value of the key.
  13. A ``StashKey`` is unique and cannot conflict with another key.
  14. """
  15. __slots__ = ()
  16. class Stash:
  17. r"""``Stash`` is a type-safe heterogeneous mutable mapping that
  18. allows keys and value types to be defined separately from
  19. where it (the ``Stash``) is created.
  20. Usually you will be given an object which has a ``Stash``, for example
  21. :class:`~pytest.Config` or a :class:`~_pytest.nodes.Node`:
  22. .. code-block:: python
  23. stash: Stash = some_object.stash
  24. If a module or plugin wants to store data in this ``Stash``, it creates
  25. :class:`StashKey`\s for its keys (at the module level):
  26. .. code-block:: python
  27. # At the top-level of the module
  28. some_str_key = StashKey[str]()
  29. some_bool_key = StashKey[bool]()
  30. To store information:
  31. .. code-block:: python
  32. # Value type must match the key.
  33. stash[some_str_key] = "value"
  34. stash[some_bool_key] = True
  35. To retrieve the information:
  36. .. code-block:: python
  37. # The static type of some_str is str.
  38. some_str = stash[some_str_key]
  39. # The static type of some_bool is bool.
  40. some_bool = stash[some_bool_key]
  41. """
  42. __slots__ = ("_storage",)
  43. def __init__(self) -> None:
  44. self._storage: Dict[StashKey[Any], object] = {}
  45. def __setitem__(self, key: StashKey[T], value: T) -> None:
  46. """Set a value for key."""
  47. self._storage[key] = value
  48. def __getitem__(self, key: StashKey[T]) -> T:
  49. """Get the value for key.
  50. Raises ``KeyError`` if the key wasn't set before.
  51. """
  52. return cast(T, self._storage[key])
  53. def get(self, key: StashKey[T], default: D) -> Union[T, D]:
  54. """Get the value for key, or return default if the key wasn't set
  55. before."""
  56. try:
  57. return self[key]
  58. except KeyError:
  59. return default
  60. def setdefault(self, key: StashKey[T], default: T) -> T:
  61. """Return the value of key if already set, otherwise set the value
  62. of key to default and return default."""
  63. try:
  64. return self[key]
  65. except KeyError:
  66. self[key] = default
  67. return default
  68. def __delitem__(self, key: StashKey[T]) -> None:
  69. """Delete the value for key.
  70. Raises ``KeyError`` if the key wasn't set before.
  71. """
  72. del self._storage[key]
  73. def __contains__(self, key: StashKey[T]) -> bool:
  74. """Return whether key was set."""
  75. return key in self._storage
  76. def __len__(self) -> int:
  77. """Return how many items exist in the stash."""
  78. return len(self._storage)