Skip to content

Commit 672df77

Browse files
committed
Add Filesystem abstraction.
1 parent a3eed7d commit 672df77

File tree

6 files changed

+136
-2
lines changed

6 files changed

+136
-2
lines changed

examples/0005_filesystem.py

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
import judge0
2+
from judge0 import File, Filesystem
3+
4+
print("Subexample 1")
5+
result = judge0.run(source_code="print('hello, world')")
6+
fs = Filesystem(result.post_execution_filesystem)
7+
for f in fs:
8+
print(f.name)
9+
print(f)
10+
print()
11+
12+
13+
print("Subexample 2")
14+
fs = Filesystem(File("my_file.txt", "hello, world"))
15+
result = judge0.run(
16+
source_code="print(open('my_file.txt').read())", additional_files=fs
17+
)
18+
print(result.stdout)
19+
for f in Filesystem(result.post_execution_filesystem):
20+
print(f.name)
21+
print(f)
22+
print()
23+
24+
25+
print("Subexample 3")
26+
fs = Filesystem(File("my_file.txt", "hello, world"))
27+
result = judge0.run(
28+
source_code="print(open('my_file.txt').read())", additional_files=fs
29+
)
30+
print(result.stdout)
31+
for f in result.post_execution_filesystem:
32+
print(f.name)
33+
print(f)
34+
print()
35+
36+
print("Subexample 4")
37+
fs = Filesystem(
38+
[
39+
File("my_file.txt", "hello, world"),
40+
File("./dir1/dir2/dir3/my_file2.txt", "hello, world2"),
41+
]
42+
)
43+
result = judge0.run(source_code="find .", additional_files=fs, language=46)
44+
print(result.stdout)
45+
for f in Filesystem(result.post_execution_filesystem):
46+
print(f.name)
47+
print(f)
48+
print()

src/judge0/__init__.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
SuluJudge0CE,
1515
SuluJudge0ExtraCE,
1616
)
17+
from .filesystem import File, Filesystem
1718
from .retry import MaxRetries, MaxWaitTime, RegularPeriodRetry
1819
from .submission import Submission
1920

@@ -22,6 +23,8 @@
2223
"ATDJudge0CE",
2324
"ATDJudge0ExtraCE",
2425
"Client",
26+
"File",
27+
"Filesystem",
2528
"Language",
2629
"LanguageAlias",
2730
"MaxRetries",

src/judge0/base_types.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,14 @@
1+
from abc import ABC, abstractmethod
12
from dataclasses import dataclass
23
from enum import IntEnum
34

45

6+
class Encodeable(ABC):
7+
@abstractmethod
8+
def encode(self) -> bytes:
9+
pass
10+
11+
512
@dataclass
613
class Language:
714
id: int

src/judge0/common.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,16 @@
11
from base64 import b64decode, b64encode
22
from typing import Union
33

4+
from .base_types import Encodeable
45

5-
def encode(content: Union[bytes, str]) -> str:
6+
7+
def encode(content: Union[bytes, str, Encodeable]) -> str:
68
if isinstance(content, bytes):
79
return b64encode(content).decode()
810
if isinstance(content, str):
911
return b64encode(content.encode()).decode()
12+
if isinstance(content, Encodeable):
13+
return b64encode(content.encode()).decode()
1014
raise ValueError(f"Unsupported type. Expected bytes or str, got {type(content)}!")
1115

1216

src/judge0/filesystem.py

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
import copy
2+
import io
3+
import zipfile
4+
5+
from base64 import b64decode, b64encode
6+
from collections import abc
7+
from typing import Iterable, Optional, Union
8+
9+
from .base_types import Encodeable
10+
11+
12+
class File:
13+
def __init__(self, name: str, content: Optional[Union[str, bytes]] = None):
14+
self.name = name
15+
16+
# Let's keep content attribute internally encoded as bytes.
17+
if isinstance(content, str):
18+
self.content = content.encode()
19+
elif isinstance(content, bytes):
20+
self.content = content
21+
else:
22+
self.content = b""
23+
24+
def __str__(self):
25+
return self.content.decode(errors="backslashreplace")
26+
27+
28+
class Filesystem(Encodeable):
29+
def __init__(
30+
self,
31+
content: Optional[Union[str, bytes, File, Iterable[File], "Filesystem"]] = None,
32+
):
33+
self.files: list[File] = []
34+
35+
if isinstance(content, (bytes, str)):
36+
if isinstance(content, bytes):
37+
zip_bytes = content
38+
else:
39+
zip_bytes = b64decode(content.encode())
40+
41+
with zipfile.ZipFile(io.BytesIO(zip_bytes), "r") as zip_file:
42+
for file_name in zip_file.namelist():
43+
with zip_file.open(file_name) as fp:
44+
self.files.append(File(file_name, fp.read()))
45+
elif isinstance(content, abc.Iterable):
46+
self.files = list(content)
47+
elif isinstance(content, File):
48+
self.files = [content]
49+
elif isinstance(content, Filesystem):
50+
self.files = copy.deepcopy(content.files)
51+
52+
def encode(self) -> bytes:
53+
zip_buffer = io.BytesIO()
54+
with zipfile.ZipFile(zip_buffer, "w") as zip_file:
55+
for file in self.files:
56+
zip_file.writestr(file.name, file.content)
57+
return zip_buffer.getvalue()
58+
59+
def __str__(self) -> str:
60+
return b64encode(self.encode()).decode()
61+
62+
def __iter__(self):
63+
return iter(self.files)

src/judge0/submission.py

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
from datetime import datetime
22
from typing import Union
33

4+
from judge0.filesystem import Filesystem
5+
46
from .base_types import LanguageAlias, Status
57
from .common import decode, encode
68

@@ -10,7 +12,12 @@
1012
"stdin",
1113
"expected_output",
1214
}
13-
ENCODED_RESPONSE_FIELDS = {"stdout", "stderr", "compile_output"}
15+
ENCODED_RESPONSE_FIELDS = {
16+
"stdout",
17+
"stderr",
18+
"compile_output",
19+
# "post_execution_filesystem",
20+
}
1421
ENCODED_FIELDS = ENCODED_REQUEST_FIELDS | ENCODED_RESPONSE_FIELDS
1522
EXTRA_REQUEST_FIELDS = {
1623
"compiler_options",
@@ -141,6 +148,8 @@ def set_attributes(self, attributes):
141148
value = datetime.strptime(value, "%Y-%m-%dT%H:%M:%S.%fZ")
142149
elif attr in FLOATING_POINT_FIELDS and value is not None:
143150
value = float(value)
151+
elif attr == "post_execution_filesystem":
152+
value = Filesystem(value)
144153

145154
setattr(self, attr, value)
146155

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy