11# Online Python Tutor extension for the IPython shell
22# http://ipython.org/ipython-doc/stable/config/extensions/index.html
33
4+ # defines a '%clear' magic to clear the user's global environment
5+
46# Tested on IPython 1.0.dev
57
68# pgbovine
3537# TODO: support incremental pushes to the OPT frontend for efficiency
3638# and better "snappiness" (although the speed seems fine for now)
3739
38- # TODO: support line number adjustments for function definitions/calls
39- # (right now opt-ipy doesn't jump into function calls at all)
40-
41- # TODO: add an IPython magic to "reset" the trace to start from scratch
42- # (although the global environment will still not be blank)
43-
4440
4541class OptHistory (object ):
4642 def __init__ (self ):
4743 self .executed_stmts = []
4844
49- # first line number of each line in self.executed_stmts
50- self .first_lineno = []
51-
52- # each element is a LIST containing an OPT trace
53- self .output_traces = []
54-
55-
5645 def pop_last (self ):
5746 self .executed_stmts .pop ()
58- self .first_lineno .pop ()
59- self .output_traces .pop ()
60-
6147
6248 def check_rep (self ):
63- assert len (self .executed_stmts ) == len (self .first_lineno ) == len (self .output_traces )
64-
49+ pass
6550
6651 def get_code (self ):
6752 return '\n ' .join (self .executed_stmts )
6853
69- def get_full_trace (self ):
70- ret = []
71- for t in self .output_traces :
72- for e in t :
73- ret .append (e )
74- return ret
75-
54+ def run_str (self , stmt_str ):
55+ self .executed_stmts .append (stmt_str )
7656
77- def run_str (self , stmt_str , user_globals ):
78- opt_trace = pg_logger .exec_str_with_user_ns (stmt_str , user_globals , lambda cod , trace : trace )
57+ opt_trace = pg_logger .exec_script_str_local (self .get_code (), [], False , False , lambda cod , trace : trace )
7958
80- # did it end in disaster?
8159 last_evt = opt_trace [- 1 ]['event' ]
8260 if last_evt == 'exception' :
83- last_exec_is_exception = True
61+ epic_fail = True
8462 else :
8563 assert last_evt == 'return'
86- last_exec_is_exception = False
87-
88-
89- end_of_last_trace = None
90-
91- # 'clean up' the trace a bit:
92- if len (self .output_traces ):
93- # for aesthetics, lop off the last element of the previous
94- # entry since it should match the first element of opt_trace
95- end_of_last_trace = self .output_traces [- 1 ].pop ()
96- #print 'END:', end_of_last_trace
97- #print 'CUR:', opt_trace[0]
98- last_ordered_globals = list (end_of_last_trace ['ordered_globals' ]) # copy just to be paranoid
99-
100- # patch up ordered_globals with last_ordered_globals to
101- # maintain continuity, i.e., prevent variable display from "jumping"
102- for t in opt_trace :
103- og = t ['ordered_globals' ]
104- og_set = set (og )
105-
106- # reorder og to use last_ordered_globals as a prefix to
107- # maintain order
108- new_og = [e for e in last_ordered_globals if e in og_set ]
109- new_og_set = set (new_og )
110-
111- # patch in leftovers
112- leftovers = [e for e in og if e not in new_og_set ]
113- new_og .extend (leftovers )
114-
115- assert len (og ) == len (new_og )
116- t ['ordered_globals' ] = new_og
117-
118- # patch up stdout to make it cumulative too
119- last_stdout = end_of_last_trace ['stdout' ]
120- for t in opt_trace :
121- t ['stdout' ] = last_stdout + t ['stdout' ]
122-
123-
124- # adjust all the line numbers in the trace
125- if len (self .executed_stmts ):
126- lineno = self .first_lineno [- 1 ] + len (self .executed_stmts [- 1 ].splitlines ())
127- else :
128- lineno = 1
129- for elt in opt_trace :
130- elt ['line' ] += (lineno - 1 )
64+ epic_fail = False
13165
132- self .executed_stmts .append (stmt_str )
133- self .first_lineno .append (lineno )
134- self .output_traces .append (opt_trace )
135-
136- # now create json_output:
137- output_dict = dict (code = self .get_code (), trace = self .get_full_trace ())
66+ output_dict = dict (code = self .get_code (), trace = opt_trace )
13867 json_output = json .dumps (output_dict , indent = INDENT_LEVEL )
13968
140-
14169 # if this statement ended in an exception, delete it from the
14270 # history and pretend it never happened
143- if last_exec_is_exception :
71+ if epic_fail :
14472 self .pop_last ()
145- # SUPER-HACK! patch end_of_last_trace back on
146- if end_of_last_trace :
147- self .output_traces [- 1 ].append (end_of_last_trace )
148-
14973
15074 self .check_rep ()
15175 return json_output
15276
15377
154-
15578# called right before a statement gets executed
15679def opt_pre_run_code_hook (self ):
157- filtered_ns = {}
158- for k , v in self .user_ns .iteritems ():
159- if k [0 ] == '_' :
160- continue
161- if k in ('In' , 'Out' , 'help' , 'quit' , 'exit' , 'get_ipython' ):
162- continue
163- filtered_ns [k ] = v
164-
16580 # when you run multiple statements on one line using a semicolon:
16681 # e.g., "print x; print y", this function will fire multiple times.
16782 # we want to avoid duplicates!
16883 last_cmd = self .history_manager .input_hist_parsed [- 1 ]
16984 last_cmd_index = len (self .history_manager .input_hist_parsed ) - 1
17085
86+ # also don't intercept special ipython commands
87+ if 'get_ipython().' in last_cmd :
88+ return
89+
17190 if self .meta .last_cmd_index == last_cmd_index :
17291 assert self .meta .last_cmd == last_cmd
17392 return # punt!!!
17493
17594 self .meta .last_cmd = last_cmd
17695 self .meta .last_cmd_index = last_cmd_index
17796
178- trace_json = self .meta .opt_history .run_str (last_cmd , filtered_ns )
97+ trace_json = self .meta .opt_history .run_str (last_cmd )
17998 #print trace_json
18099 urllib2 .urlopen ("http://localhost:8888/post" , trace_json )
181100
182101
102+ # clear global namespace and reset history
103+ def opt_clear (self , params ):
104+ ip = get_ipython ()
105+
106+ filtered_user_ns = set ()
107+ for k , v in ip .user_ns .iteritems ():
108+ if k [0 ] == '_' :
109+ continue
110+ if k in ('In' , 'Out' , 'help' , 'quit' , 'exit' , 'get_ipython' ):
111+ continue
112+ filtered_user_ns .add (k )
113+
114+ for k in filtered_user_ns :
115+ del ip .user_ns [k ]
116+
117+ ip .meta .opt_history = OptHistory () # just create a new one!
118+
119+ empty_msg = dict (code = '' , trace = [])
120+ urllib2 .urlopen ("http://localhost:8888/post" , json .dumps (empty_msg ))
121+
122+
183123def load_ipython_extension (ipython ):
184124 # The `ipython` argument is the currently active `InteractiveShell`
185125 # instance, which can be used in any way. This allows you to register
@@ -192,6 +132,7 @@ def load_ipython_extension(ipython):
192132
193133 # NB: spelling might be different in older IPython versions
194134 ipython .set_hook ('pre_run_code_hook' , opt_pre_run_code_hook )
135+ ipython .define_magic ('clear' , opt_clear )
195136
196137
197138def unload_ipython_extension (ipython ):
0 commit comments