python-3.xpath

How to force absolute path to be relative when joining?


What is the idiomatic way to turn an absolute path into a relative path when joining?

Inputs:

abs = r"\\server\share\data"
newroot = r"X:\project"

Desired result:

X:\project\server\share\data

Both os.path.join() and pathlib.Path().joinpath() trim everything after the drive letter:

>>> os.path.join(newroot, abs)
'\\\\server\\share\\data'

>>> pathlib.Path(newroot).joinpath(abs)
WindowsPath('//server/share/data')

I can make them work by stripping the leading slashes:

>>> os.path.join(newroot, abs[2:])
'X:\\project\\server\\share\\data'

but that means I need to wrap in a test to find out whether the incoming path is actually absolute and to count how many slashes to remove. This just feels like a place for me to create bugs.

Note: Windows syntax is shown but I imagine the problem is cross platform and best-practice answer is probably that way also.


Solution

  • What I'm using until a solid answer is found. It works for my situation but I wouldn't count on it to be problem free.

    Concise:

    def make_relative(path):
        '''Remove leading characters that make an absolute path'''
        while os.path.isabs(path):
            path = path[1:]
            if ':' in path[0]: path = path[1:]
        return path
    

    Informative:

    def make_relative(path, debug=False):
        '''Remove leading characters that make an absolute path'''
        msg = [f"\nIn: {repr(path)}"]
        while os.path.isabs(path):
            msg.append(f'stripping {path[0]}')
            path = path[1:]
            if ':' in path[0]:
                msg.append(f'stripping {path[0]}')
                path = path[1:]
        msg.append(f'Out: {repr(path)}')
        if debug:
            [print(x) for x in msg]
        return path
    
    if __name__ == '__main__':
        abs = r"\\server\share\data"
        newroot = r"X:\project\test"
    
        make_relative(abs, debug=True)
        make_relative(newroot, debug=True)
    

    Result:

    In: '\\\\server\\share\\data'
    stripping \
    stripping \
    Out: 'server\\share\\data'
    
    In: 'X:\\project\\test'
    stripping X
    stripping :
    stripping \
    Out: 'project\\test'