diff options
author | Terry Reedy <tjreedy@udel.edu> | 2010-12-02 07:05:56 (GMT) |
---|---|---|
committer | Terry Reedy <tjreedy@udel.edu> | 2010-12-02 07:05:56 (GMT) |
commit | 5a22b651173f142a600625a036fcf36484ade237 (patch) | |
tree | 35221434a0451ee9cf205f2e9dc17a69af69d596 | |
parent | 3cdf871a8c9bc5694a598bfdf22edece49584f48 (diff) | |
download | cpython-5a22b651173f142a600625a036fcf36484ade237.zip cpython-5a22b651173f142a600625a036fcf36484ade237.tar.gz cpython-5a22b651173f142a600625a036fcf36484ade237.tar.bz2 |
Issue 9299 Add exist_ok parameter to os.makedirs to suppress 'File exists' exception. Patch by Ray Allen.
-rw-r--r-- | Doc/library/os.rst | 16 | ||||
-rw-r--r-- | Lib/os.py | 26 | ||||
-rw-r--r-- | Lib/test/test_os.py | 22 | ||||
-rw-r--r-- | Misc/ACKS | 1 | ||||
-rw-r--r-- | Misc/NEWS | 4 |
5 files changed, 58 insertions, 11 deletions
diff --git a/Doc/library/os.rst b/Doc/library/os.rst index f3aad19..5d9a170 100644 --- a/Doc/library/os.rst +++ b/Doc/library/os.rst @@ -1143,17 +1143,20 @@ Files and Directories Availability: Unix, Windows. -.. function:: makedirs(path[, mode]) +.. function:: makedirs(path[, mode][, exist_ok=False]) .. index:: single: directory; creating single: UNC paths; and os.makedirs() Recursive directory creation function. Like :func:`mkdir`, but makes all - intermediate-level directories needed to contain the leaf directory. Raises - an :exc:`error` exception if the leaf directory already exists or cannot be - created. The default *mode* is ``0o777`` (octal). On some systems, *mode* - is ignored. Where it is used, the current umask value is first masked out. + intermediate-level directories needed to contain the leaf directory. If + the target directory with the same mode as we specified already exists, + raises an :exc:`OSError` exception if *exist_ok* is False, otherwise no + exception is raised. If the directory cannot be created in other cases, + raises an :exc:`OSError` exception. The default *mode* is ``0o777`` (octal). + On some systems, *mode* is ignored. Where it is used, the current umask + value is first masked out. .. note:: @@ -1162,6 +1165,9 @@ Files and Directories This function handles UNC paths correctly. + .. versionadded:: 3.2 + The *exist_ok* parameter. + .. function:: pathconf(path, name) @@ -114,18 +114,26 @@ SEEK_SET = 0 SEEK_CUR = 1 SEEK_END = 2 + +def _get_masked_mode(mode): + mask = umask(0) + umask(mask) + return mode & ~mask + #' # Super directory utilities. # (Inspired by Eric Raymond; the doc strings are mostly his) -def makedirs(name, mode=0o777): - """makedirs(path [, mode=0o777]) +def makedirs(name, mode=0o777, exist_ok=False): + """makedirs(path [, mode=0o777][, exist_ok=False]) Super-mkdir; create a leaf directory and all intermediate ones. Works like mkdir, except that any intermediate path segment (not - just the rightmost) will be created if it does not exist. This is - recursive. + just the rightmost) will be created if it does not exist. If the + target directory with the same mode as we specified already exists, + raises an OSError if exist_ok is False, otherwise no exception is + raised. This is recursive. """ head, tail = path.split(name) @@ -133,14 +141,20 @@ def makedirs(name, mode=0o777): head, tail = path.split(head) if head and tail and not path.exists(head): try: - makedirs(head, mode) + makedirs(head, mode, exist_ok) except OSError as e: # be happy if someone already created the path if e.errno != errno.EEXIST: raise if tail == curdir: # xxx/newdir/. exists if xxx/newdir exists return - mkdir(name, mode) + try: + mkdir(name, mode) + except OSError as e: + import stat as st + if not (e.errno == errno.EEXIST and exist_ok and path.isdir(name) and + st.S_IMODE(lstat(name).st_mode) == _get_masked_mode(mode)): + raise def removedirs(name): """removedirs(path) diff --git a/Lib/test/test_os.py b/Lib/test/test_os.py index 835e1f2..e27dd7a 100644 --- a/Lib/test/test_os.py +++ b/Lib/test/test_os.py @@ -630,6 +630,28 @@ class MakedirTests(unittest.TestCase): 'dir5', 'dir6') os.makedirs(path) + def test_exist_ok_existing_directory(self): + path = os.path.join(support.TESTFN, 'dir1') + mode = 0o777 + old_mask = os.umask(0o022) + os.makedirs(path, mode) + self.assertRaises(OSError, os.makedirs, path, mode) + self.assertRaises(OSError, os.makedirs, path, mode, exist_ok=False) + self.assertRaises(OSError, os.makedirs, path, 0o776, exist_ok=True) + os.makedirs(path, mode=mode, exist_ok=True) + os.umask(old_mask) + + def test_exist_ok_existing_regular_file(self): + base = support.TESTFN + path = os.path.join(support.TESTFN, 'dir1') + f = open(path, 'w') + f.write('abc') + f.close() + self.assertRaises(OSError, os.makedirs, path) + self.assertRaises(OSError, os.makedirs, path, exist_ok=False) + self.assertRaises(OSError, os.makedirs, path, exist_ok=True) + os.remove(path) + def tearDown(self): path = os.path.join(support.TESTFN, 'dir1', 'dir2', 'dir3', 'dir4', 'dir5', 'dir6') @@ -18,6 +18,7 @@ Matthew Ahrens Nir Aides Yaniv Aknin Jyrki Alakuijala +Ray Allen Billy G. Allie Kevin Altis Joe Amenta @@ -46,6 +46,10 @@ Core and Builtins Library ------- +- Issue #9299: Add exist_ok parameter to os.makedirs to suppress the + 'File exists' exception when a target directory already exists with the + specified mode. Patch by Ray Allen. + - Issue #9573: os.fork() now works correctly when triggered as a side effect of a module import |