Skip to content

Commit 85db18e

Browse files
authored
Merge pull request #330 from gkreitz/misc_small_fixes
A few small fixes / extra checks
2 parents e651249 + a8e80b1 commit 85db18e

File tree

5 files changed

+38
-16
lines changed

5 files changed

+38
-16
lines changed

admin/docker/Dockerfile.full

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
# PROBLEMTOOLS_VERSION but this is not checked.)
1111

1212
ARG PROBLEMTOOLS_VERSION=develop
13-
FROM problemtools/runreqs:${PROBLEMTOOLS_VERSION}
13+
FROM problemtools/fulllangs:${PROBLEMTOOLS_VERSION}
1414

1515
LABEL maintainer="contact@kattis.com"
1616
ENV DEBIAN_FRONTEND=noninteractive

problemtools/ProblemPlasTeX/ProblemsetMacros.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -114,7 +114,7 @@ def invoke(self, tex):
114114
f = self.attributes['file']
115115
ext = self.ownerDocument.userdata.getPath('packages/graphicx/extensions', ['.png', '.jpg', '.jpeg', '.gif', '.pdf'])
116116
paths = self.ownerDocument.userdata.getPath('packages/graphicx/paths', [os.path.dirname(basetex.filename)])
117-
img = None
117+
img: str | None = None
118118
# Check for file using graphicspath
119119
for p in paths:
120120
for e in [''] + ext:
@@ -134,7 +134,7 @@ def invoke(self, tex):
134134
except (OSError, IOError):
135135
pass
136136

137-
if not os.path.isfile(img):
137+
if img is None or not os.path.isfile(img):
138138
log.warning('Could not identify image "%s"' % f)
139139

140140
self.imageoverride = img

problemtools/md2html.py

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -23,11 +23,12 @@ def convert(problem_root: Path, options: argparse.Namespace, statement_file: Pat
2323
options: command-line arguments. See problem2html.py
2424
"""
2525
destfile = string.Template(options.destfile).safe_substitute(problem=problem_root.name)
26+
imgbasedir = string.Template(options.imgbasedir).safe_substitute(problem=problem_root.name)
2627

2728
command = ['pandoc', str(statement_file), '-t', 'html', '--mathjax']
2829
statement_html = subprocess.run(command, capture_output=True, text=True, shell=False, check=True).stdout
2930

30-
statement_html = sanitize_html(statement_file.parent, statement_html)
31+
statement_html = sanitize_html(statement_file.parent, statement_html, imgbasedir)
3132

3233
templatepaths = [
3334
os.path.join(os.path.dirname(__file__), 'templates/markdown_html'),
@@ -76,7 +77,7 @@ def convert(problem_root: Path, options: argparse.Namespace, statement_file: Pat
7677
return True
7778

7879

79-
def sanitize_html(statement_dir: Path, statement_html: str) -> str:
80+
def sanitize_html(statement_dir: Path, statement_html: str, imgbasedir: str) -> str:
8081
# Allow footnote ids (the anchor points you jump to)
8182
def is_fn_id(s):
8283
pattern_id_top = r'^fn\d+$'
@@ -106,7 +107,7 @@ def attribute_filter(tag, attribute, value):
106107
nonlocal image_fail_reason
107108
image_fail_reason.append(e)
108109
return None
109-
return copy_image(statement_dir, value)
110+
return copy_image(statement_dir, value, imgbasedir)
110111
return None
111112

112113
statement_html = nh3.clean(
@@ -132,7 +133,7 @@ def attribute_filter(tag, attribute, value):
132133
return statement_html
133134

134135

135-
def copy_image(statement_dir: Path, img_src: str) -> str:
136+
def copy_image(statement_dir: Path, img_src: str, imgbasedir: str) -> str:
136137
"""Copy image to working directory (with new filename) and returns the new filename
137138
138139
Args:
@@ -147,4 +148,4 @@ def copy_image(statement_dir: Path, img_src: str) -> str:
147148

148149
if not os.path.isfile(filename): # check if already copied
149150
shutil.copyfile(statement_dir / img_src, filename)
150-
return filename
151+
return imgbasedir + filename

problemtools/verifyproblem.py

Lines changed: 19 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -867,6 +867,10 @@ def check(self, context: Context) -> bool:
867867
if self._metadata.uuid is None:
868868
self.error_in_2023_07(f'Missing uuid from problem.yaml. Add "uuid: {uuid.uuid4()}" to problem.yaml.')
869869

870+
names_with_no_statement = [lang for lang in self._metadata.name if lang not in self.problem.statement.statements]
871+
if names_with_no_statement:
872+
self.error(f'Names exist for languages without problem statements: {", ".join(names_with_no_statement)}')
873+
870874
if self._metadata.legacy_grading.show_test_data_groups and self.problem.is_pass_fail():
871875
self.error('Showing test data groups is only supported for scoring problems, this is a pass-fail problem')
872876
if (
@@ -1210,6 +1214,11 @@ def setup(self):
12101214
)
12111215
self._has_precompiled = False
12121216

1217+
def uses_default_validator(self) -> bool:
1218+
if self.problem.format is FormatVersion.LEGACY:
1219+
return self.problem.metadata.legacy_validation == 'default'
1220+
return not self._validators
1221+
12131222
def __str__(self) -> str:
12141223
return 'output validators'
12151224

@@ -1234,12 +1243,15 @@ def check(self, context: Context) -> bool:
12341243
f'Output validator in {v.language.name}. Only {safe_output_validator_languages} are standardized. Check carefully if your CCS supports more (Kattis does not).'
12351244
)
12361245

