@@ -767,6 +767,111 @@ def test_div_with_dots():
767767 assert url .raw_path == "/path/to"
768768
769769
770+ # joinpath
771+
772+
773+ @pytest .mark .parametrize (
774+ "base,to_join,expected" ,
775+ [
776+ pytest .param ("" , ("path" , "to" ), "http://example.com/path/to" , id = "root" ),
777+ pytest .param (
778+ "/" , ("path" , "to" ), "http://example.com/path/to" , id = "root-with-slash"
779+ ),
780+ pytest .param ("/path" , ("to" ,), "http://example.com/path/to" , id = "path" ),
781+ pytest .param (
782+ "/path/" , ("to" ,), "http://example.com/path/to" , id = "path-with-slash"
783+ ),
784+ pytest .param (
785+ "/path?a=1#frag" ,
786+ ("to" ,),
787+ "http://example.com/path/to" ,
788+ id = "cleanup-query-and-fragment" ,
789+ ),
790+ ],
791+ )
792+ def test_joinpath (base , to_join , expected ):
793+ url = URL (f"http://example.com{ base } " )
794+ assert str (url .joinpath (* to_join )) == expected
795+
796+
797+ @pytest .mark .parametrize (
798+ "url,to_join,expected" ,
799+ [
800+ pytest .param (URL (), ("a" ,), ("a" ,), id = "empty-url" ),
801+ pytest .param (URL ("a" ), ("b" ,), ("a" , "b" ), id = "relative-path" ),
802+ pytest .param (URL ("a" ), ("b" , "" , "c" ), ("a" , "b" , "c" ), id = "empty-element" ),
803+ pytest .param (URL ("/a" ), ("b" ), ("/" , "a" , "b" ), id = "absolute-path" ),
804+ ],
805+ )
806+ def test_joinpath_relative (url , to_join , expected ):
807+ assert url .joinpath (* to_join ).raw_parts == expected
808+
809+
810+ @pytest .mark .parametrize (
811+ "url,to_join,encoded,e_path,e_raw_path,e_parts,e_raw_parts" ,
812+ [
813+ pytest .param (
814+ "http://example.com/сюда" ,
815+ ("туда" ,),
816+ False ,
817+ "/сюда/туда" ,
818+ "/%D1%81%D1%8E%D0%B4%D0%B0/%D1%82%D1%83%D0%B4%D0%B0" ,
819+ ("/" , "сюда" , "туда" ),
820+ ("/" , "%D1%81%D1%8E%D0%B4%D0%B0" , "%D1%82%D1%83%D0%B4%D0%B0" ),
821+ id = "non-ascii" ,
822+ ),
823+ pytest .param (
824+ "http://example.com/path" ,
825+ ("%cf%80" ,),
826+ False ,
827+ "/path/%cf%80" ,
828+ "/path/%25cf%2580" ,
829+ ("/" , "path" , "%cf%80" ),
830+ ("/" , "path" , "%25cf%2580" ),
831+ id = "percent-encoded" ,
832+ ),
833+ pytest .param (
834+ "http://example.com/path" ,
835+ ("%cf%80" ,),
836+ True ,
837+ "/path/π" ,
838+ "/path/%cf%80" ,
839+ ("/" , "path" , "π" ),
840+ ("/" , "path" , "%cf%80" ),
841+ id = "encoded-percent-encoded" ,
842+ ),
843+ ],
844+ )
845+ def test_joinpath_encoding (
846+ url , to_join , encoded , e_path , e_raw_path , e_parts , e_raw_parts
847+ ):
848+ joined = URL (url ).joinpath (* to_join , encoded = encoded )
849+ assert joined .path == e_path
850+ assert joined .raw_path == e_raw_path
851+ assert joined .parts == e_parts
852+ assert joined .raw_parts == e_raw_parts
853+
854+
855+ @pytest .mark .parametrize (
856+ "to_join,expected" ,
857+ [
858+ pytest .param (("path:abc@123" ,), "/base/path:abc@123" , id = "with-colon-and-at" ),
859+ pytest .param ((".." , "path" , "." , "to" ), "/path/to" , id = "with-dots" ),
860+ ],
861+ )
862+ def test_joinpath_edgecases (to_join , expected ):
863+ url = URL ("http://example.com/base" ).joinpath (* to_join )
864+ assert url .raw_path == expected
865+
866+
867+ def test_joinpath_path_starting_from_slash_is_forbidden ():
868+ url = URL ("http://example.com/path/" )
869+ with pytest .raises (
870+ ValueError , match = "Appending path .* starting from slash is forbidden"
871+ ):
872+ assert url .joinpath ("/to/others" )
873+
874+
770875# with_path
771876
772877
0 commit comments