summaryrefslogtreecommitdiff
path: root/python_server.py
diff options
context:
space:
mode:
authorPrabhu Ramachandran2011-11-15 01:22:22 +0530
committerPrabhu Ramachandran2011-11-15 01:22:22 +0530
commit019ad81a980aabb30d5f5ff205f696bab6d1a79a (patch)
tree388a45850dea79630e807ad8f20d00a31278c296 /python_server.py
parent544ccd076a4115e4870429e921ccec4a40c599bc (diff)
downloadonline_test-019ad81a980aabb30d5f5ff205f696bab6d1a79a.tar.gz
online_test-019ad81a980aabb30d5f5ff205f696bab6d1a79a.tar.bz2
online_test-019ad81a980aabb30d5f5ff205f696bab6d1a79a.zip
BUG: Adding timeout to test code evaluation.
If a user submitted code with an errant loop that loops forever or locks up for any reason, it would take the code server down. I now add a timeout of 3 seconds for the evaluation and tests failing which it is an error.
Diffstat (limited to 'python_server.py')
-rwxr-xr-xpython_server.py25
1 files changed, 24 insertions, 1 deletions
diff --git a/python_server.py b/python_server.py
index 1670f90..6ade0ff 100755
--- a/python_server.py
+++ b/python_server.py
@@ -9,19 +9,31 @@ from SimpleXMLRPCServer import SimpleXMLRPCServer
import pwd
import os
from os.path import isdir
+import signal
+# Timeout for the code to run in seconds.
+TIMEOUT = 3
# Set the effective uid
nobody = pwd.getpwnam('nobody')
os.setegid(nobody.pw_gid)
os.seteuid(nobody.pw_uid)
+# Raised when the code times-out.
+# c.f. http://pguides.net/python/timeout-a-function
+class TimeoutException(Exception):
+ pass
+
+def timeout_handler(signum, frame):
+ raise TimeoutException('Code took too long to run.')
+
def run_code(answer, test_code, in_dir=None):
"""Tests given Python function (`answer`) with the `test_code` supplied.
If the optional `in_dir` keyword argument is supplied it changes the
directory to that directory (it does not change it back to the original when
- done).
+ done). This function also timesout when the function takes more than
+ TIMEOUT seconds to run to prevent runaway code.
Returns
-------
@@ -32,6 +44,10 @@ def run_code(answer, test_code, in_dir=None):
if in_dir is not None and isdir(in_dir):
os.chdir(in_dir)
+ # Add a new signal handler for the execution of this code.
+ old_handler = signal.signal(signal.SIGALRM, timeout_handler)
+ signal.alarm(TIMEOUT)
+
success = False
tb = None
try:
@@ -40,6 +56,8 @@ def run_code(answer, test_code, in_dir=None):
exec submitted in g
_tests = compile(test_code, '<string>', mode='exec')
exec _tests in g
+ except TimeoutException:
+ err = 'Code took more than %s seconds to run.'%TIMEOUT
except AssertionError:
type, value, tb = sys.exc_info()
info = traceback.extract_tb(tb)
@@ -54,6 +72,11 @@ def run_code(answer, test_code, in_dir=None):
err = 'Correct answer'
finally:
del tb
+ # Set back any original signal handler.
+ signal.signal(signal.SIGALRM, old_handler)
+
+ # Cancel the signal if any, see signal.alarm documentation.
+ signal.alarm(0)
return success, err