Python: crear un directorio anidado de forma segura

P

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.

 

About the author

Ramiro de la Vega

Bienvenido a Pharos.sh

Soy Ramiro de la Vega, Estadounidense con raíces Españolas. Empecé a programar hace casi 20 años cuando era muy jovencito.

Espero que en mi web encuentres la inspiración y ayuda que necesitas para adentrarte en el fantástico mundo de la programación y conseguir tus objetivos por difíciles que sean.

Add comment

Sobre mi

Últimos Post

Etiquetas

Esta web utiliza cookies propias para su correcto funcionamiento. Al hacer clic en el botón Aceptar, aceptas el uso de estas tecnologías y el procesamiento de tus datos para estos propósitos. Más información
Privacidad