
Un descompilador de bytecode de Python multiversión
|buildstatus| |Pypi Installs| |Latest Version| |Supported Python Versions|
|packagestatus|
.. contents::
Un descompilador nativo de Python para múltiples versiones y descompilador de fragmentos. El sucesor de decompyle, uncompyle y uncompyle2.
Di una charla sobre esto en BlackHat Asia 2024 <https://youtu.be/H-7ZNrpsV50?si=nOaixgYHr7RbILVS>_.
uncompyle6 traduce bytecode de Python de vuelta a código fuente Python equivalente. Acepta bytecodes desde la versión 1.0 hasta la 3.8 de Python, abarcando más de 24 años de versiones de Python. Incluimos el bytecode de Python 2.5 de Dropbox y algunos bytecodes de PyPy.
Vale, lo diré: este software es increíble. Es más que un descompilador cutre normal. Usando tecnología de compiler_, el programa crea un árbol de análisis sintáctico del programa a partir de las instrucciones; nodos en los niveles superiores que se parecen un poco a lo que podría venir de un AST de Python. Así que realmente podemos clasificar y entender lo que está sucediendo en secciones de bytecode de Python.
Sobre esta base, otra cosa que hace que esto sea diferente de otros descompiladores de bytecode de CPython es que puede descompilar solo fragmentos de código fuente y dar información del código fuente alrededor de un desplazamiento de bytecode dado.
Utilizo los fragmentos de árbol para descompilar fragmentos de código en tiempo de ejecución dentro de mis depuradores trepan_. Para ello, se registran desplazamientos de bytecode y se asocian con fragmentos del código fuente. Este propósito, aunque compatible con la intención original, es un poco diferente. Vea this_ para más información.
La descompilación de fragmentos de Python, dado un desplazamiento de instrucción, es útil para mostrar trazas de pila y se puede incorporar en cualquier programa que quiera mostrar una ubicación con más detalle que solo un número de línea en tiempo de ejecución. Este código también se puede usar cuando la información del código fuente no existe y solo hay bytecode. Nuevamente, mis depuradores hacen uso de esto.
Había (y todavía hay) varios forks de decompyle, uncompyle, uncompyle2, uncompyle3. Muchos de ellos provienen básicamente de la misma base de código, y (¿casi?) todos ya no se mantienen activamente. Uno era realmente bueno descompilando Python 1.5-2.3, otro es realmente bueno en Python 2.7, pero solo eso. Otro maneja solo Python 3.2; otro parcheó ese y manejaba solo 3.3. Ya entiende la idea. Este código une todos estos forks y avanza. Hay una seria refactorización y limpieza en esta base de código en comparación con esos forks antiguos. Una refactorización aún más experimental está ocurriendo en decompyle3_.
Esto demuestra ser lo mejor en descompilación de Python en todas las versiones de Python. E incluso cuando hay otro proyecto que solo proporciona descompilación para un subconjunto de versiones de Python, generalmente también lo hacemos demostrablemente mejor para esas.
¿Cómo podemos saberlo? Tomando bytecode de Python que viene distribuido con esa versión de Python y descompilándolo. Entre aquellos que se descompilan exitosamente, podemos asegurarnos de que los programas resultantes sean sintácticamente correctos ejecutando el intérprete de Python para esa versión de bytecode. Finalmente, en los casos donde el programa tiene una prueba para sí mismo, podemos ejecutar la verificación en el código descompilado.
Usamos procesos automatizados para encontrar errores. En los rastreadores de problemas de otros descompiladores, encontrará varios errores que hemos encontrado en el camino. Muy pocos de ellos se corrigen en los otros descompiladores.
El código en el repositorio git se puede ejecutar desde Python 2.4 hasta la última versión de Python, excepto Python 3.0 hasta 3.2. Los voluntarios son bienvenidos para abordar estas deficiencias si hay deseo de hacerlo.
La forma en que lo hace, sin embargo, es segregando versiones consecutivas de Python en ramas git:
master Python 3.11 y superiores (usa instalación poetry y modismos más nuevos de Python) python-3.6-to-3.10 Python 3.6 hasta 3.10 (usa f-strings más nuevos, modismos más modernos y anotaciones de tipo más modernas) python-3.3-to-3.5 Python 3.3 hasta 3.5 (Python 3 genérico) python-3.0-to-3.2 Python 3.0 hasta 3.2 (Python 3 temprano; 3.0 estaba en algunas áreas más cerca de Python 2.6 que de Python 2.7) python-2.4-to-2.7 Python 2.4 hasta 2.7 (Python 2 genérico)
PyPy desde la versión 2.4 en adelante también funciona.
Los archivos de bytecode que puede leer se han probado en bytecodes de Python de las versiones 1.4, 2.1-2.7 y 3.0-3.8 y versiones posteriores de PyPy.
Para versiones recientes de Python (Python 3.11+), puede instalar desde PyPI usando el nombre uncompyle6::
pip install uncompyle6
Para versiones de Python anteriores a la 3.11, no instale usando PyPI, sino instale usando un archivo en la sección de Lanzamientos de GitHub. Python antiguo solía usar easy_install <https://python101.pythonlibrary.org/chapter29_pip.html#using-easy-install>_. Pero esto ya no es compatible en PyPi ni en versiones más nuevas de Python. Y viceversa, poetry ni pip (las formas más nuevas) no son compatibles en Pythons antiguos.
Si la versión de Python en la que ejecuta uncompyle6 está entre Python 2.4 y 2.7, use un tarball llamado uncompyle6_24-x.y.z.tar.gz.
Si la versión de Python en la que ejecuta uncompyle6 está entre Python 3.0 y 3.2, use un tarball llamado uncompyle6_30-x.y.z.tar.gz.
Si la versión de Python en la que ejecuta uncompyle6 está entre Python 3.3 y 3.5, use un tarball llamado uncompyle6_33-x.y.z.tar.gz.
Si la versión de Python en la que ejecuta uncompyle6 está entre Python 3.6 y 3.11, use un tarball llamado uncompyle6_36-x.y.z.tar.gz.
Si la versión de Python en la que ejecuta uncompyle6 es 3.11 o posterior, use un tarball llamado uncompyle6-x.y.z.tar.gz.
También puede probar eggs o wheels que tengan la misma designación de versión, por ejemplo, uncompyle6-x.y.z-py39-none-any.whl para una instalación de Python 3.9. Sin embargo, tenga en cuenta que la versión sin la designación significa Python 3.11 o superior.
De manera similar, un tarball sin _xx funciona solo desde Python 3.11 o superior.
Justificación del uso de ramas Git ++++++++++++++++++++++++++++++++++
Actualmente es imposible (si no poco práctico) tener un solo código fuente de Python de esta complejidad y con tantas características que pueda ejecutarse tanto en Python 2.7 como en Python 3.13+. Los lenguajes se han distanciado tanto, y el empaquetado es enormemente diferente. De hecho, la práctica de empaquetado para Python 3.11+ es incompatible con Python 2.7 (y antes, hasta Python 2.4), que favorecía "easy_install".
Instalación desde el código fuente ++++++++++++++++++++++++++++++++++
Para instalar desde el código fuente, asegúrese de tener la rama Git correcta. Consulte la sección Requisitos para los nombres de las ramas Git.
Después de configurar la rama correcta::
$ pip install -e . # configurar para ejecutar desde el árbol fuente
También se proporciona un Makefile de GNU, por lo que :code:make install (posiblemente como root o sudo) realizará los pasos anteriores.
::
make check
Se ha añadido un makefile de GNU para suavizar la configuración y ejecución del comando correcto, y ejecutar las pruebas de la más rápida a la más lenta.
Si tiene instalado remake_, puede ver la lista de todas las tareas, incluidas las pruebas, mediante :code:remake --tasks
Ejecute
::
$ uncompyle6 archivo-python-compilado-pyc-o-pyo
Para ayuda de uso:
::
$ uncompyle6 -h