Question Comment puis-je créer un répertoire imbriqué en toute sécurité dans Python?


Quelle est la manière la plus élégante de vérifier si le répertoire dans lequel un fichier va être écrit existe, et si ce n'est pas le cas, créez le répertoire en utilisant Python? Voici ce que j'ai essayé:

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)

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

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

Y a-t-il un drapeau pour "open", qui rend cela automatique?


2983
2017-11-07 18:56


origine


Réponses:


Je vois deux réponses avec de bonnes qualités, chacune avec un petit défaut, donc je vais donner mon avis dessus:

Essayer os.path.exists, et considérez 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 concurrence - si le répertoire est créé entre le os.path.exists et le os.makedirs appels, le os.makedirs va échouer avec un OSError. Malheureusement, attraper la couverture OSError et continuer n'est pas infaillible, car il ignorera l'échec de la création du répertoire en raison d'autres facteurs, tels que des autorisations insuffisantes, un disque plein, etc.

Une option serait de piéger le OSError et examinez le code d'erreur intégré (voir Existe-t-il un moyen multi-plateforme d'obtenir des informations à partir de 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 une seconde os.path.exists, mais supposons qu'un autre ait créé le répertoire après le premier contrôle, puis l'ait supprimé avant le second - nous pourrions toujours être dupés.

Selon l'application, le danger des opérations simultanées peut être plus ou moins que le danger posé par d'autres facteurs tels que les autorisations de fichiers. Le développeur devrait en savoir plus sur l'application particulière en cours de développement et son environnement attendu avant de choisir une implémentation.


3679
2017-11-07 19:06



Python 3.5+:

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

pathlib.Path.mkdir comme utilisé ci-dessus crée de façon récursive le répertoire et ne déclenche pas une exception si le répertoire existe déjà. Si vous n'avez pas besoin ou ne souhaitez pas que les parents soient créés, ignorez parents argument.

Python 3.2+:

En utilisant pathlib:

Si vous le pouvez, installez le courant 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 livré avec pathlib, il manque l'utile exist_ok option. Le backport est destiné à offrir une mise en œuvre plus récente et supérieure de mkdir qui inclut cette option manquante.

En utilisant os:

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

os.makedirs comme utilisé ci-dessus crée de façon récursive le répertoire et ne déclenche pas une exception si le répertoire existe déjà. Il a l'optionnel exist_ok argument 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. En tant que tel, il n'y a pas besoin de gestion manuelle des exceptions comme avec Python 2.7.

Python 2.7+:

En utilisant pathlib:

Si vous le pouvez, installez le courant 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.

En utilisant os:

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

Alors qu'une solution naïve peut d'abord utiliser os.path.isdir suivi par os.makedirs, la solution ci-dessus inverse l'ordre des deux opérations. Ce faisant, il empêche une condition de concurrence commune ayant trait à une tentative dupliquée de création du répertoire, et désamorce également les fichiers des répertoires.

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

Alternative:

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, une limitation importante de cette alternative est qu'elle ne fonctionne qu'une seule fois par processus Python pour un chemin donné. En d'autres termes, si vous l'utilisez pour créer un répertoire, supprimez le répertoire de l'intérieur ou de l'extérieur de Python, puis utilisez mkpath encore une fois pour recréer le même répertoire, mkpath utilisera simplement silencieusement ses informations cachées invalides pour avoir préalablement créé le répertoire, et ne fera pas le répertoire à nouveau. En revanche, os.makedirs ne compte pas sur un tel cache. Cette limitation peut convenir pour certaines applications.


En ce qui concerne le répertoire mode, s'il vous plaît se référer à la documentation si vous vous en souciez.


809
2018-01-16 17:31



Utiliser try except et le bon code d'erreur du module errno se débarrasse de la condition de concurrence et est multi-plateforme:

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. D'un autre côté, toute autre erreur est signalée. Par exemple, si vous créez préalablement le répertoire 'a' et supprimez toutes les permissions, vous obtiendrez un OSError élevé avec errno.EACCES (Autorisation refusée, erreur 13).


