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