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.
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'