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.

362 lines
10 KiB

4 years ago
  1. from __future__ import absolute_import
  2. import sys
  3. from collections import abc
  4. from collections.abc import Iterable, Set
  5. from cpython.object cimport PyObject_Str, Py_NE, PyObject_RichCompare
  6. from ._abc import MultiMapping, MutableMultiMapping
  7. from ._istr import istr
  8. from ._multidict_iter cimport *
  9. from ._multidict_views cimport *
  10. from ._pair_list cimport *
  11. cdef object _marker = object()
  12. upstr = istr # for relaxing backward compatibility problems
  13. cdef object _istr = istr
  14. pair_list_init(istr)
  15. # multidict_iter_init()
  16. multidict_views_init()
  17. def getversion(_Base md):
  18. return pair_list_version(md._impl)
  19. cdef class _Base:
  20. cdef object _impl
  21. def impl(self):
  22. return self._impl
  23. def getall(self, key, default=_marker):
  24. """Return a list of all values matching the key."""
  25. try:
  26. return pair_list_get_all(self._impl, key)
  27. except KeyError:
  28. if default is not _marker:
  29. return default
  30. else:
  31. raise
  32. def getone(self, key, default=_marker):
  33. """Get first value matching the key."""
  34. return self._getone(key, default)
  35. cdef _getone(self, key, default):
  36. try:
  37. return pair_list_get_one(self._impl, key)
  38. except KeyError:
  39. if default is not _marker:
  40. return default
  41. else:
  42. raise
  43. # Mapping interface #
  44. def __getitem__(self, key):
  45. return self._getone(key, _marker)
  46. def get(self, key, default=None):
  47. """Get first value matching the key.
  48. The method is alias for .getone().
  49. """
  50. return self._getone(key, default)
  51. def __contains__(self, key):
  52. return self._contains(key)
  53. cdef _contains(self, key):
  54. return pair_list_contains(self._impl, key)
  55. def __iter__(self):
  56. return iter(self.keys())
  57. def __len__(self):
  58. return pair_list_len(self._impl)
  59. cpdef keys(self):
  60. """Return a new view of the dictionary's keys."""
  61. return multidict_keysview_new(self)
  62. def items(self):
  63. """Return a new view of the dictionary's items *(key, value) pairs)."""
  64. return multidict_itemsview_new(self)
  65. def values(self):
  66. """Return a new view of the dictionary's values."""
  67. return multidict_valuesview_new(self)
  68. def __repr__(self):
  69. lst = []
  70. for k, v in self.items():
  71. lst.append("'{}': {!r}".format(k, v))
  72. body = ', '.join(lst)
  73. return '<{}({})>'.format(self.__class__.__name__, body)
  74. def __eq__(self, arg):
  75. cdef Py_ssize_t pos1
  76. cdef PyObject *identity1
  77. cdef PyObject *value1
  78. cdef Py_hash_t h1
  79. cdef Py_ssize_t pos2
  80. cdef PyObject *identity2
  81. cdef PyObject *value2
  82. cdef Py_hash_t h2
  83. cdef _Base other
  84. if isinstance(arg, _Base):
  85. other = <_Base>arg
  86. if pair_list_len(self._impl) != pair_list_len(other._impl):
  87. return False
  88. pos1 = pos2 = 0
  89. while (_pair_list_next(self._impl, &pos1, &identity1,
  90. NULL, &value1, &h1) and
  91. _pair_list_next(other._impl, &pos2, &identity2,
  92. NULL, &value2, &h2)):
  93. if h1 != h2:
  94. return False
  95. if PyObject_RichCompare(<object>identity1, <object>identity2, Py_NE):
  96. return False
  97. if PyObject_RichCompare(<object>value1, <object>value2, Py_NE):
  98. return False
  99. return True
  100. elif isinstance(arg, abc.Mapping):
  101. return bool(pair_list_eq_to_mapping(self._impl, arg))
  102. else:
  103. return NotImplemented
  104. cdef class MultiDictProxy(_Base):
  105. _proxy_classes = (MultiDict, MultiDictProxy)
  106. _base_class = MultiDict
  107. def __init__(self, arg):
  108. cdef _Base base
  109. if not isinstance(arg, self._proxy_classes):
  110. raise TypeError(
  111. 'ctor requires {} instance'
  112. ', not {}'.format(
  113. ' or '.join(self._proxy_classes),
  114. type(arg)))
  115. base = arg
  116. self._impl = base._impl
  117. def __reduce__(self):
  118. raise TypeError("can't pickle {} objects"
  119. .format(self.__class__.__name__))
  120. def copy(self):
  121. """Return a copy of itself."""
  122. return self._base_class(self)
  123. MultiMapping.register(MultiDictProxy)
  124. cdef class CIMultiDictProxy(MultiDictProxy):
  125. _proxy_classes = (CIMultiDict, CIMultiDictProxy)
  126. _base_class = CIMultiDict
  127. MultiMapping.register(CIMultiDictProxy)
  128. cdef str _str(key):
  129. typ = type(key)
  130. if typ is str:
  131. return <str>key
  132. if typ is _istr:
  133. return PyObject_Str(key)
  134. elif issubclass(typ, str):
  135. return str(key)
  136. else:
  137. raise TypeError("MultiDict keys should be either str "
  138. "or subclasses of str")
  139. cdef class MultiDict(_Base):
  140. """An ordered dictionary that can have multiple values for each key."""
  141. def __init__(self, *args, **kwargs):
  142. self._impl = pair_list_new()
  143. self._extend(args, kwargs, 'MultiDict', True)
  144. def __reduce__(self):
  145. return (
  146. self.__class__,
  147. (list(self.items()),)
  148. )
  149. cdef _extend(self, tuple args, dict kwargs, name, bint do_add):
  150. cdef object key
  151. cdef object value
  152. cdef object arg
  153. cdef object i
  154. if len(args) > 1:
  155. raise TypeError("{} takes at most 1 positional argument"
  156. " ({} given)".format(name, len(args)))
  157. if args:
  158. arg = args[0]
  159. if isinstance(arg, _Base) and not kwargs:
  160. if do_add:
  161. self._append_items((<_Base>arg)._impl)
  162. else:
  163. self._update_items((<_Base>arg)._impl)
  164. else:
  165. if hasattr(arg, 'items'):
  166. arg = arg.items()
  167. if kwargs:
  168. arg = list(arg)
  169. arg.extend(list(kwargs.items()))
  170. if do_add:
  171. self._append_items_seq(arg, name)
  172. else:
  173. pair_list_update_from_seq(self._impl, arg)
  174. else:
  175. arg = list(kwargs.items())
  176. if do_add:
  177. self._append_items_seq(arg, name)
  178. else:
  179. pair_list_update_from_seq(self._impl, arg)
  180. cdef object _update_items(self, object impl):
  181. pair_list_update(self._impl, impl)
  182. cdef object _append_items(self, object impl):
  183. cdef PyObject *key
  184. cdef PyObject *val
  185. cdef Py_ssize_t pos
  186. pos = 0
  187. while _pair_list_next(impl, &pos, NULL, &key, &val, NULL):
  188. self._add(<object>key, <object>val)
  189. cdef object _append_items_seq(self, object arg, object name):
  190. cdef object i
  191. cdef object key
  192. cdef object value
  193. for i in arg:
  194. if not len(i) == 2:
  195. raise TypeError(
  196. "{} takes either dict or list of (key, value) "
  197. "tuples".format(name))
  198. key = i[0]
  199. value = i[1]
  200. self._add(key, value)
  201. cdef _add(self, key, value):
  202. pair_list_add(self._impl, key, value);
  203. cdef _replace(self, key, value):
  204. pair_list_replace(self._impl, key, value)
  205. def add(self, key, value):
  206. """Add the key and value, not overwriting any previous value."""
  207. self._add(key, value)
  208. def copy(self):
  209. """Return a copy of itself."""
  210. ret = MultiDict()
  211. ret._extend((list(self.items()),), {}, 'copy', True)
  212. return ret
  213. def extend(self, *args, **kwargs):
  214. """Extend current MultiDict with more values.
  215. This method must be used instead of update.
  216. """
  217. self._extend(args, kwargs, "extend", True)
  218. def clear(self):
  219. """Remove all items from MultiDict"""
  220. pair_list_clear(self._impl)
  221. # MutableMapping interface #
  222. def __setitem__(self, key, value):
  223. self._replace(key, value)
  224. def __delitem__(self, key):
  225. pair_list_del(self._impl, key)
  226. def setdefault(self, key, default=None):
  227. """Return value for key, set value to default if key is not present."""
  228. return pair_list_set_default(self._impl, key, default)
  229. def popone(self, key, default=_marker):
  230. """Remove the last occurrence of key and return the corresponding
  231. value.
  232. If key is not found, default is returned if given, otherwise
  233. KeyError is raised.
  234. """
  235. try:
  236. return pair_list_pop_one(self._impl, key)
  237. except KeyError:
  238. if default is _marker:
  239. raise
  240. else:
  241. return default
  242. pop = popone
  243. def popall(self, key, default=_marker):
  244. """Remove all occurrences of key and return the list of corresponding
  245. values.
  246. If key is not found, default is returned if given, otherwise
  247. KeyError is raised.
  248. """
  249. try:
  250. return pair_list_pop_all(self._impl, key)
  251. except KeyError:
  252. if default is _marker:
  253. raise
  254. else:
  255. return default
  256. def popitem(self):
  257. """Remove and return an arbitrary (key, value) pair."""
  258. return pair_list_pop_item(self._impl)
  259. def update(self, *args, **kwargs):
  260. """Update the dictionary from *other*, overwriting existing keys."""
  261. self._extend(args, kwargs, "update", False)
  262. MutableMultiMapping.register(MultiDict)
  263. cdef class CIMultiDict(MultiDict):
  264. """An ordered dictionary that can have multiple values for each key."""
  265. def __init__(self, *args, **kwargs):
  266. self._impl = ci_pair_list_new()
  267. self._extend(args, kwargs, 'CIMultiDict', True)
  268. def __reduce__(self):
  269. return (
  270. self.__class__,
  271. (list(self.items()),),
  272. )
  273. def copy(self):
  274. """Return a copy of itself."""
  275. ret = CIMultiDict()
  276. ret._extend((list(self.items()),), {}, 'copy', True)
  277. return ret
  278. MutableMultiMapping.register(CIMultiDict)