ANN: unpyc3 - a python bytecode decompiler for Python3

Arnaud Delobelle arnodel at gmail.com
Wed Sep 14 15:51:37 EDT 2011


On 14 September 2011 11:03, Vincent Vande Vyvre
<vincent.vandevyvre at swing.be> wrote:
> Le 14/09/11 11:31, Arnaud Delobelle a écrit :
[...]
> Could you show me what you do to get this error?  Thank you,

> [vincent at myhost unpyc3]$ python
> Python 3.2.1 (default, Jul 11 2011, 12:37:47)
> [GCC 4.6.1] on linux2
> Type "help", "copyright", "credits" or "license" for more information.
>>>> from unpyc3 import decompile
>>>> print (decompile("shredder.pyc"))
> Traceback (most recent call last):
>   File "<stdin>", line 1, in <module>
>   File "unpyc3.py", line 110, in decompile
>     return dec_module(obj)
>   File "unpyc3.py", line 99, in dec_module
>     code = Code(code_obj)
>   File "unpyc3.py", line 211, in __init__
>     for v in code_obj.co_cellvars + code_obj.co_freevars]
> AttributeError: 'NoneType' object has no attribute 'co_cellvars'
>>>> print (decompile("unpyc3.pyc"))
> Traceback (most recent call last):
>   File "<stdin>", line 1, in <module>
>   File "unpyc3.py", line 110, in decompile
>     return dec_module(obj)
>   File "unpyc3.py", line 99, in dec_module
>     code = Code(code_obj)
>   File "unpyc3.py", line 211, in __init__
>     for v in code_obj.co_cellvars + code_obj.co_freevars]
> AttributeError: 'NoneType' object has no attribute 'co_cellvars'
>>>> import os
>>>> os.path.isfile("shredder.pyc")
> True
>>>> os.path.isfile("unpyc3.pyc")
> True
>>>>
>
> it seems the return of marshal.load(stream) is None

I think the reason may be that your unpyc3.pyc and shredder.pyc files
were compiled with a different version of python and the read_code
function returns None because the magic number in the .pyc file is
incorrect because of these two lines:

    if magic != imp.get_magic():
        return None

I have now changed this so that it loads the file anyway but prints a
warning.  I guess this may break badly though.

In Python 3.2, .pyc files are "hidden" in a __pycache__ directory.  So
the Python 3.2 specific unpyc3.pyc file for example is probably
located at .../unpyc3/__pycache__/unpyc3-cpython-32.pyc

The easiest way to find the path of the .pyc file if you know the path
of the .py file is probably as follows:

>>> import imp
>>> imp.cache_from_source("unpyc3.py")
'__pycache__/unpyc3.cpython-32.pyc'

Here's an example decompiling the dis module from the standard library:

>>> import dis
>>> dis.__file__
'/Library/Frameworks/Python.framework/Versions/3.2/lib/python3.2/dis.py'
>>> imp.cache_from_source(dis.__file__)
'/Library/Frameworks/Python.framework/Versions/3.2/lib/python3.2/__pycache__/dis.cpython-32.pyc'
>>> print(decompile(_))
__doc__ = 'Disassembler of Python byte code into mnemonics.'
import sys
import types
from opcode import *
from opcode import __all__ as _opcodes_all
__all__ = ['code_info', 'dis', 'disassemble', 'distb', 'disco',
'findlinestarts', 'findlabels', 'show_code'] + _opcodes_all
del _opcodes_all
_have_code = types.MethodType, types.FunctionType, types.CodeType, type
def _try_compile(source, name):
    try:
        c = compile(source, name, 'eval')
    except SyntaxError:
        c = compile(source, name, 'exec')
    return c
[... many more lines ...]

I hope this will work for you,

-- 
Arnaud

PS: I've also added support for the IMPORT_STAR opcode which I had overlooked.



More information about the Python-list mailing list