El módulo py_compile compila un archivo fuente Python individual en bytecode .pyc. Es la herramienta directa para editores, builds incrementales, instaladores, generadores y validadores que necesitan controlar un archivo en lugar de procesar un árbol completo.
La API permite elegir el destino, definir el filename lógico mostrado en tracebacks, seleccionar optimización y configurar cómo se invalida el cache. Para directorios completos, compileall suele ser más cómodo; para flujos archivo por archivo, py_compile ofrece precisión.
Compila un archivo fuente
La función principal es py_compile.compile().
import py_compile
ruta_pyc = py_compile.compile(
"modulo.py",
doraise=True,
)
print(ruta_pyc)
Cuando termina correctamente, devuelve la ruta del bytecode generado.
Usa doraise en automatización
Con doraise=True, los fallos generan PyCompileError.
try:
py_compile.compile("modulo.py", doraise=True)
except py_compile.PyCompileError as error:
print(error)
Este comportamiento es claro para CI, editores, generadores y package builders porque el caller puede detener el proceso y registrar un error estructurado.
Comportamiento sin doraise
Con doraise=False, el módulo puede imprimir la información en stderr en lugar de lanzar directamente.
No ignores el retorno. Un archivo .pyc antiguo puede permanecer después de un fallo y no demuestra que la fuente actual sea válida.
Detalles de PyCompileError
La excepción conserva información sobre el error original y una representación formateada.
Un diagnóstico debería mantener filename, línea, columna y tipo. Puedes recuperar un fragmento con linecache en Python.
Destino predeterminado
Sin cfile, Python calcula el path normal bajo __pycache__.
__pycache__/modulo.cpython-314.pycLa tag permite que varias versiones y optimizaciones coexistan.
Elige cfile
El parámetro cfile selecciona un destino específico.
py_compile.compile(
"src/modulo.py",
cfile="build/modulo.pyc",
doraise=True,
)
Crea el directorio padre y confirma que pertenece al workspace del build.
Protege los destinos
El módulo verifica determinados casos de sustitución insegura, incluidos enlaces simbólicos y archivos no regulares en situaciones relevantes.
Aun así, valida siempre el destino, resuélvelo contra una raíz aprobada y no aceptes rutas arbitrarias.
Escritura atómica
Python moderno escribe datos temporales y reemplaza el destino, reduciendo la posibilidad de un cache parcial.
Las garantías dependen del filesystem. Prueba volúmenes de red, mounts de containers y almacenamiento especial.
El parámetro dfile
dfile define el filename lógico guardado en el code object y mostrado en tracebacks.
py_compile.compile(
"/build/work/modulo.py",
dfile="/opt/app/modulo.py",
doraise=True,
)
Así evitas que las rutas del runner aparezcan en producción.
dfile no copia el fuente
El parámetro solo cambia la referencia lógica. No mueve el archivo ni garantiza que exista en destino.
Alinea el valor con el layout final y conserva fuente correspondiente cuando sea necesario depurar.
Selecciona optimización
optimize elige el nivel.
py_compile.compile(
"modulo.py",
optimize=1,
doraise=True,
)
El default sigue la configuración del intérprete actual.
Asserts y docstrings
El nivel 1 elimina asserts y el nivel 2 también puede eliminar docstrings.
Las reglas obligatorias de entrada, autorización y negocio deben usar comprobaciones explícitas que permanezcan activas.
Modos de invalidación
invalidation_mode controla cómo el sistema de importación decide si el bytecode todavía coincide con el fuente.
py_compile.compile(
"modulo.py",
doraise=True,
invalidation_mode=py_compile.PycInvalidationMode.CHECKED_HASH,
)
TIMESTAMP
TIMESTAMP registra metadatos como modificación y tamaño.
Es eficiente y común, aunque los artefactos reproducibles pueden preferir hash.
CHECKED_HASH
CHECKED_HASH guarda un hash del fuente y solicita verificación durante la importación.
Ofrece una relación más fuerte entre contenido y cache, con lectura adicional según la política.
UNCHECKED_HASH
UNCHECKED_HASH guarda el hash y permite que un entorno gestionado trate el artefacto como validado previamente.
Úsalo en sistemas de build confiables que controlan instalación y actualización.
SOURCE_DATE_EPOCH
Los builds reproducibles pueden definir SOURCE_DATE_EPOCH, influyendo en defaults de invalidación.
Cuando la política forme parte del contrato, establece el modo explícitamente.
Compilar no ejecuta
El compilador analiza y genera bytecode sin ejecutar imports, decorators, llamadas de módulo o inicialización de clases.
Un archivo puede compilar y fallar al importar por dependencia ausente o error de runtime.
Validación incremental
Un editor puede compilar únicamente el documento guardado.
def validar(ruta):
try:
py_compile.compile(ruta, doraise=True)
except py_compile.PyCompileError as error:
return False, str(error)
return True, None
Detecta sintaxis rápidamente, pero no reemplaza language server, linter o type checker.
Valida código generado
Los generadores pueden compilar la salida para confirmar que un template produjo Python válido.
Una secuencia fiable es escribir atómicamente, compilar y publicar el paquete solo después del éxito.
Encoding de fuente
El compilador sigue las reglas de encoding Python. Declaraciones inválidas o bytes incompatibles generan errores.
Usa tokenize.open() para inspección y consulta tokenize en Python.
Paths privados y útiles
Las rutas absolutas pueden revelar usuarios, directorios de CI e infraestructura.
Usa dfile para crear paths estables de producción sin perder información de diagnóstico.
Uso por CLI
El módulo compila archivos desde la línea de comandos.
python -m py_compile modulo.py otro.pyComprueba el exit status en CI y consulta la ayuda de la versión activa.
Varios archivos
La función Python procesa uno por vez. Para un árbol, usa compileall en Python.
Los sistemas incrementales pueden mantener una queue limitada y asociar cada resultado con la revisión del fuente.
Compilación paralela
Los archivos independientes pueden compilarse concurrentemente, pero dos workers no deben escribir el mismo destino.
Genera paths determinísticos, elimina duplicados y limita concurrencia según el storage.
Cambios durante la tarea
La fuente puede cambiar entre programación y publicación. Un editor debería comparar hash o versión del documento.
Si ya cambió, descarta el resultado antiguo y compila la versión nueva.
Directorios read-only
Si el destino predeterminado no puede crearse, compila durante el build o elige un área con permiso.
El servicio de producción no debería necesitar root para crear __pycache__.
Compatibilidad de versión
El bytecode está ligado a implementación y versión major/minor. Un magic number evita muchos usos incompatibles.
Compila con el mismo intérprete que ejecutará la aplicación.
El bytecode no protege el fuente
Las herramientas inspeccionan nombres, constantes e instrucciones.
Mantén secretos fuera del código y no uses .pyc como ofuscación fuerte.
Prueba la importación
Después de compilar, importa el módulo en el entorno final.
python -c "import modulo"Esto detecta dependencias e inicialización que la compilación no ve.
Artefactos huérfanos
Al eliminar fuentes pueden quedar caches antiguos. Un build limpio evita que módulos borrados sobrevivan en el paquete.
No mezcles caches de commits, versiones u optimizaciones diferentes sin layout intencional.
Control de paths
Valida fuente, destino, directorios padre y symlinks. Mantén toda compilación dentro del workspace.
Un servicio público debería usar worker y directorio temporal aislados.
Límites de recursos
Fuentes enormes o profundamente anidadas consumen CPU y memoria. Limita tamaño, jobs concurrentes y tiempo total.
Procesa uploads fuera del proceso principal de requests.
Observabilidad
Registra filename lógico, duración, tamaño, versión de Python, optimización, invalidación y resultado.
Evita guardar todo el fuente; un fragmento de diagnóstico suele bastar.
Pruebas
Cubre código válido, SyntaxError, encoding inválido, destino custom, dfile, modos de invalidación, optimización, permisos, symlink y cambios durante la tarea.
Verifica el path generado y el filename del traceback.
Errores comunes
Los fallos frecuentes son omitir doraise=True, ignorar el retorno, escribir en un destino no validado, compilar con otra versión, depender de asserts, confundir dfile con copia y dejar caches antiguos.
Conclusión
py_compile ofrece control preciso sobre la compilación de un archivo. Usa doraise=True, elige cfile y dfile seguros, define optimización e invalidación según el build y prueba la importación final.
Usa compileall para árboles completos. Consulta la documentación oficial de py_compile y dis en Python para inspeccionar el bytecode.







