Skip to content

Commit e797c72

Browse files
committedDec 9, 2024·
Make Submissions, File, Filesystem work with pydantic's BaseModel.
1 parent 13703bc commit e797c72

File tree

2 files changed

+60
-130
lines changed

2 files changed

+60
-130
lines changed
 

‎src/judge0/filesystem.py

+20-24
Original file line numberDiff line numberDiff line change
@@ -5,31 +5,36 @@
55
from base64 import b64decode, b64encode
66
from typing import Optional, Union
77

8+
from pydantic import BaseModel
9+
810
from .base_types import Iterable
911

1012

11-
class File:
12-
def __init__(self, name: str, content: Optional[Union[str, bytes]] = None):
13-
self.name = name
13+
class File(BaseModel):
14+
name: str
15+
content: Optional[Union[str, bytes]] = None
1416

17+
def __init__(self, **data):
18+
super().__init__(**data)
1519
# Let's keep content attribute internally encoded as bytes.
16-
if isinstance(content, str):
17-
self.content = content.encode()
18-
elif isinstance(content, bytes):
19-
self.content = content
20+
if isinstance(self.content, str):
21+
self.content = self.content.encode()
22+
elif isinstance(self.content, bytes):
23+
self.content = self.content
2024
else:
2125
self.content = b""
2226

2327
def __str__(self):
2428
return self.content.decode(errors="backslashreplace")
2529

2630

27-
class Filesystem:
28-
def __init__(
29-
self,
30-
content: Optional[Union[str, bytes, File, Iterable[File], "Filesystem"]] = None,
31-
):
32-
self.files: list[File] = []
31+
class Filesystem(BaseModel):
32+
files: list[File] = []
33+
34+
def __init__(self, **data):
35+
content = data.pop("content", None)
36+
super().__init__(**data)
37+
self.files = []
3338

