Allow `open()` to create non-existent directories in write mode

I’d like that open() creates non-existent directories, like it creates the file when opening it in write mode if it does not exist.

Example usage

It could be an optional argument to avoid introducing a breaking change.
Similar in concept to the -p, --parents option of the shell mkdir util.

with open("newdirectory/foo", mode="w", create_parents=True) as f:
    f.write("toto")

Why?

Currently, this code raises FileNotFoundError, because the directory “newdirectory” does not exist:

>>> with open("newdirectory/foo", mode="w") as f:
...    f.write("toto")
...    
FileNotFoundError: [Errno 2] No such file or directory: 'newdirectory/foo'

This behavior is surprising when you know that non-existing files are created as long as there are no parent directories:

with open("foo", mode="w") as f:  # creates `foo` if it does not exist
    f.write("toto")

My current workaround

import os
directory = os.path.join("new", "directory")
os.makedirs(directory, exist_ok=True)
with open("new/directory/foo", mode="w") as f:
    f.write("toto")

You could also do:

from pathlib import Path
file = Path("new/directory/foo")
file.parent.mkdir(parents=True, exist_ok=True)
file.write_text("toto")

Even if I agreed with your idea, changing the interface for such an old and widely used function is bound to break a lot of code. I would most certainly call this change backwards incompatible. And I do not think we should do such things without a good reason; and because the pathlib solution is essentially 2 simple lines longer, I do not consider this to be a good reason.

3 Likes

Thanks for your pathlib solution.
However, I have trouble seeing how adding a new kwarg ‘create_parents’ with a default value set to False would break that much code… It wouldn’t have any impact on the current ways the function is called, right?

2 Likes

It might not, True.

Then the question becomes: do we really need it if we have an almost equally simple solution? Remember that any change in the source code of CPython has to be both approved by a core developer and supported for decades after that (the change you’re proposing will most likely have to be supported for the entire existence of CPython). So I ask: is the work of the one who implements it, the core developer who reviews it, and PyPy, vscode extension, Pycharm, CPython, IronPython, etc developers who have to support it for the entire existence of Python truly worth that 1-2 line simplification?

You could make a valid case here, of course. You will just have to modify the current open(), write tests, and make a pull request. After that, it will be much easier to stand your ground. But I personally do not believe that we will risk doing something like this. But as always, you can try: