@@ -58,6 +58,125 @@ def N_(message):
5858 return message
5959
6060
61+ class HumanMessage (object ):
62+ """Represent a possible outcome message for a grading, to be presented
63+ to the contestants.
64+
65+ """
66+
67+ def __init__ (self , shorthand , message , help ):
68+ """Initialization.
69+
70+ shorthand (unicode): what to call this message in the code.
71+ message (unicode): the message itself.
72+ help (unicode): a longer explanation for the help page.
73+
74+ """
75+ self .shorthand = shorthand
76+ self .message = message
77+ self .help = help
78+
79+
80+ class MessageCollection (object ):
81+ """Represent a collection of messages, with error checking."""
82+
83+ def __init__ (self , messages = None ):
84+ self ._messages = {}
85+ self ._ordering = []
86+ if messages is not None :
87+ for message in messages :
88+ self .add (message )
89+
90+ def add (self , message ):
91+ if message .shorthand in self ._messages :
92+ logger .error ("Trying to registering duplicate message `%s'." ,
93+ message .shorthand )
94+ return
95+ self ._messages [message .shorthand ] = message
96+ self ._ordering .append (message .shorthand )
97+
98+ def get (self , shorthand ):
99+ if shorthand not in self ._messages :
100+ error = "Trying to get a non-existing message `%s'." % \
101+ shorthand
102+ logger .error (error )
103+ raise KeyError (error )
104+ return self ._messages [shorthand ]
105+
106+ def all (self ):
107+ ret = []
108+ for shorthand in self ._ordering :
109+ ret .append (self ._messages [shorthand ])
110+ return ret
111+
112+
113+ COMPILATION_MESSAGES = MessageCollection ([
114+ HumanMessage ("success" ,
115+ N_ ("Compilation succeeded" ),
116+ N_ ("Your submission successfully compiled to an excutable." )),
117+ HumanMessage ("fail" ,
118+ N_ ("Compilation failed" ),
119+ N_ ("Your submission did not compile correctly." )),
120+ HumanMessage ("timeout" ,
121+ N_ ("Compilation timed out" ),
122+ N_ ("Your submission exceeded the time limit while compiling. "
123+ "This might be caused by an excessive use of C++ "
124+ "templates, for example." )),
125+ HumanMessage ("signal" ,
126+ N_ ("Compilation killed with signal %s (could be triggered "
127+ "by violating memory limits)" ),
128+ N_ ("Your submission was killed with the specified signal. "
129+ "Among other things, this might be caused by exceeding "
130+ "the memory limit for the compilation, and in turn by an "
131+ "excessive use of C++ templates, for example." )),
132+ ])
133+
134+
135+ EVALUATION_MESSAGES = MessageCollection ([
136+ HumanMessage ("success" ,
137+ N_ ("Output is correct" ),
138+ N_ ("Your submission ran and gave the correct answer" )),
139+ HumanMessage ("wrong" ,
140+ N_ ("Output isn't correct" ),
141+ N_ ("Your submission ran, but gave the wrong answer" )),
142+ HumanMessage ("nooutput" ,
143+ N_ ("Evaluation didn't produce file %s" ),
144+ N_ ("Your submission ran, but did not write on the "
145+ "correct output file" )),
146+ HumanMessage ("timeout" ,
147+ N_ ("Execution timed out" ),
148+ N_ ("Your submission used too much CPU time." )),
149+ HumanMessage ("walltimeout" ,
150+ N_ ("Execution timed out (wall clock limit exceeded)" ),
151+ N_ ("Your submission used too much total time. This might "
152+ "be triggered by undefined code, or buffer overflow, "
153+ "for example. Note that in this case the CPU time "
154+ "visible in the submission details might be much smaller "
155+ "than the time limit." )),
156+ HumanMessage ("signal" ,
157+ N_ ("Execution killed with signal %d (could be triggered by "
158+ "violating memory limits)" ),
159+ N_ ("Your submission was killed with the specified signal. "
160+ "Among other things, this might be caused by exceeding "
161+ "the memory limit. Note that if this is the reason, "
162+ "the memory usage visible in the submission details is "
163+ "the usage before the allocation that caused the "
164+ "signal." )),
165+ HumanMessage ("syscall" ,
166+ N_ ("Execution killed because of forbidden syscall %s" ),
167+ N_ ("Your submission was killed because it tried to use "
168+ "the forbidden syscall specified in the message." )),
169+ HumanMessage ("fileaccess" ,
170+ N_ ("Execution killed because of forbidden file access" ),
171+ N_ ("Your submission was killed because it tried to read "
172+ "or write a forbidden file." )),
173+ HumanMessage ("returncode" ,
174+ N_ ("Execution failed because the return code was nonzero" ),
175+ N_ ("Your submission failed because it exited with a return "
176+ "code different from 0." )),
177+ ])
178+
179+
61180class JobException (Exception ):
62181 """Exception raised by a worker doing a job.
63182
@@ -273,23 +392,23 @@ def compilation_step(sandbox, commands):
273392 logger .debug ("Compilation successfully finished." )
274393 success = True
275394 compilation_success = True
276- text = [N_ ( "Compilation succeeded" ) ]
395+ text = [COMPILATION_MESSAGES . get ( "success" ). message ]
277396
278397 # Error in compilation: returning the error to the user.
279398 elif (exit_status == Sandbox .EXIT_OK and exit_code != 0 ) or \
280399 exit_status == Sandbox .EXIT_NONZERO_RETURN :
281400 logger .debug ("Compilation failed." )
282401 success = True
283402 compilation_success = False
284- text = [N_ ( "Compilation failed" ) ]
403+ text = [COMPILATION_MESSAGES . get ( "fail" ). message ]
285404
286405 # Timeout: returning the error to the user
287406 elif exit_status == Sandbox .EXIT_TIMEOUT or \
288407 exit_status == Sandbox .EXIT_TIMEOUT_WALL :
289408 logger .debug ("Compilation timed out." )
290409 success = True
291410 compilation_success = False
292- text = [N_ ( "Compilation timed out" ) ]
411+ text = [COMPILATION_MESSAGES . get ( "timeout" ). message ]
293412
294413 # Suicide with signal (probably memory limit): returning the error
295414 # to the user
@@ -299,8 +418,7 @@ def compilation_step(sandbox, commands):
299418 success = True
300419 compilation_success = False
301420 plus ["signal" ] = signal
302- text = [N_ ("Compilation killed with signal %d (could be triggered "
303- "by violating memory limits)" ), signal ]
421+ text = [COMPILATION_MESSAGES .get ("signal" ).message , signal ]
304422
305423 # Sandbox error: this isn't a user error, the administrator needs
306424 # to check the environment
@@ -497,32 +615,30 @@ def human_evaluation_message(plus):
497615 """Given the plus object returned by evaluation_step, builds a
498616 human-readable message about what happened.
499617
500- None is returned in cases when the contestant musn 't receive any
618+ None is returned in cases when the contestant mustn 't receive any
501619 message (for example, if the execution couldn't be performed) or
502620 when the message will be computed somewhere else (for example, if
503- the execution was successfull , then the comparator is supposed to
621+ the execution was successful , then the comparator is supposed to
504622 write the message).
505623
506624 """
507625 exit_status = plus ['exit_status' ]
508626 if exit_status == Sandbox .EXIT_TIMEOUT :
509- return [N_ ( "Execution timed out" ) ]
627+ return [EVALUATION_MESSAGES . get ( "timeout" ). message ]
510628 elif exit_status == Sandbox .EXIT_TIMEOUT_WALL :
511- return [N_ ( "Execution timed out (wall clock limit exceeded)" ) ]
629+ return [EVALUATION_MESSAGES . get ( "walltimeout" ). message ]
512630 elif exit_status == Sandbox .EXIT_SIGNAL :
513- return [N_ ("Execution killed with signal %d (could be triggered by "
514- "violating memory limits)" ), plus ['signal' ]]
631+ return [EVALUATION_MESSAGES .get ("signal" ).message % plus ['signal' ]]
515632 elif exit_status == Sandbox .EXIT_SANDBOX_ERROR :
516633 return None
517634 elif exit_status == Sandbox .EXIT_SYSCALL :
518- return [N_ ("Execution killed because of forbidden syscall %s" ),
519- plus ['syscall' ]]
635+ return [EVALUATION_MESSAGES .get ("syscall" ).message % plus ['syscall' ]]
520636 elif exit_status == Sandbox .EXIT_FILE_ACCESS :
521637 # Don't tell which file: would be too much information!
522- return [N_ ( "Execution killed because of forbidden file access" ) ]
638+ return [EVALUATION_MESSAGES . get ( "fileaccess" ). message ]
523639 elif exit_status == Sandbox .EXIT_NONZERO_RETURN :
524640 # Don't tell which code: would be too much information!
525- return [N_ ( "Execution failed because the return code was nonzero" ) ]
641+ return [EVALUATION_MESSAGES . get ( "returncode" ). message ]
526642 elif exit_status == Sandbox .EXIT_OK :
527643 return None
528644 else :
@@ -692,13 +808,13 @@ def white_diff_step(sandbox, output_filename,
692808 res_file = sandbox .get_file (correct_output_filename )
693809 if white_diff (out_file , res_file ):
694810 outcome = 1.0
695- text = [N_ ( "Output is correct" ) ]
811+ text = [EVALUATION_MESSAGES . get ( "success" ). message ]
696812 else :
697813 outcome = 0.0
698- text = [N_ ( "Output isn't correct" ) ]
814+ text = [EVALUATION_MESSAGES . get ( "wrong" ). message ]
699815 else :
700816 outcome = 0.0
701- text = [N_ ( "Evaluation didn't produce file %s" ) , output_filename ]
817+ text = [EVALUATION_MESSAGES . get ( "nooutput" ). message , output_filename ]
702818 return outcome , text
703819
704820
0 commit comments