Update assess.py
This commit is contained in:
@ -1,234 +1 @@
|
|||||||
import webpython, contextlib, io, json, sys, turtle, ast
|
# moved to dockerfiles/ubuntu-python
|
||||||
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()
|
|
||||||
|
Reference in New Issue
Block a user