Make tests work
This commit is contained in:
@ -2,9 +2,10 @@ FROM ubuntu:14.04
|
|||||||
MAINTAINER "Martin v. Löwis"
|
MAINTAINER "Martin v. Löwis"
|
||||||
RUN locale-gen en_US.UTF-8
|
RUN locale-gen en_US.UTF-8
|
||||||
ENV LANG en_US.UTF-8
|
ENV LANG en_US.UTF-8
|
||||||
|
ADD assess.py /usr/lib/python3.4/assess.py
|
||||||
ADD webpython.py /usr/lib/python3.4/webpython.py
|
ADD webpython.py /usr/lib/python3.4/webpython.py
|
||||||
RUN rm /usr/lib/python3.4/turtle.py
|
RUN rm /usr/lib/python3.4/turtle.py
|
||||||
ADD turtle.py /usr/lib/python3.4/turtle.py
|
ADD turtle.py /usr/lib/python3.4/turtle.py
|
||||||
RUN adduser --disabled-password --gecos Python python
|
RUN adduser --disabled-password --gecos Python python
|
||||||
USER python
|
USER python
|
||||||
WORKDIR /home/python
|
WORKDIR /usr/lib/python3.4
|
||||||
|
234
webpython/assess.py
Normal file
234
webpython/assess.py
Normal file
@ -0,0 +1,234 @@
|
|||||||
|
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()
|
@ -4,6 +4,7 @@ import io, select, sys, os, threading, code
|
|||||||
import pickle, struct, builtins, json
|
import pickle, struct, builtins, json
|
||||||
#, ressource
|
#, ressource
|
||||||
from queue import Queue
|
from queue import Queue
|
||||||
|
from argparse import ArgumentParser
|
||||||
|
|
||||||
# hard limit to 64M
|
# hard limit to 64M
|
||||||
#try:
|
#try:
|
||||||
@ -163,12 +164,15 @@ builtins.input = shell.input
|
|||||||
#iothread.start()
|
#iothread.start()
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
#script = "print (\"Hello world.\")\n\rmsg = input()\n\rprint(\"Out:\")\n\rprint(msg)"
|
parser = ArgumentParser(description='A python interpreter that generates json commands based on the standard I/O streams.')
|
||||||
#with open("programm.py", "w", encoding='utf-8') as f:
|
parser.add_argument('-f', '--filename', type=str, required=True, default='exercise.py', help='Python file to be interpreted.')
|
||||||
# f.write(script)
|
args = parser.parse_args()
|
||||||
with open(os.path.join("/", "workspace", "exercise.py"), "r", encoding='utf-8') as f:
|
|
||||||
|
filepath = os.path.join("/", "workspace", args.filename)
|
||||||
|
with open(filepath, "r", encoding='utf-8') as f:
|
||||||
script = f.read()
|
script = f.read()
|
||||||
c = compile(script, "exercise.py", 'exec')
|
c = compile(script, args.filename, 'exec')
|
||||||
exec(c, {})
|
exec(c, {})
|
||||||
|
|
||||||
# work-around for docker not terminating properly
|
# work-around for docker not terminating properly
|
||||||
shell.sendpickle({'cmd':'exit'})
|
shell.sendpickle({'cmd':'exit'})
|
||||||
|
Reference in New Issue
Block a user