2
\$\begingroup\$

I am trying to write a small function to safely create dir until dir until full path of file exists. I'm trying to do it in a recursing function and would love to get some feedback.

def makedir(filepath: str) -> bool:
    if os.path.exists(os.path.dirname(filepath)):
        return
    os.makedirs(os.path.dirname(filepath), exist_ok=True)
    makedir(filepath)

makedir('/a/b/c/d/x.text')
#should create a then b then c then d
makedir('/a/b/c/e/y.text')
#should create e only since a, b,c already exist
\$\endgroup\$
3
  • \$\begingroup\$ How is that question to do with mine? \$\endgroup\$ Mar 7, 2020 at 3:05
  • \$\begingroup\$ I don't see the connection either. Maybe they posted the wrong link? \$\endgroup\$
    – MJ713
    Mar 7, 2020 at 3:30
  • \$\begingroup\$ Sorry, that was the wrong link. \$\endgroup\$ Mar 7, 2020 at 9:55

2 Answers 2

3
\$\begingroup\$

First, os.makedirs already creates all the intermediate directories in the path. That is its job. So there is no need to use recursion.

There is also no need to use os.path.exists, because you are using the exist_ok=True argument with os.makedirs. This means that os.makedirs will not error out if the directory already exists.*

Your type hint for the return type should be None, since you are not returning any values.

I would also change the name of your function. I think the name makedir is too close to os.makedirs, which is confusing.

import os

def makedirs_for_file(filepath: str) -> None:
    os.makedirs(os.path.dirname(filepath), exist_ok=True)

*Unless you are using a Python version before 3.4.1. In this case, os.makedirs may still error out, depending on the mode of the existing directory. See the documentation for more details.

\$\endgroup\$
2
  • \$\begingroup\$ Hm..I do try to use os.makedirs But it doesn't work. Seems thank you \$\endgroup\$ Mar 7, 2020 at 4:01
  • \$\begingroup\$ What do you mean by doesn't work ? Do you have an error message ? \$\endgroup\$
    – Kate
    Apr 3, 2020 at 13:52
2
\$\begingroup\$

This function does not actually return anything. What is good is that you use type hinting for your function. So I was really expecting that you would return a boolean value, because you obviously want some kind of feedback for your request. The problem is that the function makedirs does not have a return value that you could directly utilize.

So I would approach the problem like this: if an exception occurs, return false otherwise assume everything is alright. Thus the code becomes:

import os, sys

def makedir(filepath: str) -> bool:
    try:
        if os.path.exists(filepath):
            # dir already exists
            print(f"FYI Dir already exists: {filepath}")
            return False
        else:
            # attempt to create dir
            os.makedirs(filepath)
            return True

    except (OSError) as e:
        # log the details of the exception somewhere (console, file)
        print(f"OSError exception occurred while attempting to create directory: {filepath}")
        print("Unexpected error:", sys.exc_info()[0])
        return False

# testing
ret=makedir('/tmp')
print(f"Return from function: {ret}")

Here I have decided to discard the argument exist_ok=True and deliberately fail if the directory already exists, because I like to be strict and consider there should be no reason for doing this in the first place, but it's your choice.

I have tried to be as specific as possible and not catch any type of exception. The relevant exception within the scope of this operation is OSError. This should suffice, but if necessary you can handle multiple exceptions on the same line like this:

except (OSError, ValueError) as e:

You can also decide to handle the exceptions at module level, anyway the main module should always have its own mechanism to handle exceptions you are not explicitly addressing in specific parts of your code.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.