@@ -515,16 +515,9 @@ def prepend_base(base, iri):
515
515
516
516
transform ['query' ] = rel .query
517
517
518
- # normalize path
519
- path = transform ['path' ]
520
- add_slash = path .endswith ('/' )
521
- path = posixpath .normpath (path )
522
- if not path .endswith ('/' ) and add_slash :
523
- path += '/'
524
- # do not include '.' path
525
- if path == '.' :
526
- path = ''
527
- transform ['path' ] = path
518
+ if rel .path != '' :
519
+ # normalize path
520
+ transform ['path' ] = remove_dot_segments (transform ['path' ])
528
521
529
522
transform ['fragment' ] = rel .fragment
530
523
@@ -581,6 +574,53 @@ def remove_base(base, iri):
581
574
return unparse_url ((None , None , path , rel .query , rel .fragment )) or './'
582
575
583
576
577
+ def remove_dot_segments (path ):
578
+ """
579
+ Removes dot segments from a URL path.
580
+
581
+ :param path: the path to remove dot segments from.
582
+
583
+ :return: a path with normalized dot segments.
584
+ """
585
+
586
+ # RFC 3984 5.2.4 (reworked)
587
+
588
+ # empty path shortcut
589
+ if len (path ) == 0 :
590
+ return ''
591
+
592
+ input = path .split ('/' )
593
+ output = []
594
+
595
+ while len (input ) > 0 :
596
+ next = input .pop (0 )
597
+ done = len (input ) == 0
598
+
599
+ if next == '.' :
600
+ if done :
601
+ # ensure output has trailing /
602
+ output .append ('' )
603
+ continue
604
+
605
+ if next == '..' :
606
+ if len (output ) > 0 :
607
+ output .pop ()
608
+ if done :
609
+ # ensure output has trailing /
610
+ output .append ('' )
611
+ continue
612
+
613
+ output .append (next )
614
+
615
+ # ensure output has leading /
616
+ if len (output ) > 0 and output [0 ] != '' :
617
+ output .insert (0 , '' )
618
+ if len (output ) is 1 and output [0 ] == '' :
619
+ return '/'
620
+
621
+ return '/' .join (output )
622
+
623
+
584
624
ParsedUrl = namedtuple (
585
625
'ParsedUrl' , ['scheme' , 'authority' , 'path' , 'query' , 'fragment' ])
586
626
0 commit comments