import sys import time import trac.core from interfaces import IAssignment class EvaluationError(Exception): """raise by assignments when something doesn't evaluate""" class Assignment(trac.core.Component): """superclass for assignments""" question = 'No question in assignment' def evaluate(self, c): """override, should perform tests to see if callable 'c' is valid raise an EvaluationError when validation fails """ def test_timed(self, c): """override, return some useful value (float) for execution speed""" # internal methods def test(self, username, client, result, report): if result is None: report.report_part(username) return try: compiled = compile(result, 'string', 'exec') except: report.report_error(username, result, *sys.exc_info()) return # we have usable data, perform some tests env = {} try: eval(compiled, env) except: report.report_error(username, result, *sys.exc_info()) return if not env.has_key('main'): report.report_invalid(username, result, 'no main function defined') return try: self.evaluate(env['main']) except EvaluationError, e: report.report_invalid(username, result, e) return except: report.report_error(username, result, *sys.exc_info()) return looptime = self.test_timed(env['main']) report.report_success(username, result, self._get_code_size(compiled), client.server_data['time_spent'], looptime) def _get_code_size(self, c): """get the size of the compiled code""" # XXX really not sure if this is sufficient... ret = len(c.co_code) ctype = type(c) for var in c.co_consts: if type(var) == ctype: ret += self._get_code_size(var) return ret class HelloWorld(Assignment): """simple hello, world example""" # disabled, this is nice for testing but not a very cool assignment :| trac.core.implements(IAssignment) question = "Create a callable that returns the string 'hello, world!'" def evaluate(self, c): a = c() if a != 'hello, world!': raise EvaluationError, '%r != "hello, world!"' % a def test_timed(self, c): """override, return some useful value for execution speed""" start = time.clock() for i in xrange(1000): c() return time.clock() - start class Primes(Assignment): """Produce prime numbers""" trac.core.implements(IAssignment) question = ('Create a function main(n) which returns a list of all\n' 'prime numbers between 0 and n.') primes_up_to_1000 = None def evaluate(self, c): # XXX can't override __init__, trac.core doesn't seem to like that... if self.primes_up_to_1000 is None: self.primes_up_to_1000 = p = list(self._primes(1000)) print 'p:', repr(p) ret = c(2) # exclusive!! if ret != []: raise EvaluationError, ('test failed, main(2) returned %r, ' 'not []' % (ret,)) ret = c(7) if ret != [2, 3, 5]: raise EvaluationError, ('test failed, main(7) returned %r, ' 'not [2, 3, 5]' % (ret,)) ret = c(8) if ret != [2, 3, 5, 7]: raise EvaluationError, ('test failed, main(8) returned %r, ' 'not [2, 3, 5, 7]' % (ret,)) ret = c(1000) ret.sort() if ret != self.primes_up_to_1000: raise EvaluationError, 'test failed, primes < 1000 not produced!' def test_timed(self, c): """override, return some useful value for execution speed""" start = time.clock() for i in xrange(100): c(1000) return time.clock() - start def _primes(self, n): """very naive and easy to check/debug implementation of the assignment """ for i in xrange(2, n): for j in xrange(2, i): if i % n == 0: break else: yield i