test_multidict.py 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738
  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. def test_keys_is_set_less_equal(self, cls: Type[MutableMultiMapping[str]]) -> None:
  232. d = cls([("key", "value1")])
  233. assert d.keys() <= {"key"}
  234. def test_keys_is_set_equal(self, cls: Type[MutableMultiMapping[str]]) -> None:
  235. d = cls([("key", "value1")])
  236. assert d.keys() == {"key"}
  237. def test_keys_is_set_greater(self, cls: Type[MutableMultiMapping[str]]) -> None:
  238. d = cls([("key", "value1")])
  239. assert {"key", "key2"} > d.keys()
  240. def test_keys_is_set_greater_equal(
  241. self,
  242. cls: Type[MutableMultiMapping[str]],
  243. ) -> None:
  244. d = cls([("key", "value1")])
  245. assert {"key"} >= d.keys()
  246. def test_keys_is_set_not_equal(self, cls: Type[MutableMultiMapping[str]]) -> None:
  247. d = cls([("key", "value1")])
  248. assert d.keys() != {"key2"}
  249. def test_eq(self, cls: Type[MutableMultiMapping[str]]) -> None:
  250. d = cls([("key", "value1")])
  251. assert {"key": "value1"} == d
  252. def test_eq2(self, cls: Type[MutableMultiMapping[str]]) -> None:
  253. d1 = cls([("key", "value1")])
  254. d2 = cls([("key2", "value1")])
  255. assert d1 != d2
  256. def test_eq3(self, cls: Type[MutableMultiMapping[str]]) -> None:
  257. d1 = cls([("key", "value1")])
  258. d2 = cls()
  259. assert d1 != d2
  260. def test_eq_other_mapping_contains_more_keys(
  261. self,
  262. cls: Type[MutableMultiMapping[str]],
  263. ) -> None:
  264. d1 = cls(foo="bar")
  265. d2 = dict(foo="bar", bar="baz")
  266. assert d1 != d2
  267. def test_eq_bad_mapping_len(
  268. self, cls: Union[Type[MultiDict[int]], Type[CIMultiDict[int]]]
  269. ) -> None:
  270. class BadMapping(Mapping[str, int]):
  271. def __getitem__(self, key: str) -> int:
  272. return 1 # pragma: no cover # `len()` fails earlier
  273. def __iter__(self) -> Iterator[str]:
  274. yield "a" # pragma: no cover # `len()` fails earlier
  275. def __len__(self) -> int: # type: ignore[return]
  276. 1 / 0
  277. d1 = cls(a=1)
  278. d2 = BadMapping()
  279. with pytest.raises(ZeroDivisionError):
  280. d1 == d2
  281. def test_eq_bad_mapping_getitem(
  282. self,
  283. cls: Union[Type[MultiDict[int]], Type[CIMultiDict[int]]],
  284. ) -> None:
  285. class BadMapping(Mapping[str, int]):
  286. def __getitem__(self, key: str) -> int: # type: ignore[return]
  287. 1 / 0
  288. def __iter__(self) -> Iterator[str]:
  289. yield "a" # pragma: no cover # foreign objects no iterated
  290. def __len__(self) -> int:
  291. return 1
  292. d1 = cls(a=1)
  293. d2 = BadMapping()
  294. with pytest.raises(ZeroDivisionError):
  295. d1 == d2
  296. def test_ne(self, cls: Type[MutableMultiMapping[str]]) -> None:
  297. d = cls([("key", "value1")])
  298. assert d != {"key": "another_value"}
  299. def test_and(self, cls: Type[MutableMultiMapping[str]]) -> None:
  300. d = cls([("key", "value1")])
  301. assert {"key"} == d.keys() & {"key", "key2"}
  302. def test_and2(self, cls: Type[MutableMultiMapping[str]]) -> None:
  303. d = cls([("key", "value1")])
  304. assert {"key"} == {"key", "key2"} & d.keys()
  305. def test_bitwise_and_not_implemented(
  306. self, cls: Type[MutableMultiMapping[str]]
  307. ) -> None:
  308. d = cls([("key", "value1")])
  309. sentinel_operation_result = object()
  310. class RightOperand:
  311. def __rand__(self, other: KeysView[str]) -> object:
  312. assert isinstance(other, KeysView)
  313. return sentinel_operation_result
  314. assert d.keys() & RightOperand() is sentinel_operation_result
  315. def test_bitwise_and_iterable_not_set(
  316. self, cls: Type[MutableMultiMapping[str]]
  317. ) -> None:
  318. d = cls([("key", "value1")])
  319. assert {"key"} == d.keys() & ["key", "key2"]
  320. def test_or(self, cls: Type[MutableMultiMapping[str]]) -> None:
  321. d = cls([("key", "value1")])
  322. assert {"key", "key2"} == d.keys() | {"key2"}
  323. def test_or2(self, cls: Type[MutableMultiMapping[str]]) -> None:
  324. d = cls([("key", "value1")])
  325. assert {"key", "key2"} == {"key2"} | d.keys()
  326. def test_bitwise_or_not_implemented(
  327. self, cls: Type[MutableMultiMapping[str]]
  328. ) -> None:
  329. d = cls([("key", "value1")])
  330. sentinel_operation_result = object()
  331. class RightOperand:
  332. def __ror__(self, other: KeysView[str]) -> object:
  333. assert isinstance(other, KeysView)
  334. return sentinel_operation_result
  335. assert d.keys() | RightOperand() is sentinel_operation_result
  336. def test_bitwise_or_iterable_not_set(
  337. self, cls: Type[MutableMultiMapping[str]]
  338. ) -> None:
  339. d = cls([("key", "value1")])
  340. assert {"key", "key2"} == d.keys() | ["key2"]
  341. def test_sub(self, cls: Type[MutableMultiMapping[str]]) -> None:
  342. d = cls([("key", "value1"), ("key2", "value2")])
  343. assert {"key"} == d.keys() - {"key2"}
  344. def test_sub2(self, cls: Type[MutableMultiMapping[str]]) -> None:
  345. d = cls([("key", "value1"), ("key2", "value2")])
  346. assert {"key3"} == {"key", "key2", "key3"} - d.keys()
  347. def test_sub_not_implemented(self, cls: Type[MutableMultiMapping[str]]) -> None:
  348. d = cls([("key", "value1"), ("key2", "value2")])
  349. sentinel_operation_result = object()
  350. class RightOperand:
  351. def __rsub__(self, other: KeysView[str]) -> object:
  352. assert isinstance(other, KeysView)
  353. return sentinel_operation_result
  354. assert d.keys() - RightOperand() is sentinel_operation_result
  355. def test_sub_iterable_not_set(self, cls: Type[MutableMultiMapping[str]]) -> None:
  356. d = cls([("key", "value1"), ("key2", "value2")])
  357. assert {"key"} == d.keys() - ["key2"]
  358. def test_xor(self, cls: Type[MutableMultiMapping[str]]) -> None:
  359. d = cls([("key", "value1"), ("key2", "value2")])
  360. assert {"key", "key3"} == d.keys() ^ {"key2", "key3"}
  361. def test_xor2(self, cls: Type[MutableMultiMapping[str]]) -> None:
  362. d = cls([("key", "value1"), ("key2", "value2")])
  363. assert {"key", "key3"} == {"key2", "key3"} ^ d.keys()
  364. def test_xor_not_implemented(self, cls: Type[MutableMultiMapping[str]]) -> None:
  365. d = cls([("key", "value1"), ("key2", "value2")])
  366. sentinel_operation_result = object()
  367. class RightOperand:
  368. def __rxor__(self, other: KeysView[str]) -> object:
  369. assert isinstance(other, KeysView)
  370. return sentinel_operation_result
  371. assert d.keys() ^ RightOperand() is sentinel_operation_result
  372. def test_xor_iterable_not_set(self, cls: Type[MutableMultiMapping[str]]) -> None:
  373. d = cls([("key", "value1"), ("key2", "value2")])
  374. assert {"key", "key3"} == d.keys() ^ ["key2", "key3"]
  375. @pytest.mark.parametrize(
  376. ("key", "value", "expected"),
  377. (("key2", "v", True), ("key", "value1", False)),
  378. )
  379. def test_isdisjoint(
  380. self, cls: Type[MutableMultiMapping[str]], key: str, value: str, expected: bool
  381. ) -> None:
  382. d = cls([("key", "value1")])
  383. assert d.items().isdisjoint({(key, value)}) is expected
  384. assert d.keys().isdisjoint({key}) is expected
  385. def test_repr_aiohttp_issue_410(self, cls: Type[MutableMultiMapping[str]]) -> None:
  386. d = cls()
  387. try:
  388. raise Exception
  389. pytest.fail("Should never happen") # pragma: no cover
  390. except Exception as e:
  391. repr(d)
  392. assert sys.exc_info()[1] == e # noqa: PT017
  393. @pytest.mark.parametrize(
  394. "op",
  395. (operator.or_, operator.and_, operator.sub, operator.xor),
  396. )
  397. @pytest.mark.parametrize("other", ({"other"},))
  398. def test_op_issue_aiohttp_issue_410(
  399. self,
  400. cls: Type[MutableMultiMapping[str]],
  401. op: Callable[[object, object], object],
  402. other: Set[str],
  403. ) -> None:
  404. d = cls([("key", "value")])
  405. try:
  406. raise Exception
  407. pytest.fail("Should never happen") # pragma: no cover
  408. except Exception as e:
  409. op(d.keys(), other)
  410. assert sys.exc_info()[1] == e # noqa: PT017
  411. def test_weakref(self, cls: Type[MutableMultiMapping[str]]) -> None:
  412. called = False
  413. def cb(wr: object) -> None:
  414. nonlocal called
  415. called = True
  416. d = cls()
  417. wr = weakref.ref(d, cb)
  418. del d
  419. gc.collect()
  420. assert called
  421. del wr
  422. def test_iter_length_hint_keys(
  423. self,
  424. cls: Union[Type[MultiDict[int]], Type[CIMultiDict[int]]],
  425. ) -> None:
  426. md = cls(a=1, b=2)
  427. it = iter(md.keys())
  428. assert it.__length_hint__() == 2 # type: ignore[attr-defined]
  429. def test_iter_length_hint_items(
  430. self,
  431. cls: Union[Type[MultiDict[int]], Type[CIMultiDict[int]]],
  432. ) -> None:
  433. md = cls(a=1, b=2)
  434. it = iter(md.items())
  435. assert it.__length_hint__() == 2 # type: ignore[attr-defined]
  436. def test_iter_length_hint_values(
  437. self,
  438. cls: Union[Type[MultiDict[int]], Type[CIMultiDict[int]]],
  439. ) -> None:
  440. md = cls(a=1, b=2)
  441. it = iter(md.values())
  442. assert it.__length_hint__() == 2 # type: ignore[attr-defined]
  443. def test_ctor_list_arg_and_kwds(
  444. self,
  445. cls: Union[Type[MultiDict[int]], Type[CIMultiDict[int]]],
  446. ) -> None:
  447. arg = [("a", 1)]
  448. obj = cls(arg, b=2)
  449. assert list(obj.items()) == [("a", 1), ("b", 2)]
  450. assert arg == [("a", 1)]
  451. def test_ctor_tuple_arg_and_kwds(
  452. self,
  453. cls: Union[Type[MultiDict[int]], Type[CIMultiDict[int]]],
  454. ) -> None:
  455. arg = (("a", 1),)
  456. obj = cls(arg, b=2)
  457. assert list(obj.items()) == [("a", 1), ("b", 2)]
  458. assert arg == (("a", 1),)
  459. def test_ctor_deque_arg_and_kwds(
  460. self,
  461. cls: Union[Type[MultiDict[int]], Type[CIMultiDict[int]]],
  462. ) -> None:
  463. arg = deque([("a", 1)])
  464. obj = cls(arg, b=2)
  465. assert list(obj.items()) == [("a", 1), ("b", 2)]
  466. assert arg == deque([("a", 1)])
  467. class TestMultiDict(BaseMultiDictTest):
  468. @pytest.fixture(
  469. params=[
  470. ("MultiDict",),
  471. ("MultiDict", "MultiDictProxy"),
  472. ],
  473. )
  474. def cls( # type: ignore[misc]
  475. self,
  476. request: pytest.FixtureRequest,
  477. multidict_module: ModuleType,
  478. ) -> Callable[..., MultiMapping[int | str] | MutableMultiMapping[int | str]]:
  479. """Make a case-sensitive multidict class/proxy constructor."""
  480. return chained_callable(multidict_module, request.param)
  481. def test__repr__(self, cls: Type[MultiDict[str]]) -> None:
  482. d = cls()
  483. _cls = type(d)
  484. assert str(d) == "<%s()>" % _cls.__name__
  485. d = cls([("key", "one"), ("key", "two")])
  486. assert str(d) == "<%s('key': 'one', 'key': 'two')>" % _cls.__name__
  487. def test_getall(self, cls: Type[MultiDict[str]]) -> None:
  488. d = cls([("key", "value1")], key="value2")
  489. assert d != {"key": "value1"}
  490. assert len(d) == 2
  491. assert d.getall("key") == ["value1", "value2"]
  492. with pytest.raises(KeyError, match="some_key"):
  493. d.getall("some_key")
  494. default = object()
  495. assert d.getall("some_key", default) is default
  496. def test_preserve_stable_ordering(
  497. self,
  498. cls: Type[MultiDict[Union[str, int]]],
  499. ) -> None:
  500. d = cls([("a", 1), ("b", "2"), ("a", 3)])
  501. s = "&".join("{}={}".format(k, v) for k, v in d.items())
  502. assert s == "a=1&b=2&a=3"
  503. def test_get(self, cls: Type[MultiDict[int]]) -> None:
  504. d = cls([("a", 1), ("a", 2)])
  505. assert d["a"] == 1
  506. def test_items__repr__(self, cls: Type[MultiDict[str]]) -> None:
  507. d = cls([("key", "value1")], key="value2")
  508. expected = "_ItemsView('key': 'value1', 'key': 'value2')"
  509. assert repr(d.items()) == expected
  510. def test_keys__repr__(self, cls: Type[MultiDict[str]]) -> None:
  511. d = cls([("key", "value1")], key="value2")
  512. assert repr(d.keys()) == "_KeysView('key', 'key')"
  513. def test_values__repr__(self, cls: Type[MultiDict[str]]) -> None:
  514. d = cls([("key", "value1")], key="value2")
  515. assert repr(d.values()) == "_ValuesView('value1', 'value2')"
  516. class TestCIMultiDict(BaseMultiDictTest):
  517. @pytest.fixture(
  518. params=[
  519. ("CIMultiDict",),
  520. ("CIMultiDict", "CIMultiDictProxy"),
  521. ],
  522. )
  523. def cls( # type: ignore[misc]
  524. self,
  525. request: pytest.FixtureRequest,
  526. multidict_module: ModuleType,
  527. ) -> Callable[..., MultiMapping[int | str] | MutableMultiMapping[int | str]]:
  528. """Make a case-insensitive multidict class/proxy constructor."""
  529. return chained_callable(multidict_module, request.param)
  530. def test_basics(self, cls: Type[CIMultiDict[str]]) -> None:
  531. d = cls([("KEY", "value1")], KEY="value2")
  532. assert d.getone("key") == "value1"
  533. assert d.get("key") == "value1"
  534. assert d.get("key2", "val") == "val"
  535. assert d["key"] == "value1"
  536. assert "key" in d
  537. with pytest.raises(KeyError, match="key2"):
  538. d["key2"]
  539. with pytest.raises(KeyError, match="key2"):
  540. d.getone("key2")
  541. def test_getall(self, cls: Type[CIMultiDict[str]]) -> None:
  542. d = cls([("KEY", "value1")], KEY="value2")
  543. assert not d == {"KEY": "value1"}
  544. assert len(d) == 2
  545. assert d.getall("key") == ["value1", "value2"]
  546. with pytest.raises(KeyError, match="some_key"):
  547. d.getall("some_key")
  548. def test_get(self, cls: Type[CIMultiDict[int]]) -> None:
  549. d = cls([("A", 1), ("a", 2)])
  550. assert 1 == d["a"]
  551. def test__repr__(self, cls: Type[CIMultiDict[str]]) -> None:
  552. d = cls([("KEY", "value1")], key="value2")
  553. _cls = type(d)
  554. expected = "<%s('KEY': 'value1', 'key': 'value2')>" % _cls.__name__
  555. assert str(d) == expected
  556. def test_items__repr__(self, cls: Type[CIMultiDict[str]]) -> None:
  557. d = cls([("KEY", "value1")], key="value2")
  558. expected = "_ItemsView('KEY': 'value1', 'key': 'value2')"
  559. assert repr(d.items()) == expected
  560. def test_keys__repr__(self, cls: Type[CIMultiDict[str]]) -> None:
  561. d = cls([("KEY", "value1")], key="value2")
  562. assert repr(d.keys()) == "_KeysView('KEY', 'key')"
  563. def test_values__repr__(self, cls: Type[CIMultiDict[str]]) -> None:
  564. d = cls([("KEY", "value1")], key="value2")
  565. assert repr(d.values()) == "_ValuesView('value1', 'value2')"