3131
3232from typing import Callable , Literal , Pattern , Match
3333
34+ log = logging .getLogger (__name__ )
35+
3436Verdict = Literal ['AC' , 'TLE' , 'OLE' , 'MLE' , 'RTE' , 'WA' , 'PAC' , 'JE' ]
3537
3638def is_TLE (status : int , may_signal_with_usr1 : bool = False ) -> bool :
@@ -91,6 +93,7 @@ class ProblemAspect:
9193 warnings = 0
9294 bail_on_error = False
9395 _check_res : bool | None = None
96+ consider_warnings_errors = False
9497 basename_regex = re .compile ('^[a-zA-Z0-9][a-zA-Z0-9_.-]*[a-zA-Z0-9]$' )
9598 consider_warnings_errors : bool
9699
@@ -113,25 +116,25 @@ def __append_additional_info(msg: str, additional_info: str|None) -> str:
113116 def error (self , msg : str , additional_info : str | None = None ) -> None :
114117 self ._check_res = False
115118 ProblemAspect .errors += 1
116- logging . error ('in %s: %s' , self , ProblemAspect .__append_additional_info (msg , additional_info ))
119+ self . log . error (ProblemAspect .__append_additional_info (msg , additional_info ))
117120 if ProblemAspect .bail_on_error :
118121 raise VerifyError (msg )
119122
123+ def __init__ (self , name ):
124+ self .log = log .getChild (name )
125+
120126 def warning (self , msg : str , additional_info : str | None = None ) -> None :
121127 if ProblemAspect .consider_warnings_errors :
122128 self .error (msg )
123129 return
124130 ProblemAspect .warnings += 1
125- logging .warning ('in %s: %s' , self , ProblemAspect .__append_additional_info (msg , additional_info ))
126-
127- def msg (self , msg : str ) -> None :
128- print (msg )
131+ self .log .warning (ProblemAspect .__append_additional_info (msg , additional_info ))
129132
130133 def info (self , msg : str ) -> None :
131- logging . info (': %s' , msg )
134+ self . log . info (msg )
132135
133136 def debug (self , msg : str ) -> None :
134- logging . debug (': %s' , msg )
137+ self . log . debug (msg )
135138
136139 def check_basename (self , path : str ) -> None :
137140 basename = os .path .basename (path )
@@ -140,6 +143,7 @@ def check_basename(self, path: str) -> None:
140143
141144class TestCase (ProblemAspect ):
142145 def __init__ (self , problem : Problem , base : str , testcasegroup : TestCaseGroup ):
146+ super ().__init__ (f"{ problem .shortname } .test.{ testcasegroup .name } .{ os .path .basename (base )} " )
143147 self ._base = base
144148 self .infile = f'{ base } .in'
145149 self .ansfile = f'{ base } .ans'
@@ -248,6 +252,8 @@ def _run_submission_real(self, sub, args: argparse.Namespace, timelim: int, time
248252 return (res , res_low , res_high , True )
249253
250254 outfile = os .path .join (self ._problem .tmpdir , 'output' )
255+ errfile = os .path .join (self ._problem .tmpdir , 'error' )
256+
251257 if sys .stdout .isatty ():
252258 msg = f'Running { sub } on { self } ...'
253259 sys .stdout .write (msg )
@@ -256,13 +262,16 @@ def _run_submission_real(self, sub, args: argparse.Namespace, timelim: int, time
256262 if self ._problem .is_interactive :
257263 res_high = self ._problem .output_validators .validate_interactive (self , sub , timelim_high , self ._problem .submissions )
258264 else :
259- status , runtime = sub .run (self .infile , outfile ,
265+ status , runtime = sub .run (infile = self .infile , outfile = outfile , errfile = errfile ,
260266 timelim = timelim_high + 1 ,
261267 memlim = self ._problem .config .get ('limits' )['memory' ], set_work_dir = True )
262268 if is_TLE (status ) or runtime > timelim_high :
263269 res_high = SubmissionResult ('TLE' )
264270 elif is_RTE (status ):
265- res_high = SubmissionResult ('RTE' )
271+ if os .path .isfile (errfile ):
272+ with open (errfile , mode = "rt" ) as f :
273+ info = f .read ()
274+ res_high = SubmissionResult ('RTE' , additional_info = info )
266275 else :
267276 res_high = self ._problem .output_validators .validate (self , outfile )
268277 res_high .runtime = runtime
@@ -318,8 +327,13 @@ def __init__(self, problem: Problem, datadir: str, parent: TestCaseGroup|None=No
318327 self ._parent = parent
319328 self ._problem = problem
320329 self ._datadir = datadir
330+ self .name = os .path .relpath (os .path .abspath (self ._datadir ),
331+ os .path .abspath (self ._problem .probdir )).replace ("/" , "." )
332+
333+ super ().__init__ (f"{ problem .shortname } .test.{ self .name } " )
334+
321335 self ._seen_oob_scores = False
322- self .debug (f' Loading test data group { datadir } ' )
336+ self .debug (' Loading test data group %s' % datadir )
323337 configfile = os .path .join (self ._datadir , 'testdata.yaml' )
324338 self .config = {}
325339 if os .path .isfile (configfile ):
@@ -374,7 +388,7 @@ def __init__(self, problem: Problem, datadir: str, parent: TestCaseGroup|None=No
374388
375389
376390 def __str__ (self ) -> str :
377- return f'test case group { os . path . relpath ( self ._datadir , os . path . join ( self . _problem . probdir )) } '
391+ return f'test case group { self .name } '
378392
379393 def set_symlinks (self ) -> None :
380394 for sub in self ._items :
@@ -627,6 +641,7 @@ class ProblemConfig(ProblemAspect):
627641 _VALID_LICENSES = ['unknown' , 'public domain' , 'cc0' , 'cc by' , 'cc by-sa' , 'educational' , 'permission' ]
628642
629643 def __init__ (self , problem : Problem ):
644+ super ().__init__ (f"{ problem .shortname } .config" )
630645 self .debug (' Loading problem config' )
631646 self ._problem = problem
632647 self .configfile = os .path .join (problem .probdir , 'problem.yaml' )
@@ -1061,6 +1076,7 @@ def check(self, args: argparse.Namespace) -> bool:
10611076
10621077class ProblemStatement (ProblemAspect ):
10631078 def __init__ (self , problem : Problem ):
1079+ super ().__init__ (f"{ problem .shortname } .statement" )
10641080 self .debug (' Loading problem statement' )
10651081 self ._problem = problem
10661082 self .languages = []
@@ -1136,6 +1152,7 @@ class Attachments(ProblemAspect):
11361152 """
11371153
11381154 def __init__ (self , problem : Problem ):
1155+ super ().__init__ (f"{ problem .shortname } .attachments" )
11391156 attachments_path = os .path .join (problem .probdir , 'attachments' )
11401157 self .attachments : list [str ] = []
11411158 if os .path .isdir (attachments_path ):
@@ -1185,6 +1202,7 @@ def _build_junk_modifier(desc: str, pattern: str, repl: str|Callable[[Match], st
11851202class InputFormatValidators (ProblemAspect ):
11861203
11871204 def __init__ (self , problem : Problem ):
1205+ super ().__init__ (f"{ problem .shortname } .input_validator" )
11881206 self ._problem = problem
11891207 input_validators_path = os .path .join (problem .probdir , 'input_format_validators' )
11901208 if os .path .isdir (input_validators_path ):
@@ -1304,6 +1322,7 @@ class Graders(ProblemAspect):
13041322 _default_grader = run .get_tool ('default_grader' )
13051323
13061324 def __init__ (self , problem : Problem ):
1325+ super ().__init__ (f"{ problem .shortname } .grader" )
13071326 self ._problem = problem
13081327 self ._graders : list = run .find_programs (os .path .join (problem .probdir , 'graders' ),
13091328 language_config = problem .language_config ,
@@ -1382,7 +1401,7 @@ def grade(self, sub_results: list[SubmissionResult], testcasegroup: TestCaseGrou
13821401 # TODO: check that all graders give same result
13831402
13841403 if not shadow_result :
1385- self .info (f'Grade on { testcasegroup } is { verdict } ({ score } )' )
1404+ self .debug (f'Grade on { testcasegroup } is { verdict } ({ score } )' )
13861405
13871406 return (verdict , score )
13881407
@@ -1392,6 +1411,7 @@ class OutputValidators(ProblemAspect):
13921411
13931412
13941413 def __init__ (self , problem : Problem ):
1414+ super ().__init__ (f"{ problem .shortname } .output_validator" )
13951415 self ._problem = problem
13961416 self ._validators = run .find_programs (os .path .join (problem .probdir ,
13971417 'output_validators' ),
@@ -1585,11 +1605,26 @@ def validate(self, testcase: TestCase, submission_output: str) -> SubmissionResu
15851605 for val in self ._actual_validators ():
15861606 if val is not None and val .compile ()[0 ]:
15871607 feedbackdir = tempfile .mkdtemp (prefix = 'feedback' , dir = self ._problem .tmpdir )
1608+ validator_output = tempfile .mkdtemp (prefix = 'checker_out' , dir = self ._problem .tmpdir )
1609+ outfile = validator_output + "/out.txt"
1610+ errfile = validator_output + "/err.txt"
15881611 status , runtime = val .run (submission_output ,
15891612 args = [testcase .infile , testcase .ansfile , feedbackdir ] + flags ,
1590- timelim = val_timelim , memlim = val_memlim )
1613+ timelim = val_timelim , memlim = val_memlim ,
1614+ outfile = outfile , errfile = errfile )
1615+ if log .isEnabledFor (logging .DEBUG ):
1616+ with open (outfile , mode = "rt" ) as f :
1617+ output = f .read ()
1618+ if output :
1619+ log .debug ("Validator output:\n %s" , output )
1620+ with open (errfile , mode = "rt" ) as f :
1621+ error = f .read ()
1622+ if error :
1623+ log .debug ("Validator stderr:\n %s" , error )
1624+
15911625 res = self ._parse_validator_results (val , status , feedbackdir , testcase )
15921626 shutil .rmtree (feedbackdir )
1627+ shutil .rmtree (validator_output )
15931628 if res .verdict != 'AC' :
15941629 return res
15951630
@@ -1609,6 +1644,7 @@ class Submissions(ProblemAspect):
16091644 ]
16101645
16111646 def __init__ (self , problem : Problem ):
1647+ super ().__init__ (f"{ problem .shortname } .submission" )
16121648 self ._submissions = {}
16131649 self ._problem = problem
16141650 srcdir = os .path .join (problem .probdir , 'submissions' )
@@ -1742,6 +1778,7 @@ class Problem(ProblemAspect):
17421778 def __init__ (self , probdir : str ):
17431779 self .probdir = os .path .realpath (probdir )
17441780 self .shortname : str | None = os .path .basename (self .probdir )
1781+ super ().__init__ (self .shortname )
17451782 self .language_config = languages .load_language_config ()
17461783
17471784 def __enter__ (self ) -> Problem :
0 commit comments