235 lines
6.2 KiB
Python
235 lines
6.2 KiB
Python
import webpython, contextlib, io, json, sys, turtle, ast
|
|
from webpython import shell
|
|
|
|
turtle_operations = []
|
|
bindings = {}
|
|
|
|
class RecordingPen:
|
|
_pen = None
|
|
_screen = None
|
|
def __init__(self):
|
|
self.operations = turtle_operations
|
|
self._pos = (0,0)
|
|
turtle_operations.append(('__init__',()))
|
|
|
|
def reset(self):
|
|
turtle_operations.clear()
|
|
|
|
def onclick(self, fun, btn=1, add=None):
|
|
self.operations.append(('onclick', (fun,)))
|
|
def eventfun(event):
|
|
fun(event.x, event.y)
|
|
bindings['<Button-1>'] = eventfun
|
|
|
|
def goto(self, x, y):
|
|
self._pos = (x,y)
|
|
self.operations.append(('goto', (x,y)))
|
|
|
|
def pos(self):
|
|
self.operations.append(('pos', ()))
|
|
return self._pos
|
|
|
|
def __getattr__(self, method):
|
|
def func(*args):
|
|
self.operations.append((method, args))
|
|
return func
|
|
|
|
class FakeCanvas(turtle.WebCanvas):
|
|
def flushbatch(self):
|
|
pass
|
|
|
|
def get_width(self):
|
|
return 400
|
|
|
|
def get_height(self):
|
|
return 400
|
|
|
|
def delete(self, item):
|
|
pass
|
|
|
|
def css(self, key, value):
|
|
pass
|
|
|
|
fake_events = []
|
|
def mainloop():
|
|
while fake_events:
|
|
e = turtle.Event(fake_events.pop(0))
|
|
if e.type in bindings:
|
|
bindings[e.type](e)
|
|
|
|
turtle.Turtle = RecordingPen
|
|
turtle.WebCanvas = FakeCanvas
|
|
pen = turtle._getpen()
|
|
turtle.mainloop = mainloop
|
|
|
|
def filter_operations(name):
|
|
return [o for o in turtle_operations if o[0] == name]
|
|
|
|
@contextlib.contextmanager
|
|
def capture():
|
|
global captured_out
|
|
import sys
|
|
oldout,olderr = sys.stdout, sys.stderr
|
|
try:
|
|
out=[io.StringIO(), io.StringIO()]
|
|
captured_out = out
|
|
sys.stdout,sys.stderr = out
|
|
yield out
|
|
finally:
|
|
sys.stdout,sys.stderr = oldout, olderr
|
|
out[0] = out[0].getvalue()
|
|
out[1] = out[1].getvalue()
|
|
|
|
def get_source():
|
|
message = json.loads(sys.argv[1])
|
|
return message['data']
|
|
|
|
def get_ast():
|
|
s = get_source()
|
|
return ast.parse(s, "programm.py", "exec")
|
|
|
|
def has_bare_except():
|
|
for node in ast.walk(get_ast()):
|
|
if isinstance(node, ast.ExceptHandler):
|
|
if node.type is None:
|
|
return True
|
|
return False
|
|
|
|
def runcaptured(prefix='', tracing=None, variables=None, source=''):
|
|
#message = json.loads(sys.argv[1])
|
|
#source = prefix + message['data']
|
|
with open("programm.py", "w", encoding='utf-8') as f:
|
|
f.write(source)
|
|
c = compile(source, "programm.py", 'exec')
|
|
with capture() as out, trace(tracing):
|
|
if variables is None:
|
|
variables = {}
|
|
exec(c, variables)
|
|
return source, out[0], out[1], variables
|
|
|
|
def runfunc(func, *args, tracing=None):
|
|
with capture() as out, trace(tracing):
|
|
res = func(*args)
|
|
return out[0], out[1], res
|
|
|
|
def passed():
|
|
msg_in = json.loads(sys.argv[1])
|
|
msg_out = {'cmd':'passed'}
|
|
msg_out['lis_outcome_service_url'] = msg_in['lis_outcome_service_url']
|
|
msg_out['lis_result_sourcedid'] = msg_in['lis_result_sourcedid']
|
|
webpython.shell.sendpickle(msg_out)
|
|
|
|
def failed(msg):
|
|
msg_in = json.loads(sys.argv[1])
|
|
msg_out = {'cmd':'failed', 'data':'Dein Programm ist leider falsch:\n'+msg}
|
|
msg_out['lis_outcome_service_url'] = msg_in['lis_outcome_service_url']
|
|
msg_out['lis_result_sourcedid'] = msg_in['lis_result_sourcedid']
|
|
webpython.shell.sendpickle(msg_out)
|
|
|
|
def modified(variables, name, val):
|
|
if variables.get(name) != val:
|
|
msg_in = json.loads(sys.argv[1])
|
|
msg_out = {'cmd':'failed',
|
|
'data':('Bitte lösche Deine Zuweisung der Variable %s, '+
|
|
'damit wir Dein Programm überprüfen können.') % name}
|
|
msg_out['lis_outcome_service_url'] = msg_in['lis_outcome_service_url']
|
|
msg_out['lis_result_sourcedid'] = msg_in['lis_result_sourcedid']
|
|
webpython.shell.sendpickle(msg_out)
|
|
return True
|
|
return False
|
|
|
|
undefined = object()
|
|
def getvar(variables, name):
|
|
try:
|
|
return variables['name']
|
|
except KeyError:
|
|
name = name.lower()
|
|
for k,v in variables.items():
|
|
if k.lower() == name:
|
|
return v
|
|
return undefined
|
|
|
|
def _match(n1, n2):
|
|
if n1 == n2:
|
|
return True
|
|
if n1 is None or n2 is None:
|
|
return False
|
|
return n1.lower() == n2.lower()
|
|
|
|
class Call:
|
|
def __init__(self, name, args):
|
|
self.name = name
|
|
self.args = args
|
|
self.calls = []
|
|
self.current = None
|
|
|
|
def findcall(self, f):
|
|
if _match(self.name, f):
|
|
return self
|
|
for c in self.calls:
|
|
r = c.findcall(f)
|
|
if r:
|
|
return r
|
|
return None
|
|
|
|
def calling(self, caller, callee):
|
|
if _match(self.name, caller):
|
|
for c in self.calls:
|
|
if _match(c.name, callee):
|
|
return True
|
|
for c in self.calls:
|
|
if c.calling(caller, callee):
|
|
return True
|
|
return False
|
|
|
|
def countcalls(self, caller, callee):
|
|
calls = 0
|
|
if _match(self.name, caller):
|
|
for c in self.calls:
|
|
if _match(c.name, callee):
|
|
calls += 1
|
|
return calls
|
|
for c in self.calls:
|
|
r = c.countcalls(caller, callee)
|
|
if r > 0:
|
|
return r
|
|
return 0
|
|
|
|
class Tracing(Call):
|
|
def __init__(self):
|
|
Call.__init__(self, None, None)
|
|
|
|
def trace(self, frame, event, arg):
|
|
if event == 'call':
|
|
c = Call(frame.f_code.co_name, frame.f_locals.copy())
|
|
cur = self
|
|
while cur.current:
|
|
cur = cur.current
|
|
cur.calls.append(c)
|
|
cur.current = c
|
|
return self.trace
|
|
elif event in ('return', 'exception'):
|
|
cur = self
|
|
if not cur.current:
|
|
# XXX return without call? happens when invocation of top function fails
|
|
return
|
|
while cur.current.current:
|
|
cur = cur.current
|
|
cur.current = None
|
|
|
|
def start(self):
|
|
sys.settrace(self.trace)
|
|
|
|
def stop(self):
|
|
sys.settrace(None)
|
|
|
|
@contextlib.contextmanager
|
|
def trace(t):
|
|
try:
|
|
if t:
|
|
t.start()
|
|
yield
|
|
finally:
|
|
if t:
|
|
t.stop()
|