GESTIÓN DE ERRORES EN FICHEROS¶
Trabajar con ficheros implica interactuar con el sistema operativo y el disco. Muchas cosas pueden salir mal: el fichero no existe, no tenemos permisos de lectura, el disco está lleno, la ruta apunta a un directorio... Python lanza excepciones para comunicar estos errores.
Excepciones más comunes¶
FileNotFoundError¶
Se lanza cuando se intenta abrir un fichero en modo lectura ("r") y este no existe:
PermissionError¶
Se lanza cuando el proceso no tiene permisos suficientes para abrir el fichero (por ejemplo, intentar escribir en un fichero de solo lectura o en un directorio del sistema):
IsADirectoryError¶
Se lanza cuando se intenta abrir una ruta que corresponde a un directorio, no a un fichero:
FileExistsError¶
Se lanza cuando se intenta crear un fichero en modo exclusivo ("x") y este ya existe:
El modo "x" (exclusive creation) está diseñado precisamente para evitar sobreescrituras accidentales: falla si el fichero ya existe.
OSError e IOError¶
Son la clase base de la mayoría de errores relacionados con ficheros. IOError es un alias de OSError en Python 3. Si quieres capturar cualquier error de I/O sin especificarlo, puedes usar OSError.
Manejo con try / except¶
El bloque try / except permite capturar excepciones y reaccionar de forma controlada:
Capturar múltiples excepciones en un mismo bloque¶
Si queremos manejar varios tipos de error de la misma manera, podemos agruparlos en una tupla:
Acceder al mensaje del error con as¶
La cláusula as e asigna la excepción a una variable, lo que permite mostrar el mensaje de error original:
El orden de las excepciones importa¶
Cuando se encadenan varios except, Python los evalúa de arriba a abajo y ejecuta el primero que coincida. Por eso hay que colocar siempre las excepciones más específicas antes que las más generales.
Si pones OSError primero, nunca llegarás a FileNotFoundError aunque sea la excepción que realmente se lanzó, porque FileNotFoundError es una subclase de OSError y queda "absorbida" por el bloque más general:
La jerarquía de herencia de las excepciones de ficheros en Python es:
OSError
├── FileNotFoundError
├── PermissionError
├── FileExistsError
├── IsADirectoryError
└── ... (otras subclases)
Como regla general: de más específico a más general, igual que en una cadena if / elif.
El bloque finally¶
El bloque finally se ejecuta siempre, haya error o no. Se usa para limpiar recursos o ejecutar código que debe ocurrir en cualquier caso.
Cuando usamos with, el cierre del fichero ya está garantizado. El bloque finally es útil para "limpia" o realizar tareas que se ejecutan tanto si se completó la operación con éxito como si se lanzó alguna excepción. Por ejemplo:
- Actualizar un contador.
- Escribir un log de la operación
Patrón completo: try / except / else / finally¶
Python permite usar else para ejecutar código solo cuando no se produce ninguna excepción:
Verificar antes de abrir¶
En ocasiones es más claro comprobar si el fichero existe antes de intentar abrirlo, usando pathlib.
A esta manera de gestionar los errores se le llama LBYL (Look Before You Leap).
Ejemplo:
Aunque la verificación previa es válida, en Python se prefiere el estilo EAFP (Easier to Ask Forgiveness than Permission): intentar la operación y capturar el error si ocurre, en lugar de comprobar condiciones antes de actuar.
Ambos estilos son correctos; úsalos según lo que haga el código más legible.
Mala práctica: capturar Exception de forma genérica
# Incorrecto: captura cualquier error, incluyendo bugs del programa
try:
with open("notas.txt", "r", encoding="utf-8") as f:
datos = f.read()
except Exception:
pass # Silencia todos los errores, imposible depurar
# Correcto: captura solo los errores esperados y reacciona de forma útil
try:
with open("notas.txt", "r", encoding="utf-8") as f:
datos = f.read()
except FileNotFoundError:
print("El fichero de notas no existe. Creando uno nuevo.")
datos = ""
except PermissionError:
print("Sin permisos de lectura. Comprueba la configuración.")
raise
Tipos de verificaciones¶
Aparte de comprobar si una ruta existe, pathlib.Path permite hacer otras comprobaciones sobre rutas de archivo. Estas verificaciones ayudan a dar mensajes de error más precisos antes de abrir un recurso.
Las veremos con más detalle en el siguiente apartado sobre rutas.
| Verificación | Qué comprueba | Cuándo usarla |
|---|---|---|
Path.exists() |
Si la ruta existe en el sistema de ficheros | Cuando quieres distinguir entre "no existe" y otros errores |
Path.is_file() |
Si la ruta existe y es un fichero normal | Antes de leer datos con open() |
Path.is_dir() |
Si la ruta existe y es un directorio | Cuando esperas una carpeta de trabajo |
Path.suffix |
Extensión del fichero (.txt, .csv, .json...) |
Para validar formato esperado |
Path.is_absolute() |
Si la ruta es absoluta | Para evitar ambigüedad con rutas relativas |
Ejemplo:
Este enfoque LBYL mejora los mensajes al usuario, pero no elimina la necesidad de
try/except: el estado del fichero puede cambiar entre la verificación y la apertura.
Aclaración
Un fichero se considera “normal” cuando es un archivo regular del sistema de ficheros:
- contiene datos (texto/binario)
- se puede abrir con open()
- no es un directorio,
- no es un dispositivo, socket, tubería, etc.