test_multidict.py 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825
  1. from __future__ import annotations
  2. import gc
  3. import operator
  4. import sys
  5. import weakref
  6. from collections import deque
  7. from collections.abc import Mapping
  8. from types import ModuleType
  9. from typing import (
  10. Callable,
  11. Dict,
  12. Iterable,
  13. Iterator,
  14. KeysView,
  15. List,
  16. Mapping,
  17. Set,
  18. Tuple,
  19. Type,
  20. Union,
  21. cast,
  22. )
  23. import pytest
  24. import multidict
  25. from multidict import CIMultiDict, MultiDict, MultiMapping, MutableMultiMapping
  26. def chained_callable(
  27. module: ModuleType,
  28. callables: Iterable[str],
  29. ) -> Callable[..., MultiMapping[int | str] | MutableMultiMapping[int | str]]:
  30. """
  31. Return callable that will get and call all given objects in module in
  32. exact order.
  33. """
  34. def chained_call(
  35. *args: object,
  36. **kwargs: object,
  37. ) -> MultiMapping[int | str] | MutableMultiMapping[int | str]:
  38. nonlocal callables
  39. callable_chain = (getattr(module, name) for name in callables)
  40. first_callable = next(callable_chain)
  41. value = first_callable(*args, **kwargs)
  42. for element in callable_chain:
  43. value = element(value)
  44. return cast(
  45. Union[
  46. MultiMapping[Union[int, str]],
  47. MutableMultiMapping[Union[int, str]],
  48. ],
  49. value,
  50. )
  51. return chained_call
  52. @pytest.fixture
  53. def cls( # type: ignore[misc]
  54. request: pytest.FixtureRequest,
  55. multidict_module: ModuleType,
  56. ) -> Callable[..., MultiMapping[int | str] | MutableMultiMapping[int | str]]:
  57. """Make a callable from multidict module, requested by name."""
  58. return chained_callable(multidict_module, request.param)
  59. def test_exposed_names(any_multidict_class_name: str) -> None:
  60. assert any_multidict_class_name in multidict.__all__ # type: ignore[attr-defined]
  61. @pytest.mark.parametrize(
  62. ("cls", "key_cls"),
  63. (
  64. (("MultiDict",), str),
  65. (
  66. ("MultiDict", "MultiDictProxy"),
  67. str,
  68. ),
  69. ),
  70. indirect=["cls"],
  71. )
  72. def test__iter__types(
  73. cls: Type[MultiDict[Union[str, int]]],
  74. key_cls: Type[object],
  75. ) -> None:
  76. d = cls([("key", "one"), ("key2", "two"), ("key", 3)])
  77. for i in d:
  78. assert type(i) is key_cls, (type(i), key_cls)
  79. def test_proxy_copy(
  80. any_multidict_class: Type[MutableMultiMapping[str]],
  81. any_multidict_proxy_class: Type[MultiMapping[str]],
  82. ) -> None:
  83. d1 = any_multidict_class(key="value", a="b")
  84. p1 = any_multidict_proxy_class(d1)
  85. d2 = p1.copy() # type: ignore[attr-defined]
  86. assert d1 == d2
  87. assert d1 is not d2
  88. def test_multidict_subclassing(
  89. any_multidict_class: Type[MutableMultiMapping[str]],
  90. ) -> None:
  91. class DummyMultidict(any_multidict_class): # type: ignore[valid-type,misc]
  92. pass
  93. def test_multidict_proxy_subclassing(
  94. any_multidict_proxy_class: Type[MultiMapping[str]],
  95. ) -> None:
  96. class DummyMultidictProxy(
  97. any_multidict_proxy_class, # type: ignore[valid-type,misc]
  98. ):
  99. pass
  100. class BaseMultiDictTest:
  101. def test_instantiate__empty(self, cls: Type[MutableMultiMapping[str]]) -> None:
  102. d = cls()
  103. empty: Mapping[str, str] = {}
  104. assert d == empty
  105. assert len(d) == 0
  106. assert list(d.keys()) == []
  107. assert list(d.values()) == []
  108. assert list(d.items()) == []
  109. assert cls() != list() # type: ignore[comparison-overlap]
  110. with pytest.raises(TypeError, match=r"(2 given)"):
  111. cls(("key1", "value1"), ("key2", "value2")) # type: ignore[call-arg] # noqa: E501
  112. @pytest.mark.parametrize("arg0", ([("key", "value1")], {"key": "value1"}))
  113. def test_instantiate__from_arg0(
  114. self,
  115. cls: Type[MutableMultiMapping[str]],
  116. arg0: Union[List[Tuple[str, str]], Dict[str, str]],
  117. ) -> None:
  118. d = cls(arg0)
  119. assert d == {"key": "value1"}
  120. assert len(d) == 1
  121. assert list(d.keys()) == ["key"]
  122. assert list(d.values()) == ["value1"]
  123. assert list(d.items()) == [("key", "value1")]
  124. def test_instantiate__with_kwargs(
  125. self,
  126. cls: Type[MutableMultiMapping[str]],
  127. ) -> None:
  128. d = cls([("key", "value1")], key2="value2")
  129. assert d == {"key": "value1", "key2": "value2"}
  130. assert len(d) == 2
  131. assert sorted(d.keys()) == ["key", "key2"]
  132. assert sorted(d.values()) == ["value1", "value2"]
  133. assert sorted(d.items()) == [("key", "value1"), ("key2", "value2")]
  134. def test_instantiate__from_generator(
  135. self, cls: Union[Type[MultiDict[int]], Type[CIMultiDict[int]]]
  136. ) -> None:
  137. d = cls((str(i), i) for i in range(2))
  138. assert d == {"0": 0, "1": 1}
  139. assert len(d) == 2
  140. assert sorted(d.keys()) == ["0", "1"]
  141. assert sorted(d.values()) == [0, 1]
  142. assert sorted(d.items()) == [("0", 0), ("1", 1)]
  143. def test_instantiate__from_list_of_lists(
  144. self,
  145. cls: Type[MutableMultiMapping[str]],
  146. ) -> None:
  147. # Should work at runtime, but won't type check.
  148. d = cls([["key", "value1"]]) # type: ignore[call-arg]
  149. assert d == {"key": "value1"}
  150. def test_instantiate__from_list_of_custom_pairs(
  151. self,
  152. cls: Type[MutableMultiMapping[str]],
  153. ) -> None:
  154. class Pair:
  155. def __len__(self) -> int:
  156. return 2
  157. def __getitem__(self, pos: int) -> str:
  158. return ("key", "value1")[pos]
  159. # Works at runtime, but won't type check.
  160. d = cls([Pair()])
  161. assert d == {"key": "value1"}
  162. def test_getone(self, cls: Type[MutableMultiMapping[str]]) -> None:
  163. d = cls([("key", "value1")], key="value2")
  164. assert d.getone("key") == "value1"
  165. assert d.get("key") == "value1"
  166. assert d["key"] == "value1"
  167. with pytest.raises(KeyError, match="key2"):
  168. d["key2"]
  169. with pytest.raises(KeyError, match="key2"):
  170. d.getone("key2")
  171. assert d.getone("key2", "default") == "default"
  172. def test_call_with_kwargs(self, cls: Type[MultiDict[str]]) -> None:
  173. d = cls([("present", "value")])
  174. assert d.getall(default="missing", key="notfound") == "missing"
  175. def test__iter__(
  176. self,
  177. cls: Union[
  178. Type[MultiDict[Union[str, int]]],
  179. Type[CIMultiDict[Union[str, int]]],
  180. ],
  181. ) -> None:
  182. d = cls([("key", "one"), ("key2", "two"), ("key", 3)])
  183. assert list(d) == ["key", "key2", "key"]
  184. def test_keys__contains(
  185. self,
  186. cls: Union[
  187. Type[MultiDict[Union[str, int]]],
  188. Type[CIMultiDict[Union[str, int]]],
  189. ],
  190. ) -> None:
  191. d = cls([("key", "one"), ("key2", "two"), ("key", 3)])
  192. assert list(d.keys()) == ["key", "key2", "key"]
  193. assert "key" in d.keys()
  194. assert "key2" in d.keys()
  195. assert "foo" not in d.keys()
  196. def test_values__contains(
  197. self,
  198. cls: Union[
  199. Type[MultiDict[Union[str, int]]],
  200. Type[CIMultiDict[Union[str, int]]],
  201. ],
  202. ) -> None:
  203. d = cls([("key", "one"), ("key", "two"), ("key", 3)])
  204. assert list(d.values()) == ["one", "two", 3]
  205. assert "one" in d.values()
  206. assert "two" in d.values()
  207. assert 3 in d.values()
  208. assert "foo" not in d.values()
  209. def test_items__contains(
  210. self,
  211. cls: Union[
  212. Type[MultiDict[Union[str, int]]],
  213. Type[CIMultiDict[Union[str, int]]],
  214. ],
  215. ) -> None:
  216. d = cls([("key", "one"), ("key", "two"), ("key", 3)])
  217. assert list(d.items()) == [("key", "one"), ("key", "two"), ("key", 3)]
  218. assert ("key", "one") in d.items()
  219. assert ("key", "two") in d.items()
  220. assert ("key", 3) in d.items()
  221. assert ("foo", "bar") not in d.items()
  222. def test_cannot_create_from_unaccepted(
  223. self,
  224. cls: Type[MutableMultiMapping[str]],
  225. ) -> None:
  226. with pytest.raises(TypeError):
  227. cls([(1, 2, 3)]) # type: ignore[call-arg]
  228. def test_keys_is_set_less(self, cls: Type[MutableMultiMapping[str]]) -> None:
  229. d = cls([("key", "value1")])
  230. assert d.keys() < {"key", "key2"}
  231. @pytest.mark.parametrize(
  232. ("contents", "expected"),
  233. (
  234. ([("key", "value1")], True),
  235. ([("key", "value1"), ("key2", "value2")], True),
  236. ([("key", "value1"), ("key2", "value2"), ("key3", "value3")], False),
  237. ([("key", "value1"), ("key3", "value3")], False),
  238. ),
  239. )
  240. def test_keys_is_set_less_equal(
  241. self,
  242. cls: Type[MutableMultiMapping[str]],
  243. contents: List[Tuple[str, str]],
  244. expected: bool,
  245. ) -> None:
  246. d = cls(contents)
  247. result = d.keys() <= {"key", "key2"}
  248. assert result is expected
  249. def test_keys_is_set_equal(self, cls: Type[MutableMultiMapping[str]]) -> None:
  250. d = cls([("key", "value1")])
  251. assert d.keys() == {"key"}
  252. def test_keys_is_set_greater(self, cls: Type[MutableMultiMapping[str]]) -> None:
  253. d = cls([("key", "value1"), ("key2", "value2")])
  254. assert d.keys() > {"key"}
  255. @pytest.mark.parametrize(
  256. ("set_", "expected"),
  257. (
  258. ({"key"}, True),
  259. ({"key", "key2"}, True),
  260. ({"key", "key2", "key3"}, False),
  261. ({"key3"}, False),
  262. ),
  263. )
  264. def test_keys_is_set_greater_equal(
  265. self, cls: Type[MutableMultiMapping[str]], set_: Set[str], expected: bool
  266. ) -> None:
  267. d = cls([("key", "value1"), ("key2", "value2")])
  268. result = d.keys() >= set_
  269. assert result is expected
  270. def test_keys_less_than_not_implemented(
  271. self, cls: Type[MutableMultiMapping[str]]
  272. ) -> None:
  273. d = cls([("key", "value1")])
  274. sentinel_operation_result = object()
  275. class RightOperand:
  276. def __gt__(self, other: KeysView[str]) -> object:
  277. assert isinstance(other, KeysView)
  278. return sentinel_operation_result
  279. assert (d.keys() < RightOperand()) is sentinel_operation_result
  280. def test_keys_less_than_or_equal_not_implemented(
  281. self, cls: Type[MutableMultiMapping[str]]
  282. ) -> None:
  283. d = cls([("key", "value1")])
  284. sentinel_operation_result = object()
  285. class RightOperand:
  286. def __ge__(self, other: KeysView[str]) -> object:
  287. assert isinstance(other, KeysView)
  288. return sentinel_operation_result
  289. assert (d.keys() <= RightOperand()) is sentinel_operation_result
  290. def test_keys_greater_than_not_implemented(
  291. self, cls: Type[MutableMultiMapping[str]]
  292. ) -> None:
  293. d = cls([("key", "value1")])
  294. sentinel_operation_result = object()
  295. class RightOperand:
  296. def __lt__(self, other: KeysView[str]) -> object:
  297. assert isinstance(other, KeysView)
  298. return sentinel_operation_result
  299. assert (d.keys() > RightOperand()) is sentinel_operation_result
  300. def test_keys_greater_than_or_equal_not_implemented(
  301. self, cls: Type[MutableMultiMapping[str]]
  302. ) -> None:
  303. d = cls([("key", "value1")])
  304. sentinel_operation_result = object()
  305. class RightOperand:
  306. def __le__(self, other: KeysView[str]) -> object:
  307. assert isinstance(other, KeysView)
  308. return sentinel_operation_result
  309. assert (d.keys() >= RightOperand()) is sentinel_operation_result
  310. def test_keys_is_set_not_equal(self, cls: Type[MutableMultiMapping[str]]) -> None:
  311. d = cls([("key", "value1")])
  312. assert d.keys() != {"key2"}
  313. def test_keys_not_equal_unrelated_type(
  314. self, cls: Type[MutableMultiMapping[str]]
  315. ) -> None:
  316. d = cls([("key", "value1")])
  317. assert d.keys() != "other"
  318. def test_eq(self, cls: Type[MutableMultiMapping[str]]) -> None:
  319. d = cls([("key", "value1")])
  320. assert {"key": "value1"} == d
  321. def test_eq2(self, cls: Type[MutableMultiMapping[str]]) -> None:
  322. d1 = cls([("key", "value1")])
  323. d2 = cls([("key2", "value1")])
  324. assert d1 != d2
  325. def test_eq3(self, cls: Type[MutableMultiMapping[str]]) -> None:
  326. d1 = cls([("key", "value1")])
  327. d2 = cls()
  328. assert d1 != d2
  329. def test_eq_other_mapping_contains_more_keys(
  330. self,
  331. cls: Type[MutableMultiMapping[str]],
  332. ) -> None:
  333. d1 = cls(foo="bar")
  334. d2 = dict(foo="bar", bar="baz")
  335. assert d1 != d2
  336. def test_eq_bad_mapping_len(
  337. self, cls: Union[Type[MultiDict[int]], Type[CIMultiDict[int]]]
  338. ) -> None:
  339. class BadMapping(Mapping[str, int]):
  340. def __getitem__(self, key: str) -> int:
  341. return 1 # pragma: no cover # `len()` fails earlier
  342. def __iter__(self) -> Iterator[str]:
  343. yield "a" # pragma: no cover # `len()` fails earlier
  344. def __len__(self) -> int: # type: ignore[return]
  345. 1 / 0
  346. d1 = cls(a=1)
  347. d2 = BadMapping()
  348. with pytest.raises(ZeroDivisionError):
  349. d1 == d2
  350. def test_eq_bad_mapping_getitem(
  351. self,
  352. cls: Union[Type[MultiDict[int]], Type[CIMultiDict[int]]],
  353. ) -> None:
  354. class BadMapping(Mapping[str, int]):
  355. def __getitem__(self, key: str) -> int: # type: ignore[return]
  356. 1 / 0
  357. def __iter__(self) -> Iterator[str]:
  358. yield "a" # pragma: no cover # foreign objects no iterated
  359. def __len__(self) -> int:
  360. return 1
  361. d1 = cls(a=1)
  362. d2 = BadMapping()
  363. with pytest.raises(ZeroDivisionError):
  364. d1 == d2
  365. def test_ne(self, cls: Type[MutableMultiMapping[str]]) -> None:
  366. d = cls([("key", "value1")])
  367. assert d != {"key": "another_value"}
  368. def test_and(self, cls: Type[MutableMultiMapping[str]]) -> None:
  369. d = cls([("key", "value1")])
  370. assert {"key"} == d.keys() & {"key", "key2"}
  371. def test_and2(self, cls: Type[MutableMultiMapping[str]]) -> None:
  372. d = cls([("key", "value1")])
  373. assert {"key"} == {"key", "key2"} & d.keys()
  374. def test_bitwise_and_not_implemented(
  375. self, cls: Type[MutableMultiMapping[str]]
  376. ) -> None:
  377. d = cls([("key", "value1")])
  378. sentinel_operation_result = object()
  379. class RightOperand:
  380. def __rand__(self, other: KeysView[str]) -> object:
  381. assert isinstance(other, KeysView)
  382. return sentinel_operation_result
  383. assert d.keys() & RightOperand() is sentinel_operation_result
  384. def test_bitwise_and_iterable_not_set(
  385. self, cls: Type[MutableMultiMapping[str]]
  386. ) -> None:
  387. d = cls([("key", "value1")])
  388. assert {"key"} == d.keys() & ["key", "key2"]
  389. def test_or(self, cls: Type[MutableMultiMapping[str]]) -> None:
  390. d = cls([("key", "value1")])
  391. assert {"key", "key2"} == d.keys() | {"key2"}
  392. def test_or2(self, cls: Type[MutableMultiMapping[str]]) -> None:
  393. d = cls([("key", "value1")])
  394. assert {"key", "key2"} == {"key2"} | d.keys()
  395. def test_bitwise_or_not_implemented(
  396. self, cls: Type[MutableMultiMapping[str]]
  397. ) -> None:
  398. d = cls([("key", "value1")])
  399. sentinel_operation_result = object()
  400. class RightOperand:
  401. def __ror__(self, other: KeysView[str]) -> object:
  402. assert isinstance(other, KeysView)
  403. return sentinel_operation_result
  404. assert d.keys() | RightOperand() is sentinel_operation_result
  405. def test_bitwise_or_iterable_not_set(
  406. self, cls: Type[MutableMultiMapping[str]]
  407. ) -> None:
  408. d = cls([("key", "value1")])
  409. assert {"key", "key2"} == d.keys() | ["key2"]
  410. def test_sub(self, cls: Type[MutableMultiMapping[str]]) -> None:
  411. d = cls([("key", "value1"), ("key2", "value2")])
  412. assert {"key"} == d.keys() - {"key2"}
  413. def test_sub2(self, cls: Type[MutableMultiMapping[str]]) -> None:
  414. d = cls([("key", "value1"), ("key2", "value2")])
  415. assert {"key3"} == {"key", "key2", "key3"} - d.keys()
  416. def test_sub_not_implemented(self, cls: Type[MutableMultiMapping[str]]) -> None:
  417. d = cls([("key", "value1"), ("key2", "value2")])
  418. sentinel_operation_result = object()
  419. class RightOperand:
  420. def __rsub__(self, other: KeysView[str]) -> object:
  421. assert isinstance(other, KeysView)
  422. return sentinel_operation_result
  423. assert d.keys() - RightOperand() is sentinel_operation_result
  424. def test_sub_iterable_not_set(self, cls: Type[MutableMultiMapping[str]]) -> None:
  425. d = cls([("key", "value1"), ("key2", "value2")])
  426. assert {"key"} == d.keys() - ["key2"]
  427. def test_xor(self, cls: Type[MutableMultiMapping[str]]) -> None:
  428. d = cls([("key", "value1"), ("key2", "value2")])
  429. assert {"key", "key3"} == d.keys() ^ {"key2", "key3"}
  430. def test_xor2(self, cls: Type[MutableMultiMapping[str]]) -> None:
  431. d = cls([("key", "value1"), ("key2", "value2")])
  432. assert {"key", "key3"} == {"key2", "key3"} ^ d.keys()
  433. def test_xor_not_implemented(self, cls: Type[MutableMultiMapping[str]]) -> None:
  434. d = cls([("key", "value1"), ("key2", "value2")])
  435. sentinel_operation_result = object()
  436. class RightOperand:
  437. def __rxor__(self, other: KeysView[str]) -> object:
  438. assert isinstance(other, KeysView)
  439. return sentinel_operation_result
  440. assert d.keys() ^ RightOperand() is sentinel_operation_result
  441. def test_xor_iterable_not_set(self, cls: Type[MutableMultiMapping[str]]) -> None:
  442. d = cls([("key", "value1"), ("key2", "value2")])
  443. assert {"key", "key3"} == d.keys() ^ ["key2", "key3"]
  444. @pytest.mark.parametrize(
  445. ("key", "value", "expected"),
  446. (("key2", "v", True), ("key", "value1", False)),
  447. )
  448. def test_isdisjoint(
  449. self, cls: Type[MutableMultiMapping[str]], key: str, value: str, expected: bool
  450. ) -> None:
  451. d = cls([("key", "value1")])
  452. assert d.items().isdisjoint({(key, value)}) is expected
  453. assert d.keys().isdisjoint({key}) is expected
  454. def test_repr_aiohttp_issue_410(self, cls: Type[MutableMultiMapping[str]]) -> None:
  455. d = cls()
  456. try:
  457. raise Exception
  458. pytest.fail("Should never happen") # pragma: no cover
  459. except Exception as e:
  460. repr(d)
  461. assert sys.exc_info()[1] == e # noqa: PT017
  462. @pytest.mark.parametrize(
  463. "op",
  464. (operator.or_, operator.and_, operator.sub, operator.xor),
  465. )
  466. @pytest.mark.parametrize("other", ({"other"},))
  467. def test_op_issue_aiohttp_issue_410(
  468. self,
  469. cls: Type[MutableMultiMapping[str]],
  470. op: Callable[[object, object], object],
  471. other: Set[str],
  472. ) -> None:
  473. d = cls([("key", "value")])
  474. try:
  475. raise Exception
  476. pytest.fail("Should never happen") # pragma: no cover
  477. except Exception as e:
  478. op(d.keys(), other)
  479. assert sys.exc_info()[1] == e # noqa: PT017
  480. def test_weakref(self, cls: Type[MutableMultiMapping[str]]) -> None:
  481. called = False
  482. def cb(wr: object) -> None:
  483. nonlocal called
  484. called = True
  485. d = cls()
  486. wr = weakref.ref(d, cb)
  487. del d
  488. gc.collect()
  489. assert called
  490. del wr
  491. def test_iter_length_hint_keys(
  492. self,
  493. cls: Union[Type[MultiDict[int]], Type[CIMultiDict[int]]],
  494. ) -> None:
  495. md = cls(a=1, b=2)
  496. it = iter(md.keys())
  497. assert it.__length_hint__() == 2 # type: ignore[attr-defined]
  498. def test_iter_length_hint_items(
  499. self,
  500. cls: Union[Type[MultiDict[int]], Type[CIMultiDict[int]]],
  501. ) -> None:
  502. md = cls(a=1, b=2)
  503. it = iter(md.items())
  504. assert it.__length_hint__() == 2 # type: ignore[attr-defined]
  505. def test_iter_length_hint_values(
  506. self,
  507. cls: Union[Type[MultiDict[int]], Type[CIMultiDict[int]]],
  508. ) -> None:
  509. md = cls(a=1, b=2)
  510. it = iter(md.values())
  511. assert it.__length_hint__() == 2 # type: ignore[attr-defined]
  512. def test_ctor_list_arg_and_kwds(
  513. self,
  514. cls: Union[Type[MultiDict[int]], Type[CIMultiDict[int]]],
  515. ) -> None:
  516. arg = [("a", 1)]
  517. obj = cls(arg, b=2)
  518. assert list(obj.items()) == [("a", 1), ("b", 2)]
  519. assert arg == [("a", 1)]
  520. def test_ctor_tuple_arg_and_kwds(
  521. self,
  522. cls: Union[Type[MultiDict[int]], Type[CIMultiDict[int]]],
  523. ) -> None:
  524. arg = (("a", 1),)
  525. obj = cls(arg, b=2)
  526. assert list(obj.items()) == [("a", 1), ("b", 2)]
  527. assert arg == (("a", 1),)
  528. def test_ctor_deque_arg_and_kwds(
  529. self,
  530. cls: Union[Type[MultiDict[int]], Type[CIMultiDict[int]]],
  531. ) -> None:
  532. arg = deque([("a", 1)])
  533. obj = cls(arg, b=2)
  534. assert list(obj.items()) == [("a", 1), ("b", 2)]
  535. assert arg == deque([("a", 1)])
  536. class TestMultiDict(BaseMultiDictTest):
  537. @pytest.fixture(
  538. params=[
  539. ("MultiDict",),
  540. ("MultiDict", "MultiDictProxy"),
  541. ],
  542. )
  543. def cls( # type: ignore[misc]
  544. self,
  545. request: pytest.FixtureRequest,
  546. multidict_module: ModuleType,
  547. ) -> Callable[..., MultiMapping[int | str] | MutableMultiMapping[int | str]]:
  548. """Make a case-sensitive multidict class/proxy constructor."""
  549. return chained_callable(multidict_module, request.param)
  550. def test__repr__(self, cls: Type[MultiDict[str]]) -> None:
  551. d = cls()
  552. _cls = type(d)
  553. assert str(d) == "<%s()>" % _cls.__name__
  554. d = cls([("key", "one"), ("key", "two")])
  555. assert str(d) == "<%s('key': 'one', 'key': 'two')>" % _cls.__name__
  556. def test_getall(self, cls: Type[MultiDict[str]]) -> None:
  557. d = cls([("key", "value1")], key="value2")
  558. assert d != {"key": "value1"}
  559. assert len(d) == 2
  560. assert d.getall("key") == ["value1", "value2"]
  561. with pytest.raises(KeyError, match="some_key"):
  562. d.getall("some_key")
  563. default = object()
  564. assert d.getall("some_key", default) is default
  565. def test_preserve_stable_ordering(
  566. self,
  567. cls: Type[MultiDict[Union[str, int]]],
  568. ) -> None:
  569. d = cls([("a", 1), ("b", "2"), ("a", 3)])
  570. s = "&".join("{}={}".format(k, v) for k, v in d.items())
  571. assert s == "a=1&b=2&a=3"
  572. def test_get(self, cls: Type[MultiDict[int]]) -> None:
  573. d = cls([("a", 1), ("a", 2)])
  574. assert d["a"] == 1
  575. def test_items__repr__(self, cls: Type[MultiDict[str]]) -> None:
  576. d = cls([("key", "value1")], key="value2")
  577. expected = "_ItemsView('key': 'value1', 'key': 'value2')"
  578. assert repr(d.items()) == expected
  579. def test_keys__repr__(self, cls: Type[MultiDict[str]]) -> None:
  580. d = cls([("key", "value1")], key="value2")
  581. assert repr(d.keys()) == "_KeysView('key', 'key')"
  582. def test_values__repr__(self, cls: Type[MultiDict[str]]) -> None:
  583. d = cls([("key", "value1")], key="value2")
  584. assert repr(d.values()) == "_ValuesView('value1', 'value2')"
  585. class TestCIMultiDict(BaseMultiDictTest):
  586. @pytest.fixture(
  587. params=[
  588. ("CIMultiDict",),
  589. ("CIMultiDict", "CIMultiDictProxy"),
  590. ],
  591. )
  592. def cls( # type: ignore[misc]
  593. self,
  594. request: pytest.FixtureRequest,
  595. multidict_module: ModuleType,
  596. ) -> Callable[..., MultiMapping[int | str] | MutableMultiMapping[int | str]]:
  597. """Make a case-insensitive multidict class/proxy constructor."""
  598. return chained_callable(multidict_module, request.param)
  599. def test_basics(self, cls: Type[CIMultiDict[str]]) -> None:
  600. d = cls([("KEY", "value1")], KEY="value2")
  601. assert d.getone("key") == "value1"
  602. assert d.get("key") == "value1"
  603. assert d.get("key2", "val") == "val"
  604. assert d["key"] == "value1"
  605. assert "key" in d
  606. with pytest.raises(KeyError, match="key2"):
  607. d["key2"]
  608. with pytest.raises(KeyError, match="key2"):
  609. d.getone("key2")
  610. def test_getall(self, cls: Type[CIMultiDict[str]]) -> None:
  611. d = cls([("KEY", "value1")], KEY="value2")
  612. assert not d == {"KEY": "value1"}
  613. assert len(d) == 2
  614. assert d.getall("key") == ["value1", "value2"]
  615. with pytest.raises(KeyError, match="some_key"):
  616. d.getall("some_key")
  617. def test_get(self, cls: Type[CIMultiDict[int]]) -> None:
  618. d = cls([("A", 1), ("a", 2)])
  619. assert 1 == d["a"]
  620. def test__repr__(self, cls: Type[CIMultiDict[str]]) -> None:
  621. d = cls([("KEY", "value1")], key="value2")
  622. _cls = type(d)
  623. expected = "<%s('KEY': 'value1', 'key': 'value2')>" % _cls.__name__
  624. assert str(d) == expected
  625. def test_items__repr__(self, cls: Type[CIMultiDict[str]]) -> None:
  626. d = cls([("KEY", "value1")], key="value2")
  627. expected = "_ItemsView('KEY': 'value1', 'key': 'value2')"
  628. assert repr(d.items()) == expected
  629. def test_keys__repr__(self, cls: Type[CIMultiDict[str]]) -> None:
  630. d = cls([("KEY", "value1")], key="value2")
  631. assert repr(d.keys()) == "_KeysView('KEY', 'key')"
  632. def test_values__repr__(self, cls: Type[CIMultiDict[str]]) -> None:
  633. d = cls([("KEY", "value1")], key="value2")
  634. assert repr(d.values()) == "_ValuesView('value1', 'value2')"