572
2018-02-17 17:17



Je recommande personnellement que vous utilisiez 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 tu as:

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

Et une entrée d'utilisateur idiot:

:: /tmp/dirname/filename.etc

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


85
2018-01-14 17:57



Vérifier os.makedirs: (Il s'assure que le chemin complet existe.)
 Pour gérer le fait que le répertoire puisse exister, attraper OSError. (Si exist_ok est False (valeur par défaut), une erreur OSError est déclenchée si le répertoire cible existe déjà.)

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

56
2017-11-07 19:01



Un aperçu des spécificités de cette situation

Vous donnez un fichier particulier à un certain chemin et vous tirez le répertoire du chemin du fichier. Ensuite, après vous être assuré que vous avez le répertoire, vous essayez d'ouvrir un fichier à lire. Pour commenter ce code:

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

Nous voulons éviter d'écraser la fonction intégrée, dir. Aussi, filepath ou peut-être fullfilepath est probablement un meilleur nom sémantique que filename donc ce serait mieux écrit:

import os
filepath = '/my/directory/filename.txt'
directory = os.path.dirname(filepath)

Votre but final est d'ouvrir ce fichier, vous l'énoncez initialement, pour l'écriture, mais vous approchez essentiellement de cet objectif (basé sur votre code) comme ceci, qui ouvre le fichier pour en train de lire:

if not os.path.exists(directory):
    os.makedirs(directory)
f = file(filename)

En supposant l'ouverture pour la lecture

Pourquoi voudriez-vous faire un répertoire pour un fichier que vous attendez d'être là et être capable de lire?

Essayez simplement d'ouvrir le fichier.

with open(filepath) as my_file:
    do_stuff(my_file)

Si le répertoire ou le fichier n'est pas là, vous aurez un IOError avec un numéro d'erreur associé: errno.ENOENT pointera sur le numéro d'erreur correct quelle que soit votre plate-forme. Vous pouvez l'attraper si vous le souhaitez, par exemple:

import errno
try:
    with open(filepath) as my_file:
        do_stuff(my_file)
except IOError as error:
    if error.errno == errno.ENOENT:
        print 'ignoring error because directory or file is not there'
    else:
        raise

En supposant que nous ouvrons pour l'écriture

C'est Probablement ce que tu veux.

Dans ce cas, nous ne sommes probablement pas confrontés à des conditions de course. Alors faites comme vous étiez, mais notez que pour l'écriture, vous devez ouvrir avec le w mode (ou a à ajouter). C'est aussi une bonne pratique de Python d'utiliser le gestionnaire de contexte pour ouvrir des fichiers.

import os
if not os.path.exists(directory):
    os.makedirs(directory)
with open(filepath, 'w') as my_file:
    do_stuff(my_file)

Cependant, disons que nous avons plusieurs processus Python qui tentent de mettre toutes leurs données dans le même répertoire. Ensuite, nous pouvons avoir des conflits sur la création du répertoire. Dans ce cas, il est préférable d'envelopper le makedirs appeler dans un bloc try-except.

import os
import errno
if not os.path.exists(directory):
    try:
        os.makedirs(directory)
    except OSError as error:
        if error.errno != errno.EEXIST:
            raise
with open(filepath, 'w') as my_file:
    do_stuff(my_file)

29
2018-01-22 23:49



À 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 de manière récursive le répertoire et ne déclenche pas d'exception si le répertoire existe déjà.

(tout comme os.makedirs eu un exists_ok drapeau à partir de python 3.2).


28
2017-12-14 16:06



J'ai mis le suivant en bas. Ce n'est pas totalement infaillible cependant.

import os

dirname = 'create/me'

try:
    os.makedirs(dirname)
except OSError:
    if os.path.exists(dirname):
        # We are nearly safe
        pass
    else:
        # There was an error on creation, so make sure we know about it
        raise

Maintenant, comme je le dis, ce n'est pas vraiment infaillible, parce que nous avons la possibilité de ne pas créer le répertoire, et un autre processus le créant pendant cette période.


22
2017-11-07 21:23