test_mutable_multidict.py 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660
  1. import string
  2. import sys
  3. from typing import Type
  4. import pytest
  5. from multidict import MultiMapping, MutableMultiMapping
  6. class TestMutableMultiDict:
  7. def test_copy(
  8. self,
  9. case_sensitive_multidict_class: Type[MutableMultiMapping[str]],
  10. ) -> None:
  11. d1 = case_sensitive_multidict_class(key="value", a="b")
  12. d2 = d1.copy()
  13. assert d1 == d2
  14. assert d1 is not d2
  15. def test__repr__(
  16. self,
  17. case_sensitive_multidict_class: Type[MutableMultiMapping[str]],
  18. ) -> None:
  19. d = case_sensitive_multidict_class()
  20. assert str(d) == "<%s()>" % case_sensitive_multidict_class.__name__
  21. d = case_sensitive_multidict_class([("key", "one"), ("key", "two")])
  22. expected = (
  23. f"<{case_sensitive_multidict_class.__name__}"
  24. "('key': 'one', 'key': 'two')>"
  25. )
  26. assert str(d) == expected
  27. def test_getall(
  28. self,
  29. case_sensitive_multidict_class: Type[MutableMultiMapping[str]],
  30. ) -> None:
  31. d = case_sensitive_multidict_class([("key", "value1")], key="value2")
  32. assert len(d) == 2
  33. assert d.getall("key") == ["value1", "value2"]
  34. with pytest.raises(KeyError, match="some_key"):
  35. d.getall("some_key")
  36. default = object()
  37. assert d.getall("some_key", default) is default
  38. def test_add(
  39. self,
  40. case_sensitive_multidict_class: Type[MutableMultiMapping[str]],
  41. ) -> None:
  42. d = case_sensitive_multidict_class()
  43. assert d == {}
  44. d["key"] = "one"
  45. assert d == {"key": "one"}
  46. assert d.getall("key") == ["one"]
  47. d["key"] = "two"
  48. assert d == {"key": "two"}
  49. assert d.getall("key") == ["two"]
  50. d.add("key", "one")
  51. assert 2 == len(d)
  52. assert d.getall("key") == ["two", "one"]
  53. d.add("foo", "bar")
  54. assert 3 == len(d)
  55. assert d.getall("foo") == ["bar"]
  56. def test_extend(
  57. self,
  58. case_sensitive_multidict_class: Type[MutableMultiMapping[str]],
  59. ) -> None:
  60. d = case_sensitive_multidict_class()
  61. assert d == {}
  62. d.extend([("key", "one"), ("key", "two")], key=3, foo="bar")
  63. assert d != {"key": "one", "foo": "bar"}
  64. assert 4 == len(d)
  65. itms = d.items()
  66. # we can't guarantee order of kwargs
  67. assert ("key", "one") in itms
  68. assert ("key", "two") in itms
  69. assert ("key", 3) in itms
  70. assert ("foo", "bar") in itms
  71. other = case_sensitive_multidict_class(bar="baz")
  72. assert other == {"bar": "baz"}
  73. d.extend(other)
  74. assert ("bar", "baz") in d.items()
  75. d.extend({"foo": "moo"})
  76. assert ("foo", "moo") in d.items()
  77. d.extend()
  78. assert 6 == len(d)
  79. with pytest.raises(TypeError):
  80. d.extend("foo", "bar")
  81. def test_extend_from_proxy(
  82. self,
  83. case_sensitive_multidict_class: Type[MutableMultiMapping[str]],
  84. case_sensitive_multidict_proxy_class: Type[MultiMapping[str]],
  85. ) -> None:
  86. d = case_sensitive_multidict_class([("a", "a"), ("b", "b")])
  87. proxy = case_sensitive_multidict_proxy_class(d)
  88. d2 = case_sensitive_multidict_class()
  89. d2.extend(proxy)
  90. assert [("a", "a"), ("b", "b")] == list(d2.items())
  91. def test_clear(
  92. self,
  93. case_sensitive_multidict_class: Type[MutableMultiMapping[str]],
  94. ) -> None:
  95. d = case_sensitive_multidict_class([("key", "one")], key="two", foo="bar")
  96. d.clear()
  97. assert d == {}
  98. assert list(d.items()) == []
  99. def test_del(
  100. self,
  101. case_sensitive_multidict_class: Type[MutableMultiMapping[str]],
  102. ) -> None:
  103. d = case_sensitive_multidict_class([("key", "one"), ("key", "two")], foo="bar")
  104. assert list(d.keys()) == ["key", "key", "foo"]
  105. del d["key"]
  106. assert d == {"foo": "bar"}
  107. assert list(d.items()) == [("foo", "bar")]
  108. with pytest.raises(KeyError, match="key"):
  109. del d["key"]
  110. def test_set_default(
  111. self,
  112. case_sensitive_multidict_class: Type[MutableMultiMapping[str]],
  113. ) -> None:
  114. d = case_sensitive_multidict_class([("key", "one"), ("key", "two")], foo="bar")
  115. assert "one" == d.setdefault("key", "three")
  116. assert "three" == d.setdefault("otherkey", "three")
  117. assert "otherkey" in d
  118. assert "three" == d["otherkey"]
  119. def test_popitem(
  120. self,
  121. case_sensitive_multidict_class: Type[MutableMultiMapping[str]],
  122. ) -> None:
  123. d = case_sensitive_multidict_class()
  124. d.add("key", "val1")
  125. d.add("key", "val2")
  126. assert ("key", "val1") == d.popitem()
  127. assert [("key", "val2")] == list(d.items())
  128. def test_popitem_empty_multidict(
  129. self,
  130. case_sensitive_multidict_class: Type[MutableMultiMapping[str]],
  131. ) -> None:
  132. d = case_sensitive_multidict_class()
  133. with pytest.raises(KeyError):
  134. d.popitem()
  135. def test_pop(
  136. self,
  137. case_sensitive_multidict_class: Type[MutableMultiMapping[str]],
  138. ) -> None:
  139. d = case_sensitive_multidict_class()
  140. d.add("key", "val1")
  141. d.add("key", "val2")
  142. assert "val1" == d.pop("key")
  143. assert {"key": "val2"} == d
  144. def test_pop2(
  145. self,
  146. case_sensitive_multidict_class: Type[MutableMultiMapping[str]],
  147. ) -> None:
  148. d = case_sensitive_multidict_class()
  149. d.add("key", "val1")
  150. d.add("key2", "val2")
  151. d.add("key", "val3")
  152. assert "val1" == d.pop("key")
  153. assert [("key2", "val2"), ("key", "val3")] == list(d.items())
  154. def test_pop_default(
  155. self,
  156. case_sensitive_multidict_class: Type[MutableMultiMapping[str]],
  157. ) -> None:
  158. d = case_sensitive_multidict_class(other="val")
  159. assert "default" == d.pop("key", "default")
  160. assert "other" in d
  161. def test_pop_raises(
  162. self,
  163. case_sensitive_multidict_class: Type[MutableMultiMapping[str]],
  164. ) -> None:
  165. d = case_sensitive_multidict_class(other="val")
  166. with pytest.raises(KeyError, match="key"):
  167. d.pop("key")
  168. assert "other" in d
  169. def test_replacement_order(
  170. self,
  171. case_sensitive_multidict_class: Type[MutableMultiMapping[str]],
  172. ) -> None:
  173. d = case_sensitive_multidict_class()
  174. d.add("key1", "val1")
  175. d.add("key2", "val2")
  176. d.add("key1", "val3")
  177. d.add("key2", "val4")
  178. d["key1"] = "val"
  179. expected = [("key1", "val"), ("key2", "val2"), ("key2", "val4")]
  180. assert expected == list(d.items())
  181. def test_nonstr_key(
  182. self,
  183. case_sensitive_multidict_class: Type[MutableMultiMapping[str]],
  184. ) -> None:
  185. d = case_sensitive_multidict_class()
  186. with pytest.raises(TypeError):
  187. d[1] = "val"
  188. def test_istr_key(
  189. self,
  190. case_sensitive_multidict_class: Type[MutableMultiMapping[str]],
  191. case_insensitive_str_class: Type[str],
  192. ) -> None:
  193. d = case_sensitive_multidict_class()
  194. d[case_insensitive_str_class("1")] = "val"
  195. assert type(list(d.keys())[0]) is case_insensitive_str_class
  196. def test_str_derived_key(
  197. self,
  198. case_sensitive_multidict_class: Type[MutableMultiMapping[str]],
  199. ) -> None:
  200. class A(str):
  201. pass
  202. d = case_sensitive_multidict_class()
  203. d[A("1")] = "val"
  204. assert type(list(d.keys())[0]) is A
  205. def test_istr_key_add(
  206. self,
  207. case_sensitive_multidict_class: Type[MutableMultiMapping[str]],
  208. case_insensitive_str_class: Type[str],
  209. ) -> None:
  210. d = case_sensitive_multidict_class()
  211. d.add(case_insensitive_str_class("1"), "val")
  212. assert type(list(d.keys())[0]) is case_insensitive_str_class
  213. def test_str_derived_key_add(
  214. self,
  215. case_sensitive_multidict_class: Type[MutableMultiMapping[str]],
  216. ) -> None:
  217. class A(str):
  218. pass
  219. d = case_sensitive_multidict_class()
  220. d.add(A("1"), "val")
  221. assert type(list(d.keys())[0]) is A
  222. def test_popall(
  223. self,
  224. case_sensitive_multidict_class: Type[MutableMultiMapping[str]],
  225. ) -> None:
  226. d = case_sensitive_multidict_class()
  227. d.add("key1", "val1")
  228. d.add("key2", "val2")
  229. d.add("key1", "val3")
  230. ret = d.popall("key1")
  231. assert ["val1", "val3"] == ret
  232. assert {"key2": "val2"} == d
  233. def test_popall_default(
  234. self,
  235. case_sensitive_multidict_class: Type[MutableMultiMapping[str]],
  236. ) -> None:
  237. d = case_sensitive_multidict_class()
  238. assert "val" == d.popall("key", "val")
  239. def test_popall_key_error(
  240. self,
  241. case_sensitive_multidict_class: Type[MutableMultiMapping[str]],
  242. ) -> None:
  243. d = case_sensitive_multidict_class()
  244. with pytest.raises(KeyError, match="key"):
  245. d.popall("key")
  246. def test_large_multidict_resizing(
  247. self,
  248. case_sensitive_multidict_class: Type[MutableMultiMapping[str]],
  249. ) -> None:
  250. SIZE = 1024
  251. d = case_sensitive_multidict_class()
  252. for i in range(SIZE):
  253. d["key" + str(i)] = i
  254. for i in range(SIZE - 1):
  255. del d["key" + str(i)]
  256. assert {"key" + str(SIZE - 1): SIZE - 1} == d
  257. class TestCIMutableMultiDict:
  258. def test_getall(
  259. self,
  260. case_insensitive_multidict_class: Type[MutableMultiMapping[str]],
  261. ) -> None:
  262. d = case_insensitive_multidict_class([("KEY", "value1")], KEY="value2")
  263. assert d != {"KEY": "value1"}
  264. assert len(d) == 2
  265. assert d.getall("key") == ["value1", "value2"]
  266. with pytest.raises(KeyError, match="some_key"):
  267. d.getall("some_key")
  268. def test_ctor(
  269. self,
  270. case_insensitive_multidict_class: Type[MutableMultiMapping[str]],
  271. ) -> None:
  272. d = case_insensitive_multidict_class(k1="v1")
  273. assert "v1" == d["K1"]
  274. assert ("k1", "v1") in d.items()
  275. def test_setitem(
  276. self,
  277. case_insensitive_multidict_class: Type[MutableMultiMapping[str]],
  278. ) -> None:
  279. d = case_insensitive_multidict_class()
  280. d["k1"] = "v1"
  281. assert "v1" == d["K1"]
  282. assert ("k1", "v1") in d.items()
  283. def test_delitem(
  284. self,
  285. case_insensitive_multidict_class: Type[MutableMultiMapping[str]],
  286. ) -> None:
  287. d = case_insensitive_multidict_class()
  288. d["k1"] = "v1"
  289. assert "K1" in d
  290. del d["k1"]
  291. assert "K1" not in d
  292. def test_copy(
  293. self,
  294. case_insensitive_multidict_class: Type[MutableMultiMapping[str]],
  295. ) -> None:
  296. d1 = case_insensitive_multidict_class(key="KEY", a="b")
  297. d2 = d1.copy()
  298. assert d1 == d2
  299. assert d1.items() == d2.items()
  300. assert d1 is not d2
  301. def test__repr__(
  302. self,
  303. case_insensitive_multidict_class: Type[MutableMultiMapping[str]],
  304. ) -> None:
  305. d = case_insensitive_multidict_class()
  306. assert str(d) == "<%s()>" % case_insensitive_multidict_class.__name__
  307. d = case_insensitive_multidict_class([("KEY", "one"), ("KEY", "two")])
  308. expected = (
  309. f"<{case_insensitive_multidict_class.__name__}"
  310. "('KEY': 'one', 'KEY': 'two')>"
  311. )
  312. assert str(d) == expected
  313. def test_add(
  314. self,
  315. case_insensitive_multidict_class: Type[MutableMultiMapping[str]],
  316. ) -> None:
  317. d = case_insensitive_multidict_class()
  318. assert d == {}
  319. d["KEY"] = "one"
  320. assert ("KEY", "one") in d.items()
  321. assert d == case_insensitive_multidict_class({"Key": "one"})
  322. assert d.getall("key") == ["one"]
  323. d["KEY"] = "two"
  324. assert ("KEY", "two") in d.items()
  325. assert d == case_insensitive_multidict_class({"Key": "two"})
  326. assert d.getall("key") == ["two"]
  327. d.add("KEY", "one")
  328. assert ("KEY", "one") in d.items()
  329. assert 2 == len(d)
  330. assert d.getall("key") == ["two", "one"]
  331. d.add("FOO", "bar")
  332. assert ("FOO", "bar") in d.items()
  333. assert 3 == len(d)
  334. assert d.getall("foo") == ["bar"]
  335. d.add(key="test", value="test")
  336. assert ("test", "test") in d.items()
  337. assert 4 == len(d)
  338. assert d.getall("test") == ["test"]
  339. def test_extend(
  340. self,
  341. case_insensitive_multidict_class: Type[MutableMultiMapping[str]],
  342. ) -> None:
  343. d = case_insensitive_multidict_class()
  344. assert d == {}
  345. d.extend([("KEY", "one"), ("key", "two")], key=3, foo="bar")
  346. assert 4 == len(d)
  347. itms = d.items()
  348. # we can't guarantee order of kwargs
  349. assert ("KEY", "one") in itms
  350. assert ("key", "two") in itms
  351. assert ("key", 3) in itms
  352. assert ("foo", "bar") in itms
  353. other = case_insensitive_multidict_class(Bar="baz")
  354. assert other == {"Bar": "baz"}
  355. d.extend(other)
  356. assert ("Bar", "baz") in d.items()
  357. assert "bar" in d
  358. d.extend({"Foo": "moo"})
  359. assert ("Foo", "moo") in d.items()
  360. assert "foo" in d
  361. d.extend()
  362. assert 6 == len(d)
  363. with pytest.raises(TypeError):
  364. d.extend("foo", "bar")
  365. def test_extend_from_proxy(
  366. self,
  367. case_insensitive_multidict_class: Type[MutableMultiMapping[str]],
  368. case_insensitive_multidict_proxy_class: Type[MultiMapping[str]],
  369. ) -> None:
  370. d = case_insensitive_multidict_class([("a", "a"), ("b", "b")])
  371. proxy = case_insensitive_multidict_proxy_class(d)
  372. d2 = case_insensitive_multidict_class()
  373. d2.extend(proxy)
  374. assert [("a", "a"), ("b", "b")] == list(d2.items())
  375. def test_clear(
  376. self,
  377. case_insensitive_multidict_class: Type[MutableMultiMapping[str]],
  378. ) -> None:
  379. d = case_insensitive_multidict_class([("KEY", "one")], key="two", foo="bar")
  380. d.clear()
  381. assert d == {}
  382. assert list(d.items()) == []
  383. def test_del(
  384. self,
  385. case_insensitive_multidict_class: Type[MutableMultiMapping[str]],
  386. ) -> None:
  387. d = case_insensitive_multidict_class(
  388. [("KEY", "one"), ("key", "two")],
  389. foo="bar",
  390. )
  391. del d["key"]
  392. assert d == {"foo": "bar"}
  393. assert list(d.items()) == [("foo", "bar")]
  394. with pytest.raises(KeyError, match="key"):
  395. del d["key"]
  396. def test_set_default(
  397. self,
  398. case_insensitive_multidict_class: Type[MutableMultiMapping[str]],
  399. ) -> None:
  400. d = case_insensitive_multidict_class(
  401. [("KEY", "one"), ("key", "two")],
  402. foo="bar",
  403. )
  404. assert "one" == d.setdefault("key", "three")
  405. assert "three" == d.setdefault("otherkey", "three")
  406. assert "otherkey" in d
  407. assert ("otherkey", "three") in d.items()
  408. assert "three" == d["OTHERKEY"]
  409. def test_popitem(
  410. self,
  411. case_insensitive_multidict_class: Type[MutableMultiMapping[str]],
  412. ) -> None:
  413. d = case_insensitive_multidict_class()
  414. d.add("KEY", "val1")
  415. d.add("key", "val2")
  416. pair = d.popitem()
  417. assert ("KEY", "val1") == pair
  418. assert isinstance(pair[0], str)
  419. assert [("key", "val2")] == list(d.items())
  420. def test_popitem_empty_multidict(
  421. self,
  422. case_insensitive_multidict_class: Type[MutableMultiMapping[str]],
  423. ) -> None:
  424. d = case_insensitive_multidict_class()
  425. with pytest.raises(KeyError):
  426. d.popitem()
  427. def test_pop(
  428. self,
  429. case_insensitive_multidict_class: Type[MutableMultiMapping[str]],
  430. ) -> None:
  431. d = case_insensitive_multidict_class()
  432. d.add("KEY", "val1")
  433. d.add("key", "val2")
  434. assert "val1" == d.pop("KEY")
  435. assert {"key": "val2"} == d
  436. def test_pop_lowercase(
  437. self,
  438. case_insensitive_multidict_class: Type[MutableMultiMapping[str]],
  439. ) -> None:
  440. d = case_insensitive_multidict_class()
  441. d.add("KEY", "val1")
  442. d.add("key", "val2")
  443. assert "val1" == d.pop("key")
  444. assert {"key": "val2"} == d
  445. def test_pop_default(
  446. self,
  447. case_insensitive_multidict_class: Type[MutableMultiMapping[str]],
  448. ) -> None:
  449. d = case_insensitive_multidict_class(OTHER="val")
  450. assert "default" == d.pop("key", "default")
  451. assert "other" in d
  452. def test_pop_raises(
  453. self,
  454. case_insensitive_multidict_class: Type[MutableMultiMapping[str]],
  455. ) -> None:
  456. d = case_insensitive_multidict_class(OTHER="val")
  457. with pytest.raises(KeyError, match="KEY"):
  458. d.pop("KEY")
  459. assert "other" in d
  460. def test_extend_with_istr(
  461. self,
  462. case_insensitive_multidict_class: Type[MutableMultiMapping[str]],
  463. case_insensitive_str_class: Type[str],
  464. ) -> None:
  465. us = case_insensitive_str_class("aBc")
  466. d = case_insensitive_multidict_class()
  467. d.extend([(us, "val")])
  468. assert [("aBc", "val")] == list(d.items())
  469. def test_copy_istr(
  470. self,
  471. case_insensitive_multidict_class: Type[MutableMultiMapping[str]],
  472. case_insensitive_str_class: Type[str],
  473. ) -> None:
  474. d = case_insensitive_multidict_class({case_insensitive_str_class("Foo"): "bar"})
  475. d2 = d.copy()
  476. assert d == d2
  477. def test_eq(
  478. self,
  479. case_insensitive_multidict_class: Type[MutableMultiMapping[str]],
  480. ) -> None:
  481. d1 = case_insensitive_multidict_class(Key="val")
  482. d2 = case_insensitive_multidict_class(KEY="val")
  483. assert d1 == d2
  484. @pytest.mark.skipif(
  485. sys.implementation.name == "pypy",
  486. reason="getsizeof() is not implemented on PyPy",
  487. )
  488. def test_sizeof(
  489. self,
  490. case_insensitive_multidict_class: Type[MutableMultiMapping[str]],
  491. ) -> None:
  492. md = case_insensitive_multidict_class()
  493. s1 = sys.getsizeof(md)
  494. for i in string.ascii_lowercase:
  495. for j in string.ascii_uppercase:
  496. md[i + j] = i + j
  497. # multidict should be resized
  498. s2 = sys.getsizeof(md)
  499. assert s2 > s1
  500. @pytest.mark.skipif(
  501. sys.implementation.name == "pypy",
  502. reason="getsizeof() is not implemented on PyPy",
  503. )
  504. def test_min_sizeof(
  505. self,
  506. case_insensitive_multidict_class: Type[MutableMultiMapping[str]],
  507. ) -> None:
  508. md = case_insensitive_multidict_class()
  509. assert sys.getsizeof(md) < 1024
  510. def test_issue_620_items(
  511. self,
  512. case_insensitive_multidict_class: Type[MutableMultiMapping[str]],
  513. ) -> None:
  514. # https://github.com/aio-libs/multidict/issues/620
  515. d = case_insensitive_multidict_class({"a": "123, 456", "b": "789"})
  516. before_mutation_items = d.items()
  517. d["c"] = "000"
  518. # This causes an error on pypy.
  519. list(before_mutation_items)
  520. def test_issue_620_keys(
  521. self,
  522. case_insensitive_multidict_class: Type[MutableMultiMapping[str]],
  523. ) -> None:
  524. # https://github.com/aio-libs/multidict/issues/620
  525. d = case_insensitive_multidict_class({"a": "123, 456", "b": "789"})
  526. before_mutation_keys = d.keys()
  527. d["c"] = "000"
  528. # This causes an error on pypy.
  529. list(before_mutation_keys)
  530. def test_issue_620_values(
  531. self,
  532. case_insensitive_multidict_class: Type[MutableMultiMapping[str]],
  533. ) -> None:
  534. # https://github.com/aio-libs/multidict/issues/620
  535. d = case_insensitive_multidict_class({"a": "123, 456", "b": "789"})
  536. before_mutation_values = d.values()
  537. d["c"] = "000"
  538. # This causes an error on pypy.
  539. list(before_mutation_values)