This issue tracker has been migrated to GitHub, and is currently read-only.
For more information, see the GitHub FAQs in the Python's Developer Guide.

classification
Title: os.makedirs and empty string
Type: behavior Stage: resolved
Components: Library (Lib) Versions: Python 3.6
process
Status: closed Resolution: not a bug
Dependencies: Superseder:
Assigned To: emilyemorehouse Nosy List: CarlAndersson, emilyemorehouse, serhiy.storchaka
Priority: normal Keywords:

Created on 2018-06-26 12:01 by CarlAndersson, last changed 2022-04-11 14:59 by admin. This issue is now closed.

Messages (7)
msg320486 - (view) Author: Carl Andersson (CarlAndersson) Date: 2018-06-26 12:01
os.makedirs does not handle the empty string the same way as the os.path.XX functions does. This is (to me) unexpected behaviour, since calls like `os.makedirs(os.path.dirname(filename), exist_ok=True)` raises an exception if `filename` does not contain any directories.
Also, it raises an `FileNotFoundError` regardless of the `exist_ok` flag. I would expect `os.makedirs('')` to fail with `FileExistsError` and `os.makedirs('', exist_ok=True)` to not do anything.
msg320493 - (view) Author: Emily Morehouse (emilyemorehouse) * (Python committer) Date: 2018-06-26 14:35
This is an interesting behavior to note. I think the current behavior makes the most sense, as it corresponds to the OS-level errors that you get from running the same operations.

Interestingly, at least on Unix-based file systems, we get different error messages but the same behavior when using "" vs "." as our target:

[linux]$ mkdir ""
mkdir: cannot create directory ‘’: No such file or directory
[linux]$ mkdir .
mkdir: cannot create directory ‘.’: File exists

[mac]$ mkdir ""
mkdir: .: No such file or directory
[mac]$ mkdir .
mkdir: .: File exists

Both os.mkdir and os.makedirs follow (only os.mkdir is included as the traceback is cleaner, but they raise the same errors):
>>> os.mkdir("")
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
FileNotFoundError: [Errno 2] No such file or directory: ''
>>> os.mkdir(".")
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
FileExistsError: [Errno 17] File exists: '.'


Since on an OS level the only time "File exists" is returned is when using "." and not "", os.makedirs with exists_ok=True also follows:

>>> os.makedirs("", exist_ok=True)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/usr/local/Cellar/python/3.6.5/Frameworks/Python.framework/Versions/3.6/lib/python3.6/os.py", line 220, in makedirs
    mkdir(name, mode)
FileNotFoundError: [Errno 2] No such file or directory: ''
>>> os.makedirs(".", exist_ok=True)
>>>

Basically, the FileExistsError gets silenced, but FileNotFoundError is left alone. I can see how the differences are nuanced and not obvious though.


Unless you think there is a succinct and worthwhile way of adding this to the documentation, I think this issue can be closed.
msg320495 - (view) Author: Serhiy Storchaka (serhiy.storchaka) * (Python committer) Date: 2018-06-26 14:48
Concur with Emily.
msg320497 - (view) Author: Carl Andersson (CarlAndersson) Date: 2018-06-26 14:59
I can see the point in keeping the behaviour close to the OS-level commands, but the discrepancy between os.mkdir (or os.makedirs) and os.path.dirname is not resolved.

I still think that
>>> os.makedirs(os.path.dirname(filename), exist_ok=True)
should be able to handle the case when "filename" does not contain any directories.
A possible workaround would be to use os.path.abspath somewhere in the mix.

In my humble opinion creating the directory structure required to write a file should not require this level of complexity, since it is such a common operation.
msg320498 - (view) Author: Emily Morehouse (emilyemorehouse) * (Python committer) Date: 2018-06-26 15:18
I'll defer to Serhiy's os.path expertise, but from what I know -- os.dirname is essentially a helper function for returning the first item of split. 

What I'm gathering is that you're looking for a more advanced way of parsing a file path -- say "nested/dir/sample.txt" -- to "nested/dir" while also handling parsing "" into ".". 

Not the prettiest, but you could wrap os.path.dirname in os.path.normpath:

>>> os.path.normpath(os.path.dirname("nested/dir/sample.txt"))
'nested/dir'
>>> os.path.normpath(os.path.dirname(""))
'.'
msg320552 - (view) Author: Carl Andersson (CarlAndersson) Date: 2018-06-27 07:57
That is in essence what I am looking for, yes.

As you say, it's not pretty. My opinion is still that if the os.path convention is that '' is the same as '.', this should be respected.
msg320606 - (view) Author: Emily Morehouse (emilyemorehouse) * (Python committer) Date: 2018-06-27 19:19
The os.path conventions do follow, '' and '.' are not treated the same here either --

>>> os.path.exists('')
False
>>> os.path.exists('.')
True
>>> os.path.isdir('')
False
>>> os.path.isdir('.')
True

The only os.path function that I see as potentially confusing in this discussion is dirname, as that can return an empty string which yields unexpected results when used as an argument for other functions.

>>> os.path.dirname('testdir')
''
>>> os.path.dirname('./testdir')
'.'

However, changing this functionality (e.g. os.path.dirname('testdir') returning '.') would result in backward-compatibility issues that would not be warranted (IMO).


I'll leave the final word to Serhiy to close out as 'not a bug' at his discretion.
History
Date User Action Args
2022-04-11 14:59:02adminsetgithub: 78149
2018-07-02 17:08:44emilyemorehousesetstatus: open -> closed
resolution: not a bug
stage: resolved
2018-06-27 19:19:24emilyemorehousesetassignee: emilyemorehouse

messages: + msg320606
nosy: + serhiy.storchaka
2018-06-27 07:57:30CarlAnderssonsetmessages: + msg320552
2018-06-26 15:18:03emilyemorehousesetmessages: + msg320498
2018-06-26 14:59:41CarlAnderssonsetnosy: - serhiy.storchaka
messages: + msg320497
2018-06-26 14:48:05serhiy.storchakasetnosy: + serhiy.storchaka
messages: + msg320495
2018-06-26 14:35:20emilyemorehousesetnosy: + emilyemorehouse
messages: + msg320493
2018-06-26 12:01:22CarlAnderssoncreate