如何避免与makedirs的竞争条件?

时间:2014-02-02 22:41:42

标签: python directory race-condition

我正在尝试将以下伪代码转换为Python:

If <directory> does not exist:
    Create all subdirectories for <directory>
Create a file in <directory>

这听起来很简单,可以通过os.makedirsos.path.isdir完成:

if not os.path.isdir('/some/path'):
    os.makedirs('/some/path')
open('/some/path/test.txt', 'w')

然而,经过进一步检查,显然存在竞争条件。请考虑以下时间表:

  1. 指定的目录(/some/path)不存在
  2. Python解释器执行第一行,其结果为True
  3. 另一个进程创建目录(/some/path
  4. makedirs引发OSError异常,因为该目录已存在
  5. 如果目录最初存在但在最后一行执行之前被另一个进程删除,也会出现问题。

    说到Python,“要求宽恕比获得许可更容易。”考虑到这一点,上面的片段可以写得更好:

    try:
        os.makedirs('/some/path')
    except OSError:
        pass
    open('/some/path/test.txt', 'w')
    

    这解决了上述两个问题,但创建了第三个:os.makedirs在出现下列情况之一时引发OSError异常:

    • 目录已存在
    • 无法创建目录

    这意味着无法确定引发异常的两个条件中的哪一个。换句话说,实际的失败将被忽略,这不是我想要的。

    我该如何解决这个问题?

1 个答案:

答案 0 :(得分:3)

我会注意到所有这些在python 3中都相当明显; FileExistsErrorPermissionError是独立的(OSError的子类)您可以捕获的异常,os.makedirs甚至有一个exist_ok kwarg来压制前者请确认已存在的目录。

如果您想检查OSError的原因,那么该信息位于e.args中的元组中(或者如果您只想查看错误代码,则可选e.errno):

try:
    os.makedirs('/etc/python')
except OSError as e:
    print e.args

(17, 'File exists')

try:
    os.makedirs('/etc/stuff')
except OSError as e:
    print e.args

(13, 'Permission denied')

try:
    os.makedirs('/etc/stuff')
except OSError as e:
    print e.errno

13

因此,您必须进行一些内省,并在except块中以不同方式处理这两个错误代码。