immutable.py 2.0 KB

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