2014-02-02 2 views
2

Я пытаюсь преобразовать следующий псевдо-код Python:Как избежать состояния гонки с помощью makedirs?

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

Это звучит достаточно просто выполнить с os.makedirs и os.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, поскольку каталог уже существует

re также являются проблемами, если каталог изначально существует, но удаляется другим процессом до выполнения последней строки.

Когда дело доходит до Python, «легче просить прощения, чем разрешения». Имея это в виду, фрагмент выше, может быть лучше написано:

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

Это решает две проблемы, описанные выше, но создает третий: os.makedirs поднимает OSError исключение, когда один из следующих условий:

  • каталог уже существует
  • не может быть создан каталог

Это означает, что не может определить, какое из двух условий вызвало возбуждение исключения. Другими словами, фактические провалы будут игнорироваться, и это не то, что я хочу.

Как я могу обойти эту проблему?

ответ

2

Замечу, что все это довольно разумно в python 3; FileExistsError и PermissionError являются отдельными (подкласс 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.

+2

Хорошая идея! И я могу использовать ['errno.EEXIST'] (http://docs.python.org/2/library/errno.html#errno.EEXIST) вместо hardcoding' 17'. –

+0

У нас все еще есть состояние гонки, правда, правильно? – seriousdev

Смежные вопросы