tags:

views:

29233

answers:

5

What's the most elegant way to check if the directory a file is going to be written to exists, and if not create the directory? Is there a better way than:

Update: Somehow I'd missed os.path.exists, thanks kanja, Blair, and Douglas, this is what I've got now:

def ensure_dir(f):
    d = os.path.dirname(f)
    if not os.path.exists(d):
        os.makedirs(d)

There's no magic flag to "open" that automatically does this, is there?

Initial attempt:

filename = "/my/directory/filename.txt"
dir = os.path.dirname(filename)

try:
    os.stat(dir)
except:
    os.path.mkdir(dir)

f = file(filename)
A: 

try the os.path.exists function

if not os.path.exists(dir):
 os.path.mkdir(dir)
kanja
I was going to comment on the question, but do we mean os.mkdir? My python (2.5.2) has no os.path.mkdir....
Blair Conrad
+11  A: 

Check out os.makedirs: (It makes sure the complete path exists.) To handle the fact the directory might exist, catch OSError.

import os
try:
    os.makedirs('./path/to/somewhere')
except OSError:
    pass
Douglas Mayle
with the try/except, you will mask errors in directory creation, in the case when the directory didn't exist but for some reason you can't make it
Blair Conrad
This is the only safe way.
Ali A
+36  A: 

I see two answers with good qualities, each with a small flaw, so I'll give my take on it:

Try os.path.exists, and consider os.makedirs for the creation.

if not os.path.exists(dir):
    os.makedirs(dir)

As noted in comments and elsewhere, there's a race condition - if the directory is created between the os.path.exists and the os.makedirs calls, the os.makedirs will fail with an OSError. Unfortunately, blanket-catching OSError and continuing is not foolproof, as it will ignore a failure to create the directory due to other factors, such as insufficient permissions, full disk, etc.

One option would be to trap the OSError and examine the embedded error code, if one knew what's what (on my OS, 13 seems to indicate that permission is denied, and 17 that the file exists - it's not clear that that's even remotely portable, but is explored in Is there a cross-platform way of getting information from Python’s OSError). Alterntively, there could be a second os.path.exists, but suppose another created the directory after the first check, then removed it before the second one - we could still be fooled.

Depending on the application, the danger of concurrent operations may be more or less than the danger posed by other factors such as file permissions. The developer would have to know more about the particular application being developed and its expected environment before choosing an implementation.

Blair Conrad
agreed the try/except solution is better
Corey Goldberg
The race condition is a good point, but the approach in http://stackoverflow.com/questions/273192/#273208, will mask a failure to create the directory. Don't feel bad for voting down - you don't like the answer. It's what votes are for.
Blair Conrad
+4  A: 

Ok, sorry, I made too many comments, I guess I should put it down. It's not totally foolproof though.

import os

dirname = 'create/me'

try:
    os.makedirs(dirname)
except OSError:
    if os.path.exists(dirname):
        # We are nearly safe
        pass
    else:
        # There was an error on creation, so make sure we know about it
        raise

Now as I say, this is not really foolproof, because we have the possiblity of failing to create the directory, and another process creating it during that period. I think I will ask the proper remainder as a question, as to whether the OSError codes can be used cross-platform.

Ali A
http://stackoverflow.com/questions/273698/is-there-a-cross-platform-way-of-getting-information-from-pythons-oserror
Ali A
I would make one change to make this code nearly ideal:Change "if os.path.exists(dirname)" to "if os.path.isdir(dirname)"That way you catch the case where the dir create failed because it already exists as a file.
Schof
+8  A: 

Granted.. this question has long since been answered, but I'll throw in my two cents.

I would personally recommend that you use os.path.isdir() to test instead of os.path.exists().

>>> os.path.exists('/tmp/dirname')
True
>>> os.path.exists('/tmp/dirname/filename.etc')
True
>>> os.path.isdir('/tmp/dirname/filename.etc')
False
>>> os.path.isdir('/tmp/fakedirname')
False

If you have:

>>> dir = raw_input(":: ")

And a foolish user inputs:

:: /tmp/dirname/filename.etc

... You're going to end up with a directory named filename.etc when you pass that arg to os.makedirs() if you test with os.path.exists().