experimaestro 1.6.1__py3-none-any.whl → 1.7.0__py3-none-any.whl
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.
Potentially problematic release.
This version of experimaestro might be problematic. Click here for more details.
- experimaestro/__init__.py +3 -1
- experimaestro/annotations.py +13 -3
- experimaestro/cli/filter.py +3 -3
- experimaestro/cli/jobs.py +1 -1
- experimaestro/commandline.py +3 -7
- experimaestro/connectors/__init__.py +22 -10
- experimaestro/connectors/local.py +17 -8
- experimaestro/connectors/ssh.py +1 -1
- experimaestro/core/arguments.py +26 -3
- experimaestro/core/objects.py +90 -6
- experimaestro/core/objects.pyi +7 -1
- experimaestro/core/types.py +33 -2
- experimaestro/experiments/cli.py +21 -9
- experimaestro/generators.py +6 -1
- experimaestro/ipc.py +4 -1
- experimaestro/launcherfinder/registry.py +23 -5
- experimaestro/launchers/slurm/base.py +47 -9
- experimaestro/notifications.py +1 -1
- experimaestro/run.py +1 -1
- experimaestro/scheduler/base.py +102 -6
- experimaestro/scheduler/dynamic_outputs.py +184 -0
- experimaestro/scheduler/workspace.py +2 -1
- experimaestro/scriptbuilder.py +13 -2
- experimaestro/server/data/016b4a6cdced82ab3aa1.ttf +0 -0
- experimaestro/server/data/0c35d18bf06992036b69.woff2 +0 -0
- experimaestro/server/data/1815e00441357e01619e.ttf +0 -0
- experimaestro/server/data/219aa9140e099e6c72ed.woff2 +0 -0
- experimaestro/server/data/2463b90d9a316e4e5294.woff2 +0 -0
- experimaestro/server/data/2582b0e4bcf85eceead0.ttf +0 -0
- experimaestro/server/data/3a4004a46a653d4b2166.woff +0 -0
- experimaestro/server/data/3baa5b8f3469222b822d.woff +0 -0
- experimaestro/server/data/4d73cb90e394b34b7670.woff +0 -0
- experimaestro/server/data/4ef4218c522f1eb6b5b1.woff2 +0 -0
- experimaestro/server/data/50701fbb8177c2dde530.ttf +0 -0
- experimaestro/server/data/5d681e2edae8c60630db.woff +0 -0
- experimaestro/server/data/6f420cf17cc0d7676fad.woff2 +0 -0
- experimaestro/server/data/878f31251d960bd6266f.woff2 +0 -0
- experimaestro/server/data/89999bdf5d835c012025.woff2 +0 -0
- experimaestro/server/data/914997e1bdfc990d0897.ttf +0 -0
- experimaestro/server/data/b041b1fa4fe241b23445.woff2 +0 -0
- experimaestro/server/data/b6879d41b0852f01ed5b.woff2 +0 -0
- experimaestro/server/data/c210719e60948b211a12.woff2 +0 -0
- experimaestro/server/data/c380809fd3677d7d6903.woff2 +0 -0
- experimaestro/server/data/d75e3fd1eb12e9bd6655.ttf +0 -0
- experimaestro/server/data/f882956fd323fd322f31.woff +0 -0
- experimaestro/server/data/favicon.ico +0 -0
- experimaestro/server/data/index.css +22963 -0
- experimaestro/server/data/index.css.map +1 -0
- experimaestro/server/data/index.html +27 -0
- experimaestro/server/data/index.js +101770 -0
- experimaestro/server/data/index.js.map +1 -0
- experimaestro/server/data/login.html +22 -0
- experimaestro/server/data/manifest.json +15 -0
- experimaestro/settings.py +2 -2
- experimaestro/sphinx/__init__.py +7 -17
- experimaestro/taskglobals.py +7 -2
- experimaestro/tests/definitions_types.py +5 -3
- experimaestro/tests/launchers/bin/sbatch +34 -7
- experimaestro/tests/launchers/bin/srun +5 -0
- experimaestro/tests/launchers/common.py +16 -4
- experimaestro/tests/restart.py +6 -3
- experimaestro/tests/tasks/all.py +16 -10
- experimaestro/tests/tasks/foreign.py +2 -4
- experimaestro/tests/test_forward.py +5 -5
- experimaestro/tests/test_identifier.py +61 -66
- experimaestro/tests/test_instance.py +3 -6
- experimaestro/tests/test_param.py +40 -22
- experimaestro/tests/test_tags.py +5 -11
- experimaestro/tests/test_tokens.py +3 -2
- experimaestro/tests/test_types.py +17 -14
- experimaestro/tests/test_validation.py +48 -91
- experimaestro/tokens.py +16 -5
- experimaestro/typingutils.py +7 -0
- experimaestro/utils/asyncio.py +6 -2
- experimaestro/utils/resources.py +7 -3
- {experimaestro-1.6.1.dist-info → experimaestro-1.7.0.dist-info}/METADATA +3 -4
- experimaestro-1.7.0.dist-info/RECORD +154 -0
- {experimaestro-1.6.1.dist-info → experimaestro-1.7.0.dist-info}/WHEEL +1 -1
- experimaestro-1.6.1.dist-info/RECORD +0 -122
- {experimaestro-1.6.1.dist-info → experimaestro-1.7.0.dist-info}/LICENSE +0 -0
- {experimaestro-1.6.1.dist-info → experimaestro-1.7.0.dist-info}/entry_points.txt +0 -0
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="utf-8" />
|
|
5
|
+
<meta
|
|
6
|
+
name="viewport"
|
|
7
|
+
content="width=device-width, initial-scale=1, shrink-to-fit=no"
|
|
8
|
+
/>
|
|
9
|
+
<meta name="theme-color" content="#000000" />
|
|
10
|
+
<title>Experimaestro</title>
|
|
11
|
+
</head>
|
|
12
|
+
<body>
|
|
13
|
+
<h1>Experimaestro</h1>
|
|
14
|
+
<form action="/auth" method="GET">
|
|
15
|
+
<div>
|
|
16
|
+
Token
|
|
17
|
+
<input type="text" name="xpm-token" />
|
|
18
|
+
<input type="submit" value="submit" />
|
|
19
|
+
</div>
|
|
20
|
+
</form>
|
|
21
|
+
</body>
|
|
22
|
+
</html>
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
{
|
|
2
|
+
"short_name": "Experimaestro",
|
|
3
|
+
"name": "Experimaestro Client",
|
|
4
|
+
"icons": [
|
|
5
|
+
{
|
|
6
|
+
"src": "favicon.ico",
|
|
7
|
+
"sizes": "64x64 32x32 24x24 16x16",
|
|
8
|
+
"type": "image/x-icon"
|
|
9
|
+
}
|
|
10
|
+
],
|
|
11
|
+
"start_url": "./index.html",
|
|
12
|
+
"display": "standalone",
|
|
13
|
+
"theme_color": "#000000",
|
|
14
|
+
"background_color": "#ffffff"
|
|
15
|
+
}
|
experimaestro/settings.py
CHANGED
|
@@ -37,7 +37,7 @@ class WorkspaceSettings:
|
|
|
37
37
|
|
|
38
38
|
alt_workspaces: List[str] = field(default_factory=list)
|
|
39
39
|
"""Alternative workspaces to find jobs or experiments"""
|
|
40
|
-
|
|
40
|
+
|
|
41
41
|
def __post_init__(self):
|
|
42
42
|
self.path = self.path.expanduser().resolve()
|
|
43
43
|
|
|
@@ -83,7 +83,7 @@ def get_workspace(id: Optional[str] = None) -> Optional[WorkspaceSettings]:
|
|
|
83
83
|
return None
|
|
84
84
|
|
|
85
85
|
|
|
86
|
-
def find_workspace(*, workspace: Optional[str] = None, workdir: Optional[Path] = None):
|
|
86
|
+
def find_workspace(*, workspace: Optional[str] = None, workdir: Optional[Path] = None) -> WorkspaceSettings:
|
|
87
87
|
"""Find workspace"""
|
|
88
88
|
workdir = Path(workdir) if workdir else None
|
|
89
89
|
|
experimaestro/sphinx/__init__.py
CHANGED
|
@@ -10,19 +10,13 @@ from docutils import nodes
|
|
|
10
10
|
from sphinx.application import Sphinx
|
|
11
11
|
from sphinx import addnodes
|
|
12
12
|
from sphinx.ext.autodoc import ClassDocumenter, Documenter, restify
|
|
13
|
-
from sphinx.locale import _
|
|
13
|
+
from sphinx.locale import _
|
|
14
14
|
from sphinx.util import inspect, logging
|
|
15
|
-
from sphinx.domains.python import
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
PyObject,
|
|
19
|
-
directives,
|
|
20
|
-
desc_signature,
|
|
21
|
-
_parse_annotation,
|
|
22
|
-
)
|
|
15
|
+
from sphinx.domains.python import PyClasslike, PyAttribute, directives
|
|
16
|
+
from sphinx.domains.python import PyObject # noqa: F401
|
|
17
|
+
from sphinx.addnodes import desc_signature
|
|
23
18
|
from sphinx.util.typing import OptionSpec
|
|
24
19
|
from docutils.statemachine import StringList
|
|
25
|
-
import logging
|
|
26
20
|
import re
|
|
27
21
|
|
|
28
22
|
from experimaestro import Config, Task
|
|
@@ -97,9 +91,6 @@ class ConfigDocumenter(ClassDocumenter):
|
|
|
97
91
|
can_document = inspect.isclass(member) and issubclass(member, Config)
|
|
98
92
|
return can_document
|
|
99
93
|
|
|
100
|
-
def add_directive_header(self, sig: str) -> None:
|
|
101
|
-
super().add_directive_header(sig)
|
|
102
|
-
|
|
103
94
|
def get_object_members(self, want_all: bool): # -> Tuple[bool, ObjectMembers]:
|
|
104
95
|
r = super().get_object_members(want_all)
|
|
105
96
|
return r
|
|
@@ -157,7 +148,7 @@ class ConfigDocumenter(ClassDocumenter):
|
|
|
157
148
|
|
|
158
149
|
# Our specific code
|
|
159
150
|
if issubclass(self.object, Task):
|
|
160
|
-
self.add_line(
|
|
151
|
+
self.add_line(" :task:", sourcename)
|
|
161
152
|
|
|
162
153
|
# add inheritance info, if wanted
|
|
163
154
|
if not self.doc_as_attr and self.options.show_inheritance:
|
|
@@ -196,7 +187,6 @@ class ConfigDocumenter(ClassDocumenter):
|
|
|
196
187
|
def add_content(
|
|
197
188
|
self, more_content: Optional[StringList], no_docstring: bool = False
|
|
198
189
|
) -> None:
|
|
199
|
-
|
|
200
190
|
xpminfo = getxpminfo(self.object)
|
|
201
191
|
source_name = self.get_sourcename()
|
|
202
192
|
|
|
@@ -214,9 +204,9 @@ class ConfigDocumenter(ClassDocumenter):
|
|
|
214
204
|
source_name,
|
|
215
205
|
)
|
|
216
206
|
if argument.generator:
|
|
217
|
-
self.add_line(
|
|
207
|
+
self.add_line(" :generated:", source_name)
|
|
218
208
|
elif argument.constant:
|
|
219
|
-
self.add_line(
|
|
209
|
+
self.add_line(" :constant:", source_name)
|
|
220
210
|
|
|
221
211
|
# self.add_line("", source_name)
|
|
222
212
|
if argument.help:
|
experimaestro/taskglobals.py
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
|
+
from functools import cached_property
|
|
1
2
|
from pathlib import Path
|
|
2
3
|
from typing import Optional
|
|
3
|
-
import os
|
|
4
|
-
import logging
|
|
5
4
|
|
|
6
5
|
|
|
7
6
|
class Env:
|
|
@@ -18,6 +17,12 @@ class Env:
|
|
|
18
17
|
# - no progress report
|
|
19
18
|
slave: bool = False
|
|
20
19
|
|
|
20
|
+
@cached_property
|
|
21
|
+
def xpm_path(self):
|
|
22
|
+
path = self.taskpath / ".experimaestro"
|
|
23
|
+
path.mkdir(exist_ok=True)
|
|
24
|
+
return path
|
|
25
|
+
|
|
21
26
|
@staticmethod
|
|
22
27
|
def instance():
|
|
23
28
|
if Env._instance is None:
|
|
@@ -1,15 +1,17 @@
|
|
|
1
|
-
from experimaestro import
|
|
1
|
+
from experimaestro import Param, Task
|
|
2
2
|
|
|
3
3
|
|
|
4
|
-
@argument("value", type=int)
|
|
5
4
|
class IntegerTask(Task):
|
|
5
|
+
value: Param[int]
|
|
6
|
+
|
|
6
7
|
def execute(self):
|
|
7
8
|
if not isinstance(self.value, int):
|
|
8
9
|
raise AssertionError("Not an integer")
|
|
9
10
|
|
|
10
11
|
|
|
11
|
-
@argument("value", type=float)
|
|
12
12
|
class FloatTask(Task):
|
|
13
|
+
value: Param[float]
|
|
14
|
+
|
|
13
15
|
def execute(self):
|
|
14
16
|
if not isinstance(self.value, float):
|
|
15
17
|
raise AssertionError("Not a float but %s" % type(self.value))
|
|
@@ -2,15 +2,28 @@
|
|
|
2
2
|
|
|
3
3
|
# slurm test suite
|
|
4
4
|
|
|
5
|
+
pwd 1>&2
|
|
6
|
+
CURDIR="$(realpath "$(dirname "$0")"/..)"
|
|
7
|
+
|
|
5
8
|
# Where we store the jobs
|
|
6
|
-
XPM_SLURM_DIR="$
|
|
9
|
+
XPM_SLURM_DIR="${CURDIR}/slurm"
|
|
7
10
|
if ! test -d "$XPM_SLURM_DIR"; then
|
|
8
11
|
echo "Directory $XPM_SLURM_DIR does not exist" 1>&2
|
|
9
12
|
exit 1
|
|
10
13
|
fi
|
|
11
14
|
|
|
12
15
|
mkdir -p "$XPM_SLURM_DIR/jobs"
|
|
13
|
-
echo "Slurm directory: $XPM_SLURM_DIR" >&2
|
|
16
|
+
echo "Slurm directory: $XPM_SLURM_DIR" >&2
|
|
17
|
+
|
|
18
|
+
RED='\033[0;31m'
|
|
19
|
+
NC='\033[0m' # No Color
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
if ! which flock > /dev/null; then
|
|
23
|
+
echo -e "[${RED}ERROR${NC}] flock is not installed on this system" 1>&2
|
|
24
|
+
exit 1
|
|
25
|
+
fi
|
|
26
|
+
|
|
14
27
|
|
|
15
28
|
lockpath() {
|
|
16
29
|
fid="$1"
|
|
@@ -18,7 +31,7 @@ lockpath() {
|
|
|
18
31
|
|
|
19
32
|
echo "Locking $path..." 1>&2
|
|
20
33
|
eval exec "$fid<>" $path
|
|
21
|
-
if ! flock --timeout 2 $fid; then
|
|
34
|
+
if ! flock --timeout 2 $fid; then
|
|
22
35
|
echo Could not lock "$path" - stopping 1>&2
|
|
23
36
|
exit 017
|
|
24
37
|
fi
|
|
@@ -37,12 +50,26 @@ while true; do
|
|
|
37
50
|
-o) shift; stdout="$1"; shift;;
|
|
38
51
|
-e) shift; stderr="$1"; shift;;
|
|
39
52
|
--*) args+=("$1"); shift;;
|
|
40
|
-
*) break 2;;
|
|
53
|
+
*) break 2;;
|
|
41
54
|
esac
|
|
42
55
|
done
|
|
43
56
|
|
|
44
|
-
|
|
45
|
-
|
|
57
|
+
chdir=$(pwd)
|
|
58
|
+
while IFS= read -r line; do
|
|
59
|
+
case "$line" in
|
|
60
|
+
"#SBATCH --output="*)stdout=${line#*#SBATCH --output=};;
|
|
61
|
+
"#SBATCH --error="*) stderr=${line#*#SBATCH --error=};;
|
|
62
|
+
"#SBATCH --chdir="*) chdir=${line#*#SBATCH --chdir=};;
|
|
63
|
+
esac
|
|
64
|
+
done < "$1"
|
|
65
|
+
|
|
66
|
+
cd "$chdir"
|
|
67
|
+
echo "Starting $@ ${args[@]} > $stdout 2> $stderr" >&2
|
|
68
|
+
(
|
|
69
|
+
export PATH="${CURDIR}/bin:$PATH"
|
|
70
|
+
eval "$@" "${args[@]}"
|
|
71
|
+
echo $? > "$XPM_SLURM_DIR/jobs/$$.status"
|
|
72
|
+
) > $stdout 2> $stderr &
|
|
46
73
|
JOBID="$$"
|
|
47
74
|
date > "$XPM_SLURM_DIR/jobs/$JOBID.start"
|
|
48
75
|
disown
|
|
@@ -51,4 +78,4 @@ if test "$parsable" == 0; then
|
|
|
51
78
|
echo "Submitted batch job ${JOBID}"
|
|
52
79
|
else
|
|
53
80
|
echo "${JOBID};cluster"
|
|
54
|
-
fi
|
|
81
|
+
fi
|
|
@@ -68,24 +68,36 @@ class WaitUntilTouched(Task):
|
|
|
68
68
|
|
|
69
69
|
|
|
70
70
|
def takeback(launcher, datapath, txp1, txp2):
|
|
71
|
+
"""Launch two times the same task (with two experiments)
|
|
72
|
+
|
|
73
|
+
:param launcher: The launcher
|
|
74
|
+
:param datapath: The path containing the two files that control the task, namely (1) touching which is created by the task when starting, (2) waiting which is controlled here
|
|
75
|
+
:param txp1: The first experiment
|
|
76
|
+
:param txp2: The second experiment
|
|
77
|
+
"""
|
|
71
78
|
datapath.mkdir()
|
|
72
79
|
touching = datapath / "touching"
|
|
73
80
|
waiting = datapath / "waiting"
|
|
74
81
|
|
|
75
|
-
with txp1
|
|
76
|
-
WaitUntilTouched
|
|
82
|
+
with txp1:
|
|
83
|
+
task: WaitUntilTouched = WaitUntilTouched(
|
|
84
|
+
touching=touching, waiting=waiting
|
|
85
|
+
).submit(launcher=launcher)
|
|
77
86
|
|
|
78
87
|
logger.debug("Waiting for task to create 'touching' file")
|
|
79
88
|
while not touching.is_file():
|
|
89
|
+
if task.__xpm__.job.state.finished():
|
|
90
|
+
raise Exception("Job has finished... too early")
|
|
80
91
|
time.sleep(0.01)
|
|
81
92
|
|
|
82
|
-
with txp2
|
|
93
|
+
with txp2:
|
|
83
94
|
result = WaitUntilTouched(touching=touching, waiting=waiting).submit(
|
|
84
95
|
launcher=launcher
|
|
85
96
|
)
|
|
86
97
|
|
|
87
|
-
logger.debug("Waiting for job to be
|
|
98
|
+
logger.debug("Waiting for job to be running (scheduler)")
|
|
88
99
|
while result.__xpm__.job.state != JobState.RUNNING:
|
|
89
100
|
time.sleep(0.1)
|
|
90
101
|
|
|
102
|
+
logger.debug("OK, no we can notify the task")
|
|
91
103
|
waiting.touch()
|
experimaestro/tests/restart.py
CHANGED
|
@@ -1,12 +1,14 @@
|
|
|
1
1
|
import time
|
|
2
|
+
from pathlib import Path
|
|
2
3
|
import sys
|
|
3
4
|
from typing import Callable
|
|
4
|
-
from experimaestro import Task,
|
|
5
|
+
from experimaestro import Task, Meta, field, PathGenerator
|
|
5
6
|
import psutil
|
|
6
7
|
import logging
|
|
7
8
|
import subprocess
|
|
8
9
|
import json
|
|
9
10
|
import signal
|
|
11
|
+
|
|
10
12
|
from experimaestro.scheduler.workspace import RunMode
|
|
11
13
|
from experimaestro.tests.utils import TemporaryExperiment, is_posix
|
|
12
14
|
from experimaestro.scheduler import JobState
|
|
@@ -26,9 +28,10 @@ if is_posix():
|
|
|
26
28
|
TERMINATES_FUNC.append(sigint)
|
|
27
29
|
|
|
28
30
|
|
|
29
|
-
@pathoption("touch", "touch")
|
|
30
|
-
@pathoption("wait", "wait")
|
|
31
31
|
class Restart(Task):
|
|
32
|
+
touch: Meta[Path] = field(default_factory=PathGenerator("touch"))
|
|
33
|
+
wait: Meta[Path] = field(default_factory=PathGenerator("wait"))
|
|
34
|
+
|
|
32
35
|
def execute(self):
|
|
33
36
|
# Write the file "touch" to notify that we started
|
|
34
37
|
with open(self.touch, "w") as out:
|
experimaestro/tests/tasks/all.py
CHANGED
|
@@ -1,11 +1,13 @@
|
|
|
1
|
+
from pathlib import Path
|
|
1
2
|
import time
|
|
2
3
|
from typing import List
|
|
3
4
|
from experimaestro import (
|
|
4
|
-
|
|
5
|
+
Meta,
|
|
5
6
|
Param,
|
|
7
|
+
field,
|
|
6
8
|
Task,
|
|
9
|
+
PathGenerator,
|
|
7
10
|
Config,
|
|
8
|
-
pathoption,
|
|
9
11
|
STDOUT,
|
|
10
12
|
cache,
|
|
11
13
|
)
|
|
@@ -18,8 +20,8 @@ class SimpleTask(Task):
|
|
|
18
20
|
print(self.x) # noqa: T201
|
|
19
21
|
|
|
20
22
|
|
|
21
|
-
@pathoption("out", STDOUT)
|
|
22
23
|
class Say(Task):
|
|
24
|
+
out: Meta[Path] = field(default_factory=PathGenerator(STDOUT))
|
|
23
25
|
word: Param[str]
|
|
24
26
|
|
|
25
27
|
def execute(self):
|
|
@@ -38,19 +40,20 @@ class Concat(Task):
|
|
|
38
40
|
print(" ".join(says)) # noqa: T201
|
|
39
41
|
|
|
40
42
|
|
|
41
|
-
@param("x", type=int)
|
|
42
43
|
class ForeignClassB1(Config):
|
|
43
|
-
|
|
44
|
+
x: Param[int]
|
|
44
45
|
|
|
45
46
|
|
|
46
|
-
@param("b", type=ForeignClassB1)
|
|
47
47
|
class ForeignTaskA(Task):
|
|
48
|
+
b: Param[ForeignClassB1]
|
|
49
|
+
|
|
48
50
|
def execute(self):
|
|
49
51
|
print(self.b.x) # noqa: T201
|
|
50
52
|
|
|
51
53
|
|
|
52
|
-
@pathoption("wait", "wait")
|
|
53
54
|
class Fail(Task):
|
|
55
|
+
wait: Meta[Path] = field(default_factory=PathGenerator("wait"))
|
|
56
|
+
|
|
54
57
|
def execute(self):
|
|
55
58
|
while not self.wait.is_file():
|
|
56
59
|
time.sleep(0.01)
|
|
@@ -64,14 +67,16 @@ class Fail(Task):
|
|
|
64
67
|
out.write("hello")
|
|
65
68
|
|
|
66
69
|
|
|
67
|
-
@param("fail", Fail)
|
|
68
70
|
class FailConsumer(Task):
|
|
71
|
+
fail: Param[Fail]
|
|
72
|
+
|
|
69
73
|
def execute(self):
|
|
70
74
|
return True
|
|
71
75
|
|
|
72
76
|
|
|
73
|
-
@param("a", int)
|
|
74
77
|
class Method(Task):
|
|
78
|
+
a: Param[int]
|
|
79
|
+
|
|
75
80
|
def execute(self):
|
|
76
81
|
assert self.a == 1
|
|
77
82
|
|
|
@@ -92,7 +97,8 @@ class CacheConfig(Config):
|
|
|
92
97
|
return path.read_text()
|
|
93
98
|
|
|
94
99
|
|
|
95
|
-
@param("data", type=CacheConfig)
|
|
96
100
|
class CacheConfigTask(Task):
|
|
101
|
+
data: Param[CacheConfig]
|
|
102
|
+
|
|
97
103
|
def execute(self):
|
|
98
104
|
assert self.data.get() == "hello"
|
|
@@ -1,12 +1,12 @@
|
|
|
1
|
-
from experimaestro import
|
|
1
|
+
from experimaestro import Param, Config
|
|
2
2
|
from experimaestro.click import forwardoption
|
|
3
3
|
import click
|
|
4
4
|
|
|
5
5
|
|
|
6
6
|
def test_main():
|
|
7
|
-
@argument("epochs", type=int, default=100, help="Number of learning epochs")
|
|
8
7
|
class MyModel(Config):
|
|
9
|
-
|
|
8
|
+
epochs: Param[int] = 100
|
|
9
|
+
"""Number of learning epochs"""
|
|
10
10
|
|
|
11
11
|
@forwardoption.epochs(MyModel)
|
|
12
12
|
@click.command()
|
|
@@ -18,9 +18,9 @@ def test_main():
|
|
|
18
18
|
|
|
19
19
|
|
|
20
20
|
def test_rename():
|
|
21
|
-
@argument("epochs", type=int, default=100, help="Number of learning epochs")
|
|
22
21
|
class MyModel(Config):
|
|
23
|
-
|
|
22
|
+
epochs: Param[int] = 100
|
|
23
|
+
"""Number of learning epochs"""
|
|
24
24
|
|
|
25
25
|
@forwardoption.epochs(MyModel, "my-epochs")
|
|
26
26
|
@click.command()
|