2

Example1 where path2 starts with '/' results to /dir2/dir3/ (missing path1)

path1='/Volumes/disk1/'
path2='/dir2/dir3/'
print os.path.join(path1,path2)

Example2 where path2 does NOT start with '/' results to proper /Volumes/disk1/dir2/dir3/:

path1='/Volumes/disk1/'
path2='dir2/dir3/'
print os.path.join(path1,path2)

Question: I thought the purpose of os.path.join() is allow us to avoid extra work of tedious work of verifying if it's mac, windows or linux file path: one command does it all. But now if I would have to watch if path2 starts or does not start with '/' (or '\') it ruins every hope I had and brings ton of extra code... What is the solution? I don't want to do this ugliness:

if path2 and path2.replace('\\','/')[1:] and path2.replace('\\','/').startswith('/'):
    path2=path2[1:]
1
  • This question is similar to: Why doesn't os.path.join() work in this case?. If you believe it’s different, please edit the question, make it clear how it’s different and/or how the answers on that question are not helpful for your problem. Commented Jul 6, 2024 at 14:34

2 Answers 2

1

In order to work without the hassle of checking separators you have to start without them or remove them completely before passing to os.path.join() . In the code below, I show 3 ways you can do this (Live Ideone Example to play with).

Individual Directories

import os
print os.path.join('Volumes', 'disk1', 'dir2', 'dir3')

Split Paths Then Join

path1 = '/Volumes/disk1/'
path2 = '/dir2/dir3/'

import os
# this will convert to the same as above:
# i.e., os.path.join('Volumes', 'disk1', 'dir2', 'dir3')
print os.path.join(*(path1.split(os.sep) + path2.split(os.sep)))

Custum Join Function

Using the above code, you can write a custom join() that works for either single- or multi- path strings:

def join(*paths):
    import os
    return os.path.join(*[part for path in paths for part in path.split(os.sep)])

path1 = '/Volumes/disk1/'
path2 = '/dir2/dir3/'

print join(path1, path2)

Output:

'Volumes/disk1/dir2/dir3'
Sign up to request clarification or add additional context in comments.

4 Comments

Terrific! Thanks for sharing!
I've never seen start * symbol used inside of scope (*()). What is the purpose of it in Python other than multiplication?
os.path.join() expects parameters separated by commas, not an array. The * operator converts the array to comma separated values.
In other words join(*['dir1','dir2','dir3']) = join('dir1','dir2','dir3')
1

Direct form the documentation,

Join one or more path components intelligently. If any component is an absolute path, all previous components (on Windows, including the previous drive letter, if there was one) are thrown away, and joining continues.

You're seeing the behavior you are because you are passing it an absolute path (a path that starts with a '/'). Your program needs to be able to handle the difference between the two, and if it's the thing generating the paths, make sure it's probably creating an absolute path when you want that, and a relative one when you want that.

Explanation of why this is useful

Consider the following. I have a command line interface that asks the user to specify a path for file output. In my documentation, I say the following:

path: Path to an output file. If relative, will be placed inside ~/Documents.

Now in my code, all I need to do is:

out_path = os.path.join('~','Documents', path)

and now out_path will always contain the correct path. If the user specifies volume_1/output.txt, the file will wind up in ~/Documents/volume_1/output.text. If they specify /mnt/volume_1/output.text it will wind up in /mnt/volume_1/output.text, because the absolute path overrides the relative portion we provide as a default.

4 Comments

By other words I have to watch for the second, third and etc arguments all to be "relative" paths (not starting with '/').
I mean, you shouldn't be 'watching' for anything. If part of your code is given an absolute path, it should assume it got an absolute path. In other words, it should do exactly what join does. It's up to you to generate the correct path types if you're the one generating them. If the user is the one supplying them, you should assume that if they give you an absolute path that's actually what they meant, and if it's not, that's user error.
I hoped os.path.join() is more "intelligent" when it comes time to join one or more path components intelligently. It's frustrating they mentioned "intelligently" in docs. Misleading at very least.
I'd like to point out that this isn't as useful: If you run program ./file, there is a meaning assocated with the path, i.e. that it is resolved relative to the current working directory. If you use os.path.join() as you suggested, that will change the meaning. This isn't bad in and of itself, but the program now behaves differently than most other programs that take paths and violates the Principle of Least Surprise.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.