Writing the dry run of a code manually is a tedious task especially for beginners in programming .
This project gives a dry run of the python code entered by user to analyse the changes occuring with the execution of each line.
- Ace editor is integrated into the frontend for taking user input.
- The frontend uses Fetch API to send the code entered to the backend and receive back the execution trace.
- Backend uses the Django framework and executes the python program under supervision of the standard python debugger module(bdb) .
- Program’s runtime state at every executed line is recorded and a JSON execution trace is sent back , which the frontend renders as the dry run.
Frontend Files
1.index.html
2.style.css
3.front.js - Sends the user input to backend and receives output trace .
4.visualize.js - Renders the trace as the dry run
- views.py - Receives the data from frontend . Sends it to logger.py. Sends the data received from logger.py back to frontend.
- logger.py - Runs the python program under bdb debugger module and produces output trace. Calls encoder.py to encode data structure.
- encoder.py - Adds data structure created / updated in the trace.
Backend Control Flow :
The main entry point is the following function in logger.py:
def exec_script_str(script_str,raw_input_lst_json,finalizer)
- script_str contains the entire string contents of the Python program for backend to execute.
- finalizer is the function to call after the backend has executed trace.
A look at how the dryrun(request) function from views.py calls exec_script_str :
ans=logger.exec_script_str(code_input,raw_input_json,finalizer_func)
Lets look at how the execution trace is produced. Following is the code of exec_script_str in DSLogger class of logger.py :
def exec_script_str(script_str,raw_input_lst_json,finalizer):
mylogger=DSLogger(finalizer)
global input_string_queue
input_string_queue=[]
if raw_input_lst_json:
input_string_queue=[str(e) for e in json.loads(raw_input_lst_json)]
try:
mylogger._runscript(script_str)
except bdb.BdbQuit:
pass
finally:
ans=mylogger.finalize()
return ans
DSLogger is a derived class of bdb.Bdb , which is the standard python debugger module .
The self.run method is inherited from bdb.Bdb and it executes the contents of script.str.
The Bdb debugger has methods user_call , user_return , user_exception and user_line which are inherited in DSLogger class.
As the user's pgrogram is running , bdb will pause execution at every function call , return , exception and single-line step. It transfers control to the respective handler methds . Since these methods are overridden in DSLogger , we can control how to interpret every line.
The execution point of a single line which represents the state of computer's(abstract) memory notes the following details:
- ordered_globals - List of global variables in the order the frontend should visualize them.
- stdout - Any output to print in console.
- func_name - The function inside which the current line is executing .
- stack_to_render - The current memory stack . Helps in rendering recursive functions .
- globals- All the global variables . Helps in checking if a new variable is declared .
- heap- Used for handling dynamic memory allocated
- line - The line number about to execute .
- event - Can be a function call , function return , an exception or a single line statement(most common)