Skip to content

Instantly share code, notes, and snippets.

@simonw
Last active September 28, 2024 08:10
Show Gist options
  • Save simonw/8aa492e59265c1a021f5c5618f9e6b12 to your computer and use it in GitHub Desktop.
Save simonw/8aa492e59265c1a021f5c5618f9e6b12 to your computer and use it in GitHub Desktop.
How to recover lost Python source code if it's still resident in-memory

How to recover lost Python source code if it's still resident in-memory

I screwed up using git ("git checkout --" on the wrong file) and managed to delete the code I had just written... but it was still running in a process in a docker container. Here's how I got it back, using https://pypi.python.org/pypi/pyrasite/ and https://pypi.python.org/pypi/uncompyle6

Attach a shell to the docker container

Install GDB (needed by pyrasite)

apt-get update && apt-get install gdb

Install pyrasite - this will let you attach a Python shell to the still-running process

pip install pyrasite

Install uncompyle6, which will let you get Python source code back from in-memory code objects

pip install uncompyle6

Find the PID of the process that is still running

ps aux | grep python

Attach an interactive prompt using pyrasite

pyrasite-shell <PID>

Now you're in an interactive prompt! Import the code you need to recover

>>> from my_package import my_module

Figure out which functions and classes you need to recover

>>> dir(my_module)
['MyClass', 'my_function']

Decompile the function into source code

>>> import uncompyle6
>>> import sys
>>> uncompyle6.main.uncompyle(
    2.7, my_module.my_function.func_code, sys.stdout
)
# uncompyle6 version 2.9.10
# Python bytecode 2.7
# Decompiled from: Python 2.7.12 (default, Nov 19 2016, 06:48:10) 
# [GCC 5.4.0 20160609]
# Embedded file name: /srv/my_package/my_module.py
function_body = "appears here"

For the class, you'll need to decompile each method in turn

>>> uncompyle6.main.uncompyle(
    2.7, my_module.MyClass.my_method.im_func.func_code, sys.stdout
)
# uncompyle6 version 2.9.10
# Python bytecode 2.7
# Decompiled from: Python 2.7.12 (default, Nov 19 2016, 06:48:10) 
# [GCC 5.4.0 20160609]
# Embedded file name: /srv/my_package/my_module.py
class_method_body = "appears here"
@tom-flamelit
Copy link

You just hit front page of hacker news!

Does this still work with recent Python versions?

@tg12
Copy link

tg12 commented May 17, 2024

process in a docker container.

Why could you not just jump in the docker container and copy the python file out?

@ZM-J
Copy link

ZM-J commented Sep 28, 2024

Hi, this all appears to have changed for Python 3, it appears the uncompyle6.main.uncompyle() function is gone in favor of uncompyle6.main.decompile().

Also, what would the "my_package" be named if you're just trying to recover a simple python script with no package or module? It doesn't appear __main__ works.

I got an error when I tried putting a code object into decompile:

def f(a, b):
    return a + b

import sys
uncompyle6.main.decompile(
    3.8, f.__code__, sys.stdout
)

And I got:

Traceback (most recent call last):
  File "kkp.py", line 20, in <module>
    uncompyle6.main.decompile(
  File "py38\lib\site-packages\uncompyle6\main.py", line 104, in decompile
    assert iscode(co), f"""{co} does not smell like code"""
AssertionError: 3.8 does not smell like code

I'm not sure what happened though.

@ZM-J
Copy link

ZM-J commented Sep 28, 2024

For those who might encounter similar issues just as mine, the order of input parameters of the function decompile should go like this:

def f(a, b):
    return a + b

import sys
uncompyle6.main.decompile(
    f.__code__, (3, 8), sys.stdout
)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment