patternpythonMinor
Removing final elements of a path to a file
Viewed 0 times
pathfileelementsremovingfinal
Problem
I have the use case of needing to find the parent path of a sub path for which I have written the following function:
But I find that this is awfully verbose and that I re-invented the wheel with it.
Also it breaks if the paths have an unequal amount of trailing slashes, so my method would need to normalize both paths first, which would make it even more verbose.
Is there already some library method which does that, that I may have overlooked?
Edit: Due to the problems regarding the undesired partial node-match mentioned in some answers and comments I refactored the method to this:
#! /usr/bin/env python3
from os.path import sep
def pathbase(main, sub):
if main.endswith(sub):
parent = main[:-len(sub)]
while parent.endswith(sep):
parent = parent[:-1]
return parent
else:
raise ValueError('Main path does not end on sub-path')
path1 = '/foo/bar/spamm/eggs'
path2 = 'spamm/eggs'
print(pathbase(path1, path2))But I find that this is awfully verbose and that I re-invented the wheel with it.
Also it breaks if the paths have an unequal amount of trailing slashes, so my method would need to normalize both paths first, which would make it even more verbose.
Is there already some library method which does that, that I may have overlooked?
Edit: Due to the problems regarding the undesired partial node-match mentioned in some answers and comments I refactored the method to this:
from os.path import basename, dirname, normpath
def pathbase(main, sub):
"""Extract basename of main
relative to its sub-path sub
"""
norm_main = normpath(main)
norm_sub = normpath(sub)
while basename(norm_main) == basename(norm_sub):
norm_main = dirname(norm_main)
norm_sub = dirname(norm_sub)
else:
if basename(norm_sub):
raise ValueError('"{sub}" is not a sub-path of {main}'.format(
sub=sub, main=main))
return norm_mainSolution
First, have in mind what @Daerdemandt suggested in comments (using
From the docs:
Return a relative filepath to path either from the current directory
or from an optional start directory. This is a path computation: the
filesystem is not accessed to confirm the existence or nature of path
or start.
start defaults to
Availability: Unix, Windows.
After some reading of the
Apart from this, we can cut down you method and make it straight-forward:
There's not much to say about this. I just simplified your code by removing the
Some minor style things
In the end, everything would look just like:
The above returns:
os.path.relpath - even though I think this is returning the matches but I might be wrong)From the docs:
Return a relative filepath to path either from the current directory
or from an optional start directory. This is a path computation: the
filesystem is not accessed to confirm the existence or nature of path
or start.
start defaults to
os.curdir.Availability: Unix, Windows.
After some reading of the
os module, I don't think this is possible either. Sincerely, it is an unusual path manipulation which leads to the reason of not having it in the above mentioned module.Apart from this, we can cut down you method and make it straight-forward:
import sys
def pathbase(path1, path2):
try:
if path1.endswith(path2):
return path1[:-len(path2)]
raise TypeError
except TypeError:
print('Main path does not end on sub-path')
sys.exit(0)
path1 = '/foo/bar/spamm/eggs'
path2 = 'spamm/eggs'
print(pathbase(path1, path2))There's not much to say about this. I just simplified your code by removing the
while part which didn't make too much sense to me. I also removed the else part and raised a TypeError which I used in an try-except block.Some minor style things
- you could've call your method:
get_base_path()
- you could've name your arguments:
main_pathandsub_path
In the end, everything would look just like:
import sys
def get_base_path(main_path, sub_path):
try:
if main_path.endswith(sub_path):
return main_path[:-len(sub_path)]
raise TypeError
except TypeError:
print('Main path does not end on sub-path')
sys.exit(0)
main_path = '/foo/bar/spamm/eggs'
sub_path = 'spamm/eggs'
print(get_base_path(main_path, sub_path))The above returns:
/foo/bar/Code Snippets
import sys
def pathbase(path1, path2):
try:
if path1.endswith(path2):
return path1[:-len(path2)]
raise TypeError
except TypeError:
print('Main path does not end on sub-path')
sys.exit(0)
path1 = '/foo/bar/spamm/eggs'
path2 = 'spamm/eggs'
print(pathbase(path1, path2))import sys
def get_base_path(main_path, sub_path):
try:
if main_path.endswith(sub_path):
return main_path[:-len(sub_path)]
raise TypeError
except TypeError:
print('Main path does not end on sub-path')
sys.exit(0)
main_path = '/foo/bar/spamm/eggs'
sub_path = 'spamm/eggs'
print(get_base_path(main_path, sub_path))Context
StackExchange Code Review Q#140133, answer score: 3
Revisions (0)
No revisions yet.