Python usa la indentación para definir bloques, por lo que mezclar tabs y espacios puede producir errores difíciles de ver. Un archivo puede parecer alineado en un editor y formar niveles diferentes en otro porque el ancho de la tabulación es configurable. El módulo tabnanny en Python analiza archivos y directorios para detectar indentación ambigua antes de que cause fallos o una interpretación inesperada.
Esta guía explica el uso desde la terminal, la comprobación recursiva de proyectos, check(), process_tokens() y una política confiable de whitespace. Complementa nuestros artículos sobre tokenize en Python, IndentationError, TabError, bytecode con dis y buenas prácticas en scripts.
Por qué tabs y espacios causan problemas
Un carácter tab no representa una cantidad fija de espacios visuales. Los editores pueden mostrarlo con ancho de dos, cuatro u ocho columnas. Dos líneas que parecen alineadas pueden contener secuencias distintas.
if activo:
procesar()
finalizar()Según las columnas efectivas, el intérprete puede lanzar TabError, IndentationError o interpretar bloques de forma diferente a la intención del autor.
Qué detecta tabnanny
Tabnanny busca indentación cuyo significado depende del ancho asignado a las tabs. No es un formateador y no reescribe archivos. Su objetivo es señalar líneas ambiguas para corregir la fuente original.
La documentación oficial de tabnanny indica que el módulo está pensado principalmente para ejecutarse como script, aunque una IDE puede importarlo.
Comprobar un archivo
Ejecuta el módulo con -m y proporciona una ruta.
python -m tabnanny programa.pyCuando no hay problemas, normalmente no se imprime nada. Si se encuentra ambigüedad, el diagnóstico incluye archivo, línea e información sobre el whitespace.
Comprobar un directorio
Cuando recibe un directorio, tabnanny recorre recursivamente el árbol y comprueba archivos .py.
python -m tabnanny srcLos directorios que son enlaces simbólicos no se recorren como directorios ordinarios. En proyectos grandes, elige una raíz limitada para evitar entornos virtuales, dependencias copiadas y resultados generados.
Salida detallada
La opción -v aumenta los mensajes de progreso.
python -m tabnanny -v srcRepetir la opción puede incrementar la verbosidad interna. Es útil para diagnóstico local; en integración continua suele ser preferible una salida corta.
Mostrar solo nombres
La opción -q imprime únicamente los nombres de archivos con problemas.
python -m tabnanny -q srcEl formato puede alimentar otro script, pero omite los detalles de línea. Para corregir, vuelve a ejecutar sin -q.
Usar check() desde Python
tabnanny.check() acepta un archivo o directorio.
import tabnanny
tabnanny.check("src")Los diagnósticos se escriben en stdout mediante print(). La función no fue diseñada como una API estructurada moderna, por lo que una integración debe redirigir la salida o usar piezas internas con cuidado.
Capturar la salida
from contextlib import redirect_stdout
from io import StringIO
import tabnanny
salida = StringIO()
with redirect_stdout(salida):
tabnanny.check("src")
reporte = salida.getvalue()
print(reporte)La redirección global de stdout no es segura en un servidor multithread. Las herramientas concurrentes deberían ejecutar tabnanny en un subprocess.
Procesar tokens directamente
process_tokens() recibe tokens producidos por tokenize.
import tabnanny
import tokenize
with open("programa.py", "rb") as archivo:
tokens = tokenize.tokenize(archivo.readline)
tabnanny.process_tokens(tokens)Al detectar ambigüedad, la función lanza NannyNag. check() captura la excepción y muestra el diagnóstico.
Capturar NannyNag
try:
with open("programa.py", "rb") as archivo:
tokens = tokenize.tokenize(archivo.readline)
tabnanny.process_tokens(tokens)
except tabnanny.NannyNag as error:
print("Indentación ambigua:", error)La excepción contiene información usada por el informe, pero esta superficie puede cambiar. La documentación advierte que la API programática puede no ser compatible entre versiones.
Una API que puede cambiar
Tabnanny es una utilidad antigua orientada a la terminal. Las herramientas que dependen de detalles internos deben fijar versiones, mantener pruebas de compatibilidad y ofrecer una alternativa.
Cuando se necesitan datos estructurados estables, considera invocar python -m tabnanny en subprocess o implementar una regla sobre tokens INDENT y DEDENT.
tabnanny frente a TabError
TabError aparece cuando el intérprete encuentra tabs y espacios inconsistentes durante la compilación. Tabnanny puede inspeccionar un árbol completo sin importar ni ejecutar módulos.
Por eso funciona como comprobación preventiva en commits, builds y editores.
tabnanny frente a IndentationError
IndentationError abarca problemas más amplios, como un bloque ausente, indentación inesperada o un dedent incompatible.
if activo:
print("falta indentación")Tabnanny tiene el propósito más específico de detectar ambigüedad de whitespace. El proyecto también debe compilar o parsear el código para encontrar otros errores.
Integración continua
Un job de CI puede analizar las carpetas del proyecto.
python -m tabnanny src testsConfirma el comportamiento del código de salida en la versión elegida. Como la utilidad se centra en imprimir diagnósticos, un pipeline confiable puede envolverla y fallar cuando aparece salida.
import subprocess
import sys
resultado = subprocess.run(
[sys.executable, "-m", "tabnanny", "src"],
capture_output=True,
text=True,
)
if resultado.stdout.strip() or resultado.stderr.strip():
print(resultado.stdout)
print(resultado.stderr)
raise SystemExit(1)Prueba el wrapper con un archivo deliberadamente ambiguo.
Comprobación pre-commit
Un hook de Git puede verificar los archivos Python modificados.
python -m tabnanny archivo1.py archivo2.pyLa interfaz recibe rutas, por lo que un wrapper puede invocarla una vez por archivo. Excluye entornos virtuales y artefactos generados.
Corregir la indentación
La corrección más segura consiste en convertir la indentación a espacios, normalmente cuatro por nivel, sin alterar tabs dentro de strings o datos.
Usa el comando de conversión del editor, inspecciona el diff y ejecuta tests. Una sustitución global de todos los caracteres \t puede dañar contenido intencional.
Configurar el editor
Activa:
- espacios al pulsar Tab;
- ancho visual de cuatro espacios;
- visualización de caracteres invisibles;
- eliminación de whitespace final;
- detección de indentación por archivo.
Un archivo .editorconfig ayuda a compartir la política entre IDEs.
Formateadores y linters
Formateadores como Black normalmente normalizan la indentación en código válido. Los linters detectan tabs y otros problemas. Tabnanny sigue siendo útil porque forma parte de la biblioteca estándar.
Un orden razonable es: detectar errores de sintaxis, corregir whitespace ambiguo, formatear, ejecutar el linter y correr tests.
Archivos generados y dependencias
No edites automáticamente código de terceros dentro de un entorno virtual. Excluye carpetas como .venv, build, dist y caches seleccionando la raíz correcta.
Si el código generado falla, corrige el generador en vez de parchear únicamente el resultado.
Encoding
Tabnanny depende de tokenize, que respeta BOM UTF-8 y cookies de encoding. Un problema de codificación puede aparecer antes del análisis de indentación.
Usa UTF-8 en proyectos modernos y conserva fixtures para codificaciones heredadas que deban seguir funcionando.
Código temporalmente incompleto
Mientras se escribe, un archivo puede contener una string o delimitador sin cerrar. La tokenización puede generar TokenError. Las IDE deberían esperar un pequeño intervalo o analizar después de guardar.
No conviertas cada estado intermedio en una advertencia permanente.
Verificador aislado
from pathlib import Path
import subprocess
import sys
def verificar(ruta: Path) -> list[str]:
resultado = subprocess.run(
[sys.executable, "-m", "tabnanny", str(ruta)],
capture_output=True,
text=True,
timeout=30,
)
lineas = resultado.stdout.splitlines() + resultado.stderr.splitlines()
return [linea for linea in lineas if linea.strip()]El timeout limita recorridos inesperados por árboles muy grandes.
Seguridad
La utilidad lee código sin ejecutarlo, lo cual es más seguro que importarlo. Una ruta no confiable todavía puede apuntar a árboles enormes, archivos especiales o ubicaciones fuera del proyecto autorizado.
Resuelve la raíz permitida, rechaza salidas de esa raíz, limita tamaño y cantidad de archivos y ejecuta con permisos mínimos.
Errores frecuentes
- Esperar que tabnanny formatee el archivo.
- Analizar todo el entorno virtual.
- Redirigir stdout global en un servidor multithread.
- Depender de detalles internos sin pruebas de versión.
- Sustituir tabs dentro de strings.
- Considerar todo IndentationError una ambigüedad de tabs.
- Parchear archivos generados en vez del generador.
- Analizar una ruta sin límites.
Buenas prácticas
- Estandariza cuatro espacios.
- Ejecuta tabnanny en archivos modificados y en CI.
- Combina con parser, formatter, linter y tests.
- Excluye dependencias y artefactos.
- Usa subprocess para integraciones concurrentes.
- Prueba compatibilidad en versiones soportadas.
- Corrige generadores que producen whitespace ambiguo.
- Protege la raíz y limita recursos.
Conclusión
El módulo tabnanny en Python detecta indentación ambigua causada por la combinación de tabs y espacios. Puede inspeccionar un archivo o recorrer directorios de forma recursiva, por lo que es una comprobación ligera para editores, hooks y pipelines.
Su alcance es específico y su API programática puede cambiar, pero su objetivo es claro: localizar problemas de whitespace antes de ejecutar. Con una política de cuatro espacios, caracteres invisibles visibles, un formateador y tests automáticos, tabnanny ayuda a mantener bloques Python predecibles en cualquier editor.







