Skip to content

Commit d895bf0

Browse files
committed
started implementing raw_input backend
1 parent c701cc4 commit d895bf0

6 files changed

Lines changed: 94 additions & 19 deletions

File tree

v1-v2/example-code

Lines changed: 0 additions & 1 deletion
This file was deleted.

v3/bgranger-ipynb-embedding-demo/OPT-ipynb-static_files/images

Lines changed: 0 additions & 1 deletion
This file was deleted.

v3/generate_json_trace.py

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -28,15 +28,18 @@ def js_var_finalizer(input_code, output_trace):
2828
help='render primitives as heap objects.')
2929
parser.add_option('-o', '--compact', default=False, action='store_true',
3030
help='output compact trace.')
31-
parser.add_option("--create_jsvar", dest="js_varname",
31+
parser.add_option('-i', '--input', default=False, action='store',
32+
help='JSON list of strings for simulated raw_input.', dest='raw_input_lst_json')
33+
parser.add_option("--create_jsvar", dest="js_varname", default=None,
3234
help="Create a JavaScript variable out of the trace")
35+
3336
(options, args) = parser.parse_args()
3437
INDENT_LEVEL = None if options.compact else 2
3538

3639
fin = sys.stdin if args[0] == "-" else open(args[0])
3740

3841
if options.js_varname:
3942
JS_VARNAME = options.js_varname
40-
print(pg_logger.exec_script_str_local(fin.read(), options.cumulative, options.heapPrimitives, js_var_finalizer))
43+
print(pg_logger.exec_script_str_local(fin.read(), options.raw_input_lst_json, options.cumulative, options.heapPrimitives, js_var_finalizer))
4144
else:
42-
print(pg_logger.exec_script_str_local(fin.read(), options.cumulative, options.heapPrimitives, json_finalizer))
45+
print(pg_logger.exec_script_str_local(fin.read(), options.raw_input_lst_json, options.cumulative, options.heapPrimitives, json_finalizer))

v3/pg_logger.py

Lines changed: 83 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -104,11 +104,42 @@ def __restricted_import__(*args):
104104
raise ImportError('{0} not supported'.format(args[0]))
105105

106106

107+
# Support interactive user input by:
108+
#
109+
# 1. running the entire program up to a call to raw_input (or input in py3),
110+
# 2. bailing and returning a trace ending in a special 'raw_input' event,
111+
# 3. letting the web frontend issue a prompt to the user to grab a string,
112+
# 4. RE-RUNNING the whole program with that string added to raw_input_queue,
113+
# 5. which should bring execution to the next raw_input call (if
114+
# available), or to termination.
115+
# Repeat until no more raw_input calls are encountered.
116+
# Note that this is mad inefficient, but is simple to implement!
117+
#
118+
# TODO: To make this technique more deterministic,
119+
# save away and restore the random seed.
120+
121+
# queue of input strings passed from the outside
122+
raw_input_queue = []
123+
124+
class RawInputException(Exception):
125+
pass
126+
127+
def raw_input_wrapper(prompt=''):
128+
if raw_input_queue:
129+
return raw_input_queue.pop(0)
130+
raise RawInputException(prompt)
131+
132+
107133
# blacklist of builtins
108-
BANNED_BUILTINS = ('reload', 'input', 'apply', 'open', 'compile',
134+
BANNED_BUILTINS = ['reload', 'apply', 'open', 'compile',
109135
'file', 'eval', 'exec', 'execfile',
110-
'exit', 'quit', 'raw_input', 'help',
111-
'dir', 'globals', 'locals', 'vars')
136+
'exit', 'quit', 'help',
137+
'dir', 'globals', 'locals', 'vars']
138+
139+
# ban input() in Python 2 since it does an eval!
140+
# (Python 3 input is Python 2 raw_input, so we're okay)
141+
if not is_python3:
142+
BANNED_BUILTINS.append('input')
112143

113144

114145
IGNORE_VARS = set(('__user_stdout__', '__builtins__', '__name__', '__exception__', '__doc__', '__package__'))
@@ -211,6 +242,9 @@ def __init__(self, cumulative_mode, heap_primitives, finalizer_func, disable_sec
211242
# executed line
212243
self.trace = []
213244

245+
# if this is true, don't put any more stuff into self.trace
246+
self.done = False
247+
214248
#http://stackoverflow.com/questions/2112396/in-python-in-google-app-engine-how-do-you-capture-output-produced-by-the-print
215249
self.GAE_STDOUT = sys.stdout
216250

@@ -323,6 +357,10 @@ def setup(self, f, t):
323357
# Override Bdb methods
324358

325359
def user_call(self, frame, argument_list):
360+
# TODO: figure out a way to move this down to 'def interaction'
361+
# or right before self.trace.append ...
362+
if self.done: return
363+
326364
"""This method is called when there is the remote possibility
327365
that we ever need to stop in this function."""
328366
if self._wait_for_mainpyfile:
@@ -339,6 +377,8 @@ def user_call(self, frame, argument_list):
339377
self.interaction(frame, None, 'call')
340378

341379
def user_line(self, frame):
380+
if self.done: return
381+
342382
"""This function is called when we stop or break at this line."""
343383
if self._wait_for_mainpyfile:
344384
if (self.canonic(frame.f_code.co_filename) != "<string>" or
@@ -348,19 +388,28 @@ def user_line(self, frame):
348388
self.interaction(frame, None, 'step_line')
349389

350390
def user_return(self, frame, return_value):
391+
if self.done: return
392+
351393
"""This function is called when a return trap is set here."""
352394
frame.f_locals['__return__'] = return_value
353395
self.interaction(frame, None, 'return')
354396

355397
def user_exception(self, frame, exc_info):
398+
if self.done: return
399+
356400
exc_type, exc_value, exc_traceback = exc_info
357401
"""This function is called if an exception occurs,
358402
but only if we are to stop at or just below this level."""
359403
frame.f_locals['__exception__'] = exc_type, exc_value
360404
if type(exc_type) == type(''):
361405
exc_type_name = exc_type
362406
else: exc_type_name = exc_type.__name__
363-
self.interaction(frame, exc_traceback, 'exception')
407+
408+
if exc_type_name == 'RawInputException':
409+
self.trace.append(dict(event='raw_input', prompt=exc_value.args[0]))
410+
self.done = True
411+
else:
412+
self.interaction(frame, exc_traceback, 'exception')
364413

365414

366415
# General interaction function
@@ -392,10 +441,12 @@ def interaction(self, frame, traceback, event_type):
392441

393442

394443
# debug ...
395-
#print('===', file=sys.stderr)
396-
#for (e,ln) in self.stack:
397-
# print(e.f_code.co_name + ' ' + e.f_code.co_filename + ' ' + str(ln), file=sys.stderr)
398-
#print('', file=sys.stderr)
444+
'''
445+
print >> sys.stderr, '==='
446+
for (e,ln) in self.stack:
447+
print >> sys.stderr, e.f_code.co_name + ' ' + e.f_code.co_filename + ' ' + str(ln)
448+
print >> sys.stderr
449+
'''
399450

400451

401452
# don't trace inside of our __restricted_import__ helper function
@@ -733,7 +784,13 @@ def _runscript(self, script_str):
733784
elif k == '__import__':
734785
user_builtins[k] = __restricted_import__
735786
else:
736-
user_builtins[k] = v
787+
if k == 'raw_input':
788+
user_builtins[k] = raw_input_wrapper
789+
elif k == 'input' and is_python3:
790+
# Python 3 input() is Python 2 raw_input()
791+
user_builtins[k] = raw_input_wrapper
792+
else:
793+
user_builtins[k] = v
737794

738795

739796
user_stdout = cStringIO.StringIO()
@@ -806,7 +863,8 @@ def _runscript(self, script_str):
806863
break
807864

808865
if not already_caught:
809-
self.trace.append(trace_entry)
866+
if not self.done:
867+
self.trace.append(trace_entry)
810868

811869
raise bdb.BdbQuit # need to forceably STOP execution
812870

@@ -847,11 +905,18 @@ def finalize(self):
847905
return self.finalizer_func(self.executed_script, self.trace)
848906

849907

908+
import json
850909

851910
# the MAIN meaty function!!!
852-
def exec_script_str(script_str, cumulative_mode, heap_primitives, finalizer_func):
911+
def exec_script_str(script_str, raw_input_lst_json, cumulative_mode, heap_primitives, finalizer_func):
853912
logger = PGLogger(cumulative_mode, heap_primitives, finalizer_func)
854913

914+
global raw_input_queue
915+
raw_input_queue = []
916+
if raw_input_lst_json:
917+
# TODO: if we want to support unicode, remove str() cast
918+
raw_input_queue = [str(e) for e in json.loads(raw_input_lst_json)]
919+
855920
try:
856921
logger._runscript(script_str)
857922
except bdb.BdbQuit:
@@ -863,13 +928,18 @@ def exec_script_str(script_str, cumulative_mode, heap_primitives, finalizer_func
863928
# disables security check and returns the result of finalizer_func
864929
# WARNING: ONLY RUN THIS LOCALLY and never over the web, since
865930
# security checks are disabled
866-
def exec_script_str_local(script_str, cumulative_mode, heap_primitives, finalizer_func):
931+
def exec_script_str_local(script_str, raw_input_lst_json, cumulative_mode, heap_primitives, finalizer_func):
867932
logger = PGLogger(cumulative_mode, heap_primitives, finalizer_func, disable_security_checks=True)
868933

934+
global raw_input_queue
935+
raw_input_queue = []
936+
if raw_input_lst_json:
937+
# TODO: if we want to support unicode, remove str() cast
938+
raw_input_queue = [str(e) for e in json.loads(raw_input_lst_json)]
939+
869940
try:
870941
logger._runscript(script_str)
871942
except bdb.BdbQuit:
872943
pass
873944
finally:
874945
return logger.finalize()
875-

v3/pythontutor.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,7 @@ def get(self):
7979
heap_primitives = (self.request.get('heap_primitives') == 'true')
8080

8181
pg_logger.exec_script_str(self.request.get('user_script'),
82+
self.request.get('raw_input_json'),
8283
cumulative_mode,
8384
heap_primitives,
8485
self.json_finalizer)

v3/web_exec.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,15 +66,18 @@ def cgi_finalizer(input_code, output_trace):
6666

6767
# Otherwise act like a CGI script with parameters:
6868
# user_script
69+
# raw_input_json
6970
# cumulative_mode
71+
# heap_primitives
7072
else:
7173
form = cgi.FieldStorage()
7274
user_script = form['user_script'].value
75+
raw_input_json = form['raw_input_json'].value
7376
if 'cumulative_mode' in form:
7477
# convert from string to a Python boolean ...
7578
cumulative_mode = (form['cumulative_mode'].value == 'true')
7679
if 'heap_primitives' in form:
7780
heap_primitives = (form['heap_primitives'].value == 'true')
7881

7982

80-
pg_logger.exec_script_str(user_script, cumulative_mode, heap_primitives, cgi_finalizer)
83+
pg_logger.exec_script_str(user_script, raw_input_json, cumulative_mode, heap_primitives, cgi_finalizer)

0 commit comments

Comments
 (0)