4

Because I am an idiot, I deleted some python files and failed to back them up. Before doing this I opened the python interpreter (ie, ran python) and then used the command import myfile.py.

EDIT: I actually used the command import myfile, which, apparently, is worse.

Is there any way I can recover the .pyc (or better the .py, but that seems impossible) file from the python interpreter session I have open?

9
  • I think you can even import inspect and do inspect.getsource(myfile) Commented Jul 28, 2014 at 17:38
  • You mean you used import myfile, not import myfile.py, right? Commented Jul 28, 2014 at 17:38
  • @abarnert. Yes that is what I mean. Commented Jul 28, 2014 at 17:41
  • What platform are you on? The details are pretty different from Windows vs. *nix. Commented Jul 28, 2014 at 17:41
  • @dustyrockpyle Unfortunately that produced an error as I addressed in another answer. Commented Jul 28, 2014 at 17:41

2 Answers 2

4

The byte-code decompiler uncompyle2 can decompile Python 2.x classes, methods, functions and code to source code (note: via Reassembling Python bytecode to the original code?).

This will work well enough for functions:

from StringIO import StringIO from uncompyle2 import uncompyle from inspect import * def decompile_function(f, indent=''): s = StringIO() uncompyle(2.7, f.func_code, s) return '%sdef %s%s:\n%s %s' % ( indent, f.func_name, inspect.formatargspec(*inspect.getargspec(f)), indent, ('\n ' + indent).join(''.join(s.buflist).split('\n'))) 

Unfortunately because classes are already executed it won't be able to recover their structure; you'd need to decompile the methods individually and hope that that's enough:

def decompile_class(c): return 'class %s(%s):\n' % ( c.__name__, ','.join(b.__module__ + '.' + b.__name__ for b in c.__bases__)) + \ '\n'.join(decompile_function(m.im_func, ' ') for n, m in inspect.getmembers(c) if inspect.ismethod(m)) 

Full solution:

def decompile_module(mod): return '\n\n'.join(decompile_function(m) if isfunction(m) else decompile_class(m) if isclass(m) else '# UNKNOWN: ' + repr((n, m)) for n, m in inspect.getmembers(mod) if inspect.getmodule(m) is mod) 
Sign up to request clarification or add additional context in comments.

6 Comments

Actually, inspect.getmembers(myfile) should work just as well for getting all of the members.
But this is a clever idea: rather than trying to recover the compiled code and figure out how to dump it to a .pyc, turn it back to source code and dump it to a functionally-equivalent .py. Nice solution!
on my computer this failed unless I specified the file: uncompyle.uncompyle(2.7, myfile.myfunc.func_code, open('myfile-1.py','w')) or uncompyle.uncompyle(2.7, myfile.myfunc.func_code, sys.stdout)
@goncalopp: It looks like you're using uncompyle instead of uncompyle2. AFAIK they're both forks of the same project, so using them should be pretty similar… but if someone suggests one and you use the other and it doesn't work, you should try the one he suggested.
@abarnert I did try uncompyle2, and it has the same behaviour. StringIO is a great workaround, though
|
1

inspect.getsource supports dumping modules, so you can simply

import inspect inspect.getsource(myfile) 

As that doesn't seem to work, you should at least be able to get the disassembled (".pyc) code with

import dis dis.dis(myfile) 

3 Comments

Produces the error: "IOError: source code not available."
In the case where the module was imported by .pyc not .py, and the .py file has been deleted, this won't work.
@goncalopp Alright, with that I can access the disassembled code. Obviously from here I can cobble together a working version of my code more quickly. Ideally, I would be able to recover python code from this, but that's a lossy process I imagine. Any thoughts?

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.