Search code examples
pythonpathrelative-path

how to calculate root relative path in Python3?


The task is to implement a function root_relative_path(root : str, path : str) -> str, which calculates the relative path with respect to root, with no intermediate .. go beyond root. e.g., root_relative_path('/abc', '/../def') == '/abc/def'

This question is different from How to calculate relative path in Python? because in this case, root_relative_path(root='/tmp/abc', path='/../def') should return /tmp/abc/def instead of /tmp/def.


Solution

  • I was able to implement your root_relative_path function using a combination of the posixpath and the pathlib modules. The result is

    • Platform independent (as long as the root path corresponds to the current platform)
    • The path can begin with /, ./, or ../
    • And the path will be normalized using all of the techniques covered by the normpath function which includes resolving ..s.

     

    from pathlib import Path
    import posixpath
    
    def root_relative_path(root : str, path : str) -> str:
        ''' Calculates the relative path with respect to the root. 
            Any ".."s in the relative path will be resolved, 
            but not in a way that would cause the relative path to go beyond the root. '''
    
        # posixpath.normpath doesn't work as expected if the path doesn't start with a slash, so we make sure it does
        if not path.startswith('/'):
            path = '/' + path
    
        # The normalization process includes resolving any ".."s
        # we're only using posixpath for the relative portion of the path, the outcome will still be platform independent
        path = posixpath.normpath(path)
    
        # Remove the beginning slash so that we're not trying to join two absolute paths
        path = path[1:]
    
        joined = Path(root) / path
    
        # Per the OPs requirements the result needed to be a string,
        # But if you're allowed to use Pathlib objects, you could just return joined without converting it to a string
        return str(joined)