Skip to content

Commit 7521e3e

Browse files
committed
Initial commit of Submission object and corresponding example.
1 parent ebb6162 commit 7521e3e

File tree

4 files changed

+256
-6
lines changed

4 files changed

+256
-6
lines changed

examples/sulu_submission.py

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
import os
2+
import time
3+
4+
import judge0
5+
6+
from dotenv import load_dotenv
7+
8+
load_dotenv()
9+
10+
sulu_auth_token = os.getenv("SULU_API_KEY")
11+
12+
client_ce = judge0.SuluCEClient(auth_token=sulu_auth_token)
13+
submission = judge0.SingleFileSubmission(
14+
source_code=b"print(f'Hello Judge0')",
15+
language_id=100,
16+
expected_output=b"Hello Judge0",
17+
)
18+
submission.submit(client_ce)
19+
time.sleep(1)
20+
submission.check(client_ce)
21+
22+
print(submission.status)
23+
print(submission.stdout)

src/judge0/__init__.py

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,10 @@
11
from .clients import SuluCEClient, SuluExtraCEClient
2+
from .submission import MultiFileSubmission, SingleFileSubmission, Submission
23

3-
__all__ = [SuluCEClient, SuluExtraCEClient]
4+
__all__ = [
5+
SuluCEClient,
6+
SuluExtraCEClient,
7+
Submission,
8+
SingleFileSubmission,
9+
MultiFileSubmission,
10+
]

src/judge0/clients.py

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -13,27 +13,30 @@ def __init__(
1313
*,
1414
endpoint: Union[str, None] = None,
1515
auth_token: Union[str, None] = None,
16+
wait: bool = False,
1617
):
1718
if endpoint is None:
1819
endpoint = self.default_endpoint
20+
1921
self.endpoint = endpoint
2022
self.auth_token = auth_token
21-
self._session = requests.Session()
23+
self.wait = wait
24+
self.session = requests.Session()
2225

2326
def get_about(self) -> dict:
2427
# TODO: Potentially think about caching the successful return.
2528
headers = {"Authorization": f"Bearer {self.auth_token}"}
2629
r = requests.get(f"{self.endpoint}/about", headers=headers)
2730
r.raise_for_status()
28-
self._session.headers.update(headers)
31+
self.session.headers.update(headers)
2932
return r.json()
3033

3134
def get_config_info(self) -> dict:
3235
# TODO: Potentially think about caching the successful return.
3336
headers = {"Authorization": f"Bearer {self.auth_token}"}
3437
r = requests.get(f"{self.endpoint}/config_info", headers=headers)
3538
r.raise_for_status()
36-
self._session.headers.update(headers)
39+
self.session.headers.update(headers)
3740
return r.json()
3841

3942
def get_statuses(self) -> list[dict]:
@@ -42,7 +45,7 @@ def get_statuses(self) -> list[dict]:
4245
headers = {"Authorization": f"Bearer {self.auth_token}"}
4346
r = requests.get(f"{self.endpoint}/statuses", headers=headers)
4447
r.raise_for_status()
45-
self._session.headers.update(headers)
48+
self.session.headers.update(headers)
4649
return r.json()
4750

