aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorrobot-piglet <robot-piglet@yandex-team.com>2025-06-03 08:57:25 +0300
committerrobot-piglet <robot-piglet@yandex-team.com>2025-06-03 09:12:06 +0300
commitafd25684d9409df78ca2eb4399c292bf5d16f34e (patch)
tree88c3eeb594760eb1715fc13f0c72a35847b1820e
parent2a2038ed88c9329ca3b60284e9fa32f7d6b56482 (diff)
downloadydb-afd25684d9409df78ca2eb4399c292bf5d16f34e.tar.gz
Intermediate changes
commit_hash:0a6563120495cf8a8132e2fd72fdd9d0ac36db05
-rw-r--r--contrib/python/multidict/.dist-info/METADATA2
-rw-r--r--contrib/python/multidict/multidict/__init__.py2
-rw-r--r--contrib/python/multidict/multidict/_multidict.c4
-rw-r--r--contrib/python/multidict/multidict/_multidict_py.py373
-rw-r--r--contrib/python/multidict/multidict/_multilib/views.h11
-rw-r--r--contrib/python/multidict/tests/test_multidict.py20
-rw-r--r--contrib/python/multidict/tests/test_multidict_benchmarks.py40
-rw-r--r--contrib/python/multidict/tests/test_mutable_multidict.py69
-rw-r--r--contrib/python/multidict/tests/test_version.py123
-rw-r--r--contrib/python/multidict/ya.make2
10 files changed, 424 insertions, 222 deletions
diff --git a/contrib/python/multidict/.dist-info/METADATA b/contrib/python/multidict/.dist-info/METADATA
index 9ee0987b9db..a6ee0d00f6a 100644
--- a/contrib/python/multidict/.dist-info/METADATA
+++ b/contrib/python/multidict/.dist-info/METADATA
@@ -1,6 +1,6 @@
Metadata-Version: 2.4
Name: multidict
-Version: 6.4.3
+Version: 6.4.4
Summary: multidict implementation
Home-page: https://github.com/aio-libs/multidict
Author: Andrew Svetlov
diff --git a/contrib/python/multidict/multidict/__init__.py b/contrib/python/multidict/multidict/__init__.py
index 31b077f58c0..28d72cb79c1 100644
--- a/contrib/python/multidict/multidict/__init__.py
+++ b/contrib/python/multidict/multidict/__init__.py
@@ -22,7 +22,7 @@ __all__ = (
"getversion",
)
-__version__ = "6.4.3"
+__version__ = "6.4.4"
if TYPE_CHECKING or not USE_EXTENSIONS:
diff --git a/contrib/python/multidict/multidict/_multidict.c b/contrib/python/multidict/multidict/_multidict.c
index 04af12cc416..f31a7881000 100644
--- a/contrib/python/multidict/multidict/_multidict.c
+++ b/contrib/python/multidict/multidict/_multidict.c
@@ -573,6 +573,10 @@ multidict_setdefault(MultiDictObject *self, PyObject *const *args,
"key", &key, "default", &_default) < 0) {
return NULL;
}
+ if (_default == NULL) {
+ // fixme, _default is potentially dangerous borrowed ref here
+ _default = Py_None;
+ }
return pair_list_set_default(&self->pairs, key, _default);
}
diff --git a/contrib/python/multidict/multidict/_multidict_py.py b/contrib/python/multidict/multidict/_multidict_py.py
index 3176861e787..4a24ff41bd1 100644
--- a/contrib/python/multidict/multidict/_multidict_py.py
+++ b/contrib/python/multidict/multidict/_multidict_py.py
@@ -1,7 +1,6 @@
import enum
import reprlib
import sys
-from abc import abstractmethod
from array import array
from collections.abc import (
Callable,
@@ -36,7 +35,7 @@ class istr(str):
"""Case insensitive str."""
__is_istr__ = True
- __istr_title__: Optional[str] = None
+ __istr_identity__: Optional[str] = None
_V = TypeVar("_V")
@@ -48,24 +47,6 @@ sentinel = _SENTINEL.sentinel
_version = array("Q", [0])
-class _Impl(Generic[_V]):
- __slots__ = ("_items", "_version")
-
- def __init__(self) -> None:
- self._items: list[tuple[str, str, _V]] = []
- self.incr_version()
-
- def incr_version(self) -> None:
- v = _version
- v[0] += 1
- self._version = v[0]
-
- if sys.implementation.name != "pypy":
-
- def __sizeof__(self) -> int:
- return object.__sizeof__(self) + sys.getsizeof(self._items)
-
-
class _Iter(Generic[_T]):
__slots__ = ("_size", "_iter")
@@ -86,16 +67,12 @@ class _Iter(Generic[_T]):
class _ViewBase(Generic[_V]):
def __init__(
self,
- impl: _Impl[_V],
- identfunc: Callable[[str], str],
- keyfunc: Callable[[str], str],
+ md: "MultiDict[_V]",
):
- self._impl = impl
- self._identfunc = identfunc
- self._keyfunc = keyfunc
+ self._md = md
def __len__(self) -> int:
- return len(self._impl._items)
+ return len(self._md)
class _ItemsView(_ViewBase[_V], ItemsView[str, _V]):
@@ -104,27 +81,27 @@ class _ItemsView(_ViewBase[_V], ItemsView[str, _V]):
return False
key, value = item
try:
- ident = self._identfunc(key)
+ ident = self._md._identity(key)
except TypeError:
return False
- for i, k, v in self._impl._items:
+ for i, k, v in self._md._items:
if ident == i and value == v:
return True
return False
def __iter__(self) -> _Iter[tuple[str, _V]]:
- return _Iter(len(self), self._iter(self._impl._version))
+ return _Iter(len(self), self._iter(self._md._version))
def _iter(self, version: int) -> Iterator[tuple[str, _V]]:
- for i, k, v in self._impl._items:
- if version != self._impl._version:
+ for i, k, v in self._md._items:
+ if version != self._md._version:
raise RuntimeError("Dictionary changed during iteration")
- yield self._keyfunc(k), v
+ yield self._md._key(k), v
@reprlib.recursive_repr()
def __repr__(self) -> str:
lst = []
- for i, k, v in self._impl._items:
+ for i, k, v in self._md._items:
lst.append(f"'{k}': {v!r}")
body = ", ".join(lst)
return f"<{self.__class__.__name__}({body})>"
@@ -137,7 +114,7 @@ class _ItemsView(_ViewBase[_V], ItemsView[str, _V]):
if len(arg) != 2:
return None
try:
- return (self._identfunc(arg[0]), arg[0], arg[1])
+ return (self._md._identity(arg[0]), arg[0], arg[1])
except TypeError:
return None
@@ -162,7 +139,7 @@ class _ItemsView(_ViewBase[_V], ItemsView[str, _V]):
if item is None:
continue
identity, key, value = item
- for i, k, v in self._impl._items:
+ for i, k, v in self._md._items:
if i == identity and v == value:
ret.add((k, v))
return ret
@@ -178,7 +155,7 @@ class _ItemsView(_ViewBase[_V], ItemsView[str, _V]):
if item is None:
continue
identity, key, value = item
- for i, k, v in self._impl._items:
+ for i, k, v in self._md._items:
if i == identity and v == value:
ret.add(arg)
break
@@ -196,7 +173,7 @@ class _ItemsView(_ViewBase[_V], ItemsView[str, _V]):
ret.add(arg)
continue
identity, key, value = item
- for i, k, v in self._impl._items:
+ for i, k, v in self._md._items:
if i == identity and v == value:
break
else:
@@ -210,7 +187,7 @@ class _ItemsView(_ViewBase[_V], ItemsView[str, _V]):
return NotImplemented
tmp = self._tmp_set(ret)
- for i, k, v in self._impl._items:
+ for i, k, v in self._md._items:
if (i, v) not in tmp:
ret.add((k, v))
return ret
@@ -223,7 +200,7 @@ class _ItemsView(_ViewBase[_V], ItemsView[str, _V]):
return NotImplemented
tmp = self._tmp_set(it)
- for i, k, v in self._impl._items:
+ for i, k, v in self._md._items:
if (i, v) not in tmp:
ret.add((k, v))
@@ -242,7 +219,7 @@ class _ItemsView(_ViewBase[_V], ItemsView[str, _V]):
continue
identity, key, value = item
- for i, k, v in self._impl._items:
+ for i, k, v in self._md._items:
if i == identity and v == value:
break
else:
@@ -267,7 +244,7 @@ class _ItemsView(_ViewBase[_V], ItemsView[str, _V]):
continue
identity, key, value = item
- for i, k, v in self._impl._items:
+ for i, k, v in self._md._items:
if i == identity and v == value:
return False
return True
@@ -275,24 +252,24 @@ class _ItemsView(_ViewBase[_V], ItemsView[str, _V]):
class _ValuesView(_ViewBase[_V], ValuesView[_V]):
def __contains__(self, value: object) -> bool:
- for i, k, v in self._impl._items:
+ for i, k, v in self._md._items:
if v == value:
return True
return False
def __iter__(self) -> _Iter[_V]:
- return _Iter(len(self), self._iter(self._impl._version))
+ return _Iter(len(self), self._iter(self._md._version))
def _iter(self, version: int) -> Iterator[_V]:
- for i, k, v in self._impl._items:
- if version != self._impl._version:
+ for i, k, v in self._md._items:
+ if version != self._md._version:
raise RuntimeError("Dictionary changed during iteration")
yield v
@reprlib.recursive_repr()
def __repr__(self) -> str:
lst = []
- for i, k, v in self._impl._items:
+ for i, k, v in self._md._items:
lst.append(repr(v))
body = ", ".join(lst)
return f"<{self.__class__.__name__}({body})>"
@@ -302,24 +279,24 @@ class _KeysView(_ViewBase[_V], KeysView[str]):
def __contains__(self, key: object) -> bool:
if not isinstance(key, str):
return False
- identity = self._identfunc(key)
- for i, k, v in self._impl._items:
+ identity = self._md._identity(key)
+ for i, k, v in self._md._items:
if i == identity:
return True
return False
def __iter__(self) -> _Iter[str]:
- return _Iter(len(self), self._iter(self._impl._version))
+ return _Iter(len(self), self._iter(self._md._version))
def _iter(self, version: int) -> Iterator[str]:
- for i, k, v in self._impl._items:
- if version != self._impl._version:
+ for i, k, v in self._md._items:
+ if version != self._md._version:
raise RuntimeError("Dictionary changed during iteration")
- yield self._keyfunc(k)
+ yield self._md._key(k)
def __repr__(self) -> str:
lst = []
- for i, k, v in self._impl._items:
+ for i, k, v in self._md._items:
lst.append(f"'{k}'")
body = ", ".join(lst)
return f"<{self.__class__.__name__}({body})>"
@@ -333,8 +310,8 @@ class _KeysView(_ViewBase[_V], KeysView[str]):
for key in it:
if not isinstance(key, str):
continue
- identity = self._identfunc(key)
- for i, k, v in self._impl._items:
+ identity = self._md._identity(key)
+ for i, k, v in self._md._items:
if i == identity:
ret.add(k)
return ret
@@ -348,8 +325,8 @@ class _KeysView(_ViewBase[_V], KeysView[str]):
for key in it:
if not isinstance(key, str):
continue
- identity = self._identfunc(key)
- for i, k, v in self._impl._items:
+ identity = self._md._identity(key)
+ for i, k, v in self._md._items:
if i == identity:
ret.add(key)
return cast(set[_T], ret)
@@ -364,8 +341,8 @@ class _KeysView(_ViewBase[_V], KeysView[str]):
if not isinstance(key, str):
ret.add(key)
continue
- identity = self._identfunc(key)
- for i, k, v in self._impl._items:
+ identity = self._md._identity(key)
+ for i, k, v in self._md._items:
if i == identity:
break
else:
@@ -382,10 +359,10 @@ class _KeysView(_ViewBase[_V], KeysView[str]):
for key in ret:
if not isinstance(key, str):
continue
- identity = self._identfunc(key)
+ identity = self._md._identity(key)
tmp.add(identity)
- for i, k, v in self._impl._items:
+ for i, k, v in self._md._items:
if i not in tmp:
ret.add(k)
return ret
@@ -399,8 +376,8 @@ class _KeysView(_ViewBase[_V], KeysView[str]):
for key in it:
if not isinstance(key, str):
continue
- identity = self._identfunc(key)
- for i, k, v in self._impl._items:
+ identity = self._md._identity(key)
+ for i, k, v in self._md._items:
if i == identity:
ret.discard(k)
break
@@ -414,8 +391,8 @@ class _KeysView(_ViewBase[_V], KeysView[str]):
for key in other:
if not isinstance(key, str):
continue
- identity = self._identfunc(key)
- for i, k, v in self._impl._items:
+ identity = self._md._identity(key)
+ for i, k, v in self._md._items:
if i == identity:
ret.discard(key) # type: ignore[arg-type]
break
@@ -436,18 +413,20 @@ class _KeysView(_ViewBase[_V], KeysView[str]):
for key in other:
if not isinstance(key, str):
continue
- identity = self._identfunc(key)
- for i, k, v in self._impl._items:
+ identity = self._md._identity(key)
+ for i, k, v in self._md._items:
if i == identity:
return False
return True
class _CSMixin:
+ _ci: bool = False
+
def _key(self, key: str) -> str:
return key
- def _title(self, key: str) -> str:
+ def _identity(self, key: str) -> str:
if isinstance(key, str):
return key
else:
@@ -463,12 +442,12 @@ class _CIMixin:
else:
return istr(key)
- def _title(self, key: str) -> str:
+ def _identity(self, key: str) -> str:
if isinstance(key, istr):
- ret = key.__istr_title__
+ ret = key.__istr_identity__
if ret is None:
ret = key.title()
- key.__istr_title__ = ret
+ key.__istr_identity__ = ret
return ret
if isinstance(key, str):
return key.title()
@@ -476,15 +455,15 @@ class _CIMixin:
raise TypeError("MultiDict keys should be either str or subclasses of str")
-class _Base(MultiMapping[_V]):
- _impl: _Impl[_V]
- _ci: bool = False
-
- @abstractmethod
- def _key(self, key: str) -> str: ...
+class MultiDict(_CSMixin, MutableMultiMapping[_V]):
+ """Dictionary with the support for duplicate keys."""
- @abstractmethod
- def _title(self, key: str) -> str: ...
+ def __init__(self, arg: MDArg[_V] = None, /, **kwargs: _V):
+ self._items: list[tuple[str, str, _V]] = []
+ v = _version
+ v[0] += 1
+ self._version = v[0]
+ self._extend(arg, kwargs, self.__class__.__name__, self._extend_items)
@overload
def getall(self, key: str) -> list[_V]: ...
@@ -494,8 +473,8 @@ class _Base(MultiMapping[_V]):
self, key: str, default: Union[_T, _SENTINEL] = sentinel
) -> Union[list[_V], _T]:
"""Return a list of all values matching the key."""
- identity = self._title(key)
- res = [v for i, k, v in self._impl._items if i == identity]
+ identity = self._identity(key)
+ res = [v for i, k, v in self._items if i == identity]
if res:
return res
if not res and default is not sentinel:
@@ -513,8 +492,8 @@ class _Base(MultiMapping[_V]):
Raises KeyError if the key is not found and no default is provided.
"""
- identity = self._title(key)
- for i, k, v in self._impl._items:
+ identity = self._identity(key)
+ for i, k, v in self._items:
if i == identity:
return v
if default is not sentinel:
@@ -541,33 +520,35 @@ class _Base(MultiMapping[_V]):
return iter(self.keys())
def __len__(self) -> int:
- return len(self._impl._items)
+ return len(self._items)
def keys(self) -> KeysView[str]:
"""Return a new view of the dictionary's keys."""
- return _KeysView(self._impl, self._title, self._key)
+ return _KeysView(self)
def items(self) -> ItemsView[str, _V]:
"""Return a new view of the dictionary's items *(key, value) pairs)."""
- return _ItemsView(self._impl, self._title, self._key)
+ return _ItemsView(self)
def values(self) -> _ValuesView[_V]:
"""Return a new view of the dictionary's values."""
- return _ValuesView(self._impl, self._title, self._key)
+ return _ValuesView(self)
def __eq__(self, other: object) -> bool:
if not isinstance(other, Mapping):
return NotImplemented
- if isinstance(other, _Base):
- lft = self._impl._items
- rht = other._impl._items
+ if isinstance(other, MultiDictProxy):
+ return self == other._md
+ if isinstance(other, MultiDict):
+ lft = self._items
+ rht = other._items
if len(lft) != len(rht):
return False
for (i1, k2, v1), (i2, k2, v2) in zip(lft, rht):
if i1 != i2 or v1 != v2:
return False
return True
- if len(self._impl._items) != len(other):
+ if len(self._items) != len(other):
return False
for k, v in self.items():
nv = other.get(k, sentinel)
@@ -578,38 +559,29 @@ class _Base(MultiMapping[_V]):
def __contains__(self, key: object) -> bool:
if not isinstance(key, str):
return False
- identity = self._title(key)
- for i, k, v in self._impl._items:
+ identity = self._identity(key)
+ for i, k, v in self._items:
if i == identity:
return True
return False
@reprlib.recursive_repr()
def __repr__(self) -> str:
- body = ", ".join(f"'{k}': {v!r}" for i, k, v in self._impl._items)
+ body = ", ".join(f"'{k}': {v!r}" for i, k, v in self._items)
return f"<{self.__class__.__name__}({body})>"
-
-class MultiDict(_CSMixin, _Base[_V], MutableMultiMapping[_V]):
- """Dictionary with the support for duplicate keys."""
-
- def __init__(self, arg: MDArg[_V] = None, /, **kwargs: _V):
- self._impl = _Impl()
-
- self._extend(arg, kwargs, self.__class__.__name__, self._extend_items)
-
if sys.implementation.name != "pypy":
def __sizeof__(self) -> int:
- return object.__sizeof__(self) + sys.getsizeof(self._impl)
+ return object.__sizeof__(self) + sys.getsizeof(self._items)
def __reduce__(self) -> tuple[type[Self], tuple[list[tuple[str, _V]]]]:
return (self.__class__, (list(self.items()),))
def add(self, key: str, value: _V) -> None:
- identity = self._title(key)
- self._impl._items.append((identity, key, value))
- self._impl.incr_version()
+ identity = self._identity(key)
+ self._items.append((identity, key, value))
+ self._incr_version()
def copy(self) -> Self:
"""Return a copy of itself."""
@@ -633,16 +605,18 @@ class MultiDict(_CSMixin, _Base[_V], MutableMultiMapping[_V]):
method: Callable[[list[tuple[str, str, _V]]], None],
) -> None:
if arg:
- if isinstance(arg, (MultiDict, MultiDictProxy)):
+ if isinstance(arg, MultiDictProxy):
+ arg = arg._md
+ if isinstance(arg, MultiDict):
if self._ci is not arg._ci:
- items = [(self._title(k), k, v) for _, k, v in arg._impl._items]
+ items = [(self._identity(k), k, v) for _, k, v in arg._items]
else:
- items = arg._impl._items
+ items = arg._items
if kwargs:
items = items.copy()
if kwargs:
for key, value in kwargs.items():
- items.append((self._title(key), key, value))
+ items.append((self._identity(key), key, value))
else:
if hasattr(arg, "keys"):
arg = cast(SupportsKeys[_V], arg)
@@ -657,21 +631,21 @@ class MultiDict(_CSMixin, _Base[_V], MutableMultiMapping[_V]):
f"multidict update sequence element #{pos}"
f"has length {len(item)}; 2 is required"
)
- items.append((self._title(item[0]), item[0], item[1]))
+ items.append((self._identity(item[0]), item[0], item[1]))
method(items)
else:
- method([(self._title(key), key, value) for key, value in kwargs.items()])
+ method([(self._identity(key), key, value) for key, value in kwargs.items()])
def _extend_items(self, items: Iterable[tuple[str, str, _V]]) -> None:
for identity, key, value in items:
- self._impl._items.append((identity, key, value))
- self._impl.incr_version()
+ self._items.append((identity, key, value))
+ self._incr_version()
def clear(self) -> None:
"""Remove all items from MultiDict."""
- self._impl._items.clear()
- self._impl.incr_version()
+ self._items.clear()
+ self._incr_version()
# Mapping interface #
@@ -679,8 +653,8 @@ class MultiDict(_CSMixin, _Base[_V], MutableMultiMapping[_V]):
self._replace(key, value)
def __delitem__(self, key: str) -> None:
- identity = self._title(key)
- items = self._impl._items
+ identity = self._identity(key)
+ items = self._items
found = False
for i in range(len(items) - 1, -1, -1):
if items[i][0] == identity:
@@ -689,7 +663,7 @@ class MultiDict(_CSMixin, _Base[_V], MutableMultiMapping[_V]):
if not found:
raise KeyError(key)
else:
- self._impl.incr_version()
+ self._incr_version()
@overload
def setdefault(
@@ -699,8 +673,8 @@ class MultiDict(_CSMixin, _Base[_V], MutableMultiMapping[_V]):
def setdefault(self, key: str, default: _V) -> _V: ...
def setdefault(self, key: str, default: Union[_V, None] = None) -> Union[_V, None]: # type: ignore[misc]
"""Return value for key, set value to default if key is not present."""
- identity = self._title(key)
- for i, k, v in self._impl._items:
+ identity = self._identity(key)
+ for i, k, v in self._items:
if i == identity:
return v
self.add(key, default) # type: ignore[arg-type]
@@ -719,12 +693,12 @@ class MultiDict(_CSMixin, _Base[_V], MutableMultiMapping[_V]):
KeyError is raised.
"""
- identity = self._title(key)
- for i in range(len(self._impl._items)):
- if self._impl._items[i][0] == identity:
- value = self._impl._items[i][2]
- del self._impl._items[i]
- self._impl.incr_version()
+ identity = self._identity(key)
+ for i in range(len(self._items)):
+ if self._items[i][0] == identity:
+ value = self._items[i][2]
+ del self._items[i]
+ self._incr_version()
return value
if default is sentinel:
raise KeyError(key)
@@ -750,14 +724,14 @@ class MultiDict(_CSMixin, _Base[_V], MutableMultiMapping[_V]):
"""
found = False
- identity = self._title(key)
+ identity = self._identity(key)
ret = []
- for i in range(len(self._impl._items) - 1, -1, -1):
- item = self._impl._items[i]
+ for i in range(len(self._items) - 1, -1, -1):
+ item = self._items[i]
if item[0] == identity:
ret.append(item[2])
- del self._impl._items[i]
- self._impl.incr_version()
+ del self._items[i]
+ self._incr_version()
found = True
if not found:
if default is sentinel:
@@ -770,9 +744,9 @@ class MultiDict(_CSMixin, _Base[_V], MutableMultiMapping[_V]):
def popitem(self) -> tuple[str, _V]:
"""Remove and return an arbitrary (key, value) pair."""
- if self._impl._items:
- i, k, v = self._impl._items.pop()
- self._impl.incr_version()
+ if self._items:
+ i, k, v = self._items.pop()
+ self._incr_version()
return self._key(k), v
else:
raise KeyError("empty multidict")
@@ -787,35 +761,35 @@ class MultiDict(_CSMixin, _Base[_V], MutableMultiMapping[_V]):
used_keys: dict[str, int] = {}
for identity, key, value in items:
start = used_keys.get(identity, 0)
- for i in range(start, len(self._impl._items)):
- item = self._impl._items[i]
+ for i in range(start, len(self._items)):
+ item = self._items[i]
if item[0] == identity:
used_keys[identity] = i + 1
- self._impl._items[i] = (identity, key, value)
+ self._items[i] = (identity, key, value)
break
else:
- self._impl._items.append((identity, key, value))
- used_keys[identity] = len(self._impl._items)
+ self._items.append((identity, key, value))
+ used_keys[identity] = len(self._items)
# drop tails
i = 0
- while i < len(self._impl._items):
- item = self._impl._items[i]
+ while i < len(self._items):
+ item = self._items[i]
identity = item[0]
pos = used_keys.get(identity)
if pos is None:
i += 1
continue
if i >= pos:
- del self._impl._items[i]
+ del self._items[i]
else:
i += 1
- self._impl.incr_version()
+ self._incr_version()
def _replace(self, key: str, value: _V) -> None:
- identity = self._title(key)
- items = self._impl._items
+ identity = self._identity(key)
+ items = self._items
for i in range(len(items)):
item = items[i]
@@ -823,11 +797,11 @@ class MultiDict(_CSMixin, _Base[_V], MutableMultiMapping[_V]):
items[i] = (identity, key, value)
# i points to last found item
rgt = i
- self._impl.incr_version()
+ self._incr_version()
break
else:
- self._impl._items.append((identity, key, value))
- self._impl.incr_version()
+ self._items.append((identity, key, value))
+ self._incr_version()
return
# remove all tail items
@@ -840,26 +814,109 @@ class MultiDict(_CSMixin, _Base[_V], MutableMultiMapping[_V]):
else:
i += 1
+ def _incr_version(self) -> None:
+ v = _version
+ v[0] += 1
+ self._version = v[0]
+
class CIMultiDict(_CIMixin, MultiDict[_V]):
"""Dictionary with the support for duplicate case-insensitive keys."""
-class MultiDictProxy(_CSMixin, _Base[_V]):
+class MultiDictProxy(_CSMixin, MultiMapping[_V]):
"""Read-only proxy for MultiDict instance."""
+ _md: MultiDict[_V]
+
def __init__(self, arg: Union[MultiDict[_V], "MultiDictProxy[_V]"]):
if not isinstance(arg, (MultiDict, MultiDictProxy)):
raise TypeError(
"ctor requires MultiDict or MultiDictProxy instance"
f", not {type(arg)}"
)
-
- self._impl = arg._impl
+ if isinstance(arg, MultiDictProxy):
+ self._md = arg._md
+ else:
+ self._md = arg
def __reduce__(self) -> NoReturn:
raise TypeError(f"can't pickle {self.__class__.__name__} objects")
+ @overload
+ def getall(self, key: str) -> list[_V]: ...
+ @overload
+ def getall(self, key: str, default: _T) -> Union[list[_V], _T]: ...
+ def getall(
+ self, key: str, default: Union[_T, _SENTINEL] = sentinel
+ ) -> Union[list[_V], _T]:
+ """Return a list of all values matching the key."""
+ if default is not sentinel:
+ return self._md.getall(key, default)
+ else:
+ return self._md.getall(key)
+
+ @overload
+ def getone(self, key: str) -> _V: ...
+ @overload
+ def getone(self, key: str, default: _T) -> Union[_V, _T]: ...
+ def getone(
+ self, key: str, default: Union[_T, _SENTINEL] = sentinel
+ ) -> Union[_V, _T]:
+ """Get first value matching the key.
+
+ Raises KeyError if the key is not found and no default is provided.
+ """
+ if default is not sentinel:
+ return self._md.getone(key, default)
+ else:
+ return self._md.getone(key)
+
+ # Mapping interface #
+
+ def __getitem__(self, key: str) -> _V:
+ return self.getone(key)
+
+ @overload
+ def get(self, key: str, /) -> Union[_V, None]: ...
+ @overload
+ def get(self, key: str, /, default: _T) -> Union[_V, _T]: ...
+ def get(self, key: str, default: Union[_T, None] = None) -> Union[_V, _T, None]:
+ """Get first value matching the key.
+
+ If the key is not found, returns the default (or None if no default is provided)
+ """
+ return self._md.getone(key, default)
+
+ def __iter__(self) -> Iterator[str]:
+ return iter(self._md.keys())
+
+ def __len__(self) -> int:
+ return len(self._md)
+
+ def keys(self) -> KeysView[str]:
+ """Return a new view of the dictionary's keys."""
+ return self._md.keys()
+
+ def items(self) -> ItemsView[str, _V]:
+ """Return a new view of the dictionary's items *(key, value) pairs)."""
+ return self._md.items()
+
+ def values(self) -> _ValuesView[_V]:
+ """Return a new view of the dictionary's values."""
+ return self._md.values()
+
+ def __eq__(self, other: object) -> bool:
+ return self._md == other
+
+ def __contains__(self, key: object) -> bool:
+ return key in self._md
+
+ @reprlib.recursive_repr()
+ def __repr__(self) -> str:
+ body = ", ".join(f"'{k}': {v!r}" for k, v in self.items())
+ return f"<{self.__class__.__name__}({body})>"
+
def copy(self) -> MultiDict[_V]:
"""Return a copy of itself."""
return MultiDict(self.items())
@@ -875,7 +932,7 @@ class CIMultiDictProxy(_CIMixin, MultiDictProxy[_V]):
f", not {type(arg)}"
)
- self._impl = arg._impl
+ super().__init__(arg)
def copy(self) -> CIMultiDict[_V]:
"""Return a copy of itself."""
@@ -883,6 +940,8 @@ class CIMultiDictProxy(_CIMixin, MultiDictProxy[_V]):
def getversion(md: Union[MultiDict[object], MultiDictProxy[object]]) -> int:
- if not isinstance(md, _Base):
+ if isinstance(md, MultiDictProxy):
+ md = md._md
+ elif not isinstance(md, MultiDict):
raise TypeError("Parameter should be multidict or proxy")
- return md._impl._version
+ return md._version
diff --git a/contrib/python/multidict/multidict/_multilib/views.h b/contrib/python/multidict/multidict/_multilib/views.h
index 3e195f4d5b2..e734ce57849 100644
--- a/contrib/python/multidict/multidict/_multilib/views.h
+++ b/contrib/python/multidict/multidict/_multilib/views.h
@@ -1006,7 +1006,16 @@ static PyMethodDef multidict_itemsview_methods[] = {
{NULL, NULL} /* sentinel */
};
+static inline PyObject *
+multidict_view_forbidden_new(PyTypeObject *type, PyObject *args, PyObject *kwargs)
+{
+ PyErr_Format(PyExc_TypeError,
+ "cannot create '%s' instances directly", type->tp_name);
+ return NULL;
+}
+
static PyType_Slot multidict_itemsview_slots[] = {
+ {Py_tp_new, multidict_view_forbidden_new},
{Py_tp_dealloc, multidict_view_dealloc},
{Py_tp_repr, multidict_itemsview_repr},
@@ -1561,6 +1570,7 @@ static PyMethodDef multidict_keysview_methods[] = {
};
static PyType_Slot multidict_keysview_slots[] = {
+ {Py_tp_new, multidict_view_forbidden_new},
{Py_tp_dealloc, multidict_view_dealloc},
{Py_tp_repr, multidict_keysview_repr},
@@ -1635,6 +1645,7 @@ multidict_valuesview_repr(_Multidict_ViewObject *self)
}
static PyType_Slot multidict_valuesview_slots[] = {
+ {Py_tp_new, multidict_view_forbidden_new},
{Py_tp_dealloc, multidict_view_dealloc},
{Py_tp_repr, multidict_valuesview_repr},
diff --git a/contrib/python/multidict/tests/test_multidict.py b/contrib/python/multidict/tests/test_multidict.py
index aa59d6adef4..d1539fb45d2 100644
--- a/contrib/python/multidict/tests/test_multidict.py
+++ b/contrib/python/multidict/tests/test_multidict.py
@@ -1313,6 +1313,7 @@ def test_subclassed_multidict(
any_multidict_class: type[MultiDict[str]],
) -> None:
"""Test that subclassed MultiDicts work as expected."""
+
class SubclassedMultiDict(any_multidict_class): # type: ignore[valid-type, misc]
"""Subclassed MultiDict."""
@@ -1323,3 +1324,22 @@ def test_subclassed_multidict(
assert d1 == d3
assert d1 == SubclassedMultiDict([("key", "value1")])
assert d1 != SubclassedMultiDict([("key", "value2")])
+
+
+@pytest.mark.c_extension
+def test_view_direct_instantiation_segfault() -> None:
+ """Test that view objects cannot be instantiated directly (issue: segfault).
+
+ This test only applies to the C extension implementation.
+ """
+ # Test that _ItemsView cannot be instantiated directly
+ with pytest.raises(TypeError, match="cannot create '.*_ItemsView' instances directly"):
+ multidict._ItemsView() # type: ignore[attr-defined]
+
+ # Test that _KeysView cannot be instantiated directly
+ with pytest.raises(TypeError, match="cannot create '.*_KeysView' instances directly"):
+ multidict._KeysView() # type: ignore[attr-defined]
+
+ # Test that _ValuesView cannot be instantiated directly
+ with pytest.raises(TypeError, match="cannot create '.*_ValuesView' instances directly"):
+ multidict._ValuesView() # type: ignore[attr-defined]
diff --git a/contrib/python/multidict/tests/test_multidict_benchmarks.py b/contrib/python/multidict/tests/test_multidict_benchmarks.py
index a7a6a76e728..b5cc193c6af 100644
--- a/contrib/python/multidict/tests/test_multidict_benchmarks.py
+++ b/contrib/python/multidict/tests/test_multidict_benchmarks.py
@@ -75,8 +75,8 @@ def test_cimultidict_add_istr(
def test_multidict_pop_str(
benchmark: BenchmarkFixture, any_multidict_class: Type[MultiDict[str]]
) -> None:
- md_base = any_multidict_class((str(i), str(i)) for i in range(100))
- items = [str(i) for i in range(100)]
+ md_base = any_multidict_class((str(i), str(i)) for i in range(200))
+ items = [str(i) for i in range(50, 150)]
@benchmark
def _run() -> None:
@@ -89,8 +89,8 @@ def test_cimultidict_pop_istr(
benchmark: BenchmarkFixture,
case_insensitive_multidict_class: Type[CIMultiDict[istr]],
) -> None:
- md_base = case_insensitive_multidict_class((istr(i), istr(i)) for i in range(100))
- items = [istr(i) for i in range(100)]
+ md_base = case_insensitive_multidict_class((istr(i), istr(i)) for i in range(200))
+ items = [istr(i) for i in range(50, 150)]
@benchmark
def _run() -> None:
@@ -124,7 +124,7 @@ def test_multidict_clear_str(
def test_multidict_update_str(
benchmark: BenchmarkFixture, any_multidict_class: Type[MultiDict[str]]
) -> None:
- md = any_multidict_class((str(i), str(i)) for i in range(100))
+ md = any_multidict_class((str(i), str(i)) for i in range(150))
items = {str(i): str(i) for i in range(100, 200)}
@benchmark
@@ -136,7 +136,7 @@ def test_cimultidict_update_istr(
benchmark: BenchmarkFixture,
case_insensitive_multidict_class: Type[CIMultiDict[istr]],
) -> None:
- md = case_insensitive_multidict_class((istr(i), istr(i)) for i in range(100))
+ md = case_insensitive_multidict_class((istr(i), istr(i)) for i in range(150))
items: Dict[Union[str, istr], istr] = {istr(i): istr(i) for i in range(100, 200)}
@benchmark
@@ -147,7 +147,7 @@ def test_cimultidict_update_istr(
def test_multidict_update_str_with_kwargs(
benchmark: BenchmarkFixture, any_multidict_class: Type[MultiDict[str]]
) -> None:
- md = any_multidict_class((str(i), str(i)) for i in range(100))
+ md = any_multidict_class((str(i), str(i)) for i in range(150))
items = {str(i): str(i) for i in range(100, 200)}
kwargs = {str(i): str(i) for i in range(200, 300)}
@@ -160,7 +160,7 @@ def test_cimultidict_update_istr_with_kwargs(
benchmark: BenchmarkFixture,
case_insensitive_multidict_class: Type[CIMultiDict[istr]],
) -> None:
- md = case_insensitive_multidict_class((istr(i), istr(i)) for i in range(100))
+ md = case_insensitive_multidict_class((istr(i), istr(i)) for i in range(150))
items: Dict[Union[str, istr], istr] = {istr(i): istr(i) for i in range(100, 200)}
kwargs = {str(i): istr(i) for i in range(200, 300)}
@@ -255,19 +255,21 @@ def test_cimultidict_delitem_istr(
def test_multidict_getall_str_hit(
benchmark: BenchmarkFixture, any_multidict_class: Type[MultiDict[str]]
) -> None:
- md = any_multidict_class((f"key{j}", str(f"{i}-{j}"))
- for i in range(20) for j in range(5))
+ md = any_multidict_class(
+ (f"key{j}", str(f"{i}-{j}")) for i in range(20) for j in range(5)
+ )
@benchmark
def _run() -> None:
- md.getall("key0")
+ md.getall("key3")
def test_multidict_getall_str_miss(
benchmark: BenchmarkFixture, any_multidict_class: Type[MultiDict[str]]
) -> None:
- md = any_multidict_class((f"key{j}", str(f"{i}-{j}"))
- for i in range(20) for j in range(5))
+ md = any_multidict_class(
+ (f"key{j}", str(f"{i}-{j}")) for i in range(20) for j in range(5)
+ )
@benchmark
def _run() -> None:
@@ -278,9 +280,10 @@ def test_cimultidict_getall_istr_hit(
benchmark: BenchmarkFixture,
case_insensitive_multidict_class: Type[CIMultiDict[istr]],
) -> None:
- all_istr = istr("key0")
- md = case_insensitive_multidict_class((f"key{j}", istr(f"{i}-{j}"))
- for i in range(20) for j in range(5))
+ all_istr = istr("key3")
+ md = case_insensitive_multidict_class(
+ (f"key{j}", istr(f"{i}-{j}")) for i in range(20) for j in range(5)
+ )
@benchmark
def _run() -> None:
@@ -292,8 +295,9 @@ def test_cimultidict_getall_istr_miss(
case_insensitive_multidict_class: Type[CIMultiDict[istr]],
) -> None:
miss_istr = istr("miss")
- md = case_insensitive_multidict_class((istr(f"key{j}"), istr(f"{i}-{j}"))
- for i in range(20) for j in range(5))
+ md = case_insensitive_multidict_class(
+ (istr(f"key{j}"), istr(f"{i}-{j}")) for i in range(20) for j in range(5)
+ )
@benchmark
def _run() -> None:
diff --git a/contrib/python/multidict/tests/test_mutable_multidict.py b/contrib/python/multidict/tests/test_mutable_multidict.py
index 085999fb21c..74bbd697fee 100644
--- a/contrib/python/multidict/tests/test_mutable_multidict.py
+++ b/contrib/python/multidict/tests/test_mutable_multidict.py
@@ -4,13 +4,19 @@ from typing import Union
import pytest
-from multidict import CIMultiDict, CIMultiDictProxy, MultiDictProxy, istr
+from multidict import (
+ CIMultiDict,
+ CIMultiDictProxy,
+ MultiDict,
+ MultiDictProxy,
+ istr,
+)
class TestMutableMultiDict:
def test_copy(
self,
- case_sensitive_multidict_class: type[CIMultiDict[str]],
+ case_sensitive_multidict_class: type[MultiDict[str]],
) -> None:
d1 = case_sensitive_multidict_class(key="value", a="b")
@@ -20,7 +26,7 @@ class TestMutableMultiDict:
def test__repr__(
self,
- case_sensitive_multidict_class: type[CIMultiDict[str]],
+ case_sensitive_multidict_class: type[MultiDict[str]],
) -> None:
d = case_sensitive_multidict_class()
assert str(d) == "<%s()>" % case_sensitive_multidict_class.__name__
@@ -35,7 +41,7 @@ class TestMutableMultiDict:
def test_getall(
self,
- case_sensitive_multidict_class: type[CIMultiDict[str]],
+ case_sensitive_multidict_class: type[MultiDict[str]],
) -> None:
d = case_sensitive_multidict_class([("key", "value1")], key="value2")
assert len(d) == 2
@@ -50,7 +56,7 @@ class TestMutableMultiDict:
def test_add(
self,
- case_sensitive_multidict_class: type[CIMultiDict[str]],
+ case_sensitive_multidict_class: type[MultiDict[str]],
) -> None:
d = case_sensitive_multidict_class()
@@ -73,7 +79,7 @@ class TestMutableMultiDict:
def test_extend(
self,
- case_sensitive_multidict_class: type[CIMultiDict[Union[str, int]]],
+ case_sensitive_multidict_class: type[MultiDict[Union[str, int]]],
) -> None:
d = case_sensitive_multidict_class()
assert d == {}
@@ -105,7 +111,7 @@ class TestMutableMultiDict:
def test_extend_from_proxy(
self,
- case_sensitive_multidict_class: type[CIMultiDict[str]],
+ case_sensitive_multidict_class: type[MultiDict[str]],
case_sensitive_multidict_proxy_class: type[MultiDictProxy[str]],
) -> None:
d = case_sensitive_multidict_class([("a", "a"), ("b", "b")])
@@ -118,7 +124,7 @@ class TestMutableMultiDict:
def test_clear(
self,
- case_sensitive_multidict_class: type[CIMultiDict[str]],
+ case_sensitive_multidict_class: type[MultiDict[str]],
) -> None:
d = case_sensitive_multidict_class([("key", "one")], key="two", foo="bar")
@@ -128,7 +134,7 @@ class TestMutableMultiDict:
def test_del(
self,
- case_sensitive_multidict_class: type[CIMultiDict[str]],
+ case_sensitive_multidict_class: type[MultiDict[str]],
) -> None:
d = case_sensitive_multidict_class([("key", "one"), ("key", "two")], foo="bar")
assert list(d.keys()) == ["key", "key", "foo"]
@@ -142,7 +148,7 @@ class TestMutableMultiDict:
def test_set_default(
self,
- case_sensitive_multidict_class: type[CIMultiDict[str]],
+ case_sensitive_multidict_class: type[MultiDict[str]],
) -> None:
d = case_sensitive_multidict_class([("key", "one"), ("key", "two")], foo="bar")
assert "one" == d.setdefault("key", "three")
@@ -150,9 +156,18 @@ class TestMutableMultiDict:
assert "otherkey" in d
assert "three" == d["otherkey"]
+ def test_set_default_single_arg(
+ self,
+ case_sensitive_multidict_class: type[MultiDict[str]],
+ ) -> None:
+ d = case_sensitive_multidict_class([("key", "one"), ("key", "two")], foo="bar")
+ assert d.setdefault("key") == "one" # type: ignore[call-arg]
+ assert d.setdefault("noexist") is None # type: ignore[call-arg]
+ assert d["noexist"] is None
+
def test_popitem(
self,
- case_sensitive_multidict_class: type[CIMultiDict[str]],
+ case_sensitive_multidict_class: type[MultiDict[str]],
) -> None:
d = case_sensitive_multidict_class()
d.add("key", "val1")
@@ -163,7 +178,7 @@ class TestMutableMultiDict:
def test_popitem_empty_multidict(
self,
- case_sensitive_multidict_class: type[CIMultiDict[str]],
+ case_sensitive_multidict_class: type[MultiDict[str]],
) -> None:
d = case_sensitive_multidict_class()
@@ -172,7 +187,7 @@ class TestMutableMultiDict:
def test_pop(
self,
- case_sensitive_multidict_class: type[CIMultiDict[str]],
+ case_sensitive_multidict_class: type[MultiDict[str]],
) -> None:
d = case_sensitive_multidict_class()
d.add("key", "val1")
@@ -183,7 +198,7 @@ class TestMutableMultiDict:
def test_pop2(
self,
- case_sensitive_multidict_class: type[CIMultiDict[str]],
+ case_sensitive_multidict_class: type[MultiDict[str]],
) -> None:
d = case_sensitive_multidict_class()
d.add("key", "val1")
@@ -195,7 +210,7 @@ class TestMutableMultiDict:
def test_pop_default(
self,
- case_sensitive_multidict_class: type[CIMultiDict[str]],
+ case_sensitive_multidict_class: type[MultiDict[str]],
) -> None:
d = case_sensitive_multidict_class(other="val")
@@ -204,7 +219,7 @@ class TestMutableMultiDict:
def test_pop_raises(
self,
- case_sensitive_multidict_class: type[CIMultiDict[str]],
+ case_sensitive_multidict_class: type[MultiDict[str]],
) -> None:
d = case_sensitive_multidict_class(other="val")
@@ -215,7 +230,7 @@ class TestMutableMultiDict:
def test_replacement_order(
self,
- case_sensitive_multidict_class: type[CIMultiDict[str]],
+ case_sensitive_multidict_class: type[MultiDict[str]],
) -> None:
d = case_sensitive_multidict_class()
d.add("key1", "val1")
@@ -231,7 +246,7 @@ class TestMutableMultiDict:
def test_nonstr_key(
self,
- case_sensitive_multidict_class: type[CIMultiDict[str]],
+ case_sensitive_multidict_class: type[MultiDict[str]],
) -> None:
d = case_sensitive_multidict_class()
with pytest.raises(TypeError):
@@ -239,7 +254,7 @@ class TestMutableMultiDict:
def test_istr_key(
self,
- case_sensitive_multidict_class: type[CIMultiDict[str]],
+ case_sensitive_multidict_class: type[MultiDict[str]],
case_insensitive_str_class: type[str],
) -> None:
d = case_sensitive_multidict_class()
@@ -248,7 +263,7 @@ class TestMutableMultiDict:
def test_str_derived_key(
self,
- case_sensitive_multidict_class: type[CIMultiDict[str]],
+ case_sensitive_multidict_class: type[MultiDict[str]],
) -> None:
class A(str):
pass
@@ -259,7 +274,7 @@ class TestMutableMultiDict:
def test_istr_key_add(
self,
- case_sensitive_multidict_class: type[CIMultiDict[str]],
+ case_sensitive_multidict_class: type[MultiDict[str]],
case_insensitive_str_class: type[str],
) -> None:
d = case_sensitive_multidict_class()
@@ -268,7 +283,7 @@ class TestMutableMultiDict:
def test_str_derived_key_add(
self,
- case_sensitive_multidict_class: type[CIMultiDict[str]],
+ case_sensitive_multidict_class: type[MultiDict[str]],
) -> None:
class A(str):
pass
@@ -279,7 +294,7 @@ class TestMutableMultiDict:
def test_popall(
self,
- case_sensitive_multidict_class: type[CIMultiDict[str]],
+ case_sensitive_multidict_class: type[MultiDict[str]],
) -> None:
d = case_sensitive_multidict_class()
d.add("key1", "val1")
@@ -291,14 +306,14 @@ class TestMutableMultiDict:
def test_popall_default(
self,
- case_sensitive_multidict_class: type[CIMultiDict[str]],
+ case_sensitive_multidict_class: type[MultiDict[str]],
) -> None:
d = case_sensitive_multidict_class()
assert "val" == d.popall("key", "val")
def test_popall_key_error(
self,
- case_sensitive_multidict_class: type[CIMultiDict[str]],
+ case_sensitive_multidict_class: type[MultiDict[str]],
) -> None:
d = case_sensitive_multidict_class()
with pytest.raises(KeyError, match="key"):
@@ -306,7 +321,7 @@ class TestMutableMultiDict:
def test_large_multidict_resizing(
self,
- case_sensitive_multidict_class: type[CIMultiDict[int]],
+ case_sensitive_multidict_class: type[MultiDict[int]],
) -> None:
SIZE = 1024
d = case_sensitive_multidict_class()
@@ -320,7 +335,7 @@ class TestMutableMultiDict:
def test_update(
self,
- case_sensitive_multidict_class: type[CIMultiDict[Union[str, int]]],
+ case_sensitive_multidict_class: type[MultiDict[Union[str, int]]],
) -> None:
d = case_sensitive_multidict_class()
assert d == {}
diff --git a/contrib/python/multidict/tests/test_version.py b/contrib/python/multidict/tests/test_version.py
index 4fe209c6786..a430a2e862a 100644
--- a/contrib/python/multidict/tests/test_version.py
+++ b/contrib/python/multidict/tests/test_version.py
@@ -30,200 +30,289 @@ def test_ctor(
def test_add(
any_multidict_class: type[MultiDict[str]],
+ any_multidict_proxy_class: type[MultiDictProxy[str]],
multidict_getversion_callable: GetVersion[str],
) -> None:
m = any_multidict_class()
+ p = any_multidict_proxy_class(m)
v = multidict_getversion_callable(m)
+ assert v == multidict_getversion_callable(p)
m.add("key", "val")
- assert multidict_getversion_callable(m) > v
+ v2 = multidict_getversion_callable(m)
+ assert v2 > v
+ assert v2 == multidict_getversion_callable(p)
def test_delitem(
any_multidict_class: type[MultiDict[str]],
+ any_multidict_proxy_class: type[MultiDictProxy[str]],
multidict_getversion_callable: GetVersion[str],
) -> None:
m = any_multidict_class()
+ p = any_multidict_proxy_class(m)
m.add("key", "val")
v = multidict_getversion_callable(m)
+ assert v == multidict_getversion_callable(p)
del m["key"]
- assert multidict_getversion_callable(m) > v
+ v2 = multidict_getversion_callable(m)
+ assert v2 > v
+ assert v2 == multidict_getversion_callable(p)
def test_delitem_not_found(
any_multidict_class: type[MultiDict[str]],
+ any_multidict_proxy_class: type[MultiDictProxy[str]],
multidict_getversion_callable: GetVersion[str],
) -> None:
m = any_multidict_class()
+ p = any_multidict_proxy_class(m)
m.add("key", "val")
v = multidict_getversion_callable(m)
+ assert v == multidict_getversion_callable(p)
with pytest.raises(KeyError):
del m["notfound"]
assert multidict_getversion_callable(m) == v
+ assert v == multidict_getversion_callable(p)
def test_setitem(
any_multidict_class: type[MultiDict[str]],
+ any_multidict_proxy_class: type[MultiDictProxy[str]],
multidict_getversion_callable: GetVersion[str],
) -> None:
m = any_multidict_class()
+ p = any_multidict_proxy_class(m)
m.add("key", "val")
v = multidict_getversion_callable(m)
+ assert v == multidict_getversion_callable(p)
m["key"] = "val2"
- assert multidict_getversion_callable(m) > v
+ v2 = multidict_getversion_callable(m)
+ assert v2 > v
+ assert v2 == multidict_getversion_callable(p)
def test_setitem_not_found(
any_multidict_class: type[MultiDict[str]],
+ any_multidict_proxy_class: type[MultiDictProxy[str]],
multidict_getversion_callable: GetVersion[str],
) -> None:
m = any_multidict_class()
+ p = any_multidict_proxy_class(m)
m.add("key", "val")
v = multidict_getversion_callable(m)
+ assert v == multidict_getversion_callable(p)
m["notfound"] = "val2"
- assert multidict_getversion_callable(m) > v
+ v2 = multidict_getversion_callable(m)
+ assert v2 > v
+ assert v2 == multidict_getversion_callable(p)
def test_clear(
any_multidict_class: type[MultiDict[str]],
+ any_multidict_proxy_class: type[MultiDictProxy[str]],
multidict_getversion_callable: GetVersion[str],
) -> None:
m = any_multidict_class()
+ p = any_multidict_proxy_class(m)
m.add("key", "val")
v = multidict_getversion_callable(m)
+ assert v == multidict_getversion_callable(p)
m.clear()
- assert multidict_getversion_callable(m) > v
+ v2 = multidict_getversion_callable(m)
+ assert v2 > v
+ assert v2 == multidict_getversion_callable(p)
def test_setdefault(
any_multidict_class: type[MultiDict[str]],
+ any_multidict_proxy_class: type[MultiDictProxy[str]],
multidict_getversion_callable: GetVersion[str],
) -> None:
m = any_multidict_class()
+ p = any_multidict_proxy_class(m)
m.add("key", "val")
v = multidict_getversion_callable(m)
+ assert v == multidict_getversion_callable(p)
m.setdefault("key2", "val2")
- assert multidict_getversion_callable(m) > v
+ v2 = multidict_getversion_callable(m)
+ assert v2 > v
+ assert v2 == multidict_getversion_callable(p)
def test_popone(
any_multidict_class: type[MultiDict[str]],
+ any_multidict_proxy_class: type[MultiDictProxy[str]],
multidict_getversion_callable: GetVersion[str],
) -> None:
m = any_multidict_class()
+ p = any_multidict_proxy_class(m)
m.add("key", "val")
v = multidict_getversion_callable(m)
+ assert v == multidict_getversion_callable(p)
m.popone("key")
- assert multidict_getversion_callable(m) > v
+ v2 = multidict_getversion_callable(m)
+ assert v2 > v
+ assert v2 == multidict_getversion_callable(p)
def test_popone_default(
any_multidict_class: type[MultiDict[str]],
+ any_multidict_proxy_class: type[MultiDictProxy[str]],
multidict_getversion_callable: GetVersion[str],
) -> None:
m = any_multidict_class()
+ p = any_multidict_proxy_class(m)
m.add("key", "val")
v = multidict_getversion_callable(m)
+ assert v == multidict_getversion_callable(p)
m.popone("key2", "default")
- assert multidict_getversion_callable(m) == v
+ v2 = multidict_getversion_callable(m)
+ assert v2 == v
+ assert v2 == multidict_getversion_callable(p)
def test_popone_key_error(
any_multidict_class: type[MultiDict[str]],
+ any_multidict_proxy_class: type[MultiDictProxy[str]],
multidict_getversion_callable: GetVersion[str],
) -> None:
m = any_multidict_class()
+ p = any_multidict_proxy_class(m)
m.add("key", "val")
v = multidict_getversion_callable(m)
+ assert v == multidict_getversion_callable(p)
with pytest.raises(KeyError):
m.popone("key2")
- assert multidict_getversion_callable(m) == v
+ v2 = multidict_getversion_callable(m)
+ assert v2 == v
+ assert v2 == multidict_getversion_callable(p)
def test_pop(
any_multidict_class: type[MultiDict[str]],
+ any_multidict_proxy_class: type[MultiDictProxy[str]],
multidict_getversion_callable: GetVersion[str],
) -> None:
m = any_multidict_class()
+ p = any_multidict_proxy_class(m)
m.add("key", "val")
v = multidict_getversion_callable(m)
+ assert v == multidict_getversion_callable(p)
m.pop("key")
- assert multidict_getversion_callable(m) > v
+ v2 = multidict_getversion_callable(m)
+ assert v2 > v
+ assert v2 == multidict_getversion_callable(p)
def test_pop_default(
any_multidict_class: type[MultiDict[str]],
+ any_multidict_proxy_class: type[MultiDictProxy[str]],
multidict_getversion_callable: GetVersion[str],
) -> None:
m = any_multidict_class()
+ p = any_multidict_proxy_class(m)
m.add("key", "val")
v = multidict_getversion_callable(m)
+ assert v == multidict_getversion_callable(p)
m.pop("key2", "default")
- assert multidict_getversion_callable(m) == v
+ v2 = multidict_getversion_callable(m)
+ assert v2 == v
+ assert v2 == multidict_getversion_callable(p)
def test_pop_key_error(
any_multidict_class: type[MultiDict[str]],
+ any_multidict_proxy_class: type[MultiDictProxy[str]],
multidict_getversion_callable: GetVersion[str],
) -> None:
m = any_multidict_class()
+ p = any_multidict_proxy_class(m)
m.add("key", "val")
v = multidict_getversion_callable(m)
+ assert v == multidict_getversion_callable(p)
with pytest.raises(KeyError):
m.pop("key2")
- assert multidict_getversion_callable(m) == v
+ v2 = multidict_getversion_callable(m)
+ assert v2 == v
+ assert v2 == multidict_getversion_callable(p)
def test_popall(
any_multidict_class: type[MultiDict[str]],
+ any_multidict_proxy_class: type[MultiDictProxy[str]],
multidict_getversion_callable: GetVersion[str],
) -> None:
m = any_multidict_class()
+ p = any_multidict_proxy_class(m)
m.add("key", "val")
v = multidict_getversion_callable(m)
+ assert v == multidict_getversion_callable(p)
m.popall("key")
- assert multidict_getversion_callable(m) > v
+ v2 = multidict_getversion_callable(m)
+ assert v2 > v
+ assert v2 == multidict_getversion_callable(p)
def test_popall_default(
any_multidict_class: type[MultiDict[str]],
+ any_multidict_proxy_class: type[MultiDictProxy[str]],
multidict_getversion_callable: GetVersion[str],
) -> None:
m = any_multidict_class()
+ p = any_multidict_proxy_class(m)
m.add("key", "val")
v = multidict_getversion_callable(m)
+ assert v == multidict_getversion_callable(p)
m.popall("key2", "default")
- assert multidict_getversion_callable(m) == v
+ v2 = multidict_getversion_callable(m)
+ assert v2 == v
+ assert v2 == multidict_getversion_callable(p)
def test_popall_key_error(
any_multidict_class: type[MultiDict[str]],
+ any_multidict_proxy_class: type[MultiDictProxy[str]],
multidict_getversion_callable: GetVersion[str],
) -> None:
m = any_multidict_class()
+ p = any_multidict_proxy_class(m)
m.add("key", "val")
v = multidict_getversion_callable(m)
+ assert v == multidict_getversion_callable(p)
with pytest.raises(KeyError):
m.popall("key2")
- assert multidict_getversion_callable(m) == v
+ v2 = multidict_getversion_callable(m)
+ assert v2 == v
+ assert v2 == multidict_getversion_callable(p)
def test_popitem(
any_multidict_class: type[MultiDict[str]],
+ any_multidict_proxy_class: type[MultiDictProxy[str]],
multidict_getversion_callable: GetVersion[str],
) -> None:
m = any_multidict_class()
+ p = any_multidict_proxy_class(m)
m.add("key", "val")
v = multidict_getversion_callable(m)
+ assert v == multidict_getversion_callable(p)
m.popitem()
- assert multidict_getversion_callable(m) > v
+ v2 = multidict_getversion_callable(m)
+ assert v2 > v
+ assert v2 == multidict_getversion_callable(p)
def test_popitem_key_error(
any_multidict_class: type[MultiDict[str]],
+ any_multidict_proxy_class: type[MultiDictProxy[str]],
multidict_getversion_callable: GetVersion[str],
) -> None:
m = any_multidict_class()
+ p = any_multidict_proxy_class(m)
v = multidict_getversion_callable(m)
+ assert v == multidict_getversion_callable(p)
with pytest.raises(KeyError):
m.popitem()
- assert multidict_getversion_callable(m) == v
+ v2 = multidict_getversion_callable(m)
+ assert v2 == v
+ assert v2 == multidict_getversion_callable(p)
diff --git a/contrib/python/multidict/ya.make b/contrib/python/multidict/ya.make
index ad0d3d0777c..81c5990eb8a 100644
--- a/contrib/python/multidict/ya.make
+++ b/contrib/python/multidict/ya.make
@@ -2,7 +2,7 @@
PY3_LIBRARY()
-VERSION(6.4.3)
+VERSION(6.4.4)
LICENSE(Apache-2.0)