This article shows how to write a Python program which destructs itself when it is called, by deleting its own source code, and then creates a copy of itself just to call this copy to “live” on forever while reproducing itself. You might ask, why would anyone ever need such a program? I must admit that such a program doesn’t have any practical value. However, it is an interesting challenge which helps you to learn the concepts of code inspection at run time, file I/O, and processes of the given programming language.
To start off, let’s implement a Python program which takes a single number as a command line parameter that is then printed:
1
2
3
4
5
6
7
8
import sys
if len(sys.argv) < 2:
number = 0
else:
number = int(sys.argv[1])
print(number)
This program makes use of the Python module sys
which provides system-specific parameters and functions such as the sys.argv
list which contains all command line parameters that are passed to a program. The value at position sys.argv[0]
is always the name of the program or Python script which was called. Therefore, the first command line parameter passed to that program is at sys.argv[1]
. To check if any command line parameters have been passed, you need to check if sys.argv
contains more than one value using the len()
function. In the program above, the variable number
is set to 0
if no command line parameters have been passed, otherwise number
is set to the integer value which was passed as the first command line parameter. Lastly, number
is printed on the command line.
As a next step, the program should be able to clone itself. For this purpose, the Python module inspect
is used which allows accessing the code of a Python program while it is running:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
import sys
import inspect
if len(sys.argv) < 2:
number = 0
else:
number = int(sys.argv[1])
# get program code
code = inspect.getsource(inspect.currentframe())
print(code)
print(f"number: {number}")
With inspect.currentframe()
the currently running program part can be accessed as a code object. Passing a code object to the inspect.getsource()
function returns the source code of the code object as a string. The program above does exactly that, and stores the code of the current running program in the code
variable.
To create a clone of the currently running program, the string in the code
a new file needs to be created and the contents of the code
variable written into it. Opening a file to write into Python is straightforward using with
and open
. If you are new to Python, check out my video on reading and writing files in Python
And pick up your free Python file I/O cheat sheet from my Gumroad shop:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import sys
import inspect
if len(sys.argv) < 2:
number = 0
else:
number = int(sys.argv[1])
# get program code
code = inspect.getsource(inspect.currentframe())
# write program code into cloned program
with open("tmp.py", "w") as f:
f.write(code)
print(f"number: {number}")
When you run the program above, you will see that the code was copied into the file tmp.py
.
Now comes the self-destruction part. Deleting a file is made easy using the Python os
module, which provides access to several operating system functions such as file system manipulation. When running the program, os.remove(__file__)
is called after the program’s source code was stored in the code
variable. The special variable __file__
returns the path to the current Python file which is being executed, afterward __file__
can be used to write the code back to disk:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
import sys
import inspect
import os
if len(sys.argv) < 2:
number = 0
else:
number = int(sys.argv[1])
# get program code
code = inspect.getsource(inspect.currentframe())
# delete file
os.remove(__file__)
# write program code into cloned program
with open(__file__, "w") as f:
f.write(code)
print(f"number: {number}")
The program above already self-destructs and then creates a clone with the same file name. The last part that is missing is to start the execution of the clone. To achieve this, the Python modules shlex
and subprocess
are used. In this context, shlex
helps to format a string (cmd
) into a command list (cmd_list
) using shlex.split()
which is then passed to the Popen()
function of subprocess
which executes a command. subprocess.Popen()
is instructed to start a new session with start_new_session=True
, which allows the original (and deleted) program to terminate because the launched clone is detached from it:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
import sys
import inspect
import os
import shlex
import subprocess
if len(sys.argv) < 2:
number = 0
else:
number = int(sys.argv[1])
print(f"start: {number}")
# get program code
code = inspect.getsource(inspect.currentframe())
# delete file
os.remove(__file__)
# write program code into cloned program
with open(__file__, "w") as f:
f.write(code)
# run cloned program
cmd = f"python {__file__} {number + 1}"
cmd_list = shlex.split(cmd)
p = subprocess.Popen(cmd_list, start_new_session=True)
print(f"end: {number}")
Run this program in a new terminal window, it will live on forever by constantly self-destructing and cloning itself. Every time a new instance is launched, the number
is incremented by one, resulting in an output looking like this:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
start: 0
end: 0
start: 1
end: 1
start: 2
end: 2
start: 3
end: 3
start: 4
end: 4
start: 5
end: 5
start: 6
end: 6
start: 7
end: 7
start: 8
end: 8
start: 9
end: 9
start: 10
end: 10
start: 11
end: 11
start: 12
end: 12
start: 13
end: 13
start: 14
end: 14
This concludes this silly experiment. You’ve learned how to get the source code of a running Python program, how to delete the source code file at runtime, and how to create a copy of the program so it can start itself just in time to stay alive indefinitely.