5491 votes

Vérifier si un répertoire existe et de le créer si nécessaire

Quelle est la façon la plus élégante pour vérifier si le répertoire d'un fichier va être écrit existe, et si pas, créez le répertoire? Voici ce que j'ai essayé:

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

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

f = file(filename)

D'une certaine manière, j'ai manqué os.path.exists (merci kanja, Blair, et Douglas). C'est ce que j'ai maintenant:

def ensure_dir(f):
    d = os.path.dirname(f)
    if not os.path.exists(d):
        os.makedirs(d)

Est-il un drapeau "open", qui fait de ce lieu automatiquement?

40 votes

En général, vous devrez tenir compte du cas où le nom du fichier ne contient pas de répertoire. Sur ma machine, dirname('foo.txt') donne '', qui n'existe pas et fait échouer makedirs().

14 votes

Dans python 2.7 os.path.mkdir n'existe pas. C'est os.mkdir .

9 votes

Si le chemin existe il faut non seulement vérifier si c'est un répertoire et non un fichier normal ou un autre objet (beaucoup de réponses vérifient cela) mais il faut aussi vérifier s'il est accessible en écriture (je n'ai pas trouvé de réponse qui vérifie cela).

6887voto

Blair Conrad Points 56195

Sur Python ≥ 3.5, utilisez pathlib.Path.mkdir :

from pathlib import Path
Path("/my/directory").mkdir(parents=True, exist_ok=True)

Pour les anciennes versions de Python, je vois deux réponses avec de bonnes qualités, chacune avec un petit défaut, donc je vais donner mon point de vue :

Essayez os.path.exists et considérer os.makedirs pour la création.

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

Comme indiqué dans les commentaires et ailleurs, il y a une condition de course - si le répertoire est créé entre la date d'achèvement de l'opération et la date d'achèvement de l'opération. os.path.exists et le os.makedirs appels, le os.makedirs échouera avec un OSError . Malheureusement, la prise en charge des couvertures OSError et continuer n'est pas infaillible, car il ignorera un échec de la création du répertoire dû à d'autres facteurs, tels que des permissions insuffisantes, un disque plein, etc.

Une option serait de piéger le OSError et examiner le code d'erreur incorporé (voir Existe-t-il un moyen multiplateforme d'obtenir des informations à partir de l'erreur OSError de Python ? ) :

import os, errno

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

Alternativement, il pourrait y avoir un deuxième os.path.exists Mais supposons qu'une autre personne ait créé le répertoire après la première vérification, puis l'ait supprimé avant la seconde - nous pourrions encore nous faire avoir.

Selon l'application, le danger des opérations simultanées peut être plus ou moins grand que le danger posé par d'autres facteurs tels que les autorisations de fichiers. Le développeur devra en savoir plus sur l'application particulière qu'il développe et sur son environnement attendu avant de choisir une mise en œuvre.

Les versions modernes de Python améliorent considérablement ce code, en exposant à la fois FileExistsError (en 3.3+)...

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

...et en permettant un argument de mot-clé pour os.makedirs appelé exist_ok (dans 3.2+).

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

12 votes

La condition de course est un bon point, mais l'approche en stackoverflow.com/questions/273192/#273208 masquera l'échec de la création du répertoire. Ne vous sentez pas mal de voter contre - vous n'aimez pas la réponse. C'est à cela que servent les votes.

38 votes

Rappelez-vous que os.path.exists() n'est pas libre. Si le cas normal est que le répertoire sera là, alors le cas où il ne l'est pas devrait être traité comme une exception. En d'autres termes, essayez d'ouvrir et d'écrire dans votre fichier, attrapez l'exception OSError et, en fonction de errno, faites votre makedir() et réessayez ou relancez. Cela crée une duplication de code à moins que vous n'enveloppiez l'écriture dans une méthode locale.

27 votes

os.path.exists retourne également True pour un dossier. J'ai publié une réponse à ce sujet.

1501voto

A-B-B Points 797

Python 3.5+ :

import pathlib
pathlib.Path('/my/directory').mkdir(parents=True, exist_ok=True) 

pathlib.Path.mkdir tel qu'utilisé ci-dessus crée récursivement le répertoire et ne lève pas d'exception si le répertoire existe déjà. Si vous n'avez pas besoin ou ne voulez pas que les parents soient créés, sautez la commande parents argument.

Python 3.2+ :

Utilisation de pathlib :

Si vous le pouvez, installez la version actuelle de pathlib backport nommé pathlib2 . N'installez pas l'ancien backport non maintenu nommé pathlib . Ensuite, reportez-vous à la section Python 3.5+ ci-dessus et utilisez-la de la même manière.

Si vous utilisez Python 3.4, même s'il est fourni avec le logiciel pathlib il manque l'utile exist_ok option. Le backport a pour but d'offrir une implémentation plus récente et supérieure de l'option mkdir qui comprend cette option manquante.

Utilisation de os :

import os
os.makedirs(path, exist_ok=True)

os.makedirs tel qu'utilisé ci-dessus crée récursivement le répertoire et ne lève pas d'exception si le répertoire existe déjà. Il possède l'option exist_ok uniquement si vous utilisez Python 3.2+, avec une valeur par défaut de False . Cet argument n'existe pas dans Python 2.x jusqu'à 2.7. Il n'est donc pas nécessaire de gérer manuellement les exceptions comme dans Python 2.7.

Python 2.7+ :

Utilisation de pathlib :

Si vous le pouvez, installez la version actuelle de pathlib backport nommé pathlib2 . N'installez pas l'ancien backport non maintenu nommé pathlib . Ensuite, reportez-vous à la section Python 3.5+ ci-dessus et utilisez-la de la même manière.

Utilisation de os :

import os
try: 
    os.makedirs(path)
except OSError:
    if not os.path.isdir(path):
        raise

Alors qu'une solution naïve pourrait d'abord utiliser os.path.isdir suivi par os.makedirs la solution ci-dessus inverse l'ordre des deux opérations. Ce faisant, elle évite une situation de concurrence courante liée à une double tentative de création du répertoire, et permet également de distinguer les fichiers des répertoires.

Notez que la capture de l'exception et l'utilisation de errno est d'une utilité limitée car OSError: [Errno 17] File exists c'est-à-dire errno.EEXIST est affiché pour les fichiers et les répertoires. Il est plus fiable de vérifier simplement si le répertoire existe.

Alternatif :

mkpath crée le répertoire imbriqué, et ne fait rien si le répertoire existe déjà. Cela fonctionne à la fois dans Python 2 et 3.

import distutils.dir_util
distutils.dir_util.mkpath(path)

Par Bogue 10948 Cette solution présente une limite importante : elle ne fonctionne qu'une fois par processus python pour un chemin donné. En d'autres termes, si vous l'utilisez pour créer un répertoire, puis supprimez ce répertoire à l'intérieur ou à l'extérieur de Python, puis utilisez la commande mkpath pour recréer le même répertoire, mkpath se contentera d'utiliser silencieusement l'information invalide qu'il a mise en cache concernant la création antérieure du répertoire, et ne créera pas réellement le répertoire à nouveau. En revanche, os.makedirs ne dépend pas d'un tel cache. Cette limitation peut être acceptable pour certaines applications.


En ce qui concerne l'annuaire mode veuillez vous référer à la documentation si vous y tenez.

17 votes

Cette réponse couvre pratiquement tous les cas particuliers, pour autant que je sache. Je prévois d'envelopper cela dans un "if not os.path.isdir()" puisque je m'attends à ce que le répertoire existe presque toujours et je peux éviter l'exception de cette façon.

7 votes

@CharlesL. Une exception est probablement moins chère que l'IO disque de la vérification, si votre raison est la performance.

2 votes

@jpmc26 mais makedirs fait des stat, umask, lstat supplémentaires alors qu'il ne vérifie que pour lancer OSError.

658voto

Heikki Toivonen Points 11133

L'utilisation de try except et du bon code d'erreur du module errno permet de se débarrasser de la condition de course et est multiplateforme :

import os
import errno

def make_sure_path_exists(path):
    try:
        os.makedirs(path)
    except OSError as exception:
        if exception.errno != errno.EEXIST:
            raise

En d'autres termes, nous essayons de créer les répertoires, mais s'ils existent déjà, nous ignorons l'erreur. En revanche, toute autre erreur est signalée. Par exemple, si vous créez le répertoire 'a' au préalable et que vous lui enlevez toutes les permissions, vous obtiendrez un message d'erreur. OSError a relancé avec errno.EACCES (Permission refusée, erreur 13).

2 votes

Quelle est la raison commune contre Pourquoi les gens préfèrent-ils l'instruction if à l'instruction try/except ? Est-ce que l'instruction try/except est plus dangereuse si vous ratez l'erreur ?

26 votes

La réponse acceptée est en fait dangereuse car elle comporte une condition de race. Elle est cependant plus simple, donc si vous n'êtes pas au courant de la condition de race, ou si vous pensez qu'elle ne s'applique pas à vous, ce serait votre premier choix évident.

18 votes

Lever l'exception uniquement lorsque exception.errno != errno.EEXIST ignorera involontairement le cas où path existe mais est un objet non-répertoire tel qu'un fichier. L'exception devrait idéalement être levée si le chemin est un objet non répertoire.

137voto

hiro protagonist Points 1983

À partir de Python 3.5, pathlib.Path.mkdir a un exist_ok drapeau :

from pathlib import Path
path = Path('/my/directory/filename.txt')
path.parent.mkdir(parents=True, exist_ok=True) 
# path.parent ~ os.path.dirname(path)

Cela crée récursivement le répertoire et ne lève pas d'exception si le répertoire existe déjà.

(tout comme os.makedirs a obtenu un exist_ok à partir de python 3.2, par exemple os.makedirs(path, exist_ok=True) )


Note : lorsque j'ai posté cette réponse, aucune des autres réponses ne mentionnait exist_ok ...

131voto

Je vous recommande personnellement d'utiliser os.path.isdir() à tester au lieu de os.path.exists() .

>>> os.path.exists('/tmp/dirname')
True
>>> os.path.exists('/tmp/dirname/filename.etc')
True
>>> os.path.isdir('/tmp/dirname/filename.etc')
False
>>> os.path.isdir('/tmp/fakedirname')
False

Si vous avez :

>>> dir = raw_input(":: ")

Et une entrée utilisateur stupide :

:: /tmp/dirname/filename.etc

... Vous allez vous retrouver avec un répertoire nommé filename.etc lorsque vous passez cet argument à os.makedirs() si vous testez avec os.path.exists() .

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X