views:

13516

answers:

6

Is there a way to get functionality similar to mkdir -p on the shell... from within python. I am looking for a solution other than a system call. I am sure the code is less than 20 lines... really I am wondering if someone has already written it?

+16  A: 

This should be all you need

import os
os.makedirs('/path/to/create')
Asa Ayers
I originally marked this answer as correct however, there is a problem with the solution: if the directory already exists there is an error. mkdir -p does not give an error in this case.
SetJmp
@setjmp then use try/except
Loïc Wolff
+1  A: 

I think Asa's answer is essentially correct, but you could extend it a little to act more like mkdir -p, either:

import os

def mkdir_path(path):
    if not os.access(path, os.F_OK):
        os.mkdirs(path)

or

import os
import errno

def mkdir_path(path):
    try:
        os.mkdirs(path)
    except os.error, e:
        if e.errno != errno.EEXIST:
            raise

These both handle the case where the path already exists silently but let other errors bubble up.

davidavr
+56  A: 

mkdir -p functionality as follows:

import os, errno

def mkdir_p(path):
    try:
        os.makedirs(path)
    except OSError as exc: # Python >2.5
        if exc.errno == errno.EEXIST:
            pass
        else: raise
ΤΖΩΤΖΙΟΥ
Thanks! A friend pointed out that this solution is the most efficient; requiring one call to the file system. On a contended file system, this is the way to go. Hopefully, your solution will get more up votes and make it to the second place slot.
SetJmp
This is the correct Python idiom, EAFP, or "It is Easier to Ask for Forgiveness than Permission." (see http://en.wikipedia.org/wiki/Python_syntax_and_semantics#Exceptions). Other implementations that don't include the exception handling have a potential race condition bug.
lambacck
`except OSError, exc:` for Python <2.5
JohnnyLambada
@JohnnyLambada: for Python ≤2.5, you surely meant
ΤΖΩΤΖΙΟΥ
Thank you JohnnyLambada! I needed the older flavor.
Chris Quenelle
+5  A: 

mkdir -p gives you an error if you the file already exists:

$ touch /tmp/foo
$ mkdir -p /tmp/foo
mkdir: cannot create directory `/tmp/foo': File exists

So a refinement to the previous suggestions would be to re-raise the exception if os.path.isdir returns False (when checking for errno.EEXIST).

(Update) See also this highly similar question; I agree with the accepted answer (and caveats) except I would recommend os.path.isdir instead of os.path.exists.

(Update) Per a suggestion in the comments, the full function would look like:

import os
def mkdirp(directory):
    if not os.path.isdir(directory):
        os.makedirs(directory)
Jacob Gabrielson
You are absolutely correct about this case; however, the program should catch exceptions later on e.g. when trying to open("/tmp/foo/a_file", "w"), so I don't think an update is necessary. You could update your answer with Python code instead, and watch it being upvoted ;)
ΤΖΩΤΖΙΟΥ
In a lot of cases that would probably be fine. In general, though, I would prefer the code to fail as early as possible so it's clear what really caused the problem.
Jacob Gabrielson
+23  A: 

This is easier than trapping the exception:

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

UPDATE 4/20/2010

I originally presented this solution because I assumed the OP was looking for something quick and dirty, perhaps for a script or a program that doesn't need to guard against a race condition where another process creates the path out from underneath it. If your program must guard against that sort of condition, don't use this solution, instead go with the accepted solution provided by ΤΖΩΤΖΙΟΥ.

However, this solution can be used for simple, throwaway code or something that will run in a controlled environment where you know for certain you will not encounter the race condition mentioned above.

When in doubt, use the accepted solution as it's correct in more cases than this one. This solution is not necessarily wrong, but it can be broken when used naively.

Joe Holloway
This way, you make it less probable but not impossible that makedirs will fail, in all multitasking operating systems. It's like saying "256 chars should be enough for any path created".
ΤΖΩΤΖΙΟΥ
I like this solution, though a friend pointed out that the longer exception based solution would only need one file system call. So on a really high latency corporate NFS server, I would want to go with the exception based approach.
SetJmp
@setjmp I agree. You probably don't want to use this approach in an enterprise setting, but would work just fine for some simple scripting. I inferred that is what you were looking for from the way you phrased your question.
Joe Holloway
consider what happens in this code if the path doesn't exist, but you don't have permission to create the folder. You still get an exception.
Asa Ayers
@Asa Of course. And mkdir -p would complain about that too. Did I miss your point?
Joe Holloway
@jholloway7: based on the requirements ("mkdir -p"-like functionality) Asa's comment is unnecessary. However, I would like to know whether you do acknowledge that it's possible that the directory can be non-existent when .exists is called, and existent when .makedirs is called.
ΤΖΩΤΖΙΟΥ
@TZ Yes, I certainly acknowledge that. Again, without complete specifications from the original poster, my assumption was that he/she wanted a solution that could be used to create a directory tree if not already existing in a simple script, not a HA enterprisey production solution with SLAs.
Joe Holloway
@Asa That's what exceptions are for, something unexpected went wrong. If you don't have permissions the exception bubbles all the way up and you notice to fix the permissions. As it should be.
darkporter
A: 

eh..., "mkdir -p" creates all non-existing parents as well.

That the above solutions do not.

Check the online help for os.mkdirs:import oshelp(os.mkdirs)
SetJmp
@SetJmp:1. not os.mkdirs, but os.mkdir2. Help on built-in function mkdir in module posix:=====mkdir(...) mkdir(path [, mode=0777]) Create a directory.=====So, nothing about non-existing parents.
Sorry, I meant: `import oshelp(os.makedirs)`The accepted answer does not use os.mkdir()
SetJmp