Jak mogę bezpiecznie utworzyć katalog zagnieżdżony?

Jaki jest najbardziej elegancki sposób na sprawdzenie, czy katalog, do którego ma być zapisany plik, istnieje, a jeśli nie, utworzenie katalogu za pomocą Pythona? Oto, co próbowałem:

import os

file_path = "/my/directory/filename.txt"
directory = os.path.dirname(file_path)

try:
    os.stat(directory)
except:
    os.mkdir(directory)       

f = file(filename)

W jakiś sposób przegapiłem os.path.exists (dzięki kanja, Blair i Douglas). To jest to, co mam teraz:

def ensure_dir(file_path):
    directory = os.path.dirname(file_path)
    if not os.path.exists(directory):
        os.makedirs(directory)

Czy jest jakaś flaga dla "open", która sprawia, że dzieje się to automatycznie?

Rozwiązanie

Widzę dwie odpowiedzi z dobrymi cechami, każda z małą wadą, więc dam moje ujęcie na ten temat:

Spróbuj os.path.exists, i rozważ os.makedirs do tworzenia.

import os
if not os.path.exists(directory):
    os.makedirs(directory)

Jak zauważono w komentarzach i gdzie indziej, istnieje warunek wyścigu – jeśli katalog zostanie utworzony pomiędzy wywołaniami os.path.exists i os.makedirs, os.makedirs zawiedzie z OSError. Niestety, wyłapywanie OSError i kontynuowanie nie jest niezawodne, ponieważ zignoruje niepowodzenie tworzenia katalogu z powodu innych czynników, takich jak niewystarczające uprawnienia, pełny dysk, itp.

Jedną z opcji byłoby złapanie OSError i zbadanie osadzonego kodu błędu (zobacz Czy istnieje międzyplatformowy sposób na uzyskanie informacji z Python's OSError):

import os, errno

try:
    os.makedirs(directory)
except OSError as e:
    if e.errno != errno.EEXIST:
        raise

Alternatywnie, może być drugi os.path.exists, ale załóżmy, że inny utworzył katalog po pierwszym sprawdzeniu, a następnie usunął go przed drugim – nadal możemy zostać oszukani.

W zależności od aplikacji, niebezpieczeństwo współbieżnych operacji może być większe lub mniejsze niż niebezpieczeństwo stwarzane przez inne czynniki, takie jak uprawnienia do plików. Programista musiałby wiedzieć więcej o konkretnej tworzonej aplikacji i jej oczekiwanym środowisku przed wyborem implementacji.

Współczesne wersje Pythona dość mocno poprawiają ten kod, zarówno poprzez wyeksponowanie FileExistsError (w wersji 3.3+)...

try:
    os.makedirs("path/to/directory")
except FileExistsError:
    # directory already exists
    pass

...i przez umożliwienie argumentu słowa kluczowego do os.makedirs o nazwie exist_ok (w 3.2+).

os.makedirs("path/to/directory", exist_ok=True)  # succeeds even if directory exists.
Komentarze (8)

Sprawdź os.makedirs: (Upewnia się, że kompletna ścieżka istnieje). Aby obsłużyć fakt, że katalog może istnieć, złap OSError. (Jeśli exist_ok jest False (domyślnie), to OSError jest podnoszony jeśli katalog docelowy już istnieje).

import os
try:
    os.makedirs('./path/to/somewhere')
except OSError:
    pass
Komentarze (3)

Spróbuj funkcji os.path.exists

if not os.path.exists(dir):
    os.mkdir(dir)
Komentarze (3)