Python: crear un directorio anidado de forma segura

    Introducción

    La manipulación de archivos es una de las habilidades más importantes para dominar en cualquier lenguaje de programación, y hacerlo correctamente es de suma importancia. Cometer un error podría causar un problema en su programa, otros programas que se ejecutan en el mismo sistema e incluso en el propio sistema.

    Los posibles errores pueden ocurrir debido a que el directorio principal no existe, o porque otros programas cambian archivos en el sistema de archivos al mismo tiempo, creando algo que se llama condición de carrera.

    Una condición de carrera (en este caso llamada carrera de datos) ocurre cuando dos o más programas quieren crear un archivo con el mismo nombre en el mismo lugar. Si se produce este tipo de error, es muy difícil de encontrar y corregir, ya que no es determinista, o simplemente, pueden suceder cosas diferentes dependiendo del momento exacto en que los dos corredores compiten por los datos.

    En este artículo, veremos cómo crear un subdirectorio en Python de forma segura, paso a paso. A partir de ahora, todo funcionará en Mac, Linux y Windows.

    Creación segura de un directorio anidado con pathlib

    Hay muchas formas de crear un subdirectorio, pero quizás la más simple sea usar el pathlib módulo. los pathlib El módulo está diseñado principalmente para ayudar a abstraer diferentes sistemas de archivos del sistema operativo y proporcionar una interfaz uniforme para trabajar con la mayoría de ellos.

    Gracias a él, su código debería ser independiente de la plataforma. Tenga en cuenta que esto solo funciona en versiones más recientes de Python (3.5 y posteriores).

    Digamos que tenemos una ruta absoluta de un directorio que se nos ha dado como una cadena y deseamos crear un subdirectorio con un nombre dado. Creemos un directorio llamado OuterDirectory, y lugar InnerDirectory dentro de eso.

    Importaremos Path desde el pathlib módulo, cree un Path objeto con la ruta deseada para nuestro nuevo archivo, y use el mkdir() método que tiene la siguiente firma:

    Path.mkdir(mode=0o777, parents=False, exist_ok=False)
    

    El siguiente fragmento de código hace lo que describimos anteriormente:

    from pathlib import Path # Import the module
    path = Path("/home/kristina/OuterDirectory/InnerDirectory") # Create Path object
    path.mkdir() # Cake the directory
    

    Si mkdir() no tiene éxito, no se creará ningún directorio y se generará un error.

    Opciones y errores de mkdir ()

    Si ejecuta el código sin crear el OuterDirectory, verá el siguiente error:

    Traceback (most recent call last):
      File "makesubdir.py", line 3, in <module>
        path.mkdir()
      File "/home/kristina/anaconda3/lib/python3.7/pathlib.py", line 1230, in mkdir
        self._accessor.mkdir(self, mode)
    FileNotFoundError: [Errno 2] No such file or directory: '/home/kristina/OuterDirectory/InnerDirectory'
    

    O si InnerDirectory ya existe:

    Traceback (most recent call last):
      File "/home/kristina/Desktop/UNM/makesubdir.py", line 3, in <module>
        path.mkdir()
      File "/home/kristina/anaconda3/lib/python3.7/pathlib.py", line 1230, in mkdir
        self._accessor.mkdir(self, mode)
    FileExistsError: [Errno 17] File exists: '/home/kristina/OuterDirectory/InnerDirectory'
    

    Si ya existe un directorio, el error generado será FileExistsError, y si el padre no existe, el FileNotFoundError se levantará.

    Como no queremos que nuestro programa se rompa cada vez que encuentre un error como este, colocaremos este código en un bloque de prueba:

    from pathlib import Path 
    path = Path("/home/kristina/OuterDirectory/InnerDir") 
    try:
        path.mkdir() 
    except OSError:
        print("Failed to make nested directory")
    else:
        print("Nested directory made")
    

    Cuando se ejecuta, si el directorio se crea correctamente, la salida será:

    Nested directory made
    

    Si nos encontramos con errores, se generará lo siguiente:

    Failed to make a nested directory
    

    los mkdir() El método toma tres parámetros: mode, parentsy exit_ok.

    • los mode parámetro, si se da, combinado con umask indica qué usuarios tienen privilegios de lectura, escritura y ejecución. De forma predeterminada, todos los usuarios tienen todos los privilegios que pueden no ser los que queremos si la seguridad es un problema. Tocaremos más sobre esto más adelante.
    • parents indica, en el caso de que falte el directorio principal, si el método:
    • Cree el directorio principal que falta en sí mismo (true)
    • O para generar un error, como en nuestro segundo ejemplo (false)
    • exist_ok especifica si el FileExistsError debería aparecer si ya existe un directorio con el mismo nombre. Tenga en cuenta que este error seguirá apareciendo si el archivo del mismo nombre no es un directorio.

    Asignar privilegios de acceso

    Hagamos un directorio llamado SecondInnerDirectory donde solo el propietario tiene todos los privilegios de lectura, escritura y ejecución, dentro del inexistente SecondOuterDirectory:

    from pathlib import Path
    path = Path("/home/kristina/SecondOuterDirectory/SecondInnerDirectory")
    path.mkdir(mode = 0o007, parents= True, exist_ok= True)
    

    Esto debería ejecutarse sin errores. Si navegamos al SecondOuterDirectory y verifique su contenido desde la consola así:

    $ ls -al
    

    Deberíamos obtener la salida:

    total 12
    drwxrwxr-x  3 kristina kristina 4096 dec 10 01:26 .
    drwxr-xr-x 77 kristina kristina 4096 dec 10 01:26 ..
    d------r-x  2 kristina kristina 4096 dec 10 01:26 SecondInnerDirectory
    

    Bien, podemos ver que el directorio principal se creó correctamente, pero los privilegios no son los esperados. El propietario carece de privilegios de escritura.

    El problema que tenemos aquí es que umask no nos permite crear los privilegios deseados. Para evitar esto, ahorraremos umaskvalor original, cámbielo temporalmente y, finalmente, devuélvalo a su valor original utilizando el umask() método desde el módulo OS. umask() devuelve el antiguo valor de umask.

    Reescribamos nuestro código para probar esto:

    from pathlib import Path
    import os 
    
    old_mask = os.umask(0) # Saving the old umask value and setting umask to 0
    
    path = Path("/home/kristina/SecondOuterDirectory/SecondInnerDirectory")
    path.mkdir(mode = 0o007, parents= True, exist_ok= True)
    
    os.umask(old_mask) # Reverting umask value
    

    Ejecutando este código y usando el ls -al comando nuevamente dará como resultado la siguiente salida:

    total 12
    drwxrwxrwx  3 kristina kristina 4096 dec 10 01:45 . 
    drwxr-xr-x 77 kristina kristina 4096 dec 10 01:45 ..
    d------rwx  2 kristina kristina 4096 dec 10 01:45 SecondInnerDirectory
    

    Conclusión

    Para manipular archivos de forma segura en muchos sistemas diferentes, necesitamos una forma sólida de manejar errores como las carreras de datos. Python ofrece un gran soporte para esto a través del pathlib módulo.

    Los errores siempre pueden ocurrir cuando se trabaja con sistemas de archivos, y la mejor manera de lidiar con esto es configurando cuidadosamente los sistemas para detectar todos los errores que potencialmente pueden bloquear nuestro programa o causar otros problemas. Escribir código limpio hace que los programas sean duraderos.

     

    Etiquetas:

    Deja una respuesta

    Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *