Python os.path.join on Windows

I am trying to learn python and am making a program that will output a script. I want to use os.path.join, but am pretty confused. According to the docs if I say:

os.path.join('c:', 'sourcedir')

I get "C:sourcedir". According to the docs, this is normal, right?

But when I use the copytree command, Python will output it the desired way, for example:

import shutil
src = os.path.join('c:', 'src')
dst = os.path.join('c:', 'dst')
shutil.copytree(src, dst)

Here is the error code I get:

WindowsError: [Error 3] The system cannot find the path specified: 'C:src/*.*'

If I wrap the os.path.join with os.path.normpath I get the same error.

If this os.path.join can’t be used this way, then I am confused as to its purpose.

According to the pages suggested by Stack Overflow, slashes should not be used in join—that is correct, I assume?

Answers:

Thank you for visiting the Q&A section on Magenaut. Please note that all the answers may not help you solve the issue immediately. So please treat them as advisements. If you found the post helpful (or not), leave a comment & I’ll get back to you as soon as possible.

Method 1

To be even more pedantic, the most python doc consistent answer would be:

mypath = os.path.join('c:', os.sep, 'sourcedir')

Since you also need os.sep for the posix root path:

mypath = os.path.join(os.sep, 'usr', 'lib')

Method 2

Windows has a concept of current directory for each drive. Because of that, "c:sourcedir" means “sourcedir” inside the current C: directory, and you’ll need to specify an absolute directory.

Any of these should work and give the same result, but I don’t have a Windows VM fired up at the moment to double check:

"c:/sourcedir"
os.path.join("/", "c:", "sourcedir")
os.path.join("c:/", "sourcedir")

Method 3

To be pedantic, it’s probably not good to hardcode either / or as the path separator. Maybe this would be best?

mypath = os.path.join('c:%s' % os.sep, 'sourcedir')

or

mypath = os.path.join('c:' + os.sep, 'sourcedir')

Method 4

For a system-agnostic solution that works on both Windows and Linux, no matter what the input path, one could use os.path.join(os.sep, rootdir + os.sep, targetdir)

On WIndows:

>>> os.path.join(os.sep, "C:" + os.sep, "Windows")
'C:\Windows'

On Linux:

>>> os.path.join(os.sep, "usr" + os.sep, "lib")
'/usr/lib'

Method 5

The reason os.path.join('C:', 'src') is not working as you expect is because of something in the documentation that you linked to:

Note that on Windows, since there is a
current directory for each drive,
os.path.join(“c:”, “foo”) represents a
path relative to the current directory
on drive C: (c:foo), not c:foo.

As ghostdog said, you probably want mypath=os.path.join('c:\', 'sourcedir')

Method 6

I’d say this is a (windows)python bug.

Why bug?

I think this statement should be True

os.path.join(*os.path.dirname(os.path.abspath(__file__)).split(os.path.sep))==os.path.dirname(os.path.abspath(__file__))

But it is False on windows machines.

Method 7

to join a windows path, try

mypath=os.path.join('c:\', 'sourcedir')

basically, you will need to escape the slash

Method 8

You have a few possible approaches to treat path on Windows, from the most hardcoded ones (as using raw string literals or escaping backslashes) to the least ones. Here follows a few examples that will work as expected. Use what better fits your needs.

In[1]: from os.path import join, isdir

In[2]: from os import sep

In[3]: isdir(join("c:", "\", "Users"))
Out[3]: True

In[4]: isdir(join("c:", "/", "Users"))
Out[4]: True

In[5]: isdir(join("c:", sep, "Users"))
Out[5]: True

Method 9

Consent with @georg-

I would say then why we need lame os.path.join– better to use str.join or unicode.join e.g.

sys.path.append('{0}'.join(os.path.dirname(__file__).split(os.path.sep)[0:-1]).format(os.path.sep))

Method 10

answering to your comment : “the others ‘//’ ‘c:’, ‘c:\’ did not work (C:\ created two backslashes, C: didn’t work at all)”

On windows using
os.path.join('c:', 'sourcedir')
will automatically add two backslashes \ in front of sourcedir.

To resolve the path, as python works on windows also with forward slashes -> ‘/’, simply add .replace('\','/') with os.path.join as below:-

os.path.join('c:\', 'sourcedir').replace('\','/')

e.g: os.path.join('c:\', 'temp').replace('\','/')

output : ‘C:/temp’

Method 11

The proposed solutions are interesting and offer a good reference, however they are only partially satisfying. It is ok to manually add the separator when you have a single specific case or you know the format of the input string, but there can be cases where you want to do it programmatically on generic inputs.

With a bit of experimenting, I believe the criteria is that the path delimiter is not added if the first segment is a drive letter, meaning a single letter followed by a colon, no matter if it corresponds to a real unit.

For example:

import os
testval = ['c:','c:\','d:','j:','jr:','data:']

for t in testval:
    print ('test value: ',t,', join to "folder"',os.path.join(t,'folder'))
test value:  c: , join to "folder" c:folder
test value:  c: , join to "folder" c:folder
test value:  d: , join to "folder" d:folder
test value:  j: , join to "folder" j:folder
test value:  jr: , join to "folder" jr:folder
test value:  data: , join to "folder" data:folder

A convenient way to test for the criteria and apply a path correction can be to use os.path.splitdrive comparing the first returned element to the test value, like t+os.path.sep if os.path.splitdrive(t)[0]==t else t.

Test:

for t in testval:
    corrected = t+os.path.sep if os.path.splitdrive(t)[0]==t else t
    print ('original: %stcorrected: %s'%(t,corrected),' join corrected->',os.path.join(corrected,'folder'))
original: c:    corrected: c:  join corrected-> c:folder
original: c:   corrected: c:  join corrected-> c:folder
original: d:    corrected: d:  join corrected-> d:folder
original: j:    corrected: j:  join corrected-> j:folder
original: jr:   corrected: jr:  join corrected-> jr:folder
original: data: corrected: data:  join corrected-> data:folder

it can be probably be improved to be more robust for trailing spaces, and I have tested it only on windows, but I hope it gives an idea.
See also Os.path : can you explain this behavior? for interesting details on systems other then windows.


All methods was sourced from stackoverflow.com or stackexchange.com, is licensed under cc by-sa 2.5, cc by-sa 3.0 and cc by-sa 4.0

0 0 votes
Article Rating
Subscribe
Notify of
guest

0 Comments
Oldest
Newest Most Voted
Inline Feedbacks
View all comments
0
Would love your thoughts, please comment.x
()
x