Objetivo de Aprendizaje
with) y bloques de manejo de excepciones (try/except), para garantizar que el software se recupere con elegancia ante fallos del sistema, fomentando así una mentalidad resiliente, analítica y orientada a la calidad en la resolución de problemas reales del entorno productivo.
🚀 ¿Tu programa funciona o simplemente sobrevive?
Imagina esta situación: estás en un banco, a punto de transferir dinero para un proyecto importante. Haces clic en "Enviar" y, de repente, se va el internet. ¿Qué hace la aplicación del banco?
¿Se cierra de golpe mostrando un gigantesco error rojo? ¿Ignora el problema y dice "éxito" sin guardar nada? ¡No! Una aplicación profesional te muestra un mensaje claro: "Conexión interrumpida. Tu dinero está seguro, inténtalo en unos momentos".
En el mundo real del desarrollo de software, las cosas salen mal constantemente: un usuario borra un archivo por accidente, un disco USB se desconecta, el disco duro se llena o un archivo de configuración se corrompe por un apagón. La gran pregunta es: ¿Qué hace tu programa cuando esto ocurre? Hoy dejarás de escribir código que solo funciona "cuando todo sale bien" y empezarás a construir sistemas que se recuperan con gracia, profesionalismo y resiliencia.
🛡️ Desarrollo: La Caja de Herramientas del Desarrollador Resiliente
1. Conoce a tus "enemigos": Los errores más comunes
Antes de defender tu código, debes saber contra qué luchas. Estos son los fallos clásicos al trabajar con archivos:
FileNotFoundError
Causa: El archivo no existe, está en otra carpeta o escribiste mal el nombre.
PermissionError
Causa: Intentas escribir o leer en una carpeta protegida del sistema (ej. C:/Windows).
IsADirectoryError
Causa: Intentas leer una carpeta como si fuera un archivo de texto.
UnicodeDecodeError
Causa: El archivo tiene una codificación diferente a UTF-8 (ej. Windows-1252).
json.JSONDecodeError
Causa: El archivo JSON tiene un error de sintaxis (falta una coma o una llave).
OSError / IOError
Causa: Problemas generales de hardware: disco lleno, red caída, dispositivo extraído.
2. La red de seguridad: try / except
La forma de manejar errores en Python es envolver el código peligroso en un bloque try. Si falla, en lugar de colapsar, salta al bloque except correspondiente.
try:
# Intenta hacer esto
with open("datos.txt", "r") as archivo:
contenido = archivo.read()
print(f"✅ Leí {len(contenido)} caracteres")
except FileNotFoundError:
# Si falla con este error específico, haz esto
print("❌ El archivo 'datos.txt' no existe. ¿Lo creaste?")
except Exception as e:
# Última línea de defensa para cualquier otro error
print(f"❌ Error inesperado: {e}")
FileNotFoundError), un vendaje para problemas de permisos (PermissionError), y una ambulancia de emergencia para lo inesperado (Exception as e).
3. Tu mejor amigo: El gestor de contexto with
Si usas open() tradicional y ocurre un error antes de close(), el archivo queda abierto, consumiendo memoria y bloqueando recursos. El bloque with garantiza que el archivo se cierre automáticamente, pase lo que pase (incluso si hay un error).
# ✅ FORMA PROFESIONAL: Múltiples archivos seguros
try:
with open("origen.txt", "r") as origen, open("destino.txt", "w") as destino:
contenido = origen.read()
destino.write(contenido)
print("✅ Archivo copiado correctamente")
except Exception as e:
print(f"❌ Error: {e}")
4. Manos a la obra: Cargador de Configuración para Punto de Venta
Imagina que desarrollas un sistema para una tienda de artesanías en Otavalo. La primera vez que el usuario ejecuta el programa, el archivo config.json no existe. ¿Colapsa el programa? ¡No! Nuestro cargador inteligente intenta leerlo, y si no existe o está corrupto, crea uno con valores por defecto o hace un backup.
# ==========================================
# 1. IMPORTACIONES
# ==========================================
import json
from pathlib import Path
from datetime import datetime
import shutil
# ==========================================
# 2. VARIABLES Y CONFIGURACIÓN POR DEFECTO
# ==========================================
ARCHIVO_CONFIG = Path("config.json")
ARCHIVO_BACKUP = Path("config_backup.json")
CONFIG_POR_DEFECTO = {
"negocio": {"nombre": "Artesanías Otavalo", "ruc": "0000000000001"},
"facturacion": {"moneda": "USD", "iva_porcentaje": 15},
"sistema": {"version": "1.0.0", "ultimo_acceso": None}
}
# ==========================================
# 3. FUNCIONES DE MANEJO Y RECUPERACIÓN
# ==========================================
def cargar_configuracion():
print("🔧 Cargando configuración del sistema...")
# Caso 1: El archivo no existe
if not ARCHIVO_CONFIG.exists():
print("⚠️ No se encontró. Creando configuración inicial...")
return crear_configuracion_inicial()
# Caso 2: Intentar leer el archivo existente
try:
with open(ARCHIVO_CONFIG, "r", encoding="utf-8") as archivo:
config = json.load(archivo)
# Actualizar último acceso y guardar
config["sistema"]["ultimo_acceso"] = datetime.now().strftime("%d/%m/%Y %H:%M:%S")
guardar_configuracion(config)
return config
except json.JSONDecodeError as e:
print(f"❌ Archivo corrupto: {e}. Creando backup y nuevo archivo...")
shutil.copy2(ARCHIVO_CONFIG, ARCHIVO_BACKUP)
return crear_configuracion_inicial()
except PermissionError:
print("❌ No tienes permisos. Ejecuta como administrador.")
return None
except Exception as e:
print(f"❌ Error inesperado: {e}")
return None
def crear_configuracion_inicial():
try:
with open(ARCHIVO_CONFIG, "w", encoding="utf-8") as archivo:
json.dump(CONFIG_POR_DEFECTO, archivo, indent=4, ensure_ascii=False)
print("✅ Archivo creado con valores por defecto.")
return CONFIG_POR_DEFECTO.copy()
except Exception as e:
print(f"❌ Error al crear: {e}")
return None
def guardar_configuracion(config):
with open(ARCHIVO_CONFIG, "w", encoding="utf-8") as archivo:
json.dump(config, archivo, indent=4, ensure_ascii=False)
# ==========================================
# 4. PROGRAMA PRINCIPAL
# ==========================================
if __name__ == "__main__":
configuracion = cargar_configuracion()
if configuracion:
print(f"✅ Sistema listo. Negocio: {configuracion['negocio']['nombre']}")
else:
print("❌ No se pudo iniciar el sistema.")
5. Buenas Prácticas Profesionales
- Sé específico con los errores: No uses un
except:genérico al inicio. Oculta bugs reales. AtrapaFileNotFoundErrorantes queException. - Usa
loggingen lugar deprint: Los logs se guardan en un archivo con fecha y hora, esencial para depurar en producción. - No silencies errores: Un
except: passes como esconder la basura bajo la alfombra. Siempre registra o maneja el fallo. - Falla rápido, falla claro: Valida las condiciones al inicio de la función y usa
raisesi algo es inválido, en lugar de dejar que el código avance con datos corruptos.
6. Patrones Avanzados: else, finally y raise
else: Se ejecuta solo si el bloque try fue exitoso. Ideal para separar la lógica de éxito de la de error.
finally: Se ejecuta siempre, haya error o no. Perfecto para limpieza de recursos (aunque with ya lo hace para archivos, es vital para conexiones a bases de datos).
raise: Te permite lanzar tus propias excepciones cuando detectas una condición inválida en tu lógica de negocio.
def validar_edad(edad):
if edad < 0 or edad > 150:
raise ValueError(f"Edad inválida: {edad}. Debe estar entre 0 y 150")
return True
7. La conexión con el mundo real: Bases de Datos
El manejo de errores que aprendiste es crítico en bases de datos. La lógica es idéntica, solo cambian los nombres:
| Concepto en Archivos | Equivalente en Base de Datos |
|---|---|
FileNotFoundError | Tabla o registro no existe |
PermissionError | Usuario sin privilegios |
try / except | Transacciones (commit / rollback) |
finally | Cerrar conexiones a la base de datos |
raise | Lanzar excepciones de negocio |
🧩 Reto DUA: Elige tu camino
Ahora que dominas el manejo de errores, elige el reto que más se alinee con tu estilo de aprendizaje y pasiones. ¡Tú tienes el control!
Reto Visual
Crea un sistema de logs que genere un reporte HTML con gráficos de colores mostrando la frecuencia de cada tipo de error del día.
Reto Lógico
Construye un "probador de robustez" que simule escenarios (disco lleno, permisos denegados) y mida el tiempo de recuperación de tu programa.
Reto de Expansión
Mejora el POS para que sea multi-sucursal. Si falla el config.json local, intenta cargar un config_global.json de respaldo.
Reto de Accesibilidad
Traduce todos los mensajes de error a lenguaje coloquial, agrega sugerencias paso a paso y crea un "modo diagnóstico" para usuarios no técnicos.
🎮 Evaluación Lúdica: Pon a prueba tu conocimiento
Cargando pregunta...
¡Evaluación completada! 🎉
Tu puntuación final es: