Skip to content

Commit abb49f8

Browse files
committed
Allow parsing Person as map of name/email/kattis/orcid keys
See Kattis/problem-package-format#415 I'm not a big fan of the partially-copied parse_optional_list_persons, but I fought the type checker for long enough to say this is better than some unreadable abstraction 😛
1 parent 30b7a27 commit abb49f8

File tree

3 files changed

+56
-24
lines changed

3 files changed

+56
-24
lines changed

bin/problem.py

Lines changed: 39 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -34,18 +34,21 @@ def check_unknown_keys(yaml_data: dict[str, Any], sub_key: Optional[str] = None)
3434

3535

3636
class Person:
37-
def __init__(self, name: str):
38-
match = re.match("(.*)<(.*)>", name)
39-
self.name: str = (match[1] if match else name).strip()
40-
self.email: Optional[str] = match[2].strip() if match else None
37+
def __init__(self, yaml_data: str | dict[str, Any]):
38+
if isinstance(yaml_data, dict):
39+
self.name: str = parse_setting(yaml_data, "name", "")
40+
self.email: Optional[str] = parse_optional_setting(yaml_data, "email", str)
41+
self.kattis: Optional[str] = parse_optional_setting(yaml_data, "kattis", str)
42+
self.orcid: Optional[str] = parse_optional_setting(yaml_data, "orcid", str)
43+
else:
44+
match = re.match("(.*)<(.*)>", yaml_data)
45+
self.name = (match[1] if match else yaml_data).strip()
46+
self.email = match[2].strip() if match else None
47+
self.kattis = self.orcid = None
4148

4249

4350
class ProblemCredits:
44-
def __init__(
45-
self,
46-
yaml_data: dict[str, Any],
47-
problem_settings: "ProblemSettings",
48-
):
51+
def __init__(self, yaml_data: dict[str, Any]):
4952
self.authors: list[Person] = []
5053
self.contributors: list[Person] = []
5154
self.testers: list[Person] = []
@@ -61,23 +64,36 @@ def __init__(
6164
return
6265

6366
credits = parse_setting(yaml_data, "credits", dict[str, Any]())
64-
self.authors = [Person(s) for s in parse_optional_list_setting(credits, "authors", str)]
65-
self.contributors = [
66-
Person(s) for s in parse_optional_list_setting(credits, "contributors", str)
67-
]
67+
self.authors = self.parse_optional_list_persons(credits, "authors")
68+
self.contributors = self.parse_optional_list_persons(credits, "contributors")
6869
self.translators = parse_setting(credits, "translators", {})
6970
for lang in list(self.translators.keys()):
70-
self.translators[lang] = [
71-
Person(s) for s in parse_optional_list_setting(self.translators, lang, str)
72-
]
73-
self.testers = [Person(s) for s in parse_optional_list_setting(credits, "testers", str)]
74-
self.packagers = [Person(s) for s in parse_optional_list_setting(credits, "packagers", str)]
75-
self.acknowledgements = [
76-
Person(s) for s in parse_optional_list_setting(credits, "acknowledgements", str)
77-
]
71+
self.translators[lang] = self.parse_optional_list_persons(self.translators, lang)
72+
self.testers = self.parse_optional_list_persons(credits, "testers")
73+
self.packagers = self.parse_optional_list_persons(credits, "packagers")
74+
self.acknowledgements = self.parse_optional_list_persons(credits, "acknowledgements")
7875

7976
check_unknown_keys(credits, "credits")
8077

78+
# Based on parse_optional_list_setting: the type checker does not like type unions like `str | dict`.
79+
@staticmethod
80+
def parse_optional_list_persons(yaml_data: dict[str, Any], key: str) -> list[Person]:
81+
if key in yaml_data:
82+
value = yaml_data.pop(key)
83+
if isinstance(value, str | dict):
84+
return [Person(value)]
85+
if isinstance(value, list):
86+
if not all(isinstance(v, str | dict) for v in value):
87+
warn(
88+
f"some values for key '{key}' in problem.yaml do not have type str or dict. SKIPPED."
89+
)
90+
return []
91+
if not value:
92+
warn(f"value for '{key}' in problem.yaml should not be an empty list.")
93+
return list(map(Person, value))
94+
warn(f"incompatible value for key '{key}' in problem.yaml. SKIPPED.")
95+
return []
96+
8197

8298
class ProblemSource:
8399
def __init__(self, name: str, url: Optional[str] = None):
@@ -253,7 +269,7 @@ def __init__(
253269
self.name[lang] = parse_setting(self.name, lang, "")
254270
self.uuid: str = parse_setting(yaml_data, "uuid", "")
255271
self.version: str = parse_setting(yaml_data, "version", "")
256-
self.credits: ProblemCredits = ProblemCredits(yaml_data, self)
272+
self.credits: ProblemCredits = ProblemCredits(yaml_data)
257273
self.source: ProblemSources = ProblemSources(yaml_data)
258274
self.license: str = parse_setting(yaml_data, "license", "unknown")
259275
self.rights_owner: Optional[str] = parse_optional_setting(yaml_data, "rights_owner", str)
@@ -271,7 +287,7 @@ def __init__(
271287
)
272288

273289
self.keywords: list[str] = parse_optional_list_setting(yaml_data, "keywords", str)
274-
# Not implemented in BAPCtools. We always test all languges in langauges.yaml.
290+
# Not implemented in BAPCtools. We always test all languages in languages.yaml.
275291
self.languages: list[str] = parse_optional_list_setting(yaml_data, "languages", str)
276292
# Not implemented in BAPCtools
277293
self.allow_file_writing: bool = parse_setting(yaml_data, "allow_file_writing", False)

test/yaml/problem/invalid.yaml

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -164,6 +164,14 @@ yaml:
164164
- name: Audrey Authorson
165165
email: bob@foo.bar
166166
warn: "incompatible value for key 'credits' in problem.yaml. SKIPPED."
167+
---
168+
yaml:
169+
problem_format_version: 2023-07-draft
170+
name: Cannot specify multiple authors in credits
171+
credits:
172+
authors:
173+
- name: 42
174+
warn: "incompatible value for key 'name' in problem.yaml. SKIPPED."
167175

168176
---
169177
# Source

test/yaml/problem/valid.yaml

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -105,13 +105,21 @@ eq:
105105
---
106106
yaml:
107107
problem_format_version: 2023-07-draft
108-
credits: A. U. Thor <author@example.com>
108+
credits:
109+
authors: A. U. Thor <author@example.com>
109110
eq:
110111
credits:
111112
authors:
112113
- name: A. U. Thor
113114
email: author@example.com
114115
---
116+
yaml:
117+
problem_format_version: 2023-07-draft
118+
credits:
119+
authors:
120+
- name: A. U. Thor
121+
email: author@example.com
122+
---
115123
yaml:
116124
problem_format_version: 2023-07-draft
117125
credits:

0 commit comments

Comments
 (0)