2
2
"""This template is used inside snekbox to evaluate and test user code."""
3
3
import ast
4
4
import base64
5
+ import functools
5
6
import io
6
- import os
7
7
import sys
8
8
import traceback
9
+ import typing
9
10
import unittest
10
11
from itertools import chain
11
12
from types import ModuleType , SimpleNamespace
12
13
from typing import NoReturn
13
- from unittest import mock
14
+
14
15
15
16
### USER CODE
16
17
@@ -20,9 +21,10 @@ class RunnerTestCase(unittest.IsolatedAsyncioTestCase):
20
21
21
22
22
23
normal_exit = False
24
+ _EXIT_WRAPPER_TYPE = typing .Callable [[int ], None ]
23
25
24
26
25
- def _exit_sandbox (code : int ) -> NoReturn :
27
+ def _exit_sandbox (code : int , stdout : io . StringIO , result_writer : io . StringIO ) -> NoReturn :
26
28
"""
27
29
Exit the sandbox by printing the result to the actual stdout and exit with the provided code.
28
30
@@ -34,64 +36,69 @@ def _exit_sandbox(code: int) -> NoReturn:
34
36
35
37
137 can also be generated by NsJail when killing the process.
36
38
"""
37
- print (RESULT .getvalue (), file = ORIGINAL_STDOUT , end = "" )
39
+ print (result_writer .getvalue (), file = stdout , end = "" )
38
40
global normal_exit
39
41
normal_exit = True
40
42
sys .exit (code )
41
43
42
44
43
- def _load_user_module () -> ModuleType :
45
+ def _load_user_module (result_writer , exit_wrapper : _EXIT_WRAPPER_TYPE ) -> ModuleType :
44
46
"""Load the user code into a new module and return it."""
45
47
code = base64 .b64decode (USER_CODE ).decode ("utf8" )
46
48
try :
47
49
ast .parse (code , "<input>" )
48
50
except SyntaxError :
49
- RESULT .write ("" .join (traceback .format_exception (* sys .exc_info (), limit = 0 )))
50
- _exit_sandbox (5 )
51
+ result_writer .write ("" .join (traceback .format_exception (* sys .exc_info (), limit = 0 )))
52
+ exit_wrapper (5 )
51
53
52
54
_module = ModuleType ("module" )
53
55
exec (code , _module .__dict__ )
54
56
55
57
return _module
56
58
57
59
58
- def _main () -> None :
60
+ def _main (result_writer : io . StringIO , module : ModuleType , exit_wrapper : _EXIT_WRAPPER_TYPE ) -> None :
59
61
suite = unittest .defaultTestLoader .loadTestsFromTestCase (RunnerTestCase )
62
+ globals ()["module" ] = module
60
63
result = suite .run (unittest .TestResult ())
61
64
62
- RESULT .write (str (int (result .wasSuccessful ())))
65
+ result_writer .write (str (int (result .wasSuccessful ())))
63
66
64
67
if not result .wasSuccessful ():
65
- RESULT .write (
68
+ result_writer .write (
66
69
";" .join (chain (
67
70
(error [0 ]._testMethodName .removeprefix ("test_" ) for error in result .errors ),
68
71
(failure [0 ]._testMethodName .removeprefix ("test_" ) for failure in result .failures )
69
72
))
70
73
)
71
74
72
- _exit_sandbox (0 )
75
+ exit_wrapper (0 )
73
76
74
77
75
- try :
76
- # Fake file object not writing anything
77
- DEVNULL = SimpleNamespace ( write = lambda * _ : None , flush = lambda * _ : None )
78
+ def _entry () :
79
+ result_writer = io . StringIO ()
80
+ exit_wrapper = functools . partial ( _exit_sandbox , stdout = sys . stdout , result_writer = result_writer )
78
81
79
- RESULT = io .StringIO ()
80
- ORIGINAL_STDOUT = sys .__stdout__
82
+ try :
83
+ # Fake file object not writing anything
84
+ devnull = SimpleNamespace (write = lambda * _ : None , flush = lambda * _ : None )
81
85
82
- # stdout/err is patched in order to control what is outputted by the runner
83
- sys .__stdout__ = sys .stdout = DEVNULL
84
- sys .__stderr__ = sys .stderr = DEVNULL
86
+ # stdout/err is patched in order to control what is outputted by the runner
87
+ sys .__stdout__ = sys .stdout = devnull
88
+ sys .__stderr__ = sys .stderr = devnull
85
89
86
- # Load the user code as a global module variable
87
- try :
88
- module = _load_user_module ()
90
+ # Load the user code as a global module variable
91
+ try :
92
+ module = _load_user_module (result_writer , exit_wrapper )
93
+ except BaseException as e :
94
+ result_writer .write (f"Uncaught exception while loading user code: { e } " )
95
+ exit_wrapper (6 )
96
+
97
+ _main (result_writer , module , exit_wrapper )
89
98
except BaseException as e :
90
- RESULT .write (f"Uncaught exception while loading user code: { e } " )
91
- _exit_sandbox (6 )
92
- _main ()
93
- except BaseException as e :
94
- if isinstance (e , SystemExit ) and normal_exit :
95
- raise e from None
96
- RESULT .write (f"Uncaught exception inside runner: { e } " )
97
- _exit_sandbox (99 )
99
+ if isinstance (e , SystemExit ) and normal_exit :
100
+ raise e from None
101
+ result_writer .write (f"Uncaught exception inside runner: { e } " )
102
+ exit_wrapper (99 )
103
+
104
+ _entry ()
0 commit comments