immutable.py 2.0 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768
  1. # Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license
  2. import collections.abc
  3. from typing import Any, Callable
  4. from dns._immutable_ctx import immutable
  5. @immutable
  6. class Dict(collections.abc.Mapping): # lgtm[py/missing-equals]
  7. def __init__(
  8. self,
  9. dictionary: Any,
  10. no_copy: bool = False,
  11. map_factory: Callable[[], collections.abc.MutableMapping] = dict,
  12. ):
  13. """Make an immutable dictionary from the specified dictionary.
  14. If *no_copy* is `True`, then *dictionary* will be wrapped instead
  15. of copied. Only set this if you are sure there will be no external
  16. references to the dictionary.
  17. """
  18. if no_copy and isinstance(dictionary, collections.abc.MutableMapping):
  19. self._odict = dictionary
  20. else:
  21. self._odict = map_factory()
  22. self._odict.update(dictionary)
  23. self._hash = None
  24. def __getitem__(self, key):
  25. return self._odict.__getitem__(key)
  26. def __hash__(self): # pylint: disable=invalid-hash-returned
  27. if self._hash is None:
  28. h = 0
  29. for key in sorted(self._odict.keys()):
  30. h ^= hash(key)
  31. object.__setattr__(self, "_hash", h)
  32. # this does return an int, but pylint doesn't figure that out
  33. return self._hash
  34. def __len__(self):
  35. return len(self._odict)
  36. def __iter__(self):
  37. return iter(self._odict)
  38. def constify(o: Any) -> Any:
  39. """
  40. Convert mutable types to immutable types.
  41. """
  42. if isinstance(o, bytearray):
  43. return bytes(o)
  44. if isinstance(o, tuple):
  45. try:
  46. hash(o)
  47. return o
  48. except Exception:
  49. return tuple(constify(elt) for elt in o)
  50. if isinstance(o, list):
  51. return tuple(constify(elt) for elt in o)
  52. if isinstance(o, dict):
  53. cdict = dict()
  54. for k, v in o.items():
  55. cdict[k] = constify(v)
  56. return Dict(cdict, True)
  57. return o