1237-
if self.problem.metadata.legacy_validation == 'default' and self._validators:
1246+
if len(self._validators) > 1:
1247+
self.error_in_2023_07('Found more than one output validator. This was allowed in legacy (but not on Kattis)')
1248+
1249+
if self.uses_default_validator() and self._validators:
12381250
self.error('There are validator programs but problem.yaml has validation = "default"')
1239-
elif self.problem.metadata.legacy_validation.startswith('custom') and not self._validators:
1251+
elif not self.uses_default_validator() and not self._validators:
12401252
self.fatal('problem.yaml specifies custom validator but no validator programs found')
12411253

1242-
if self.problem.metadata.legacy_validation == 'default' and self._default_validator is None:
1254+
if self.uses_default_validator() and self._default_validator is None:
12431255
self.fatal('Unable to locate default validator')
12441256

12451257
for val in self._validators[:]:
@@ -1332,10 +1344,9 @@ def _parse_validator_results(self, val, status: int, feedbackdir, testcase: Test
13321344
return SubmissionResult('AC', score=score)
13331345

13341346
def _actual_validators(self) -> list:
1335-
vals = self._validators
1336-
if self.problem.metadata.legacy_validation == 'default' or (self.problem.format is FormatVersion.V_2023_07 and not vals):
1337-
vals = [self._default_validator]
1338-
return [val for val in vals if val is not None]
1347+
if self.uses_default_validator():
1348+
return [self._default_validator]
1349+
return self._validators
13391350

13401351
def validate_interactive(self, testcase: TestCase, submission, timelim: int, errorhandler: Submissions) -> SubmissionResult:
13411352
# This may be called off-main thread.
@@ -1401,7 +1412,6 @@ def validate_interactive(self, testcase: TestCase, submission, timelim: int, err
14011412
shutil.rmtree(feedbackdir)
14021413
if res.verdict != 'AC':
14031414
return res
1404-
# TODO: check that all output validators give same result
14051415
return res
14061416

14071417
def validate(self, testcase: TestCase, submission_output: str) -> SubmissionResult:
@@ -1800,6 +1810,7 @@ def load(self) -> None:
18001810
self.graders = Graders(self)
18011811
self.testdata = TestCaseGroup(self, os.path.join(self.probdir, 'data'))
18021812
self.submissions = Submissions(self)
1813+
self.loaded = True
18031814

18041815
def __enter__(self) -> Problem:
18051816
self.tmpdir = tempfile.mkdtemp(prefix=f'verify-{self.shortname}-')

tests/test_verify_hello.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,3 +21,13 @@ def test_load_hello():
2121
assert not p.is_interactive()
2222
assert not p.is_multi_pass()
2323
assert not p.is_submit_answer()
24+
25+
26+
def test_load_twice():
27+
directory = pathlib.Path(__file__).parent / 'hello'
28+
string = str(directory.resolve())
29+
30+
args = verify.argparser().parse_args([string])
31+
with verify.Problem(string, args) as p:
32+
p.load()
33+
p.load()

0 commit comments

Comments
 (0)