Nie możesz wybrać więcej, niż 25 tematów Tematy muszą się zaczynać od litery lub cyfry, mogą zawierać myślniki ('-') i mogą mieć do 35 znaków.
 
 
 
 

200 wiersze
7.7 KiB

  1. """
  2. """
  3. # Created on 2014.08.23
  4. #
  5. # Author: Giovanni Cannata
  6. #
  7. # Copyright 2014 - 2020 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. try:
  25. from collections.abc import MutableMapping, Mapping
  26. except ImportError:
  27. from collections import MutableMapping, Mapping
  28. from .. import SEQUENCE_TYPES
  29. class CaseInsensitiveDict(MutableMapping):
  30. def __init__(self, other=None, **kwargs):
  31. self._store = dict() # store use the original key
  32. self._case_insensitive_keymap = dict() # is a mapping ci_key -> key
  33. if other or kwargs:
  34. if other is None:
  35. other = dict()
  36. self.update(other, **kwargs)
  37. def __contains__(self, item):
  38. try:
  39. self.__getitem__(item)
  40. return True
  41. except KeyError:
  42. return False
  43. @staticmethod
  44. def _ci_key(key):
  45. return key.strip().lower() if hasattr(key, 'lower') else key
  46. def __delitem__(self, key):
  47. ci_key = self._ci_key(key)
  48. del self._store[self._case_insensitive_keymap[ci_key]]
  49. del self._case_insensitive_keymap[ci_key]
  50. def __setitem__(self, key, item):
  51. ci_key = self._ci_key(key)
  52. if ci_key in self._case_insensitive_keymap: # updates existing value
  53. self._store[self._case_insensitive_keymap[ci_key]] = item
  54. else: # new key
  55. self._store[key] = item
  56. self._case_insensitive_keymap[ci_key] = key
  57. def __getitem__(self, key):
  58. return self._store[self._case_insensitive_keymap[self._ci_key(key)]]
  59. def __iter__(self):
  60. return self._store.__iter__()
  61. def __len__(self): # if len is 0 then the cidict appears as False in IF statement
  62. return len(self._store)
  63. def __repr__(self):
  64. return repr(self._store)
  65. def __str__(self):
  66. return str(self._store)
  67. def keys(self):
  68. return self._store.keys()
  69. def values(self):
  70. return self._store.values()
  71. def items(self):
  72. return self._store.items()
  73. def __eq__(self, other):
  74. if not isinstance(other, (Mapping, dict)):
  75. return NotImplemented
  76. if isinstance(other, CaseInsensitiveDict):
  77. if len(self.items()) != len(other.items()):
  78. return False
  79. else:
  80. for key, value in self.items():
  81. if not (key in other and other[key] == value):
  82. return False
  83. return True
  84. return self == CaseInsensitiveDict(other)
  85. def copy(self):
  86. return CaseInsensitiveDict(self._store)
  87. class CaseInsensitiveWithAliasDict(CaseInsensitiveDict):
  88. def __init__(self, other=None, **kwargs):
  89. self._aliases = dict()
  90. self._alias_keymap = dict() # is a mapping key -> [alias1, alias2, ...]
  91. CaseInsensitiveDict.__init__(self, other, **kwargs)
  92. def aliases(self):
  93. return self._aliases.keys()
  94. def __setitem__(self, key, value):
  95. if isinstance(key, SEQUENCE_TYPES):
  96. ci_key = self._ci_key(key[0])
  97. if ci_key not in self._aliases:
  98. CaseInsensitiveDict.__setitem__(self, key[0], value)
  99. self.set_alias(ci_key, key[1:])
  100. else:
  101. raise KeyError('\'' + str(key[0] + ' already used as alias'))
  102. else:
  103. ci_key = self._ci_key(key)
  104. if ci_key not in self._aliases:
  105. CaseInsensitiveDict.__setitem__(self, key, value)
  106. else:
  107. self[self._aliases[ci_key]] = value
  108. def __delitem__(self, key):
  109. ci_key = self._ci_key(key)
  110. try:
  111. CaseInsensitiveDict.__delitem__(self, ci_key)
  112. if ci_key in self._alias_keymap:
  113. 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
  114. self.remove_alias(alias)
  115. return
  116. except KeyError: # try to remove alias
  117. if ci_key in self._aliases:
  118. self.remove_alias(ci_key)
  119. def set_alias(self, key, alias, ignore_duplicates=False):
  120. if not isinstance(alias, SEQUENCE_TYPES):
  121. alias = [alias]
  122. for alias_to_add in alias:
  123. ci_key = self._ci_key(key)
  124. if ci_key in self._case_insensitive_keymap:
  125. ci_alias = self._ci_key(alias_to_add)
  126. if ci_alias not in self._case_insensitive_keymap: # checks if alias is used a key
  127. if ci_alias not in self._aliases: # checks if alias is used as another alias
  128. self._aliases[ci_alias] = ci_key
  129. if ci_key in self._alias_keymap: # extends alias keymap
  130. self._alias_keymap[ci_key].append(self._ci_key(ci_alias))
  131. else:
  132. self._alias_keymap[ci_key] = list()
  133. self._alias_keymap[ci_key].append(self._ci_key(ci_alias))
  134. else:
  135. if ci_key in self._alias_keymap and ci_alias in self._alias_keymap[ci_key]: # passes if alias is already defined to the same key
  136. pass
  137. elif not ignore_duplicates:
  138. raise KeyError('\'' + str(alias_to_add) + '\' already used as alias')
  139. else:
  140. if ci_key == self._ci_key(self._case_insensitive_keymap[ci_alias]): # passes if alias is already defined to the same key
  141. pass
  142. elif not ignore_duplicates:
  143. raise KeyError('\'' + str(alias_to_add) + '\' already used as key')
  144. else:
  145. for keymap in self._alias_keymap:
  146. if ci_key in self._alias_keymap[keymap]: # kye is already aliased
  147. self.set_alias(keymap, alias + [ci_key], ignore_duplicates=ignore_duplicates)
  148. break
  149. else:
  150. raise KeyError('\'' + str(ci_key) + '\' is not an existing alias or key')
  151. def remove_alias(self, alias):
  152. if not isinstance(alias, SEQUENCE_TYPES):
  153. alias = [alias]
  154. for alias_to_remove in alias:
  155. ci_alias = self._ci_key(alias_to_remove)
  156. self._alias_keymap[self._aliases[ci_alias]].remove(ci_alias)
  157. if not self._alias_keymap[self._aliases[ci_alias]]: # remove keymap if empty
  158. del self._alias_keymap[self._aliases[ci_alias]]
  159. del self._aliases[ci_alias]
  160. def __getitem__(self, key):
  161. try:
  162. return CaseInsensitiveDict.__getitem__(self, key)
  163. except KeyError:
  164. return CaseInsensitiveDict.__getitem__(self, self._aliases[self._ci_key(key)])
  165. def copy(self):
  166. new = CaseInsensitiveWithAliasDict(self._store)
  167. new._aliases = self._aliases.copy()
  168. new._alias_keymap = self._alias_keymap
  169. return new