Add webpython + instructions
This commit is contained in:
11
webpython/Dockerfile
Normal file
11
webpython/Dockerfile
Normal file
@ -0,0 +1,11 @@
|
||||
FROM ubuntu:14.04
|
||||
MAINTAINER "Martin v. Löwis"
|
||||
RUN locale-gen en_US.UTF-8
|
||||
ENV LANG en_US.UTF-8
|
||||
ADD webpython.py /usr/lib/python3.4/webpython.py
|
||||
RUN rm /usr/lib/python3.4/turtle.py
|
||||
ADD turtle.py /usr/lib/python3.4/turtle.py
|
||||
ADD assess /usr/lib/python3.4/assess
|
||||
RUN adduser --disabled-password --gecos Python python
|
||||
USER python
|
||||
WORKDIR /home/python
|
58
webpython/Makefile
Normal file
58
webpython/Makefile
Normal file
@ -0,0 +1,58 @@
|
||||
CODEMIRROR=html/js/codemirror.js
|
||||
|
||||
all: logs
|
||||
|
||||
logs:
|
||||
mkdir logs
|
||||
|
||||
# These are put into source control
|
||||
generated: $(CODEMIRROR)
|
||||
|
||||
|
||||
$(CODEMIRROR): CodeMirror/lib/codemirror.js CodeMirror/mode/python/python.js
|
||||
(cd CodeMirror; bin/compress codemirror python) > $@
|
||||
|
||||
run: all kill
|
||||
twistd -l logs/webpython.log -y webpython.tac
|
||||
|
||||
runpy: killpy
|
||||
twistd --pidfile pylaunch.pid -l logs/pylaunch.log -y pylaunch.tac
|
||||
|
||||
manager: all killmanager
|
||||
twistd --pidfile manager.pid -l logs/manager.log -y manager.tac
|
||||
|
||||
kill:
|
||||
if [ -f twistd.pid ];\
|
||||
then\
|
||||
kill `cat twistd.pid`;\
|
||||
fi
|
||||
|
||||
killpy:
|
||||
if [ -f pylaunch.pid ];\
|
||||
then\
|
||||
kill `cat pylaunch.pid`;\
|
||||
fi
|
||||
|
||||
killmanager:
|
||||
if [ -f manager.pid ];\
|
||||
then\
|
||||
kill `cat manager.pid`;\
|
||||
fi
|
||||
|
||||
docker:
|
||||
docker.io build --rm -t webpython .
|
||||
|
||||
update:
|
||||
git pull
|
||||
make docker
|
||||
service webpython-worker restart
|
||||
|
||||
rmexited:
|
||||
docker.io ps -a|grep 'Exit '|awk '{print $$1;}'|xargs docker.io rm
|
||||
|
||||
rmi:
|
||||
docker.io rmi $$(docker.io images | grep "^<none>" | awk '{print $$3;}')
|
||||
|
||||
killold:
|
||||
-docker.io ps | egrep 'hours? ago' |awk '{print $$1}'|xargs docker.io kill
|
||||
-killall -o 30m -9 python3
|
47
webpython/README.md
Normal file
47
webpython/README.md
Normal file
@ -0,0 +1,47 @@
|
||||
Local setup
|
||||
===========
|
||||
|
||||
1. `git checkout webpython-hybrid`
|
||||
2. Make sure to install all dependencies and migrations:
|
||||
|
||||
rake db:migrate
|
||||
bundle install
|
||||
|
||||
3. Create a new docker image containing the Turtle library and the i/o wrapper:
|
||||
|
||||
cd webpython
|
||||
docker build -t IMAGE_NAME .
|
||||
|
||||
4. Configure your Docker host at `config/docker.yml.erb`. Make sure to add a websocket host, for example like this (this is probably different for you):
|
||||
|
||||
host: tcp://localhost:2375
|
||||
ws_host: ws://localhost:2375
|
||||
|
||||
5. Run the CodeOcean server with `rails s -p 3333`
|
||||
|
||||
6. Login with admin@example.org (pw: admin) and create a new execution environment picking the newly created Docker image from the dropdown. Set the initial command to:
|
||||
|
||||
cd /usr/lib/python3.4 && python3 webpython.py
|
||||
|
||||
7. Create a new exercise for the newly created execution environment with an arbritrary main file.
|
||||
8. Implement the exercise. The code below can be used as an example to see the canvas and I/O in action:
|
||||
|
||||
import turtle
|
||||
wn = turtle.Screen()
|
||||
alex = turtle.Turtle()
|
||||
|
||||
# i/o test
|
||||
print("hello!")
|
||||
print("please enter your name")
|
||||
name = input()
|
||||
print("your name is", name)
|
||||
|
||||
# canvas test
|
||||
alex.forward(50)
|
||||
alex.right(90)
|
||||
alex.forward(30)
|
||||
alex.right(90)
|
||||
alex.forward(30)
|
||||
|
||||
wn.mainloop()
|
||||
|
4078
webpython/turtle.py
Normal file
4078
webpython/turtle.py
Normal file
File diff suppressed because it is too large
Load Diff
174
webpython/webpython.py
Normal file
174
webpython/webpython.py
Normal file
@ -0,0 +1,174 @@
|
||||
#!/usr/bin/python3
|
||||
# Main interpreter entry for webpython
|
||||
import io, select, sys, os, threading, code
|
||||
import pickle, struct, builtins, json
|
||||
#, ressource
|
||||
from queue import Queue
|
||||
|
||||
# hard limit to 64M
|
||||
#try:
|
||||
# resource.setrlimit(resource.RLIMIT_AS, (1<<26, 1<<26))
|
||||
#except ValueError:
|
||||
# tried to raise it
|
||||
# pass
|
||||
|
||||
# output limit 16MiB
|
||||
output_capacity = 16*1024*1024
|
||||
# adapted from IDLE (PyShell.py)
|
||||
class PseudoFile(io.TextIOBase):
|
||||
|
||||
def __init__(self, shell, name):
|
||||
self.shell = shell
|
||||
self._name = name
|
||||
|
||||
@property
|
||||
def encoding(self):
|
||||
return "UTF-8"
|
||||
|
||||
@property
|
||||
def name(self):
|
||||
return '<%s>' % self._name
|
||||
|
||||
def isatty(self):
|
||||
return True
|
||||
|
||||
class PseudoInputFile(PseudoFile):
|
||||
|
||||
def __init__(self, shell, name):
|
||||
PseudoFile.__init__(self, shell, name)
|
||||
self._line_buffer = ''
|
||||
|
||||
def readable(self):
|
||||
return True
|
||||
|
||||
def read(self, size=-1):
|
||||
if self.closed:
|
||||
raise ValueError("read from closed file")
|
||||
if size is None:
|
||||
size = -1
|
||||
elif not isinstance(size, int):
|
||||
raise TypeError('must be int, not ' + type(size).__name__)
|
||||
result = self._line_buffer
|
||||
self._line_buffer = ''
|
||||
if size < 0:
|
||||
while True:
|
||||
line = self.shell.readline()
|
||||
if not line: break
|
||||
result += line
|
||||
else:
|
||||
while len(result) < size:
|
||||
line = self.shell.readline()
|
||||
if not line: break
|
||||
result += line
|
||||
self._line_buffer = result[size:]
|
||||
result = result[:size]
|
||||
return result
|
||||
|
||||
def readline(self, size=-1):
|
||||
if self.closed:
|
||||
raise ValueError("read from closed file")
|
||||
if size is None:
|
||||
size = -1
|
||||
elif not isinstance(size, int):
|
||||
raise TypeError('must be int, not ' + type(size).__name__)
|
||||
line = self._line_buffer or self.shell.readline()
|
||||
if size < 0:
|
||||
size = len(line)
|
||||
self._line_buffer = line[size:]
|
||||
return line[:size]
|
||||
|
||||
def close(self):
|
||||
self.shell.close()
|
||||
|
||||
class PseudoOutputFile(PseudoFile):
|
||||
|
||||
def writable(self):
|
||||
return True
|
||||
|
||||
def write(self, s):
|
||||
if self.closed:
|
||||
raise ValueError("write to closed file")
|
||||
if not isinstance(s, str):
|
||||
raise TypeError('must be str, not ' + type(s).__name__)
|
||||
return self.shell.write(s, self._name)
|
||||
|
||||
# RPC proxy
|
||||
orig_stdin = sys.stdin
|
||||
orig_stdout = sys.stdout
|
||||
orig_stderr = sys.stderr
|
||||
class Shell:
|
||||
def __init__(self):
|
||||
self.stdin = io.FileIO(0)
|
||||
self.buf = b''
|
||||
self.canvas = []
|
||||
self.messages = []
|
||||
self.capacity = output_capacity
|
||||
|
||||
# PseudoFile interaction
|
||||
#def readline(self):
|
||||
# self.sendpickle({'cmd':'readline',
|
||||
# 'stream':'stdin',
|
||||
# })
|
||||
# return self.inputq.get()
|
||||
|
||||
def write(self, data, name):
|
||||
self.sendpickle({'cmd':'write',
|
||||
'stream':name,
|
||||
'data':data
|
||||
})
|
||||
|
||||
def input(self, prompt=''):
|
||||
self.sendpickle({'cmd':'input',
|
||||
'stream':'stdin',
|
||||
'data':prompt})
|
||||
result = self.receivemsg()
|
||||
return result['data']
|
||||
|
||||
# internal
|
||||
def sendpickle(self, data):
|
||||
data = json.dumps(data) + "\n\r"
|
||||
self.capacity -= len(data)
|
||||
if self.capacity < 0:
|
||||
data = json.dumps({'cmd':'stop',
|
||||
'timedout':True}, 2)
|
||||
orig_stdout.write(data)
|
||||
raise SystemExit
|
||||
orig_stdout.write(data)
|
||||
|
||||
def receivepickle(self):
|
||||
msg = json.loads(orig_stdin.readline())
|
||||
if msg['cmd'] == 'canvasevent':
|
||||
self.canvas.append(msg)
|
||||
else:
|
||||
self.messages.append(msg)
|
||||
|
||||
def receivemsg(self):
|
||||
while not self.messages:
|
||||
self.receivepickle()
|
||||
return self.messages.pop()
|
||||
|
||||
def receivecanvas(self):
|
||||
while not self.canvas:
|
||||
self.receivepickle()
|
||||
return self.canvas.pop(0)
|
||||
|
||||
# Hide 0/1 from sys
|
||||
shell = Shell()
|
||||
sys.__stdin__ = sys.stdin = PseudoInputFile(shell, 'stdin')
|
||||
sys.__stdout__ = sys.stdout = PseudoOutputFile(shell, 'stdout')
|
||||
#sys.__stderr__ = sys.stderr = PseudoOutputFile(shell, 'stderr')
|
||||
builtins.input = shell.input
|
||||
|
||||
#iothread = threading.Thread(target=shell.run)
|
||||
#iothread.start()
|
||||
|
||||
if __name__ == '__main__':
|
||||
#script = "print (\"Hello world.\")\n\rmsg = input()\n\rprint(\"Out:\")\n\rprint(msg)"
|
||||
#with open("programm.py", "w", encoding='utf-8') as f:
|
||||
# f.write(script)
|
||||
with open(os.path.join("/", "workspace", "exercise.py"), "r", encoding='utf-8') as f:
|
||||
script = f.read()
|
||||
c = compile(script, "exercise.py", 'exec')
|
||||
exec(c, {})
|
||||
# work-around for docker not terminating properly
|
||||
shell.sendpickle({'cmd':'exit'})
|
Reference in New Issue
Block a user