@@ -515,16 +515,9 @@ def prepend_base(base, iri):
515515
516516 transform ['query' ] = rel .query
517517
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' ])
528521
529522 transform ['fragment' ] = rel .fragment
530523
@@ -581,6 +574,53 @@ def remove_base(base, iri):
581574 return unparse_url ((None , None , path , rel .query , rel .fragment )) or './'
582575
583576
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+
584624ParsedUrl = namedtuple (
585625 'ParsedUrl' , ['scheme' , 'authority' , 'path' , 'query' , 'fragment' ])
586626
0 commit comments