Development of an internal social media platform with personalised dashboards for students
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

ciDict.py 7.2KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190
  1. """
  2. """
  3. # Created on 2014.08.23
  4. #
  5. # Author: Giovanni Cannata
  6. #
  7. # Copyright 2014 - 2018 Giovanni Cannata
  8. #
  9. # This file is part of ldap3.
  10. #
  11. # ldap3 is free software: you can redistribute it and/or modify
  12. # it under the terms of the GNU Lesser General Public License as published
  13. # by the Free Software Foundation, either version 3 of the License, or
  14. # (at your option) any later version.
  15. #
  16. # ldap3 is distributed in the hope that it will be useful,
  17. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  18. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  19. # GNU Lesser General Public License for more details.
  20. #
  21. # You should have received a copy of the GNU Lesser General Public License
  22. # along with ldap3 in the COPYING and COPYING.LESSER files.
  23. # If not, see <http://www.gnu.org/licenses/>.
  24. import collections
  25. from .. import SEQUENCE_TYPES
  26. class CaseInsensitiveDict(collections.MutableMapping):
  27. def __init__(self, other=None, **kwargs):
  28. self._store = dict() # store use the original key
  29. self._case_insensitive_keymap = dict() # is a mapping ci_key -> key
  30. if other or kwargs:
  31. if other is None:
  32. other = dict()
  33. self.update(other, **kwargs)
  34. def __contains__(self, item):
  35. try:
  36. self.__getitem__(item)
  37. return True
  38. except KeyError:
  39. return False
  40. @staticmethod
  41. def _ci_key(key):
  42. return key.strip().lower() if hasattr(key, 'lower') else key
  43. def __delitem__(self, key):
  44. ci_key = self._ci_key(key)
  45. del self._store[self._case_insensitive_keymap[ci_key]]
  46. del self._case_insensitive_keymap[ci_key]
  47. def __setitem__(self, key, item):
  48. ci_key = self._ci_key(key)
  49. if ci_key in self._case_insensitive_keymap: # updates existing value
  50. self._store[self._case_insensitive_keymap[ci_key]] = item
  51. else: # new key
  52. self._store[key] = item
  53. self._case_insensitive_keymap[ci_key] = key
  54. def __getitem__(self, key):
  55. return self._store[self._case_insensitive_keymap[self._ci_key(key)]]
  56. def __iter__(self):
  57. return self._store.__iter__()
  58. def __len__(self): # if len is 0 then the cidict appears as False in IF statement
  59. return len(self._store)
  60. def __repr__(self):
  61. return repr(self._store)
  62. def __str__(self):
  63. return str(self._store)
  64. def keys(self):
  65. return self._store.keys()
  66. def values(self):
  67. return self._store.values()
  68. def items(self):
  69. return self._store.items()
  70. def __eq__(self, other):
  71. if not isinstance(other, (collections.Mapping, dict)):
  72. return NotImplemented
  73. if isinstance(other, CaseInsensitiveDict):
  74. if len(self.items()) != len(other.items()):
  75. return False
  76. else:
  77. for key, value in self.items():
  78. if not (key in other and other[key] == value):
  79. return False
  80. return True
  81. return self == CaseInsensitiveDict(other)
  82. def copy(self):
  83. return CaseInsensitiveDict(self._store)
  84. class CaseInsensitiveWithAliasDict(CaseInsensitiveDict):
  85. def __init__(self, other=None, **kwargs):
  86. self._aliases = dict()
  87. self._alias_keymap = dict() # is a mapping key -> [alias1, alias2, ...]
  88. CaseInsensitiveDict.__init__(self, other, **kwargs)
  89. def aliases(self):
  90. return self._aliases.keys()
  91. def __setitem__(self, key, value):
  92. if isinstance(key, SEQUENCE_TYPES):
  93. ci_key = self._ci_key(key[0])
  94. if ci_key not in self._aliases:
  95. CaseInsensitiveDict.__setitem__(self, key[0], value)
  96. self.set_alias(ci_key, key[1:])
  97. else:
  98. raise KeyError('\'' + str(key[0] + ' already used as alias'))
  99. else:
  100. ci_key = self._ci_key(key)
  101. if ci_key not in self._aliases:
  102. CaseInsensitiveDict.__setitem__(self, key, value)
  103. else:
  104. self[self._aliases[ci_key]] = value
  105. def __delitem__(self, key):
  106. ci_key = self._ci_key(key)
  107. try:
  108. CaseInsensitiveDict.__delitem__(self, ci_key)
  109. if ci_key in self._alias_keymap:
  110. for alias in self._alias_keymap[ci_key][:]: # removes aliases, uses a copy of _alias_keymap because iterator gets confused when aliases are removed from _alias_keymap
  111. self.remove_alias(alias)
  112. return
  113. except KeyError: # try to remove alias
  114. if ci_key in self._aliases:
  115. self.remove_alias(ci_key)
  116. def set_alias(self, key, alias):
  117. if not isinstance(alias, SEQUENCE_TYPES):
  118. alias = [alias]
  119. for alias_to_add in alias:
  120. ci_key = self._ci_key(key)
  121. if ci_key in self._case_insensitive_keymap:
  122. ci_alias = self._ci_key(alias_to_add)
  123. if ci_alias not in self._case_insensitive_keymap: # checks if alias is used a key
  124. if ci_alias not in self._aliases: # checks if alias is used as another alias
  125. self._aliases[ci_alias] = ci_key
  126. if ci_key in self._alias_keymap: # extend alias keymap
  127. self._alias_keymap[ci_key].append(self._ci_key(ci_alias))
  128. else:
  129. self._alias_keymap[ci_key] = list()
  130. self._alias_keymap[ci_key].append(self._ci_key(ci_alias))
  131. else:
  132. if ci_key == self._ci_key(self._alias_keymap[ci_alias]): # passes if alias is already defined to the same key
  133. pass
  134. else:
  135. raise KeyError('\'' + str(alias_to_add) + '\' already used as alias')
  136. else:
  137. if ci_key == self._ci_key(self._case_insensitive_keymap[ci_alias]): # passes if alias is already defined to the same key
  138. pass
  139. else:
  140. raise KeyError('\'' + str(alias_to_add) + '\' already used as key')
  141. else:
  142. raise KeyError('\'' + str(ci_key) + '\' is not an existing key')
  143. def remove_alias(self, alias):
  144. if not isinstance(alias, SEQUENCE_TYPES):
  145. alias = [alias]
  146. for alias_to_remove in alias:
  147. ci_alias = self._ci_key(alias_to_remove)
  148. self._alias_keymap[self._aliases[ci_alias]].remove(ci_alias)
  149. if not self._alias_keymap[self._aliases[ci_alias]]: # remove keymap if empty
  150. del self._alias_keymap[self._aliases[ci_alias]]
  151. del self._aliases[ci_alias]
  152. def __getitem__(self, key):
  153. try:
  154. return CaseInsensitiveDict.__getitem__(self, key)
  155. except KeyError:
  156. return CaseInsensitiveDict.__getitem__(self, self._aliases[self._ci_key(key)])
  157. def copy(self):
  158. new = CaseInsensitiveWithAliasDict(self._store)
  159. new._aliases = self._aliases.copy()
  160. new._alias_keymap = self._alias_keymap
  161. return new