3439
if isinstance(content, (bytes, str)):
3540
if isinstance(content, bytes):
@@ -40,15 +45,15 @@ def __init__(
4045
with zipfile.ZipFile(io.BytesIO(zip_bytes), "r") as zip_file:
4146
for file_name in zip_file.namelist():
4247
with zip_file.open(file_name) as fp:
43-
self.files.append(File(file_name, fp.read()))
48+
self.files.append(File(name=file_name, content=fp.read()))
4449
elif isinstance(content, Iterable):
4550
self.files = list(content)
4651
elif isinstance(content, File):
4752
self.files = [content]
4853
elif isinstance(content, Filesystem):
4954
self.files = copy.deepcopy(content.files)
5055
elif content is None:
51-
pass
56+
self.files = []
5257
else:
5358
raise ValueError(
5459
"Unsupported type for content argument. Expected "
@@ -67,15 +72,6 @@ def encode(self) -> bytes:
6772
zip_file.writestr(file.name, file.content)
6873
return zip_buffer.getvalue()
6974

70-
def to_dict(self) -> dict:
71-
"""Pack the Filesystem object to a dictionary."""
72-
return {"filesystem": str(self)}
73-
74-
@staticmethod
75-
def from_dict(filesystem_dict: dict) -> "Filesystem":
76-
"""Create a Filesystem object from dictionary."""
77-
return Filesystem(filesystem_dict.get("filesystem"))
78-
7975
def __str__(self) -> str:
8076
"""Create string representation of Filesystem object."""
8177
return b64encode(self.encode()).decode()

‎src/judge0/submission.py

+40-106
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,11 @@
22
from datetime import datetime
33
from typing import Any, Optional, Union
44

5-
from judge0.filesystem import Filesystem
5+
from pydantic import BaseModel
66

77
from .base_types import Iterable, LanguageAlias, Status
88
from .common import decode, encode
9+
from .filesystem import Filesystem
910

1011
ENCODED_REQUEST_FIELDS = {
1112
"source_code",
@@ -65,7 +66,7 @@
6566
Submissions = Iterable["Submission"]
6667

6768

68-
class Submission:
69+
class Submission(BaseModel):
6970
"""
7071
Stores a representation of a Submission to/from Judge0.
7172
@@ -125,72 +126,42 @@ class Submission:
125126
URL for a callback to report execution results or status.
126127
"""
127128

128-
def __init__(
129-
self,
130-
*,
131-
source_code: Optional[str] = None,
132-
language: Union[LanguageAlias, int] = LanguageAlias.PYTHON,
133-
additional_files: Optional[str] = None,
134-
compiler_options: Optional[str] = None,
135-
command_line_arguments: Optional[str] = None,
136-
stdin: Optional[str] = None,
137-
expected_output: Optional[str] = None,
138-
cpu_time_limit: Optional[float] = None,
139-
cpu_extra_time: Optional[float] = None,
140-
wall_time_limit: Optional[float] = None,
141-
memory_limit: Optional[float] = None,
142-
stack_limit: Optional[int] = None,
143-
max_processes_and_or_threads: Optional[int] = None,
144-
enable_per_process_and_thread_time_limit: Optional[bool] = None,
145-
enable_per_process_and_thread_memory_limit: Optional[bool] = None,
146-
max_file_size: Optional[int] = None,
147-
redirect_stderr_to_stdout: Optional[bool] = None,
148-
enable_network: Optional[bool] = None,
149-
number_of_runs: Optional[int] = None,
150-
callback_url: Optional[str] = None,
151-
):
152-
self.source_code = source_code
153-
self.language = language
154-
self.additional_files = additional_files
155-
156-
# Extra pre-execution submission attributes.
157-
self.compiler_options = compiler_options
158-
self.command_line_arguments = command_line_arguments
159-
self.stdin = stdin
160-
self.expected_output = expected_output
161-
self.cpu_time_limit = cpu_time_limit
162-
self.cpu_extra_time = cpu_extra_time
163-
self.wall_time_limit = wall_time_limit
164-
self.memory_limit = memory_limit
165-
self.stack_limit = stack_limit
166-
self.max_processes_and_or_threads = max_processes_and_or_threads
167-
self.enable_per_process_and_thread_time_limit = (
168-
enable_per_process_and_thread_time_limit
169-
)
170-
self.enable_per_process_and_thread_memory_limit = (
171-
enable_per_process_and_thread_memory_limit
172-
)
173-
self.max_file_size = max_file_size
174-
self.redirect_stderr_to_stdout = redirect_stderr_to_stdout
175-
self.enable_network = enable_network
176-
self.number_of_runs = number_of_runs
177-
self.callback_url = callback_url
178-
179-
# Post-execution submission attributes.
180-
self.stdout: Optional[str] = None
181-
self.stderr: Optional[str] = None
182-
self.compile_output: Optional[str] = None
183-
self.message: Optional[str] = None
184-
self.exit_code: Optional[int] = None
185-
self.exit_signal: Optional[int] = None
186-
self.status: Optional[Status] = None
187-
self.created_at: Optional[datetime] = None
188-
self.finished_at: Optional[datetime] = None
189-
self.token: str = ""
190-
self.time: Optional[float] = None
191-
self.wall_time: Optional[float] = None
192-
self.memory: Optional[float] = None
193-
self.post_execution_filesystem: Optional[Filesystem] = None
129+
source_code: Optional[str] = None
130+
language: Union[LanguageAlias, int] = LanguageAlias.PYTHON
131+
additional_files: Optional[str] = None
132+
compiler_options: Optional[str] = None
133+
command_line_arguments: Optional[str] = None
134+
stdin: Optional[str] = None
135+
expected_output: Optional[str] = None
136+
cpu_time_limit: Optional[float] = None
137+
cpu_extra_time: Optional[float] = None
138+
wall_time_limit: Optional[float] = None
139+
memory_limit: Optional[float] = None
140+
stack_limit: Optional[int] = None
141+
max_processes_and_or_threads: Optional[int] = None
142+
enable_per_process_and_thread_time_limit: Optional[bool] = None
143+
enable_per_process_and_thread_memory_limit: Optional[bool] = None
144+
max_file_size: Optional[int] = None
145+
redirect_stderr_to_stdout: Optional[bool] = None
146+
enable_network: Optional[bool] = None
147+
number_of_runs: Optional[int] = None
148+
callback_url: Optional[str] = None
149+
150+
# Post-execution submission attributes.
151+
stdout: Optional[str] = None
152+
stderr: Optional[str] = None
153+
compile_output: Optional[str] = None
154+
message: Optional[str] = None
155+
exit_code: Optional[int] = None
156+
exit_signal: Optional[int] = None
157+
status: Optional[Status] = None
158+
created_at: Optional[datetime] = None
159+
finished_at: Optional[datetime] = None
160+
token: str = ""
161+
time: Optional[float] = None
162+
wall_time: Optional[float] = None
163+
memory: Optional[float] = None
164+
post_execution_filesystem: Optional[Filesystem] = None
194165

195166
def set_attributes(self, attributes: dict[str, Any]) -> None:
196167
"""Set Submissions attributes while taking into account different
@@ -215,7 +186,7 @@ def set_attributes(self, attributes: dict[str, Any]) -> None:
215186
elif attr in FLOATING_POINT_FIELDS and value is not None:
216187
value = float(value)
217188
elif attr == "post_execution_filesystem":
218-
value = Filesystem(value)
189+
value = Filesystem(content=value)
219190

220191
setattr(self, attr, value)
221192

@@ -240,43 +211,6 @@ def as_body(self, client: "Client") -> dict:
240211

241212
return body
242213

243-
def to_dict(self) -> dict:
244-
encoded_request_fields = {
245-
field_name: encode(getattr(self, field_name))
246-
for field_name in ENCODED_REQUEST_FIELDS
247-
if getattr(self, field_name) is not None
248-
}
249-
extra_request_fields = {
250-
field_name: getattr(self, field_name)
251-
for field_name in EXTRA_REQUEST_FIELDS
252-
if getattr(self, field_name) is not None
253-
}
254-
encoded_response_fields = {
255-
field_name: encode(getattr(self, field_name))
256-
for field_name in ENCODED_RESPONSE_FIELDS
257-
if getattr(self, field_name) is not None
258-
}
259-
extra_response_fields = {
260-
field_name: getattr(self, field_name)
261-
for field_name in EXTRA_RESPONSE_FIELDS
262-
if getattr(self, field_name) is not None
263-
}
264-
265-
submission_dict = (
266-
encoded_request_fields
267-
| extra_request_fields
268-
| encoded_response_fields
269-
| extra_response_fields
270-
)
271-
272-
return submission_dict
273-
274-
@staticmethod
275-
def from_dict(submission_dict) -> "Submission":
276-
submission = Submission()
277-
submission.set_attributes(submission_dict)
278-
return submission
279-
280214
def is_done(self) -> bool:
281215
"""Check if submission is finished processing.
282216

0 commit comments

Comments
 (0)
Please sign in to comment.