from __future__ import absolute_import import sys from collections import abc from collections.abc import Iterable, Set from cpython.object cimport PyObject_Str, Py_NE, PyObject_RichCompare from ._abc import MultiMapping, MutableMultiMapping from ._istr import istr from ._multidict_iter cimport * from ._multidict_views cimport * from ._pair_list cimport * cdef object _marker = object() upstr = istr # for relaxing backward compatibility problems cdef object _istr = istr pair_list_init(istr) # multidict_iter_init() multidict_views_init() def getversion(_Base md): return pair_list_version(md._impl) cdef class _Base: cdef object _impl def impl(self): return self._impl def getall(self, key, default=_marker): """Return a list of all values matching the key.""" try: return pair_list_get_all(self._impl, key) except KeyError: if default is not _marker: return default else: raise def getone(self, key, default=_marker): """Get first value matching the key.""" return self._getone(key, default) cdef _getone(self, key, default): try: return pair_list_get_one(self._impl, key) except KeyError: if default is not _marker: return default else: raise # Mapping interface # def __getitem__(self, key): return self._getone(key, _marker) def get(self, key, default=None): """Get first value matching the key. The method is alias for .getone(). """ return self._getone(key, default) def __contains__(self, key): return self._contains(key) cdef _contains(self, key): return pair_list_contains(self._impl, key) def __iter__(self): return iter(self.keys()) def __len__(self): return pair_list_len(self._impl) cpdef keys(self): """Return a new view of the dictionary's keys.""" return multidict_keysview_new(self) def items(self): """Return a new view of the dictionary's items *(key, value) pairs).""" return multidict_itemsview_new(self) def values(self): """Return a new view of the dictionary's values.""" return multidict_valuesview_new(self) def __repr__(self): lst = [] for k, v in self.items(): lst.append("'{}': {!r}".format(k, v)) body = ', '.join(lst) return '<{}({})>'.format(self.__class__.__name__, body) def __eq__(self, arg): cdef Py_ssize_t pos1 cdef PyObject *identity1 cdef PyObject *value1 cdef Py_hash_t h1 cdef Py_ssize_t pos2 cdef PyObject *identity2 cdef PyObject *value2 cdef Py_hash_t h2 cdef _Base other if isinstance(arg, _Base): other = <_Base>arg if pair_list_len(self._impl) != pair_list_len(other._impl): return False pos1 = pos2 = 0 while (_pair_list_next(self._impl, &pos1, &identity1, NULL, &value1, &h1) and _pair_list_next(other._impl, &pos2, &identity2, NULL, &value2, &h2)): if h1 != h2: return False if PyObject_RichCompare(identity1, identity2, Py_NE): return False if PyObject_RichCompare(value1, value2, Py_NE): return False return True elif isinstance(arg, abc.Mapping): return bool(pair_list_eq_to_mapping(self._impl, arg)) else: return NotImplemented cdef class MultiDictProxy(_Base): _proxy_classes = (MultiDict, MultiDictProxy) _base_class = MultiDict def __init__(self, arg): cdef _Base base if not isinstance(arg, self._proxy_classes): raise TypeError( 'ctor requires {} instance' ', not {}'.format( ' or '.join(self._proxy_classes), type(arg))) base = arg self._impl = base._impl def __reduce__(self): raise TypeError("can't pickle {} objects" .format(self.__class__.__name__)) def copy(self): """Return a copy of itself.""" return self._base_class(self) MultiMapping.register(MultiDictProxy) cdef class CIMultiDictProxy(MultiDictProxy): _proxy_classes = (CIMultiDict, CIMultiDictProxy) _base_class = CIMultiDict MultiMapping.register(CIMultiDictProxy) cdef str _str(key): typ = type(key) if typ is str: return key if typ is _istr: return PyObject_Str(key) elif issubclass(typ, str): return str(key) else: raise TypeError("MultiDict keys should be either str " "or subclasses of str") cdef class MultiDict(_Base): """An ordered dictionary that can have multiple values for each key.""" def __init__(self, *args, **kwargs): self._impl = pair_list_new() self._extend(args, kwargs, 'MultiDict', True) def __reduce__(self): return ( self.__class__, (list(self.items()),) ) cdef _extend(self, tuple args, dict kwargs, name, bint do_add): cdef object key cdef object value cdef object arg cdef object i if len(args) > 1: raise TypeError("{} takes at most 1 positional argument" " ({} given)".format(name, len(args))) if args: arg = args[0] if isinstance(arg, _Base) and not kwargs: if do_add: self._append_items((<_Base>arg)._impl) else: self._update_items((<_Base>arg)._impl) else: if hasattr(arg, 'items'): arg = arg.items() if kwargs: arg = list(arg) arg.extend(list(kwargs.items())) if do_add: self._append_items_seq(arg, name) else: pair_list_update_from_seq(self._impl, arg) else: arg = list(kwargs.items()) if do_add: self._append_items_seq(arg, name) else: pair_list_update_from_seq(self._impl, arg) cdef object _update_items(self, object impl): pair_list_update(self._impl, impl) cdef object _append_items(self, object impl): cdef PyObject *key cdef PyObject *val cdef Py_ssize_t pos pos = 0 while _pair_list_next(impl, &pos, NULL, &key, &val, NULL): self._add(key, val) cdef object _append_items_seq(self, object arg, object name): cdef object i cdef object key cdef object value for i in arg: if not len(i) == 2: raise TypeError( "{} takes either dict or list of (key, value) " "tuples".format(name)) key = i[0] value = i[1] self._add(key, value) cdef _add(self, key, value): pair_list_add(self._impl, key, value); cdef _replace(self, key, value): pair_list_replace(self._impl, key, value) def add(self, key, value): """Add the key and value, not overwriting any previous value.""" self._add(key, value) def copy(self): """Return a copy of itself.""" ret = MultiDict() ret._extend((list(self.items()),), {}, 'copy', True) return ret def extend(self, *args, **kwargs): """Extend current MultiDict with more values. This method must be used instead of update. """ self._extend(args, kwargs, "extend", True) def clear(self): """Remove all items from MultiDict""" pair_list_clear(self._impl) # MutableMapping interface # def __setitem__(self, key, value): self._replace(key, value) def __delitem__(self, key): pair_list_del(self._impl, key) def setdefault(self, key, default=None): """Return value for key, set value to default if key is not present.""" return pair_list_set_default(self._impl, key, default) def popone(self, key, default=_marker): """Remove the last occurrence of key and return the corresponding value. If key is not found, default is returned if given, otherwise KeyError is raised. """ try: return pair_list_pop_one(self._impl, key) except KeyError: if default is _marker: raise else: return default pop = popone def popall(self, key, default=_marker): """Remove all occurrences of key and return the list of corresponding values. If key is not found, default is returned if given, otherwise KeyError is raised. """ try: return pair_list_pop_all(self._impl, key) except KeyError: if default is _marker: raise else: return default def popitem(self): """Remove and return an arbitrary (key, value) pair.""" return pair_list_pop_item(self._impl) def update(self, *args, **kwargs): """Update the dictionary from *other*, overwriting existing keys.""" self._extend(args, kwargs, "update", False) MutableMultiMapping.register(MultiDict) cdef class CIMultiDict(MultiDict): """An ordered dictionary that can have multiple values for each key.""" def __init__(self, *args, **kwargs): self._impl = ci_pair_list_new() self._extend(args, kwargs, 'CIMultiDict', True) def __reduce__(self): return ( self.__class__, (list(self.items()),), ) def copy(self): """Return a copy of itself.""" ret = CIMultiDict() ret._extend((list(self.items()),), {}, 'copy', True) return ret MutableMultiMapping.register(CIMultiDict)