Pregunta ¿Cómo puedo crear de manera segura un directorio anidado en Python?


¿Cuál es la forma más elegante de verificar si el directorio donde se va a escribir un archivo existe, y si no, crear el directorio usando Python? Esto es lo que intenté:

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)

De alguna manera, extrañé os.path.exists (gracias Kanija, Blair y Douglas). Esto es lo que tengo ahora:

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

¿Hay una bandera para "abrir", eso hace que esto suceda automáticamente?


2983
2017-11-07 18:56


origen


Respuestas:


Veo dos respuestas con buenas cualidades, cada una con un pequeño defecto, así que daré mi opinión:

Tratar os.path.existsy considera os.makedirs para la creación

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

Como se menciona en los comentarios y en otros lugares, hay una condición de carrera: si el directorio se crea entre os.path.exists y el os.makedirs llamadas, el os.makedirs fallará con un OSError. Desafortunadamente, atrapar mantas OSError y continuar no es infalible, ya que ignorará la falla al crear el directorio debido a otros factores, como permisos insuficientes, disco lleno, etc.

Una opción sería atrapar el OSError y examinar el código de error incrustado (ver ¿Existe una forma de plataforma cruzada para obtener información del OSError de Python?)

import os, errno

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

Alternativamente, podría haber un segundo os.path.exists, pero supongamos que otro creó el directorio después de la primera comprobación, luego lo eliminó antes que la segunda, aún podríamos ser engañados.

Dependiendo de la aplicación, el peligro de las operaciones concurrentes puede ser mayor o menor que el peligro planteado por otros factores, como los permisos de archivos. El desarrollador debería saber más sobre la aplicación particular que se está desarrollando y su entorno esperado antes de elegir una implementación.


3679
2017-11-07 19:06



Python 3.5+:

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

pathlib.Path.mkdir como se usa arriba, recursivamente crea el directorio y no genera una excepción si el directorio ya existe. Si no necesita o desea que se creen los padres, omita el parents argumento.

Python 3.2+:

Utilizando pathlib:

Si puedes, instala la corriente pathlib backport named pathlib2. No instale el backport no mantenido más antiguo llamado pathlib. A continuación, consulte la sección anterior de Python 3.5 y utilícela de la misma manera.

Si usa Python 3.4, aunque viene con pathlib, falta el útil exist_ok opción. El backport está destinado a ofrecer una implementación más nueva y superior de mkdir que incluye esta opción faltante

Utilizando os:

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

os.makedirs como se usa arriba, recursivamente crea el directorio y no genera una excepción si el directorio ya existe. Tiene el opcional exist_ok argumento solo si se usa Python 3.2+, con un valor predeterminado de False. Este argumento no existe en Python 2.x hasta 2.7. Como tal, no es necesario el manejo manual de excepciones como con Python 2.7.

Python 2.7+:

Utilizando pathlib:

Si puedes, instala la corriente pathlib backport named pathlib2. No instale el backport no mantenido más antiguo llamado pathlib. A continuación, consulte la sección anterior de Python 3.5 y utilícela de la misma manera.

Utilizando os:

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

Mientras que una solución ingenua puede usar primero os.path.isdir seguido por os.makedirs, la solución anterior invierte el orden de las dos operaciones. Al hacerlo, evita que una condición de carrera común tenga que ver con un intento duplicado de crear el directorio, y también elimina la ambigüedad de los archivos de los directorios.

Tenga en cuenta que la captura de la excepción y el uso errno es de utilidad limitada porque OSError: [Errno 17] File exists, es decir errno.EEXIST, se genera para ambos archivos y directorios. Es más confiable simplemente verificar si el directorio existe.

Alternativa:

mkpath crea el directorio anidado y no hace nada si el directorio ya existe. Esto funciona tanto en Python 2 como en 3.

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

Por Error 10948, una limitación severa de esta alternativa es que solo funciona una vez por proceso de python para una ruta determinada. En otras palabras, si lo usa para crear un directorio, elimine el directorio desde dentro o fuera de Python, luego use mkpath de nuevo para recrear el mismo directorio, mkpath simplemente usará silenciosamente su información no válida en caché de haber creado previamente el directorio, y no volverá a crear el directorio. A diferencia de, os.makedirs no depende de tal caché. Esta limitación puede estar bien para algunas aplicaciones.


Con respecto al directorio modo, consulte la documentación si le interesa.


809
2018-01-16 17:31



Usando try except y el código de error correcto del módulo errno se deshace de la condición de carrera y es multiplataforma:

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 otras palabras, tratamos de crear los directorios, pero si ya existen ignoramos el error. Por otro lado, cualquier otro error se informa. Por ejemplo, si crea el directorio 'a' de antemano y quita todos los permisos de él, obtendrá un OSError levantado con errno.EACCES (Permiso denegado, error 13).


572
2018-02-17 17:17



Yo personalmente recomendaría que uses os.path.isdir() para probar en lugar 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 usted tiene:

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

Y una entrada de usuario tonta:

:: /tmp/dirname/filename.etc

... Vas a terminar con un directorio llamado filename.etc cuando pasas ese argumento a os.makedirs() si prueba con os.path.exists().


85
2018-01-14 17:57



Comprobar os.makedirs: (Se asegura de que exista la ruta completa).
 Para manejar el hecho de que el directorio podría existir, atrapa OSError. (Si existe_ok es False (el valor predeterminado), se genera un OSError si el directorio de destino ya existe).

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

56
2017-11-07 19:01



Información sobre los detalles de esta situación

Le da un archivo determinado en una ruta determinada y saca el directorio de la ruta del archivo. Luego, después de asegurarse de tener el directorio, intenta abrir un archivo para leer. Para comentar sobre este código:

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

Queremos evitar sobrescribir la función integrada, dir. También, filepath o quizás fullfilepath es probablemente un mejor nombre semántico que filename entonces esto estaría mejor escrito:

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

Su objetivo final es abrir este archivo, inicialmente, para escribir, pero básicamente se está acercando a este objetivo (basado en su código) como este, que abre el archivo para leyendo:

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

Asumiendo apertura para leer

¿Por qué crearías un directorio para un archivo que esperas que esté allí y puedas leer?

Intenta abrir el archivo.

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

Si el directorio o archivo no está allí, obtendrá un IOError con un número de error asociado: errno.ENOENT señalará el número de error correcto independientemente de su plataforma. Puede atraparlo si lo desea, por ejemplo:

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

Suponiendo que estamos abriendo para escribir

Esto es probablemente lo que estás queriendo

En este caso, probablemente no enfrentamos ninguna condición de carrera. Así que haz lo que eras, pero ten en cuenta que para escribir debes abrir con el w modo (o a para anexar). También es una mejor práctica de Python usar el administrador de contexto para abrir archivos.

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

Sin embargo, digamos que tenemos varios procesos de Python que intentan poner todos sus datos en el mismo directorio. Entonces podemos tener disputa sobre la creación del directorio. En ese caso, es mejor envolver el makedirs llamar en un bloque 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



A partir de Python 3.5, pathlib.Path.mkdir tiene un exist_ok bandera:

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

Esto crea recursivamente el directorio y no genera una excepción si el directorio ya existe.

(Tal como os.makedirs consiguió una exists_ok bandera comenzando desde python 3.2).


28
2017-12-14 16:06



He puesto lo siguiente abajo. Aunque no es totalmente infalible.

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

Ahora bien, como digo, esto no es realmente infalible, porque tenemos la posibilidad de no crear el directorio y otro proceso de creación durante ese período.


22
2017-11-07 21:23



Prueba el os.path.exists función

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

22
2017-11-07 19:00