test_url.py 45 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733
  1. from enum import Enum
  2. from urllib.parse import SplitResult
  3. import pytest
  4. from yarl import URL
  5. def test_inheritance():
  6. with pytest.raises(TypeError) as ctx:
  7. class MyURL(URL): # type: ignore[misc]
  8. pass
  9. assert (
  10. "Inheriting a class "
  11. "<class '__tests__.test_url.test_inheritance.<locals>.MyURL'> "
  12. "from URL is forbidden" == str(ctx.value)
  13. )
  14. def test_str_subclass():
  15. class S(str):
  16. pass
  17. assert str(URL(S("http://example.com"))) == "http://example.com"
  18. def test_is():
  19. u1 = URL("http://example.com")
  20. u2 = URL(u1)
  21. assert u1 is u2
  22. def test_bool():
  23. assert URL("http://example.com")
  24. assert not URL()
  25. assert not URL("")
  26. def test_absolute_url_without_host():
  27. with pytest.raises(ValueError):
  28. URL("http://:8080/")
  29. def test_url_is_not_str():
  30. url = URL("http://example.com")
  31. assert not isinstance(url, str)
  32. def test_str():
  33. url = URL("http://example.com:8888/path/to?a=1&b=2")
  34. assert str(url) == "http://example.com:8888/path/to?a=1&b=2"
  35. def test_repr():
  36. url = URL("http://example.com")
  37. assert "URL('http://example.com')" == repr(url)
  38. def test_origin():
  39. url = URL("http://user:password@example.com:8888/path/to?a=1&b=2")
  40. assert URL("http://example.com:8888") == url.origin()
  41. def test_origin_nonascii():
  42. url = URL("http://user:password@оун-упа.укр:8888/path/to?a=1&b=2")
  43. assert str(url.origin()) == "http://xn----8sb1bdhvc.xn--j1amh:8888"
  44. def test_origin_ipv6():
  45. url = URL("http://user:password@[::1]:8888/path/to?a=1&b=2")
  46. assert str(url.origin()) == "http://[::1]:8888"
  47. def test_origin_not_absolute_url():
  48. url = URL("/path/to?a=1&b=2")
  49. with pytest.raises(ValueError):
  50. url.origin()
  51. def test_origin_no_scheme():
  52. url = URL("//user:password@example.com:8888/path/to?a=1&b=2")
  53. with pytest.raises(ValueError):
  54. url.origin()
  55. def test_drop_dots():
  56. u = URL("http://example.com/path/../to")
  57. assert str(u) == "http://example.com/to"
  58. def test_abs_cmp():
  59. assert URL("http://example.com:8888") == URL("http://example.com:8888")
  60. assert URL("http://example.com:8888/") == URL("http://example.com:8888/")
  61. assert URL("http://example.com:8888/") == URL("http://example.com:8888")
  62. assert URL("http://example.com:8888") == URL("http://example.com:8888/")
  63. def test_abs_hash():
  64. url = URL("http://example.com:8888")
  65. url_trailing = URL("http://example.com:8888/")
  66. assert hash(url) == hash(url_trailing)
  67. # properties
  68. def test_scheme():
  69. url = URL("http://example.com")
  70. assert "http" == url.scheme
  71. def test_raw_user():
  72. url = URL("http://user@example.com")
  73. assert "user" == url.raw_user
  74. def test_raw_user_non_ascii():
  75. url = URL("http://бажан@example.com")
  76. assert "%D0%B1%D0%B0%D0%B6%D0%B0%D0%BD" == url.raw_user
  77. def test_no_user():
  78. url = URL("http://example.com")
  79. assert url.user is None
  80. def test_user_non_ascii():
  81. url = URL("http://бажан@example.com")
  82. assert "бажан" == url.user
  83. def test_raw_password():
  84. url = URL("http://user:password@example.com")
  85. assert "password" == url.raw_password
  86. def test_raw_password_non_ascii():
  87. url = URL("http://user:пароль@example.com")
  88. assert "%D0%BF%D0%B0%D1%80%D0%BE%D0%BB%D1%8C" == url.raw_password
  89. def test_password_non_ascii():
  90. url = URL("http://user:пароль@example.com")
  91. assert "пароль" == url.password
  92. def test_password_without_user():
  93. url = URL("http://:password@example.com")
  94. assert url.user is None
  95. assert "password" == url.password
  96. def test_user_empty_password():
  97. url = URL("http://user:@example.com")
  98. assert "user" == url.user
  99. assert "" == url.password
  100. def test_raw_host():
  101. url = URL("http://example.com")
  102. assert "example.com" == url.raw_host
  103. def test_raw_host_non_ascii():
  104. url = URL("http://оун-упа.укр")
  105. assert "xn----8sb1bdhvc.xn--j1amh" == url.raw_host
  106. def test_host_non_ascii():
  107. url = URL("http://оун-упа.укр")
  108. assert "оун-упа.укр" == url.host
  109. def test_localhost():
  110. url = URL("http://[::1]")
  111. assert "::1" == url.host
  112. def test_host_with_underscore():
  113. url = URL("http://abc_def.com")
  114. assert "abc_def.com" == url.host
  115. def test_raw_host_when_port_is_specified():
  116. url = URL("http://example.com:8888")
  117. assert "example.com" == url.raw_host
  118. def test_raw_host_from_str_with_ipv4():
  119. url = URL("http://127.0.0.1:80")
  120. assert url.raw_host == "127.0.0.1"
  121. def test_raw_host_from_str_with_ipv6():
  122. url = URL("http://[::1]:80")
  123. assert url.raw_host == "::1"
  124. def test_authority_full() -> None:
  125. url = URL("http://user:passwd@host.com:8080/path")
  126. assert url.raw_authority == "user:passwd@host.com:8080"
  127. assert url.authority == "user:passwd@host.com:8080"
  128. def test_authority_short() -> None:
  129. url = URL("http://host.com/path")
  130. assert url.raw_authority == "host.com"
  131. def test_authority_full_nonasci() -> None:
  132. url = URL("http://степан:пароль@слава.укр:8080/path")
  133. assert url.raw_authority == (
  134. "%D1%81%D1%82%D0%B5%D0%BF%D0%B0%D0%BD:"
  135. "%D0%BF%D0%B0%D1%80%D0%BE%D0%BB%D1%8C@"
  136. "xn--80aaf8a3a.xn--j1amh:8080"
  137. )
  138. assert url.authority == "степан:пароль@слава.укр:8080"
  139. def test_lowercase():
  140. url = URL("http://gitHUB.com")
  141. assert url.raw_host == "github.com"
  142. assert url.host == url.raw_host
  143. def test_lowercase_nonascii():
  144. url = URL("http://Слава.Укр")
  145. assert url.raw_host == "xn--80aaf8a3a.xn--j1amh"
  146. assert url.host == "слава.укр"
  147. def test_compressed_ipv6():
  148. url = URL("http://[1DEC:0:0:0::1]")
  149. assert url.raw_host == "1dec::1"
  150. assert url.host == url.raw_host
  151. def test_ipv4_zone():
  152. # I'm unsure if it is correct.
  153. url = URL("http://1.2.3.4%тест%42:123")
  154. assert url.raw_host == "1.2.3.4%тест%42"
  155. assert url.host == url.raw_host
  156. def test_port_for_explicit_port():
  157. url = URL("http://example.com:8888")
  158. assert 8888 == url.port
  159. def test_port_for_implicit_port():
  160. url = URL("http://example.com")
  161. assert 80 == url.port
  162. def test_port_for_relative_url():
  163. url = URL("/path/to")
  164. assert url.port is None
  165. def test_port_for_unknown_scheme():
  166. url = URL("unknown://example.com")
  167. assert url.port is None
  168. def test_explicit_port_for_explicit_port():
  169. url = URL("http://example.com:8888")
  170. assert 8888 == url.explicit_port
  171. def test_explicit_port_for_implicit_port():
  172. url = URL("http://example.com")
  173. assert url.explicit_port is None
  174. def test_explicit_port_for_relative_url():
  175. url = URL("/path/to")
  176. assert url.explicit_port is None
  177. def test_explicit_port_for_unknown_scheme():
  178. url = URL("unknown://example.com")
  179. assert url.explicit_port is None
  180. def test_raw_path_string_empty():
  181. url = URL("http://example.com")
  182. assert "/" == url.raw_path
  183. def test_raw_path():
  184. url = URL("http://example.com/path/to")
  185. assert "/path/to" == url.raw_path
  186. def test_raw_path_non_ascii():
  187. url = URL("http://example.com/шлях/сюди")
  188. assert "/%D1%88%D0%BB%D1%8F%D1%85/%D1%81%D1%8E%D0%B4%D0%B8" == url.raw_path
  189. def test_path_non_ascii():
  190. url = URL("http://example.com/шлях/сюди")
  191. assert "/шлях/сюди" == url.path
  192. def test_path_with_spaces():
  193. url = URL("http://example.com/a b/test")
  194. assert "/a b/test" == url.path
  195. url = URL("http://example.com/a b")
  196. assert "/a b" == url.path
  197. def test_raw_path_for_empty_url():
  198. url = URL()
  199. assert "" == url.raw_path
  200. def test_raw_path_for_colon_and_at():
  201. url = URL("http://example.com/path:abc@123")
  202. assert url.raw_path == "/path:abc@123"
  203. def test_raw_query_string():
  204. url = URL("http://example.com?a=1&b=2")
  205. assert url.raw_query_string == "a=1&b=2"
  206. def test_raw_query_string_non_ascii():
  207. url = URL("http://example.com?б=в&ю=к")
  208. assert url.raw_query_string == "%D0%B1=%D0%B2&%D1%8E=%D0%BA"
  209. def test_query_string_non_ascii():
  210. url = URL("http://example.com?б=в&ю=к")
  211. assert url.query_string == "б=в&ю=к"
  212. def test_path_qs():
  213. url = URL("http://example.com/")
  214. assert url.path_qs == "/"
  215. url = URL("http://example.com/?б=в&ю=к")
  216. assert url.path_qs == "/?б=в&ю=к"
  217. url = URL("http://example.com/path?б=в&ю=к")
  218. assert url.path_qs == "/path?б=в&ю=к"
  219. def test_raw_path_qs():
  220. url = URL("http://example.com/")
  221. assert url.raw_path_qs == "/"
  222. url = URL("http://example.com/?б=в&ю=к")
  223. assert url.raw_path_qs == "/?%D0%B1=%D0%B2&%D1%8E=%D0%BA"
  224. url = URL("http://example.com/path?б=в&ю=к")
  225. assert url.raw_path_qs == "/path?%D0%B1=%D0%B2&%D1%8E=%D0%BA"
  226. url = URL("http://example.com/шлях?a=1&b=2")
  227. assert url.raw_path_qs == "/%D1%88%D0%BB%D1%8F%D1%85?a=1&b=2"
  228. def test_query_string_spaces():
  229. url = URL("http://example.com?a+b=c+d&e=f+g")
  230. assert url.query_string == "a b=c d&e=f g"
  231. # raw fragment
  232. def test_raw_fragment_empty():
  233. url = URL("http://example.com")
  234. assert "" == url.raw_fragment
  235. def test_raw_fragment():
  236. url = URL("http://example.com/path#anchor")
  237. assert "anchor" == url.raw_fragment
  238. def test_raw_fragment_non_ascii():
  239. url = URL("http://example.com/path#якір")
  240. assert "%D1%8F%D0%BA%D1%96%D1%80" == url.raw_fragment
  241. def test_raw_fragment_safe():
  242. url = URL("http://example.com/path#a?b/c:d@e")
  243. assert "a?b/c:d@e" == url.raw_fragment
  244. def test_fragment_non_ascii():
  245. url = URL("http://example.com/path#якір")
  246. assert "якір" == url.fragment
  247. def test_raw_parts_empty():
  248. url = URL("http://example.com")
  249. assert ("/",) == url.raw_parts
  250. def test_raw_parts():
  251. url = URL("http://example.com/path/to")
  252. assert ("/", "path", "to") == url.raw_parts
  253. def test_raw_parts_without_path():
  254. url = URL("http://example.com")
  255. assert ("/",) == url.raw_parts
  256. def test_raw_path_parts_with_2F_in_path():
  257. url = URL("http://example.com/path%2Fto/three")
  258. assert ("/", "path%2Fto", "three") == url.raw_parts
  259. def test_raw_path_parts_with_2f_in_path():
  260. url = URL("http://example.com/path%2fto/three")
  261. assert ("/", "path%2Fto", "three") == url.raw_parts
  262. def test_raw_parts_for_relative_path():
  263. url = URL("path/to")
  264. assert ("path", "to") == url.raw_parts
  265. def test_raw_parts_for_relative_path_starting_from_slash():
  266. url = URL("/path/to")
  267. assert ("/", "path", "to") == url.raw_parts
  268. def test_raw_parts_for_relative_double_path():
  269. url = URL("path/to")
  270. assert ("path", "to") == url.raw_parts
  271. def test_parts_for_empty_url():
  272. url = URL()
  273. assert ("",) == url.raw_parts
  274. def test_raw_parts_non_ascii():
  275. url = URL("http://example.com/шлях/сюди")
  276. assert (
  277. "/",
  278. "%D1%88%D0%BB%D1%8F%D1%85",
  279. "%D1%81%D1%8E%D0%B4%D0%B8",
  280. ) == url.raw_parts
  281. def test_parts_non_ascii():
  282. url = URL("http://example.com/шлях/сюди")
  283. assert ("/", "шлях", "сюди") == url.parts
  284. def test_name_for_empty_url():
  285. url = URL()
  286. assert "" == url.raw_name
  287. def test_raw_name():
  288. url = URL("http://example.com/path/to#frag")
  289. assert "to" == url.raw_name
  290. def test_raw_name_root():
  291. url = URL("http://example.com/#frag")
  292. assert "" == url.raw_name
  293. def test_raw_name_root2():
  294. url = URL("http://example.com")
  295. assert "" == url.raw_name
  296. def test_raw_name_root3():
  297. url = URL("http://example.com/")
  298. assert "" == url.raw_name
  299. def test_relative_raw_name():
  300. url = URL("path/to")
  301. assert "to" == url.raw_name
  302. def test_relative_raw_name_starting_from_slash():
  303. url = URL("/path/to")
  304. assert "to" == url.raw_name
  305. def test_relative_raw_name_slash():
  306. url = URL("/")
  307. assert "" == url.raw_name
  308. def test_name_non_ascii():
  309. url = URL("http://example.com/шлях")
  310. assert url.name == "шлях"
  311. def test_suffix_for_empty_url():
  312. url = URL()
  313. assert "" == url.raw_suffix
  314. def test_raw_suffix():
  315. url = URL("http://example.com/path/to.txt#frag")
  316. assert ".txt" == url.raw_suffix
  317. def test_raw_suffix_root():
  318. url = URL("http://example.com/#frag")
  319. assert "" == url.raw_suffix
  320. def test_raw_suffix_root2():
  321. url = URL("http://example.com")
  322. assert "" == url.raw_suffix
  323. def test_raw_suffix_root3():
  324. url = URL("http://example.com/")
  325. assert "" == url.raw_suffix
  326. def test_relative_raw_suffix():
  327. url = URL("path/to")
  328. assert "" == url.raw_suffix
  329. def test_relative_raw_suffix_starting_from_slash():
  330. url = URL("/path/to")
  331. assert "" == url.raw_suffix
  332. def test_relative_raw_suffix_dot():
  333. url = URL(".")
  334. assert "" == url.raw_suffix
  335. def test_suffix_non_ascii():
  336. url = URL("http://example.com/шлях.суфікс")
  337. assert url.suffix == ".суфікс"
  338. def test_suffix_with_empty_name():
  339. url = URL("http://example.com/.hgrc")
  340. assert "" == url.raw_suffix
  341. def test_suffix_multi_dot():
  342. url = URL("http://example.com/doc.tar.gz")
  343. assert ".gz" == url.raw_suffix
  344. def test_suffix_with_dot_name():
  345. url = URL("http://example.com/doc.")
  346. assert "" == url.raw_suffix
  347. def test_suffixes_for_empty_url():
  348. url = URL()
  349. assert () == url.raw_suffixes
  350. def test_raw_suffixes():
  351. url = URL("http://example.com/path/to.txt#frag")
  352. assert (".txt",) == url.raw_suffixes
  353. def test_raw_suffixes_root():
  354. url = URL("http://example.com/#frag")
  355. assert () == url.raw_suffixes
  356. def test_raw_suffixes_root2():
  357. url = URL("http://example.com")
  358. assert () == url.raw_suffixes
  359. def test_raw_suffixes_root3():
  360. url = URL("http://example.com/")
  361. assert () == url.raw_suffixes
  362. def test_relative_raw_suffixes():
  363. url = URL("path/to")
  364. assert () == url.raw_suffixes
  365. def test_relative_raw_suffixes_starting_from_slash():
  366. url = URL("/path/to")
  367. assert () == url.raw_suffixes
  368. def test_relative_raw_suffixes_dot():
  369. url = URL(".")
  370. assert () == url.raw_suffixes
  371. def test_suffixes_non_ascii():
  372. url = URL("http://example.com/шлях.суфікс")
  373. assert url.suffixes == (".суфікс",)
  374. def test_suffixes_with_empty_name():
  375. url = URL("http://example.com/.hgrc")
  376. assert () == url.raw_suffixes
  377. def test_suffixes_multi_dot():
  378. url = URL("http://example.com/doc.tar.gz")
  379. assert (".tar", ".gz") == url.raw_suffixes
  380. def test_suffixes_with_dot_name():
  381. url = URL("http://example.com/doc.")
  382. assert () == url.raw_suffixes
  383. def test_plus_in_path():
  384. url = URL("http://example.com/test/x+y%2Bz/:+%2B/")
  385. assert "/test/x+y+z/:++/" == url.path
  386. def test_nonascii_in_qs():
  387. url = URL("http://example.com")
  388. url2 = url.with_query({"f\xf8\xf8": "f\xf8\xf8"})
  389. assert "http://example.com/?f%C3%B8%C3%B8=f%C3%B8%C3%B8" == str(url2)
  390. def test_percent_encoded_in_qs():
  391. url = URL("http://example.com")
  392. url2 = url.with_query({"k%cf%80": "v%cf%80"})
  393. assert str(url2) == "http://example.com/?k%25cf%2580=v%25cf%2580"
  394. assert url2.raw_query_string == "k%25cf%2580=v%25cf%2580"
  395. assert url2.query_string == "k%cf%80=v%cf%80"
  396. assert url2.query == {"k%cf%80": "v%cf%80"}
  397. # modifiers
  398. def test_parent_raw_path():
  399. url = URL("http://example.com/path/to")
  400. assert url.parent.raw_path == "/path"
  401. def test_parent_raw_parts():
  402. url = URL("http://example.com/path/to")
  403. assert url.parent.raw_parts == ("/", "path")
  404. def test_double_parent_raw_path():
  405. url = URL("http://example.com/path/to")
  406. assert url.parent.parent.raw_path == "/"
  407. def test_empty_parent_raw_path():
  408. url = URL("http://example.com/")
  409. assert url.parent.parent.raw_path == "/"
  410. def test_empty_parent_raw_path2():
  411. url = URL("http://example.com")
  412. assert url.parent.parent.raw_path == "/"
  413. def test_clear_fragment_on_getting_parent():
  414. url = URL("http://example.com/path/to#frag")
  415. assert URL("http://example.com/path") == url.parent
  416. def test_clear_fragment_on_getting_parent_toplevel():
  417. url = URL("http://example.com/#frag")
  418. assert URL("http://example.com/") == url.parent
  419. def test_clear_query_on_getting_parent():
  420. url = URL("http://example.com/path/to?a=b")
  421. assert URL("http://example.com/path") == url.parent
  422. def test_clear_query_on_getting_parent_toplevel():
  423. url = URL("http://example.com/?a=b")
  424. assert URL("http://example.com/") == url.parent
  425. # truediv
  426. def test_div_root():
  427. url = URL("http://example.com") / "path" / "to"
  428. assert str(url) == "http://example.com/path/to"
  429. assert url.raw_path == "/path/to"
  430. def test_div_root_with_slash():
  431. url = URL("http://example.com/") / "path" / "to"
  432. assert str(url) == "http://example.com/path/to"
  433. assert url.raw_path == "/path/to"
  434. def test_div():
  435. url = URL("http://example.com/path") / "to"
  436. assert str(url) == "http://example.com/path/to"
  437. assert url.raw_path == "/path/to"
  438. def test_div_with_slash():
  439. url = URL("http://example.com/path/") / "to"
  440. assert str(url) == "http://example.com/path/to"
  441. assert url.raw_path == "/path/to"
  442. def test_div_path_starting_from_slash_is_forbidden():
  443. url = URL("http://example.com/path/")
  444. with pytest.raises(ValueError):
  445. url / "/to/others"
  446. class StrEnum(str, Enum):
  447. spam = "ham"
  448. def __str__(self):
  449. return self.value
  450. def test_div_path_srting_subclass():
  451. url = URL("http://example.com/path/") / StrEnum.spam
  452. assert str(url) == "http://example.com/path/ham"
  453. def test_div_bad_type():
  454. url = URL("http://example.com/path/")
  455. with pytest.raises(TypeError):
  456. url / 3
  457. def test_div_cleanup_query_and_fragment():
  458. url = URL("http://example.com/path?a=1#frag")
  459. assert str(url / "to") == "http://example.com/path/to"
  460. def test_div_for_empty_url():
  461. url = URL() / "a"
  462. assert url.raw_parts == ("a",)
  463. def test_div_for_relative_url():
  464. url = URL("a") / "b"
  465. assert url.raw_parts == ("a", "b")
  466. def test_div_for_relative_url_started_with_slash():
  467. url = URL("/a") / "b"
  468. assert url.raw_parts == ("/", "a", "b")
  469. def test_div_non_ascii():
  470. url = URL("http://example.com/сюди")
  471. url2 = url / "туди"
  472. assert url2.path == "/сюди/туди"
  473. assert url2.raw_path == "/%D1%81%D1%8E%D0%B4%D0%B8/%D1%82%D1%83%D0%B4%D0%B8"
  474. assert url2.parts == ("/", "сюди", "туди")
  475. assert url2.raw_parts == (
  476. "/",
  477. "%D1%81%D1%8E%D0%B4%D0%B8",
  478. "%D1%82%D1%83%D0%B4%D0%B8",
  479. )
  480. def test_div_percent_encoded():
  481. url = URL("http://example.com/path")
  482. url2 = url / "%cf%80"
  483. assert url2.path == "/path/%cf%80"
  484. assert url2.raw_path == "/path/%25cf%2580"
  485. assert url2.parts == ("/", "path", "%cf%80")
  486. assert url2.raw_parts == ("/", "path", "%25cf%2580")
  487. def test_div_with_colon_and_at():
  488. url = URL("http://example.com/base") / "path:abc@123"
  489. assert url.raw_path == "/base/path:abc@123"
  490. def test_div_with_dots():
  491. url = URL("http://example.com/base") / "../path/./to"
  492. assert url.raw_path == "/path/to"
  493. # joinpath
  494. @pytest.mark.parametrize(
  495. "base,to_join,expected",
  496. [
  497. pytest.param("", ("path", "to"), "http://example.com/path/to", id="root"),
  498. pytest.param(
  499. "/", ("path", "to"), "http://example.com/path/to", id="root-with-slash"
  500. ),
  501. pytest.param("/path", ("to",), "http://example.com/path/to", id="path"),
  502. pytest.param(
  503. "/path/", ("to",), "http://example.com/path/to", id="path-with-slash"
  504. ),
  505. pytest.param(
  506. "/path?a=1#frag",
  507. ("to",),
  508. "http://example.com/path/to",
  509. id="cleanup-query-and-fragment",
  510. ),
  511. pytest.param("", ("path/",), "http://example.com/path/", id="trailing-slash"),
  512. pytest.param(
  513. "", ("path/", "to/"), "http://example.com/path/to/", id="duplicate-slash"
  514. ),
  515. pytest.param("", (), "http://example.com", id="empty-segments"),
  516. pytest.param(
  517. "/", ("path/",), "http://example.com/path/", id="base-slash-trailing-slash"
  518. ),
  519. pytest.param(
  520. "/",
  521. ("path/", "to/"),
  522. "http://example.com/path/to/",
  523. id="base-slash-duplicate-slash",
  524. ),
  525. pytest.param("/", (), "http://example.com", id="base-slash-empty-segments"),
  526. ],
  527. )
  528. def test_joinpath(base, to_join, expected):
  529. url = URL(f"http://example.com{base}")
  530. assert str(url.joinpath(*to_join)) == expected
  531. @pytest.mark.parametrize(
  532. "url,to_join,expected",
  533. [
  534. pytest.param(URL(), ("a",), ("a",), id="empty-url"),
  535. pytest.param(URL("a"), ("b",), ("a", "b"), id="relative-path"),
  536. pytest.param(URL("a"), ("b", "", "c"), ("a", "b", "c"), id="empty-element"),
  537. pytest.param(URL("/a"), ("b"), ("/", "a", "b"), id="absolute-path"),
  538. pytest.param(URL(), ("a/",), ("a", ""), id="trailing-slash"),
  539. pytest.param(URL(), ("a/", "b/"), ("a", "b", ""), id="duplicate-slash"),
  540. pytest.param(URL(), (), ("",), id="empty-segments"),
  541. ],
  542. )
  543. def test_joinpath_relative(url, to_join, expected):
  544. assert url.joinpath(*to_join).raw_parts == expected
  545. @pytest.mark.parametrize(
  546. "url,to_join,encoded,e_path,e_raw_path,e_parts,e_raw_parts",
  547. [
  548. pytest.param(
  549. "http://example.com/сюди",
  550. ("туди",),
  551. False,
  552. "/сюди/туди",
  553. "/%D1%81%D1%8E%D0%B4%D0%B8/%D1%82%D1%83%D0%B4%D0%B8",
  554. ("/", "сюди", "туди"),
  555. ("/", "%D1%81%D1%8E%D0%B4%D0%B8", "%D1%82%D1%83%D0%B4%D0%B8"),
  556. id="non-ascii",
  557. ),
  558. pytest.param(
  559. "http://example.com/path",
  560. ("%cf%80",),
  561. False,
  562. "/path/%cf%80",
  563. "/path/%25cf%2580",
  564. ("/", "path", "%cf%80"),
  565. ("/", "path", "%25cf%2580"),
  566. id="percent-encoded",
  567. ),
  568. pytest.param(
  569. "http://example.com/path",
  570. ("%cf%80",),
  571. True,
  572. "/path/π",
  573. "/path/%cf%80",
  574. ("/", "path", "π"),
  575. ("/", "path", "%cf%80"),
  576. id="encoded-percent-encoded",
  577. ),
  578. ],
  579. )
  580. def test_joinpath_encoding(
  581. url, to_join, encoded, e_path, e_raw_path, e_parts, e_raw_parts
  582. ):
  583. joined = URL(url).joinpath(*to_join, encoded=encoded)
  584. assert joined.path == e_path
  585. assert joined.raw_path == e_raw_path
  586. assert joined.parts == e_parts
  587. assert joined.raw_parts == e_raw_parts
  588. @pytest.mark.parametrize(
  589. "to_join,expected",
  590. [
  591. pytest.param(("path:abc@123",), "/base/path:abc@123", id="with-colon-and-at"),
  592. pytest.param(("..", "path", ".", "to"), "/path/to", id="with-dots"),
  593. ],
  594. )
  595. def test_joinpath_edgecases(to_join, expected):
  596. url = URL("http://example.com/base").joinpath(*to_join)
  597. assert url.raw_path == expected
  598. def test_joinpath_path_starting_from_slash_is_forbidden():
  599. url = URL("http://example.com/path/")
  600. with pytest.raises(
  601. ValueError, match="Appending path .* starting from slash is forbidden"
  602. ):
  603. assert url.joinpath("/to/others")
  604. # with_path
  605. def test_with_path():
  606. url = URL("http://example.com")
  607. url2 = url.with_path("/test")
  608. assert str(url2) == "http://example.com/test"
  609. assert url2.raw_path == "/test"
  610. assert url2.path == "/test"
  611. def test_with_path_nonascii():
  612. url = URL("http://example.com")
  613. url2 = url.with_path("/π")
  614. assert str(url2) == "http://example.com/%CF%80"
  615. assert url2.raw_path == "/%CF%80"
  616. assert url2.path == "/π"
  617. def test_with_path_percent_encoded():
  618. url = URL("http://example.com")
  619. url2 = url.with_path("/%cf%80")
  620. assert str(url2) == "http://example.com/%25cf%2580"
  621. assert url2.raw_path == "/%25cf%2580"
  622. assert url2.path == "/%cf%80"
  623. def test_with_path_encoded():
  624. url = URL("http://example.com")
  625. url2 = url.with_path("/test", encoded=True)
  626. assert str(url2) == "http://example.com/test"
  627. assert url2.raw_path == "/test"
  628. assert url2.path == "/test"
  629. def test_with_path_encoded_nonascii():
  630. url = URL("http://example.com")
  631. url2 = url.with_path("/π", encoded=True)
  632. assert str(url2) == "http://example.com/π"
  633. assert url2.raw_path == "/π"
  634. assert url2.path == "/π"
  635. def test_with_path_encoded_percent_encoded():
  636. url = URL("http://example.com")
  637. url2 = url.with_path("/%cf%80", encoded=True)
  638. assert str(url2) == "http://example.com/%cf%80"
  639. assert url2.raw_path == "/%cf%80"
  640. assert url2.path == "/π"
  641. def test_with_path_dots():
  642. url = URL("http://example.com")
  643. assert str(url.with_path("/test/.")) == "http://example.com/test/"
  644. def test_with_path_relative():
  645. url = URL("/path")
  646. assert str(url.with_path("/new")) == "/new"
  647. def test_with_path_query():
  648. url = URL("http://example.com?a=b")
  649. assert str(url.with_path("/test")) == "http://example.com/test"
  650. def test_with_path_fragment():
  651. url = URL("http://example.com#frag")
  652. assert str(url.with_path("/test")) == "http://example.com/test"
  653. def test_with_path_empty():
  654. url = URL("http://example.com/test")
  655. assert str(url.with_path("")) == "http://example.com"
  656. def test_with_path_leading_slash():
  657. url = URL("http://example.com")
  658. assert url.with_path("test").path == "/test"
  659. # with_fragment
  660. def test_with_fragment():
  661. url = URL("http://example.com")
  662. url2 = url.with_fragment("frag")
  663. assert str(url2) == "http://example.com/#frag"
  664. assert url2.raw_fragment == "frag"
  665. assert url2.fragment == "frag"
  666. def test_with_fragment_safe():
  667. url = URL("http://example.com")
  668. u2 = url.with_fragment("a:b?c@d/e")
  669. assert str(u2) == "http://example.com/#a:b?c@d/e"
  670. def test_with_fragment_non_ascii():
  671. url = URL("http://example.com")
  672. url2 = url.with_fragment("фрагм")
  673. assert url2.raw_fragment == "%D1%84%D1%80%D0%B0%D0%B3%D0%BC"
  674. assert url2.fragment == "фрагм"
  675. def test_with_fragment_percent_encoded():
  676. url = URL("http://example.com")
  677. url2 = url.with_fragment("%cf%80")
  678. assert str(url2) == "http://example.com/#%25cf%2580"
  679. assert url2.raw_fragment == "%25cf%2580"
  680. assert url2.fragment == "%cf%80"
  681. def test_with_fragment_None():
  682. url = URL("http://example.com/path#frag")
  683. url2 = url.with_fragment(None)
  684. assert str(url2) == "http://example.com/path"
  685. def test_with_fragment_None_matching():
  686. url = URL("http://example.com/path")
  687. url2 = url.with_fragment(None)
  688. assert url is url2
  689. def test_with_fragment_matching():
  690. url = URL("http://example.com/path#frag")
  691. url2 = url.with_fragment("frag")
  692. assert url is url2
  693. def test_with_fragment_bad_type():
  694. url = URL("http://example.com")
  695. with pytest.raises(TypeError):
  696. url.with_fragment(123)
  697. # with_name
  698. def test_with_name():
  699. url = URL("http://example.com/a/b")
  700. assert url.raw_parts == ("/", "a", "b")
  701. url2 = url.with_name("c")
  702. assert url2.raw_parts == ("/", "a", "c")
  703. assert url2.parts == ("/", "a", "c")
  704. assert url2.raw_path == "/a/c"
  705. assert url2.path == "/a/c"
  706. def test_with_name_for_naked_path():
  707. url = URL("http://example.com")
  708. url2 = url.with_name("a")
  709. assert url2.raw_parts == ("/", "a")
  710. def test_with_name_for_relative_path():
  711. url = URL("a")
  712. url2 = url.with_name("b")
  713. assert url2.raw_parts == ("b",)
  714. def test_with_name_for_relative_path2():
  715. url = URL("a/b")
  716. url2 = url.with_name("c")
  717. assert url2.raw_parts == ("a", "c")
  718. def test_with_name_for_relative_path_starting_from_slash():
  719. url = URL("/a")
  720. url2 = url.with_name("b")
  721. assert url2.raw_parts == ("/", "b")
  722. def test_with_name_for_relative_path_starting_from_slash2():
  723. url = URL("/a/b")
  724. url2 = url.with_name("c")
  725. assert url2.raw_parts == ("/", "a", "c")
  726. def test_with_name_empty():
  727. url = URL("http://example.com/path/to").with_name("")
  728. assert str(url) == "http://example.com/path/"
  729. def test_with_name_non_ascii():
  730. url = URL("http://example.com/path").with_name("шлях")
  731. assert url.path == "/шлях"
  732. assert url.raw_path == "/%D1%88%D0%BB%D1%8F%D1%85"
  733. assert url.parts == ("/", "шлях")
  734. assert url.raw_parts == ("/", "%D1%88%D0%BB%D1%8F%D1%85")
  735. def test_with_name_percent_encoded():
  736. url = URL("http://example.com/path")
  737. url2 = url.with_name("%cf%80")
  738. assert url2.raw_parts == ("/", "%25cf%2580")
  739. assert url2.parts == ("/", "%cf%80")
  740. assert url2.raw_path == "/%25cf%2580"
  741. assert url2.path == "/%cf%80"
  742. def test_with_name_with_slash():
  743. with pytest.raises(ValueError):
  744. URL("http://example.com").with_name("a/b")
  745. def test_with_name_non_str():
  746. with pytest.raises(TypeError):
  747. URL("http://example.com").with_name(123)
  748. def test_with_name_within_colon_and_at():
  749. url = URL("http://example.com/oldpath").with_name("path:abc@123")
  750. assert url.raw_path == "/path:abc@123"
  751. def test_with_name_dot():
  752. with pytest.raises(ValueError):
  753. URL("http://example.com").with_name(".")
  754. def test_with_name_double_dot():
  755. with pytest.raises(ValueError):
  756. URL("http://example.com").with_name("..")
  757. # with_suffix
  758. def test_with_suffix():
  759. url = URL("http://example.com/a/b")
  760. assert url.raw_parts == ("/", "a", "b")
  761. url2 = url.with_suffix(".c")
  762. assert url2.raw_parts == ("/", "a", "b.c")
  763. assert url2.parts == ("/", "a", "b.c")
  764. assert url2.raw_path == "/a/b.c"
  765. assert url2.path == "/a/b.c"
  766. def test_with_suffix_for_naked_path():
  767. url = URL("http://example.com")
  768. with pytest.raises(ValueError) as excinfo:
  769. url.with_suffix(".a")
  770. (msg,) = excinfo.value.args
  771. assert msg == f"{url!r} has an empty name"
  772. def test_with_suffix_for_relative_path():
  773. url = URL("a")
  774. url2 = url.with_suffix(".b")
  775. assert url2.raw_parts == ("a.b",)
  776. def test_with_suffix_for_relative_path2():
  777. url = URL("a/b")
  778. url2 = url.with_suffix(".c")
  779. assert url2.raw_parts == ("a", "b.c")
  780. def test_with_suffix_for_relative_path_starting_from_slash():
  781. url = URL("/a")
  782. url2 = url.with_suffix(".b")
  783. assert url2.raw_parts == ("/", "a.b")
  784. def test_with_suffix_for_relative_path_starting_from_slash2():
  785. url = URL("/a/b")
  786. url2 = url.with_suffix(".c")
  787. assert url2.raw_parts == ("/", "a", "b.c")
  788. def test_with_suffix_empty():
  789. url = URL("http://example.com/path/to").with_suffix("")
  790. assert str(url) == "http://example.com/path/to"
  791. def test_with_suffix_non_ascii():
  792. url = URL("http://example.com/path").with_suffix(".шлях")
  793. assert url.path == "/path.шлях"
  794. assert url.raw_path == "/path.%D1%88%D0%BB%D1%8F%D1%85"
  795. assert url.parts == ("/", "path.шлях")
  796. assert url.raw_parts == ("/", "path.%D1%88%D0%BB%D1%8F%D1%85")
  797. def test_with_suffix_percent_encoded():
  798. url = URL("http://example.com/path")
  799. url2 = url.with_suffix(".%cf%80")
  800. assert url2.raw_parts == ("/", "path.%25cf%2580")
  801. assert url2.parts == ("/", "path.%cf%80")
  802. assert url2.raw_path == "/path.%25cf%2580"
  803. assert url2.path == "/path.%cf%80"
  804. def test_with_suffix_without_dot():
  805. with pytest.raises(ValueError) as excinfo:
  806. URL("http://example.com/a").with_suffix("b")
  807. (msg,) = excinfo.value.args
  808. assert msg == "Invalid suffix 'b'"
  809. def test_with_suffix_non_str():
  810. with pytest.raises(TypeError) as excinfo:
  811. URL("http://example.com").with_suffix(123)
  812. (msg,) = excinfo.value.args
  813. assert msg == "Invalid suffix type"
  814. def test_with_suffix_dot():
  815. with pytest.raises(ValueError) as excinfo:
  816. URL("http://example.com").with_suffix(".")
  817. (msg,) = excinfo.value.args
  818. assert msg == "Invalid suffix '.'"
  819. def test_with_suffix_with_slash():
  820. with pytest.raises(ValueError) as excinfo:
  821. URL("http://example.com/a").with_suffix("/.b")
  822. (msg,) = excinfo.value.args
  823. assert msg == "Invalid suffix '/.b'"
  824. def test_with_suffix_with_slash2():
  825. with pytest.raises(ValueError) as excinfo:
  826. URL("http://example.com/a").with_suffix(".b/.d")
  827. (msg,) = excinfo.value.args
  828. assert msg == "Slash in name is not allowed"
  829. def test_with_suffix_replace():
  830. url = URL("/a.b")
  831. url2 = url.with_suffix(".c")
  832. assert url2.raw_parts == ("/", "a.c")
  833. # is_absolute
  834. def test_is_absolute_for_relative_url():
  835. url = URL("/path/to")
  836. assert not url.is_absolute()
  837. def test_is_absolute_for_absolute_url():
  838. url = URL("http://example.com")
  839. assert url.is_absolute()
  840. def test_is_non_absolute_for_empty_url():
  841. url = URL()
  842. assert not url.is_absolute()
  843. def test_is_non_absolute_for_empty_url2():
  844. url = URL("")
  845. assert not url.is_absolute()
  846. def test_is_absolute_path_starting_from_double_slash():
  847. url = URL("//www.python.org")
  848. assert url.is_absolute()
  849. # is_default_port
  850. def test_is_default_port_for_relative_url():
  851. url = URL("/path/to")
  852. assert not url.is_default_port()
  853. def test_is_default_port_for_absolute_url_without_port():
  854. url = URL("http://example.com")
  855. assert url.is_default_port()
  856. def test_is_default_port_for_absolute_url_with_default_port():
  857. url = URL("http://example.com:80")
  858. assert url.is_default_port()
  859. def test_is_default_port_for_absolute_url_with_nondefault_port():
  860. url = URL("http://example.com:8080")
  861. assert not url.is_default_port()
  862. def test_is_default_port_for_unknown_scheme():
  863. url = URL("unknown://example.com:8080")
  864. assert not url.is_default_port()
  865. #
  866. def test_no_scheme():
  867. url = URL("example.com")
  868. assert url.raw_host is None
  869. assert url.raw_path == "example.com"
  870. assert str(url) == "example.com"
  871. def test_no_scheme2():
  872. url = URL("example.com/a/b")
  873. assert url.raw_host is None
  874. assert url.raw_path == "example.com/a/b"
  875. assert str(url) == "example.com/a/b"
  876. def test_from_non_allowed():
  877. with pytest.raises(TypeError):
  878. URL(1234)
  879. def test_from_idna():
  880. url = URL("http://xn--jxagkqfkduily1i.eu")
  881. assert "http://xn--jxagkqfkduily1i.eu" == str(url)
  882. url = URL("http://xn--einla-pqa.de/") # needs idna 2008
  883. assert "http://xn--einla-pqa.de/" == str(url)
  884. def test_to_idna():
  885. url = URL("http://εμπορικόσήμα.eu")
  886. assert "http://xn--jxagkqfkduily1i.eu" == str(url)
  887. url = URL("http://einlaß.de/")
  888. assert "http://xn--einla-pqa.de/" == str(url)
  889. def test_from_ascii_login():
  890. url = URL("http://" "%D0%B2%D0%B0%D1%81%D1%8F" "@host:1234/")
  891. assert ("http://" "%D0%B2%D0%B0%D1%81%D1%8F" "@host:1234/") == str(url)
  892. def test_from_non_ascii_login():
  893. url = URL("http://бажан@host:1234/")
  894. assert ("http://%D0%B1%D0%B0%D0%B6%D0%B0%D0%BD@host:1234/") == str(url)
  895. def test_from_ascii_login_and_password():
  896. url = URL(
  897. "http://"
  898. "%D0%B2%D0%B0%D1%81%D1%8F"
  899. ":%D0%BF%D0%B0%D1%80%D0%BE%D0%BB%D1%8C"
  900. "@host:1234/"
  901. )
  902. assert (
  903. "http://"
  904. "%D0%B2%D0%B0%D1%81%D1%8F"
  905. ":%D0%BF%D0%B0%D1%80%D0%BE%D0%BB%D1%8C"
  906. "@host:1234/"
  907. ) == str(url)
  908. def test_from_non_ascii_login_and_password():
  909. url = URL("http://бажан:пароль@host:1234/")
  910. assert (
  911. "http://"
  912. "%D0%B1%D0%B0%D0%B6%D0%B0%D0%BD"
  913. ":%D0%BF%D0%B0%D1%80%D0%BE%D0%BB%D1%8C"
  914. "@host:1234/"
  915. ) == str(url)
  916. def test_from_ascii_path():
  917. url = URL("http://example.com/" "%D0%BF%D1%83%D1%82%D1%8C/%D1%82%D1%83%D0%B4%D0%B0")
  918. assert (
  919. "http://example.com/" "%D0%BF%D1%83%D1%82%D1%8C/%D1%82%D1%83%D0%B4%D0%B0"
  920. ) == str(url)
  921. def test_from_ascii_path_lower_case():
  922. url = URL("http://example.com/" "%d0%bf%d1%83%d1%82%d1%8c/%d1%82%d1%83%d0%b4%d0%b0")
  923. assert (
  924. "http://example.com/" "%D0%BF%D1%83%D1%82%D1%8C/%D1%82%D1%83%D0%B4%D0%B0"
  925. ) == str(url)
  926. def test_from_non_ascii_path():
  927. url = URL("http://example.com/шлях/туди")
  928. assert (
  929. "http://example.com/%D1%88%D0%BB%D1%8F%D1%85/%D1%82%D1%83%D0%B4%D0%B8"
  930. ) == str(url)
  931. def test_bytes():
  932. url = URL("http://example.com/шлях/туди")
  933. assert (
  934. b"http://example.com/%D1%88%D0%BB%D1%8F%D1%85/%D1%82%D1%83%D0%B4%D0%B8"
  935. == bytes(url)
  936. )
  937. def test_from_ascii_query_parts():
  938. url = URL(
  939. "http://example.com/"
  940. "?%D0%BF%D0%B0%D1%80%D0%B0%D0%BC"
  941. "=%D0%B7%D0%BD%D0%B0%D1%87"
  942. )
  943. assert (
  944. "http://example.com/"
  945. "?%D0%BF%D0%B0%D1%80%D0%B0%D0%BC"
  946. "=%D0%B7%D0%BD%D0%B0%D1%87"
  947. ) == str(url)
  948. def test_from_non_ascii_query_parts():
  949. url = URL("http://example.com/?парам=знач")
  950. assert (
  951. "http://example.com/"
  952. "?%D0%BF%D0%B0%D1%80%D0%B0%D0%BC"
  953. "=%D0%B7%D0%BD%D0%B0%D1%87"
  954. ) == str(url)
  955. def test_from_non_ascii_query_parts2():
  956. url = URL("http://example.com/?п=з&ю=б")
  957. assert "http://example.com/?%D0%BF=%D0%B7&%D1%8E=%D0%B1" == str(url)
  958. def test_from_ascii_fragment():
  959. url = URL("http://example.com/" "#%D1%84%D1%80%D0%B0%D0%B3%D0%BC%D0%B5%D0%BD%D1%82")
  960. assert (
  961. "http://example.com/" "#%D1%84%D1%80%D0%B0%D0%B3%D0%BC%D0%B5%D0%BD%D1%82"
  962. ) == str(url)
  963. def test_from_bytes_with_non_ascii_fragment():
  964. url = URL("http://example.com/#фрагмент")
  965. assert (
  966. "http://example.com/" "#%D1%84%D1%80%D0%B0%D0%B3%D0%BC%D0%B5%D0%BD%D1%82"
  967. ) == str(url)
  968. def test_to_str():
  969. url = URL("http://εμπορικόσήμα.eu/")
  970. assert "http://xn--jxagkqfkduily1i.eu/" == str(url)
  971. def test_to_str_long():
  972. url = URL(
  973. "https://host-12345678901234567890123456789012345678901234567890" "-name:8888/"
  974. )
  975. expected = (
  976. "https://host-"
  977. "12345678901234567890123456789012345678901234567890"
  978. "-name:8888/"
  979. )
  980. assert expected == str(url)
  981. def test_decoding_with_2F_in_path():
  982. url = URL("http://example.com/path%2Fto")
  983. assert "http://example.com/path%2Fto" == str(url)
  984. assert url == URL(str(url))
  985. def test_decoding_with_26_and_3D_in_query():
  986. url = URL("http://example.com/?%26=%3D")
  987. assert "http://example.com/?%26=%3D" == str(url)
  988. assert url == URL(str(url))
  989. def test_fragment_only_url():
  990. url = URL("#frag")
  991. assert str(url) == "#frag"
  992. def test_url_from_url():
  993. url = URL("http://example.com")
  994. assert URL(url) == url
  995. assert URL(url).raw_parts == ("/",)
  996. def test_lowercase_scheme():
  997. url = URL("HTTP://example.com")
  998. assert str(url) == "http://example.com"
  999. def test_str_for_empty_url():
  1000. url = URL()
  1001. assert "" == str(url)
  1002. def test_parent_for_empty_url():
  1003. url = URL()
  1004. assert url is url.parent
  1005. def test_empty_value_for_query():
  1006. url = URL("http://example.com/path").with_query({"a": ""})
  1007. assert str(url) == "http://example.com/path?a="
  1008. def test_none_value_for_query():
  1009. with pytest.raises(TypeError):
  1010. URL("http://example.com/path").with_query({"a": None})
  1011. def test_decode_pct_in_path():
  1012. url = URL("http://www.python.org/%7Eguido")
  1013. assert "http://www.python.org/~guido" == str(url)
  1014. def test_decode_pct_in_path_lower_case():
  1015. url = URL("http://www.python.org/%7eguido")
  1016. assert "http://www.python.org/~guido" == str(url)
  1017. # join
  1018. def test_join():
  1019. base = URL("http://www.cwi.nl/%7Eguido/Python.html")
  1020. url = URL("FAQ.html")
  1021. url2 = base.join(url)
  1022. assert str(url2) == "http://www.cwi.nl/~guido/FAQ.html"
  1023. def test_join_absolute():
  1024. base = URL("http://www.cwi.nl/%7Eguido/Python.html")
  1025. url = URL("//www.python.org/%7Eguido")
  1026. url2 = base.join(url)
  1027. assert str(url2) == "http://www.python.org/~guido"
  1028. def test_join_non_url():
  1029. base = URL("http://example.com")
  1030. with pytest.raises(TypeError):
  1031. base.join("path/to")
  1032. NORMAL = [
  1033. ("g:h", "g:h"),
  1034. ("g", "http://a/b/c/g"),
  1035. ("./g", "http://a/b/c/g"),
  1036. ("g/", "http://a/b/c/g/"),
  1037. ("/g", "http://a/g"),
  1038. ("//g", "http://g"),
  1039. ("?y", "http://a/b/c/d;p?y"),
  1040. ("g?y", "http://a/b/c/g?y"),
  1041. ("#s", "http://a/b/c/d;p?q#s"),
  1042. ("g#s", "http://a/b/c/g#s"),
  1043. ("g?y#s", "http://a/b/c/g?y#s"),
  1044. (";x", "http://a/b/c/;x"),
  1045. ("g;x", "http://a/b/c/g;x"),
  1046. ("g;x?y#s", "http://a/b/c/g;x?y#s"),
  1047. ("", "http://a/b/c/d;p?q"),
  1048. (".", "http://a/b/c/"),
  1049. ("./", "http://a/b/c/"),
  1050. ("..", "http://a/b/"),
  1051. ("../", "http://a/b/"),
  1052. ("../g", "http://a/b/g"),
  1053. ("../..", "http://a/"),
  1054. ("../../", "http://a/"),
  1055. ("../../g", "http://a/g"),
  1056. ]
  1057. @pytest.mark.parametrize("url,expected", NORMAL)
  1058. def test_join_from_rfc_3986_normal(url, expected):
  1059. # test case from https://tools.ietf.org/html/rfc3986.html#section-5.4
  1060. base = URL("http://a/b/c/d;p?q")
  1061. url = URL(url)
  1062. expected = URL(expected)
  1063. assert base.join(url) == expected
  1064. ABNORMAL = [
  1065. ("../../../g", "http://a/g"),
  1066. ("../../../../g", "http://a/g"),
  1067. ("/./g", "http://a/g"),
  1068. ("/../g", "http://a/g"),
  1069. ("g.", "http://a/b/c/g."),
  1070. (".g", "http://a/b/c/.g"),
  1071. ("g..", "http://a/b/c/g.."),
  1072. ("..g", "http://a/b/c/..g"),
  1073. ("./../g", "http://a/b/g"),
  1074. ("./g/.", "http://a/b/c/g/"),
  1075. ("g/./h", "http://a/b/c/g/h"),
  1076. ("g/../h", "http://a/b/c/h"),
  1077. ("g;x=1/./y", "http://a/b/c/g;x=1/y"),
  1078. ("g;x=1/../y", "http://a/b/c/y"),
  1079. ("g?y/./x", "http://a/b/c/g?y/./x"),
  1080. ("g?y/../x", "http://a/b/c/g?y/../x"),
  1081. ("g#s/./x", "http://a/b/c/g#s/./x"),
  1082. ("g#s/../x", "http://a/b/c/g#s/../x"),
  1083. ]
  1084. @pytest.mark.parametrize("url,expected", ABNORMAL)
  1085. def test_join_from_rfc_3986_abnormal(url, expected):
  1086. # test case from https://tools.ietf.org/html/rfc3986.html#section-5.4.2
  1087. base = URL("http://a/b/c/d;p?q")
  1088. url = URL(url)
  1089. expected = URL(expected)
  1090. assert base.join(url) == expected
  1091. def test_split_result_non_decoded():
  1092. with pytest.raises(ValueError):
  1093. URL(SplitResult("http", "example.com", "path", "qs", "frag"))
  1094. def test_human_repr():
  1095. url = URL("http://бажан:пароль@хост.домен:8080/шлях/сюди?арг=вал#фраг")
  1096. s = url.human_repr()
  1097. assert URL(s) == url
  1098. assert s == "http://бажан:пароль@хост.домен:8080/шлях/сюди?арг=вал#фраг"
  1099. def test_human_repr_defaults():
  1100. url = URL("шлях")
  1101. s = url.human_repr()
  1102. assert s == "шлях"
  1103. def test_human_repr_default_port():
  1104. url = URL("http://бажан:пароль@хост.домен/шлях/сюди?арг=вал#фраг")
  1105. s = url.human_repr()
  1106. assert URL(s) == url
  1107. assert s == "http://бажан:пароль@хост.домен/шлях/сюди?арг=вал#фраг"
  1108. def test_human_repr_ipv6():
  1109. url = URL("http://[::1]:8080/path")
  1110. s = url.human_repr()
  1111. url2 = URL(s)
  1112. assert url2 == url
  1113. assert url2.host == "::1"
  1114. assert s == "http://[::1]:8080/path"
  1115. def test_human_repr_delimiters():
  1116. url = URL.build(
  1117. scheme="http",
  1118. user=" !\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~",
  1119. password=" !\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~",
  1120. host="хост.домен",
  1121. port=8080,
  1122. path="/ !\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~",
  1123. query={
  1124. " !\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~": " !\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~"
  1125. },
  1126. fragment=" !\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~",
  1127. )
  1128. s = url.human_repr()
  1129. assert URL(s) == url
  1130. assert (
  1131. s == "http:// !\"%23$%25&'()*+,-.%2F%3A;<=>%3F%40%5B\\%5D^_`{|}~"
  1132. ": !\"%23$%25&'()*+,-.%2F%3A;<=>%3F%40%5B\\%5D^_`{|}~"
  1133. "@хост.домен:8080"
  1134. "/ !\"%23$%25&'()*+,-./:;<=>%3F@[\\]^_`{|}~"
  1135. "? !\"%23$%25%26'()*%2B,-./:%3B<%3D>?@[\\]^_`{|}~"
  1136. "= !\"%23$%25%26'()*%2B,-./:%3B<%3D>?@[\\]^_`{|}~"
  1137. "# !\"#$%25&'()*+,-./:;<=>?@[\\]^_`{|}~"
  1138. )
  1139. def test_human_repr_non_printable():
  1140. url = URL.build(
  1141. scheme="http",
  1142. user="бажан\n\xad\u200b",
  1143. password="пароль\n\xad\u200b",
  1144. host="хост.домен",
  1145. port=8080,
  1146. path="/шлях\n\xad\u200b",
  1147. query={"арг\n\xad\u200b": "вал\n\xad\u200b"},
  1148. fragment="фраг\n\xad\u200b",
  1149. )
  1150. s = url.human_repr()
  1151. assert URL(s) == url
  1152. assert (
  1153. s == "http://бажан%0A%C2%AD%E2%80%8B:пароль%0A%C2%AD%E2%80%8B"
  1154. "@хост.домен:8080"
  1155. "/шлях%0A%C2%AD%E2%80%8B"
  1156. "?арг%0A%C2%AD%E2%80%8B=вал%0A%C2%AD%E2%80%8B"
  1157. "#фраг%0A%C2%AD%E2%80%8B"
  1158. )
  1159. # relative
  1160. def test_relative():
  1161. url = URL("http://user:pass@example.com:8080/path?a=b#frag")
  1162. rel = url.relative()
  1163. assert str(rel) == "/path?a=b#frag"
  1164. def test_relative_is_relative():
  1165. url = URL("http://user:pass@example.com:8080/path?a=b#frag")
  1166. rel = url.relative()
  1167. assert not rel.is_absolute()
  1168. def test_relative_abs_parts_are_removed():
  1169. url = URL("http://user:pass@example.com:8080/path?a=b#frag")
  1170. rel = url.relative()
  1171. assert not rel.scheme
  1172. assert not rel.user
  1173. assert not rel.password
  1174. assert not rel.host
  1175. assert not rel.port
  1176. def test_relative_fails_on_rel_url():
  1177. with pytest.raises(ValueError):
  1178. URL("/path?a=b#frag").relative()
  1179. def test_slash_and_question_in_query():
  1180. u = URL("http://example.com/path?http://example.com/p?a#b")
  1181. assert u.query_string == "http://example.com/p?a"
  1182. def test_slash_and_question_in_fragment():
  1183. u = URL("http://example.com/path#http://example.com/p?a")
  1184. assert u.fragment == "http://example.com/p?a"
  1185. def test_requoting():
  1186. u = URL("http://127.0.0.1/?next=http%3A//example.com/")
  1187. assert u.raw_query_string == "next=http://example.com/"
  1188. assert str(u) == "http://127.0.0.1/?next=http://example.com/"