4851
def get_languages(
@@ -56,7 +59,7 @@ def get_languages(
5659
headers.update({"Accept": "application/json"})
5760
r = requests.get(request_url, headers=headers)
5861
r.raise_for_status()
59-
self._session.headers.update(headers)
62+
self.session.headers.update(headers)
6063
return r.json()
6164

6265
@property

src/judge0/submission.py

Lines changed: 217 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,217 @@
1+
from base64 import b64decode, b64encode
2+
3+
ENCODED_REQUEST_FIELDS = {
4+
"source_code",
5+
"additional_files",
6+
"stdin",
7+
"expected_output",
8+
}
9+
ENCODED_RESPONSE_FIELDS = {"stdout", "stderr", "compile_output"}
10+
ENCODED_FIELDS = ENCODED_REQUEST_FIELDS | ENCODED_RESPONSE_FIELDS
11+
EXTRA_REQUEST_FIELDS = {
12+
"compiler_options",
13+
"command_line_arguments",
14+
"cpu_time_limit",
15+
"cpu_extra_time",
16+
"wall_time_limit",
17+
"memory_limit",
18+
"stack_limit",
19+
"max_processes_and_or_threads",
20+
"enable_per_process_and_thread_time_limit",
21+
"enable_per_process_and_thread_memory_limit",
22+
"max_file_size",
23+
"redirect_stderr_to_stdout",
24+
"enable_network",
25+
"number_of_runs",
26+
"callback_url",
27+
}
28+
EXTRA_RESPONSE_FIELDS = {
29+
"message",
30+
"exit_code",
31+
"exit_signal",
32+
"status",
33+
"created_at",
34+
"finished_at",
35+
"token",
36+
"time",
37+
"wall_time",
38+
"memory",
39+
}
40+
REQUEST_FIELDS = ENCODED_REQUEST_FIELDS | EXTRA_REQUEST_FIELDS
41+
RESPONSE_FIELDS = ENCODED_RESPONSE_FIELDS | EXTRA_RESPONSE_FIELDS
42+
FIELDS = REQUEST_FIELDS | RESPONSE_FIELDS
43+
44+
45+
class Submission:
46+
"""
47+
Stores a representation of a Submission to/from Judge0.
48+
"""
49+
50+
def __init__(
51+
self,
52+
source_code,
53+
language_id,
54+
additional_files,
55+
*,
56+
compiler_options=None,
57+
command_line_arguments=None,
58+
stdin=None,
59+
expected_output=None,
60+
cpu_time_limit=None,
61+
cpu_extra_time=None,
62+
wall_time_limit=None,
63+
memory_limit=None,
64+
stack_limit=None,
65+
max_processes_and_or_threads=None,
66+
enable_per_process_and_thread_time_limit=None,
67+
enable_per_process_and_thread_memory_limit=None,
68+
max_file_size=None,
69+
redirect_stderr_to_stdout=None,
70+
enable_network=None,
71+
number_of_runs=None,
72+
callback_url=None,
73+
):
74+
self.source_code = source_code # stored as bytes internally
75+
self.language_id = language_id
76+
self.additional_files = additional_files # stored as bytes internally
77+
78+
# Extra pre-execution submission attributes.
79+
self.compiler_options = compiler_options
80+
self.command_line_arguments = command_line_arguments
81+
self.stdin = stdin # stored as bytes internally
82+
self.expected_output = expected_output # stored as bytes internally
83+
self.cpu_time_limit = cpu_time_limit
84+
self.cpu_extra_time = cpu_extra_time
85+
self.wall_time_limit = wall_time_limit
86+
self.memory_limit = memory_limit
87+
self.stack_limit = stack_limit
88+
self.max_processes_and_or_threads = max_processes_and_or_threads
89+
self.enable_per_process_and_thread_time_limit = (
90+
enable_per_process_and_thread_time_limit
91+
)
92+
self.enable_per_process_and_thread_memory_limit = (
93+
enable_per_process_and_thread_memory_limit
94+
)
95+
self.max_file_size = max_file_size
96+
self.redirect_stderr_to_stdout = redirect_stderr_to_stdout
97+
self.enable_network = enable_network
98+
self.number_of_runs = number_of_runs
99+
self.callback_url = callback_url
100+
101+
# Post-execution submission attributes.
102+
self.stdout = None # stored as bytes internally
103+
self.stderr = None # stored as bytes internally
104+
self.compile_output = None # stored as bytes internally
105+
self.message = None
106+
self.exit_code = None
107+
self.exit_signal = None
108+
self.status = None
109+
self.created_at = None
110+
self.finished_at = None
111+
self.token = None
112+
self.time = None
113+
self.wall_time = None
114+
self.memory = None
115+
116+
def check(self, client, *, fields=None):
117+
"""Check the submission status."""
118+
headers = {
119+
"Accept": "application/json",
120+
# NOTE: Only valid for Sulu clients.
121+
"Authorization": f"Bearer {client.auth_token}",
122+
}
123+
params = {
124+
"base64_encoded": "true",
125+
}
126+
127+
if fields is not None:
128+
params["fields"] = ",".join(fields)
129+
130+
resp = client.session.get(
131+
f"{client.endpoint}/submissions/{self.token}",
132+
headers=headers,
133+
params=params,
134+
)
135+
resp.raise_for_status()
136+
137+
self.set_properties(resp.json())
138+
139+
def submit(self, client):
140+
headers = {
141+
"Accept": "application/json",
142+
# NOTE: Only valid for Sulu clients.
143+
"Authorization": f"Bearer {client.auth_token}",
144+
}
145+
params = {
146+
"base64_encoded": "true",
147+
"wait": str(client.wait).lower(),
148+
}
149+
150+
body = {
151+
"source_code": b64encode(self.source_code).decode(),
152+
"language_id": self.language_id,
153+
}
154+
155+
if self.stdin:
156+
body["stdin"] = b64encode(self.stdin).decode()
157+
if self.expected_output:
158+
body["expected_output"] = b64encode(self.expected_output).decode()
159+
160+
for field in EXTRA_REQUEST_FIELDS:
161+
value = getattr(self, field)
162+
if value is not None:
163+
body[field] = value
164+
165+
resp = client.session.post(
166+
f"{client.endpoint}/submissions",
167+
headers=headers,
168+
params=params,
169+
json=body,
170+
)
171+
resp.raise_for_status()
172+
173+
self.set_properties(resp.json())
174+
175+
def set_properties(self, d):
176+
for key, value in d.items():
177+
if key in ENCODED_FIELDS:
178+
setattr(self, key, b64decode(value.encode()) if value else None)
179+
else:
180+
setattr(self, key, value)
181+
182+
183+
class SingleFileSubmission(Submission):
184+
def __init__(
185+
self,
186+
source_code: str = None,
187+
language_id: int = None,
188+
additional_files=None,
189+
**kwargs,
190+
):
191+
if source_code is None:
192+
raise ValueError(
193+
"Argument source_code should not be None for SingleFileSubmission."
194+
)
195+
196+
if language_id is None:
197+
raise ValueError(
198+
"Argument language_id should not be None for SingleFileSubmission."
199+
)
200+
201+
super().__init__(source_code, language_id, additional_files, **kwargs)
202+
203+
204+
class MultiFileSubmission(Submission):
205+
def __init__(
206+
self,
207+
source_code: str = None,
208+
language_id: int = 89,
209+
additional_files=None,
210+
**kwargs,
211+
):
212+
if additional_files is None:
213+
raise ValueError(
214+
"Argument additional_files should not be None for MultiFileSubmission."
215+
)
216+
217+
super().__init__(source_code, language_id, additional_files, **kwargs)

0 commit comments

Comments
 (0)