mlsec-raid 0.3.0__tar.gz → 0.4.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.3
|
|
2
2
|
Name: mlsec-raid
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.4.0
|
|
4
4
|
Summary: Client for the RAID competition system
|
|
5
5
|
Requires-Python: >=3.9
|
|
6
6
|
Description-Content-Type: text/markdown
|
|
@@ -15,9 +15,13 @@ export RAID_KEY=<your-api-key> # or pass --api-key <y
|
|
|
15
15
|
|
|
16
16
|
task-fetch <challenge> # downloads the challenge into ./<challenge>/
|
|
17
17
|
task-check <challenge> # scores it locally with scoring/score.py on data/
|
|
18
|
-
task-submit <challenge> # submits ./<challenge>/ without data/, scoring/ and .raidignore patterns
|
|
18
|
+
task-submit <challenge> # submits ./<challenge>/ without data/, scoring/ and .raidignore patterns
|
|
19
|
+
task-show # lists your jobs with their state (pending, running, success, failed) and score
|
|
20
|
+
task-log <job> # prints the log of a finished job, including the errors of your solution
|
|
19
21
|
```
|
|
20
22
|
|
|
23
|
+
Every command also works as `task <command>`, e.g. `task submit <challenge>`.
|
|
24
|
+
|
|
21
25
|
The same is available in Python: `Raid(api_key).fetch(...)`, `.check(...)`, `.submit(...)`, `.push(...)`, `.wait(id)`, `.status()`, `.log(id)`, `.howto()`.
|
|
22
26
|
|
|
23
27
|
Challenge authors: `task-check <challenge> --hidden --reference` scores `reference/` on `hidden/`. Admins upload a challenge directory with `task-push <directory>` and their own API key (created in the admin console): the directory's `challenge.yaml` sets the name and version (and the wave, baseline and badge of a new challenge), and the upload becomes the active version.
|
|
@@ -8,9 +8,13 @@ export RAID_KEY=<your-api-key> # or pass --api-key <y
|
|
|
8
8
|
|
|
9
9
|
task-fetch <challenge> # downloads the challenge into ./<challenge>/
|
|
10
10
|
task-check <challenge> # scores it locally with scoring/score.py on data/
|
|
11
|
-
task-submit <challenge> # submits ./<challenge>/ without data/, scoring/ and .raidignore patterns
|
|
11
|
+
task-submit <challenge> # submits ./<challenge>/ without data/, scoring/ and .raidignore patterns
|
|
12
|
+
task-show # lists your jobs with their state (pending, running, success, failed) and score
|
|
13
|
+
task-log <job> # prints the log of a finished job, including the errors of your solution
|
|
12
14
|
```
|
|
13
15
|
|
|
16
|
+
Every command also works as `task <command>`, e.g. `task submit <challenge>`.
|
|
17
|
+
|
|
14
18
|
The same is available in Python: `Raid(api_key).fetch(...)`, `.check(...)`, `.submit(...)`, `.push(...)`, `.wait(id)`, `.status()`, `.log(id)`, `.howto()`.
|
|
15
19
|
|
|
16
20
|
Challenge authors: `task-check <challenge> --hidden --reference` scores `reference/` on `hidden/`. Admins upload a challenge directory with `task-push <directory>` and their own API key (created in the admin console): the directory's `challenge.yaml` sets the name and version (and the wave, baseline and badge of a new challenge), and the upload becomes the active version.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "mlsec-raid"
|
|
3
|
-
version = "0.
|
|
3
|
+
version = "0.4.0"
|
|
4
4
|
description = "Client for the RAID competition system"
|
|
5
5
|
readme = "README.md"
|
|
6
6
|
requires-python = ">=3.9"
|
|
@@ -10,7 +10,10 @@ dependencies = []
|
|
|
10
10
|
task-fetch = "raid.cli:fetch"
|
|
11
11
|
task-check = "raid.cli:check"
|
|
12
12
|
task-submit = "raid.cli:submit"
|
|
13
|
+
task-show = "raid.cli:show"
|
|
14
|
+
task-log = "raid.cli:log"
|
|
13
15
|
task-push = "raid.cli:push"
|
|
16
|
+
task = "raid.cli:main"
|
|
14
17
|
|
|
15
18
|
[build-system]
|
|
16
19
|
requires = ["uv_build>=0.8.17,<0.9.0"]
|
|
@@ -15,7 +15,7 @@ import urllib.request
|
|
|
15
15
|
from pathlib import Path
|
|
16
16
|
|
|
17
17
|
__all__ = ["Raid", "RaidError"]
|
|
18
|
-
__version__ = "0.
|
|
18
|
+
__version__ = "0.4.0"
|
|
19
19
|
|
|
20
20
|
DEFAULT_URL = "https://raid.mlsec.tu-berlin.de"
|
|
21
21
|
PARTS = ("source", "data", "scoring") # source/ is unpacked into the challenge directory itself
|
|
@@ -110,7 +110,7 @@ class Raid:
|
|
|
110
110
|
"POST", f"/api/submit/{challenge}", data=f, timeout=900,
|
|
111
111
|
headers={"Content-Type": "application/gzip", "Content-Length": str(size)}) as r:
|
|
112
112
|
result = json.load(r)
|
|
113
|
-
print(
|
|
113
|
+
print("Job submitted!\nRun task-show to see the status of your jobs.")
|
|
114
114
|
return result["id"]
|
|
115
115
|
|
|
116
116
|
def push(self, directory):
|
|
@@ -149,20 +149,27 @@ class Raid:
|
|
|
149
149
|
return sub
|
|
150
150
|
|
|
151
151
|
def status(self, challenge=None):
|
|
152
|
-
"""Print your submissions
|
|
152
|
+
"""Print your jobs (submissions, newest first) and return them as a list of dicts."""
|
|
153
153
|
query = f"?challenge={_check(challenge)}" if challenge else ""
|
|
154
154
|
subs = self._json(f"/api/submissions{query}")["submissions"]
|
|
155
|
-
print(f"{'
|
|
155
|
+
print(f"{'JOB':>6} {'CHALLENGE':<20} {'VERSION':<8} {'STATE':<8} {'SCORE':>10} SUBMITTED")
|
|
156
156
|
for s in subs:
|
|
157
157
|
score = "" if s["score"] is None else f"{s['score']:.4f}"
|
|
158
158
|
print(f"{s['id']:>6} {s['challenge']:<20} {s['version']:<8} {s['status']:<8} {score:>10} {s['submitted_at'][:19]}")
|
|
159
159
|
if not subs:
|
|
160
|
-
print("No
|
|
160
|
+
print("No jobs yet.")
|
|
161
|
+
else:
|
|
162
|
+
print("Run task-log <job> to see the log of a finished job.")
|
|
161
163
|
return subs
|
|
162
164
|
|
|
163
165
|
def log(self, submission_id):
|
|
164
|
-
"""Return the scoring log of one of your submissions."""
|
|
165
|
-
|
|
166
|
+
"""Return the scoring log of one of your finished submissions: the output and errors of the scoring run."""
|
|
167
|
+
if not str(submission_id).isdigit():
|
|
168
|
+
raise RaidError(f"Invalid job id {submission_id!r}. task-show lists your jobs.")
|
|
169
|
+
sub = self._json(f"/api/submissions/{submission_id}")
|
|
170
|
+
if sub["status"] in ("pending", "running"):
|
|
171
|
+
raise RaidError(f"Job {sub['id']} is {sub['status']}. Its log is available once it has finished.")
|
|
172
|
+
return sub["log"] or "No log output."
|
|
166
173
|
|
|
167
174
|
def _request(self, method, endpoint, data=None, headers=None, timeout=60):
|
|
168
175
|
if not self.api_key:
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
"""Command line tools: task-fetch, task-check, task-submit and task-push."""
|
|
1
|
+
"""Command line tools: task-fetch, task-check, task-submit, task-show, task-log and task-push (also as `task <command>`)."""
|
|
2
2
|
import argparse
|
|
3
3
|
import sys
|
|
4
4
|
|
|
@@ -11,7 +11,8 @@ def _run(description, action, key=True, options=(), target=("challenge", "challe
|
|
|
11
11
|
parser.add_argument("--api-key", help="your API key (default: $RAID_KEY)")
|
|
12
12
|
for flag, text in options:
|
|
13
13
|
parser.add_argument(flag, action="store_true", help=text)
|
|
14
|
-
|
|
14
|
+
if target:
|
|
15
|
+
parser.add_argument(target[0], help=target[1])
|
|
15
16
|
args = parser.parse_args()
|
|
16
17
|
try:
|
|
17
18
|
action(Raid(getattr(args, "api_key", None)), args)
|
|
@@ -31,16 +32,30 @@ def check():
|
|
|
31
32
|
|
|
32
33
|
|
|
33
34
|
def submit():
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
35
|
+
_run("Submit ./<challenge>/ for scoring (without data/, scoring/ and .raidignore patterns).",
|
|
36
|
+
lambda raid, a: raid.submit(a.challenge))
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def show():
|
|
40
|
+
_run("List your jobs (submissions) with their state and score.", lambda raid, a: raid.status(), target=None)
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def log():
|
|
44
|
+
_run("Print the log of a finished job: the output and errors of its scoring run.",
|
|
45
|
+
lambda raid, a: print(raid.log(a.job)), target=("job", "job id, see task-show"))
|
|
41
46
|
|
|
42
47
|
|
|
43
48
|
def push():
|
|
44
49
|
_run("Admins: upload a challenge directory (without reference/) as its new active version. "
|
|
45
50
|
"challenge.yaml supplies the name, the version and the metadata of a new challenge.",
|
|
46
51
|
lambda raid, a: raid.push(a.directory), target=("directory", "challenge directory, e.g. self-assessment"))
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
def main():
|
|
55
|
+
"""`task submit <challenge>` is the same as `task-submit <challenge>`."""
|
|
56
|
+
commands = {"fetch": fetch, "check": check, "submit": submit, "show": show, "log": log, "push": push}
|
|
57
|
+
if len(sys.argv) < 2 or sys.argv[1] not in commands:
|
|
58
|
+
sys.exit("Usage: task {" + ",".join(commands) + "} ...")
|
|
59
|
+
command = sys.argv.pop(1)
|
|
60
|
+
sys.argv[0] = f"task {command}"
|
|
61
|
+
commands[command]()
|