experimaestro 1.5.1__py3-none-any.whl → 2.0.0a8__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 +14 -4
- experimaestro/__main__.py +3 -423
- experimaestro/annotations.py +14 -4
- experimaestro/cli/__init__.py +311 -0
- experimaestro/{filter.py → cli/filter.py} +23 -9
- experimaestro/cli/jobs.py +268 -0
- experimaestro/cli/progress.py +269 -0
- experimaestro/click.py +0 -35
- experimaestro/commandline.py +3 -7
- experimaestro/connectors/__init__.py +29 -14
- experimaestro/connectors/local.py +19 -10
- experimaestro/connectors/ssh.py +27 -8
- experimaestro/core/arguments.py +45 -3
- experimaestro/core/callbacks.py +52 -0
- experimaestro/core/context.py +8 -9
- experimaestro/core/identifier.py +310 -0
- experimaestro/core/objects/__init__.py +44 -0
- experimaestro/core/{objects.py → objects/config.py} +399 -772
- experimaestro/core/objects/config_utils.py +58 -0
- experimaestro/core/objects/config_walk.py +151 -0
- experimaestro/core/objects.pyi +15 -45
- experimaestro/core/serialization.py +63 -9
- experimaestro/core/serializers.py +1 -8
- experimaestro/core/types.py +104 -66
- experimaestro/experiments/cli.py +154 -72
- experimaestro/experiments/configuration.py +10 -1
- experimaestro/generators.py +6 -1
- experimaestro/ipc.py +4 -1
- experimaestro/launcherfinder/__init__.py +1 -1
- experimaestro/launcherfinder/base.py +2 -18
- experimaestro/launcherfinder/parser.py +8 -3
- experimaestro/launcherfinder/registry.py +52 -140
- experimaestro/launcherfinder/specs.py +49 -10
- experimaestro/launchers/direct.py +0 -47
- experimaestro/launchers/slurm/base.py +54 -14
- experimaestro/mkdocs/__init__.py +1 -1
- experimaestro/mkdocs/base.py +6 -8
- experimaestro/notifications.py +38 -12
- experimaestro/progress.py +406 -0
- experimaestro/run.py +24 -3
- experimaestro/scheduler/__init__.py +18 -1
- experimaestro/scheduler/base.py +108 -808
- experimaestro/scheduler/dynamic_outputs.py +184 -0
- experimaestro/scheduler/experiment.py +387 -0
- experimaestro/scheduler/jobs.py +475 -0
- experimaestro/scheduler/signal_handler.py +32 -0
- experimaestro/scheduler/state.py +75 -0
- experimaestro/scheduler/workspace.py +27 -8
- experimaestro/scriptbuilder.py +18 -3
- experimaestro/server/__init__.py +36 -5
- experimaestro/server/data/1815e00441357e01619e.ttf +0 -0
- experimaestro/server/data/2463b90d9a316e4e5294.woff2 +0 -0
- experimaestro/server/data/2582b0e4bcf85eceead0.ttf +0 -0
- experimaestro/server/data/89999bdf5d835c012025.woff2 +0 -0
- experimaestro/server/data/914997e1bdfc990d0897.ttf +0 -0
- experimaestro/server/data/c210719e60948b211a12.woff2 +0 -0
- experimaestro/server/data/index.css +5187 -5068
- experimaestro/server/data/index.css.map +1 -1
- experimaestro/server/data/index.js +68887 -68064
- experimaestro/server/data/index.js.map +1 -1
- experimaestro/settings.py +45 -5
- experimaestro/sphinx/__init__.py +7 -17
- experimaestro/taskglobals.py +7 -2
- experimaestro/tests/core/__init__.py +0 -0
- experimaestro/tests/core/test_generics.py +206 -0
- 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 +17 -5
- experimaestro/tests/launchers/config_slurm/launchers.py +25 -0
- experimaestro/tests/restart.py +10 -5
- experimaestro/tests/tasks/all.py +23 -10
- experimaestro/tests/tasks/foreign.py +2 -4
- experimaestro/tests/test_checkers.py +2 -2
- experimaestro/tests/test_dependencies.py +11 -17
- experimaestro/tests/test_experiment.py +73 -0
- experimaestro/tests/test_file_progress.py +425 -0
- experimaestro/tests/test_file_progress_integration.py +477 -0
- experimaestro/tests/test_findlauncher.py +12 -5
- experimaestro/tests/test_forward.py +5 -5
- experimaestro/tests/test_generators.py +93 -0
- experimaestro/tests/test_identifier.py +182 -158
- experimaestro/tests/test_instance.py +19 -27
- experimaestro/tests/test_objects.py +13 -20
- experimaestro/tests/test_outputs.py +6 -6
- experimaestro/tests/test_param.py +68 -30
- experimaestro/tests/test_progress.py +4 -4
- experimaestro/tests/test_serializers.py +24 -64
- experimaestro/tests/test_ssh.py +7 -0
- experimaestro/tests/test_tags.py +50 -21
- experimaestro/tests/test_tasks.py +42 -51
- experimaestro/tests/test_tokens.py +11 -8
- experimaestro/tests/test_types.py +24 -21
- experimaestro/tests/test_validation.py +67 -110
- experimaestro/tests/token_reschedule.py +1 -1
- experimaestro/tokens.py +24 -13
- experimaestro/tools/diff.py +8 -1
- experimaestro/typingutils.py +20 -11
- experimaestro/utils/asyncio.py +6 -2
- experimaestro/utils/multiprocessing.py +44 -0
- experimaestro/utils/resources.py +11 -3
- {experimaestro-1.5.1.dist-info → experimaestro-2.0.0a8.dist-info}/METADATA +28 -36
- experimaestro-2.0.0a8.dist-info/RECORD +166 -0
- {experimaestro-1.5.1.dist-info → experimaestro-2.0.0a8.dist-info}/WHEEL +1 -1
- {experimaestro-1.5.1.dist-info → experimaestro-2.0.0a8.dist-info}/entry_points.txt +0 -4
- experimaestro/launchers/slurm/cli.py +0 -29
- experimaestro/launchers/slurm/configuration.py +0 -597
- experimaestro/scheduler/environment.py +0 -94
- experimaestro/server/data/016b4a6cdced82ab3aa1.ttf +0 -0
- experimaestro/server/data/50701fbb8177c2dde530.ttf +0 -0
- experimaestro/server/data/878f31251d960bd6266f.woff2 +0 -0
- experimaestro/server/data/b041b1fa4fe241b23445.woff2 +0 -0
- experimaestro/server/data/b6879d41b0852f01ed5b.woff2 +0 -0
- experimaestro/server/data/d75e3fd1eb12e9bd6655.ttf +0 -0
- experimaestro/tests/launchers/config_slurm/launchers.yaml +0 -134
- experimaestro/utils/yaml.py +0 -202
- experimaestro-1.5.1.dist-info/RECORD +0 -148
- {experimaestro-1.5.1.dist-info → experimaestro-2.0.0a8.dist-info/licenses}/LICENSE +0 -0
|
@@ -3,8 +3,7 @@ from pathlib import Path
|
|
|
3
3
|
import pytest
|
|
4
4
|
from experimaestro import Config, Task, Annotated, copyconfig, default
|
|
5
5
|
from experimaestro.core.arguments import Param
|
|
6
|
-
from experimaestro.core.objects import
|
|
7
|
-
from experimaestro.core.types import XPMValue
|
|
6
|
+
from experimaestro.core.objects import ConfigMixin
|
|
8
7
|
from experimaestro.generators import pathgenerator
|
|
9
8
|
from experimaestro.scheduler.workspace import RunMode
|
|
10
9
|
from experimaestro.tests.utils import TemporaryExperiment
|
|
@@ -27,7 +26,7 @@ def test_object_default():
|
|
|
27
26
|
|
|
28
27
|
|
|
29
28
|
class B(Config):
|
|
30
|
-
a: Param[A] = A(x=3)
|
|
29
|
+
a: Param[A] = A.C(x=3)
|
|
31
30
|
|
|
32
31
|
|
|
33
32
|
class C(B):
|
|
@@ -39,43 +38,37 @@ class D(B, A):
|
|
|
39
38
|
|
|
40
39
|
|
|
41
40
|
class DefaultAnnotationConfig(Config):
|
|
42
|
-
a: Annotated[A, default(A(x=3))]
|
|
41
|
+
a: Annotated[A, default(A.C(x=3))]
|
|
43
42
|
|
|
44
43
|
|
|
45
44
|
def test_object_config_default():
|
|
46
45
|
"""Test default configurations as default values"""
|
|
47
|
-
b = B()
|
|
46
|
+
b = B.C()
|
|
48
47
|
assert b.a.x == 3
|
|
49
48
|
|
|
50
|
-
c = C()
|
|
49
|
+
c = C.C()
|
|
51
50
|
assert c.a.x == 3
|
|
52
51
|
|
|
53
|
-
annotationConfig = DefaultAnnotationConfig()
|
|
52
|
+
annotationConfig = DefaultAnnotationConfig.C()
|
|
54
53
|
assert annotationConfig.a.x == 3
|
|
55
54
|
|
|
56
55
|
|
|
57
56
|
def test_hierarchy():
|
|
58
57
|
"""Test if the object hierarchy is OK"""
|
|
59
|
-
OA = A.__getxpmtype__().
|
|
60
|
-
OB = B.__getxpmtype__().
|
|
61
|
-
OC = C.__getxpmtype__().
|
|
62
|
-
OD = D.__getxpmtype__().objecttype
|
|
58
|
+
OA = A.__getxpmtype__().value_type
|
|
59
|
+
OB = B.__getxpmtype__().value_type
|
|
60
|
+
OC = C.__getxpmtype__().value_type
|
|
63
61
|
|
|
64
62
|
assert issubclass(A, Config)
|
|
65
63
|
assert issubclass(B, Config)
|
|
66
64
|
assert issubclass(C, Config)
|
|
67
65
|
|
|
68
|
-
assert not issubclass(OA,
|
|
69
|
-
assert not issubclass(OB,
|
|
70
|
-
assert not issubclass(OC,
|
|
66
|
+
assert not issubclass(OA, ConfigMixin)
|
|
67
|
+
assert not issubclass(OB, ConfigMixin)
|
|
68
|
+
assert not issubclass(OC, ConfigMixin)
|
|
71
69
|
|
|
72
70
|
assert issubclass(C, B)
|
|
73
71
|
|
|
74
|
-
assert OA.__bases__ == (A, XPMValue)
|
|
75
|
-
assert OB.__bases__ == (B, XPMValue)
|
|
76
|
-
assert OC.__bases__ == (C, B.XPMValue)
|
|
77
|
-
assert OD.__bases__ == (D, B.XPMValue, A.XPMValue)
|
|
78
|
-
|
|
79
72
|
|
|
80
73
|
class CopyConfig(Task):
|
|
81
74
|
path: Annotated[Path, pathgenerator("hello.txt")]
|
|
@@ -83,7 +76,7 @@ class CopyConfig(Task):
|
|
|
83
76
|
|
|
84
77
|
|
|
85
78
|
def test_copyconfig(xp):
|
|
86
|
-
b = CopyConfig(x=2)
|
|
79
|
+
b = CopyConfig.C(x=2)
|
|
87
80
|
|
|
88
81
|
b.submit()
|
|
89
82
|
|
|
@@ -32,17 +32,17 @@ class MainB(Task):
|
|
|
32
32
|
|
|
33
33
|
|
|
34
34
|
def test_output_taskoutput():
|
|
35
|
-
a = A(b=B())
|
|
36
|
-
output, ioutput = Main(a=a).submit(run_mode=RunMode.DRY_RUN)
|
|
35
|
+
a = A.C(b=B.C())
|
|
36
|
+
output, ioutput = Main.C(a=a).submit(run_mode=RunMode.DRY_RUN)
|
|
37
37
|
|
|
38
38
|
# Direct
|
|
39
|
-
Main(a=output)
|
|
39
|
+
Main.C(a=output)
|
|
40
40
|
|
|
41
41
|
# Via getattr
|
|
42
|
-
Main(a=A(b=output.b))
|
|
42
|
+
Main.C(a=A.C(b=output.b))
|
|
43
43
|
|
|
44
44
|
# Via getitem
|
|
45
|
-
Main(a=ioutput["a"])
|
|
45
|
+
Main.C(a=ioutput["a"])
|
|
46
46
|
|
|
47
47
|
# Now, submits
|
|
48
|
-
Main(a=output).submit(run_mode=RunMode.DRY_RUN)
|
|
48
|
+
Main.C(a=output).submit(run_mode=RunMode.DRY_RUN)
|
|
@@ -13,14 +13,16 @@ from experimaestro.core.types import DictType, IntType, StrType
|
|
|
13
13
|
from enum import Enum
|
|
14
14
|
import pytest
|
|
15
15
|
from experimaestro import (
|
|
16
|
-
config,
|
|
17
16
|
Option,
|
|
18
17
|
Constant,
|
|
19
18
|
Param,
|
|
20
19
|
Task,
|
|
21
20
|
default,
|
|
21
|
+
Meta,
|
|
22
22
|
Config,
|
|
23
23
|
pathgenerator,
|
|
24
|
+
PathGenerator,
|
|
25
|
+
field,
|
|
24
26
|
Annotated,
|
|
25
27
|
)
|
|
26
28
|
import experimaestro.core.types as types
|
|
@@ -29,25 +31,24 @@ from experimaestro.xpmutils import DirectoryContext
|
|
|
29
31
|
# --- Test manual name for configuration
|
|
30
32
|
|
|
31
33
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
+
class B(Config):
|
|
35
|
+
__xpmid__ = "annotations.b"
|
|
34
36
|
pass
|
|
35
37
|
|
|
36
38
|
|
|
37
39
|
def test_fullname():
|
|
38
|
-
assert str(B.
|
|
40
|
+
assert str(B.__getxpmtype__().identifier) == "annotations.b"
|
|
39
41
|
|
|
40
42
|
|
|
41
43
|
# --- Automatic name for configuration
|
|
42
44
|
|
|
43
45
|
|
|
44
|
-
|
|
45
|
-
class A:
|
|
46
|
+
class A(Config):
|
|
46
47
|
pass
|
|
47
48
|
|
|
48
49
|
|
|
49
50
|
def test_noname():
|
|
50
|
-
assert str(A.
|
|
51
|
+
assert str(A.__getxpmtype__().identifier) == "experimaestro.tests.test_param.a"
|
|
51
52
|
|
|
52
53
|
|
|
53
54
|
def serializeCycle(config: Config):
|
|
@@ -69,8 +70,7 @@ def ArgumentValue(default=None, *, help=""):
|
|
|
69
70
|
def test_type_hinting():
|
|
70
71
|
"""Test for type hinting"""
|
|
71
72
|
|
|
72
|
-
|
|
73
|
-
class MyConfig:
|
|
73
|
+
class MyConfig(Config):
|
|
74
74
|
"""A configuration
|
|
75
75
|
|
|
76
76
|
Attributes:
|
|
@@ -89,7 +89,7 @@ def test_type_hinting():
|
|
|
89
89
|
path: Annotated[Path, pathgenerator("world")]
|
|
90
90
|
option: Option[str]
|
|
91
91
|
|
|
92
|
-
ot = MyConfig.
|
|
92
|
+
ot = MyConfig.__getxpmtype__()
|
|
93
93
|
|
|
94
94
|
# Check required parameter
|
|
95
95
|
arg_x = ot.getArgument("x")
|
|
@@ -139,7 +139,7 @@ def test_type_hinting():
|
|
|
139
139
|
|
|
140
140
|
def test_generatedpath():
|
|
141
141
|
class A(Config):
|
|
142
|
-
path:
|
|
142
|
+
path: Meta[Path] = field(default_factory=PathGenerator("test.txt"))
|
|
143
143
|
|
|
144
144
|
class B(Config):
|
|
145
145
|
a: Param[A]
|
|
@@ -148,7 +148,7 @@ def test_generatedpath():
|
|
|
148
148
|
b: Param[B]
|
|
149
149
|
|
|
150
150
|
basepath = Path("/tmp/testconflict")
|
|
151
|
-
c = C(b=B(a=A())).instance(DirectoryContext(basepath))
|
|
151
|
+
c = C.C(b=B.C(a=A.C())).instance(DirectoryContext(basepath))
|
|
152
152
|
assert c.b.a.path.relative_to(basepath) == Path("out/b/a/test.txt")
|
|
153
153
|
|
|
154
154
|
|
|
@@ -158,13 +158,13 @@ def test_config_class():
|
|
|
158
158
|
class A(Config):
|
|
159
159
|
x: Param[int]
|
|
160
160
|
|
|
161
|
-
a = A(x=1)
|
|
161
|
+
a = A.C(x=1)
|
|
162
162
|
assert a.x == 1
|
|
163
163
|
|
|
164
164
|
class B(A):
|
|
165
165
|
y: Param[int]
|
|
166
166
|
|
|
167
|
-
b = B(x=1, y=2)
|
|
167
|
+
b = B.C(x=1, y=2)
|
|
168
168
|
assert b.x == 1
|
|
169
169
|
assert b.y == 2
|
|
170
170
|
|
|
@@ -174,16 +174,15 @@ def test_config_class():
|
|
|
174
174
|
class C(Config):
|
|
175
175
|
d: Param[D]
|
|
176
176
|
|
|
177
|
-
c = C(d=D(x=1))
|
|
177
|
+
c = C.C(d=D.C(x=1))
|
|
178
178
|
assert c.d.x == 1
|
|
179
179
|
|
|
180
180
|
|
|
181
181
|
def test_constant():
|
|
182
|
-
|
|
183
|
-
class A:
|
|
182
|
+
class A(Config):
|
|
184
183
|
x: Constant[int] = 2
|
|
185
184
|
|
|
186
|
-
a = A()
|
|
185
|
+
a = A.C()
|
|
187
186
|
assert a.x == 2, "Constant value not set"
|
|
188
187
|
|
|
189
188
|
# We should not be able to change the value
|
|
@@ -203,7 +202,7 @@ class EnumConfig(Config):
|
|
|
203
202
|
def test_param_enum():
|
|
204
203
|
"""Test for enum values"""
|
|
205
204
|
|
|
206
|
-
a = EnumConfig(x=EnumParam.OTHER)
|
|
205
|
+
a = EnumConfig.C(x=EnumParam.OTHER)
|
|
207
206
|
_a = serializeCycle(a)
|
|
208
207
|
|
|
209
208
|
assert isinstance(_a, EnumConfig)
|
|
@@ -217,23 +216,21 @@ def test_inheritance():
|
|
|
217
216
|
class B(A):
|
|
218
217
|
y: Param[int] = 3
|
|
219
218
|
|
|
220
|
-
b = B()
|
|
219
|
+
b = B.C()
|
|
221
220
|
b.x = 2
|
|
222
221
|
assert b.__xpm__.values["y"] == 3
|
|
223
222
|
assert b.__xpm__.values["x"] == 2
|
|
224
223
|
|
|
225
224
|
|
|
226
225
|
def test_redefined_param():
|
|
227
|
-
|
|
228
|
-
class A:
|
|
226
|
+
class A(Config):
|
|
229
227
|
x: Param[int]
|
|
230
228
|
|
|
231
|
-
|
|
232
|
-
class B:
|
|
229
|
+
class B(Config):
|
|
233
230
|
x: Param[int] = 3
|
|
234
231
|
|
|
235
|
-
atx = A.C.
|
|
236
|
-
btx = B.C.
|
|
232
|
+
atx = A.C.__getxpmtype__().getArgument("x")
|
|
233
|
+
btx = B.C.__getxpmtype__().getArgument("x")
|
|
237
234
|
|
|
238
235
|
assert atx.required
|
|
239
236
|
|
|
@@ -250,7 +247,7 @@ def test_param_dict():
|
|
|
250
247
|
assert isinstance(xarg.keytype, StrType)
|
|
251
248
|
assert isinstance(xarg.valuetype, IntType)
|
|
252
249
|
|
|
253
|
-
A(x={"OK": 1})
|
|
250
|
+
A.C(x={"OK": 1})
|
|
254
251
|
|
|
255
252
|
with pytest.raises(TypeError):
|
|
256
253
|
A(x={"wrong": "string"})
|
|
@@ -258,18 +255,59 @@ def test_param_dict():
|
|
|
258
255
|
A(x={"wrong": 1.2})
|
|
259
256
|
|
|
260
257
|
|
|
258
|
+
# --- Default
|
|
259
|
+
|
|
260
|
+
|
|
261
|
+
class ConfigWithDefault(Config):
|
|
262
|
+
x: Param[int] = field(default=1)
|
|
263
|
+
|
|
264
|
+
|
|
265
|
+
def test_param_default():
|
|
266
|
+
assert ConfigWithDefault.C().x == 1
|
|
267
|
+
|
|
268
|
+
|
|
269
|
+
class ConfigWithDefaultFactory(Config):
|
|
270
|
+
x: Param[int] = field(default_factory=lambda: 1)
|
|
271
|
+
|
|
272
|
+
|
|
273
|
+
def test_param_default_factory():
|
|
274
|
+
value = ConfigWithDefaultFactory.C()
|
|
275
|
+
context = DirectoryContext(Path("/__fakepath__"))
|
|
276
|
+
value.__xpm__.seal(context)
|
|
277
|
+
assert value.x == 1
|
|
278
|
+
|
|
279
|
+
|
|
261
280
|
# --- Task annotations
|
|
262
281
|
|
|
263
282
|
|
|
264
283
|
def test_default_mismatch():
|
|
265
284
|
"""Test mismatch between default and type"""
|
|
266
285
|
|
|
267
|
-
|
|
268
|
-
class A:
|
|
286
|
+
class A(Config):
|
|
269
287
|
x: Param[int] = 0.2
|
|
270
288
|
|
|
271
289
|
with pytest.raises(TypeError):
|
|
272
|
-
A.
|
|
290
|
+
A.__getxpmtype__().getArgument("x")
|
|
291
|
+
|
|
292
|
+
|
|
293
|
+
# --- Handling default with field
|
|
294
|
+
|
|
295
|
+
|
|
296
|
+
def test_param_default_set():
|
|
297
|
+
"""Test that the default setting is well set"""
|
|
298
|
+
|
|
299
|
+
class A0(Config):
|
|
300
|
+
x: Param[int] = 2
|
|
301
|
+
|
|
302
|
+
assert A0.C().instance().x == 2
|
|
303
|
+
assert A0.C(x=3).instance().x == 3
|
|
304
|
+
|
|
305
|
+
class A(Config):
|
|
306
|
+
x: Param[int] = field(default_factory=lambda: 2)
|
|
307
|
+
|
|
308
|
+
assert A.C().instance().x == 2
|
|
309
|
+
|
|
310
|
+
assert A.C(x=3).instance().x == 3
|
|
273
311
|
|
|
274
312
|
|
|
275
313
|
# --- Handling help annotations
|
|
@@ -72,7 +72,7 @@ def test_progress_basic():
|
|
|
72
72
|
listener = ProgressListener()
|
|
73
73
|
xp.scheduler.addlistener(listener)
|
|
74
74
|
|
|
75
|
-
out = ProgressingTask().submit()
|
|
75
|
+
out = ProgressingTask.C().submit()
|
|
76
76
|
path = out.path # type: Path
|
|
77
77
|
job = out.__xpm__.job
|
|
78
78
|
|
|
@@ -103,7 +103,7 @@ def test_progress_multiple():
|
|
|
103
103
|
listener1 = ProgressListener()
|
|
104
104
|
xp1.scheduler.addlistener(listener1)
|
|
105
105
|
|
|
106
|
-
out = ProgressingTask().submit()
|
|
106
|
+
out = ProgressingTask.C().submit()
|
|
107
107
|
path = out.path # type: Path
|
|
108
108
|
job = out.__xpm__.job
|
|
109
109
|
|
|
@@ -122,7 +122,7 @@ def test_progress_multiple():
|
|
|
122
122
|
listener2 = ProgressListener()
|
|
123
123
|
xp2.scheduler.addlistener(listener2)
|
|
124
124
|
|
|
125
|
-
out = ProgressingTask().submit()
|
|
125
|
+
out = ProgressingTask.C().submit()
|
|
126
126
|
job = out.__xpm__.job # type: CommandLineJob
|
|
127
127
|
logger.info("Waiting for job to start (2)")
|
|
128
128
|
while job.state.notstarted():
|
|
@@ -217,7 +217,7 @@ def test_progress_nested():
|
|
|
217
217
|
listener = ProgressListener()
|
|
218
218
|
xp.scheduler.addlistener(listener)
|
|
219
219
|
|
|
220
|
-
out = NestedProgressingTask().submit()
|
|
220
|
+
out = NestedProgressingTask.C().submit()
|
|
221
221
|
job = out.__xpm__.job
|
|
222
222
|
path = out.path # type: Path
|
|
223
223
|
|
|
@@ -1,70 +1,12 @@
|
|
|
1
|
+
from typing import Optional
|
|
1
2
|
from experimaestro import (
|
|
2
3
|
Config,
|
|
3
|
-
Task,
|
|
4
4
|
Param,
|
|
5
|
-
SerializationLWTask,
|
|
6
|
-
copyconfig,
|
|
7
5
|
state_dict,
|
|
8
6
|
from_state_dict,
|
|
9
7
|
)
|
|
10
8
|
from experimaestro.core.context import SerializationContext
|
|
11
|
-
from experimaestro.core.objects import
|
|
12
|
-
from experimaestro.tests.utils import TemporaryExperiment
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
class SubModel(Config):
|
|
16
|
-
pass
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
class Model(Config):
|
|
20
|
-
submodel: Param[SubModel]
|
|
21
|
-
|
|
22
|
-
def __post_init__(self):
|
|
23
|
-
self.initialized = False
|
|
24
|
-
self.submodel.initialized = False
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
class LoadModel(SerializationLWTask):
|
|
28
|
-
def execute(self):
|
|
29
|
-
self.value.initialized = True
|
|
30
|
-
self.value.submodel.initialized = True
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
class Trainer(Task):
|
|
34
|
-
model: Param[Config]
|
|
35
|
-
|
|
36
|
-
def task_outputs(self, dep):
|
|
37
|
-
model = copyconfig(self.model)
|
|
38
|
-
return model.add_pretasks(dep(LoadModel(value=model)))
|
|
39
|
-
|
|
40
|
-
def execute(self):
|
|
41
|
-
assert not self.model.initialized, "Model not initialized"
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
class Evaluate(Task):
|
|
45
|
-
model: Param[Config]
|
|
46
|
-
is_submodel: Param[bool] = False
|
|
47
|
-
|
|
48
|
-
def execute(self):
|
|
49
|
-
assert self.model.initialized, "Model not initialized"
|
|
50
|
-
if self.is_submodel:
|
|
51
|
-
assert isinstance(self.model, SubModel)
|
|
52
|
-
else:
|
|
53
|
-
assert isinstance(self.model, Model)
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
def test_serializers_xp():
|
|
57
|
-
with TemporaryExperiment("serializers", maxwait=20, port=0):
|
|
58
|
-
model = Model(submodel=SubModel())
|
|
59
|
-
trained_model: Model = Trainer(model=model).submit()
|
|
60
|
-
|
|
61
|
-
# Use the model itself
|
|
62
|
-
Evaluate(model=trained_model).submit()
|
|
63
|
-
|
|
64
|
-
# Use a submodel
|
|
65
|
-
Evaluate(model=trained_model.submodel, is_submodel=True).add_pretasks_from(
|
|
66
|
-
trained_model
|
|
67
|
-
).submit()
|
|
9
|
+
from experimaestro.core.objects import ConfigMixin
|
|
68
10
|
|
|
69
11
|
|
|
70
12
|
class Object1(Config):
|
|
@@ -78,17 +20,35 @@ class Object2(Config):
|
|
|
78
20
|
def test_serializers_serialization():
|
|
79
21
|
context = SerializationContext(save_directory=None)
|
|
80
22
|
|
|
81
|
-
obj1 = Object1()
|
|
82
|
-
obj2 = Object2(object=obj1)
|
|
23
|
+
obj1 = Object1.C()
|
|
24
|
+
obj2 = Object2.C(object=obj1)
|
|
83
25
|
|
|
84
26
|
data = state_dict(context, [obj1, obj2])
|
|
85
27
|
|
|
86
28
|
[obj1, obj2] = from_state_dict(data)
|
|
87
|
-
assert isinstance(obj1, Object1) and isinstance(obj1,
|
|
29
|
+
assert isinstance(obj1, Object1) and isinstance(obj1, ConfigMixin)
|
|
88
30
|
assert isinstance(obj2, Object2)
|
|
89
31
|
assert obj2.object is obj1
|
|
90
32
|
|
|
91
33
|
[obj1, obj2] = from_state_dict(data, as_instance=True)
|
|
92
|
-
assert isinstance(obj1, Object1) and not isinstance(obj1,
|
|
34
|
+
assert isinstance(obj1, Object1) and not isinstance(obj1, ConfigMixin)
|
|
93
35
|
assert isinstance(obj2, Object2)
|
|
94
36
|
assert obj2.object is obj1
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
class SubConfig(Config):
|
|
40
|
+
pass
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
class MultiParamObject(Config):
|
|
44
|
+
opt_a: Param[Optional[int]]
|
|
45
|
+
|
|
46
|
+
x: Param[dict[str, Optional[SubConfig]]]
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
def test_serializers_types():
|
|
50
|
+
context = SerializationContext(save_directory=None)
|
|
51
|
+
|
|
52
|
+
config = MultiParamObject.C(x={"a": None})
|
|
53
|
+
config.__xpm__.seal(context)
|
|
54
|
+
state_dict(context, config)
|
experimaestro/tests/test_ssh.py
CHANGED
|
@@ -1,14 +1,18 @@
|
|
|
1
|
+
import sys
|
|
2
|
+
import pytest
|
|
1
3
|
from experimaestro.connectors.ssh import SshPath
|
|
2
4
|
|
|
3
5
|
# --- Test SSH path and SSH path manipulation
|
|
4
6
|
|
|
5
7
|
|
|
8
|
+
@pytest.mark.skipif(sys.version_info >= (3, 12), reason="requires python3.10 or higher")
|
|
6
9
|
def test_absolute():
|
|
7
10
|
path = SshPath("ssh://host//a/path")
|
|
8
11
|
assert path.host == "host"
|
|
9
12
|
assert path.is_absolute()
|
|
10
13
|
|
|
11
14
|
|
|
15
|
+
@pytest.mark.skipif(sys.version_info >= (3, 12), reason="requires python3.10 or higher")
|
|
12
16
|
def test_relative():
|
|
13
17
|
path = SshPath("ssh://host")
|
|
14
18
|
assert path.host == "host"
|
|
@@ -17,6 +21,7 @@ def test_relative():
|
|
|
17
21
|
assert not path.is_absolute()
|
|
18
22
|
|
|
19
23
|
|
|
24
|
+
@pytest.mark.skipif(sys.version_info >= (3, 12), reason="requires python3.10 or higher")
|
|
20
25
|
def test_relative_withpath():
|
|
21
26
|
path = SshPath("ssh://host/relative/path")
|
|
22
27
|
assert path.host == "host"
|
|
@@ -24,6 +29,7 @@ def test_relative_withpath():
|
|
|
24
29
|
assert not path.is_absolute()
|
|
25
30
|
|
|
26
31
|
|
|
32
|
+
@pytest.mark.skipif(sys.version_info >= (3, 12), reason="requires python3.10 or higher")
|
|
27
33
|
def test_relative_absolute():
|
|
28
34
|
path = SshPath("ssh://host") / "/absolute/path"
|
|
29
35
|
assert path.host == "host"
|
|
@@ -31,6 +37,7 @@ def test_relative_absolute():
|
|
|
31
37
|
assert path.is_absolute()
|
|
32
38
|
|
|
33
39
|
|
|
40
|
+
@pytest.mark.skipif(sys.version_info >= (3, 12), reason="requires python3.10 or higher")
|
|
34
41
|
def test_relative_compose():
|
|
35
42
|
path = SshPath("ssh://host/abc") / "relative/path"
|
|
36
43
|
assert path.host == "host"
|
experimaestro/tests/test_tags.py
CHANGED
|
@@ -2,8 +2,7 @@ from typing import Dict
|
|
|
2
2
|
from pathlib import Path
|
|
3
3
|
from experimaestro import (
|
|
4
4
|
tag,
|
|
5
|
-
|
|
6
|
-
argument,
|
|
5
|
+
LightweightTask,
|
|
7
6
|
Config,
|
|
8
7
|
Task,
|
|
9
8
|
Param,
|
|
@@ -12,34 +11,30 @@ from experimaestro.scheduler.workspace import RunMode
|
|
|
12
11
|
from experimaestro.xpmutils import DirectoryContext
|
|
13
12
|
|
|
14
13
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
class Config1:
|
|
18
|
-
pass
|
|
14
|
+
class Config1(Config):
|
|
15
|
+
x: Param[int]
|
|
19
16
|
|
|
20
17
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
class Config2:
|
|
25
|
-
pass
|
|
18
|
+
class Config2(Config):
|
|
19
|
+
x: Param[int]
|
|
20
|
+
c: Param[Config1]
|
|
26
21
|
|
|
27
22
|
|
|
28
23
|
def test_tag():
|
|
29
|
-
c = Config1(x=5)
|
|
24
|
+
c = Config1.C(x=5)
|
|
30
25
|
c.tag("x", 5)
|
|
31
26
|
assert c.tags() == {"x": 5}
|
|
32
27
|
|
|
33
28
|
|
|
34
29
|
def test_taggedvalue():
|
|
35
|
-
c = Config1(x=tag(5))
|
|
30
|
+
c = Config1.C(x=tag(5))
|
|
36
31
|
assert c.tags() == {"x": 5}
|
|
37
32
|
|
|
38
33
|
|
|
39
34
|
def test_tagcontain():
|
|
40
35
|
"""Test that tags are not propagated to the upper configurations"""
|
|
41
|
-
c1 = Config1(x=5)
|
|
42
|
-
c2 = Config2(c=c1, x=tag(3)).tag("out", 1)
|
|
36
|
+
c1 = Config1.C(x=5)
|
|
37
|
+
c2 = Config2.C(c=c1, x=tag(3)).tag("out", 1)
|
|
43
38
|
assert c1.tags() == {}
|
|
44
39
|
assert c2.tags() == {"x": 3, "out": 1}
|
|
45
40
|
|
|
@@ -55,20 +50,54 @@ def test_inneroutput():
|
|
|
55
50
|
class Evaluate(Task):
|
|
56
51
|
task: Param[MyTask]
|
|
57
52
|
|
|
58
|
-
output = Output().tag("hello", "world")
|
|
59
|
-
task = MyTask(outputs={}, mainoutput=output)
|
|
53
|
+
output = Output.C().tag("hello", "world")
|
|
54
|
+
task = MyTask.C(outputs={}, mainoutput=output)
|
|
60
55
|
task.submit(run_mode=RunMode.DRY_RUN)
|
|
61
56
|
assert output.tags() == {"hello": "world"}
|
|
62
57
|
|
|
63
|
-
output = Output().tag("hello", "world")
|
|
64
|
-
task = MyTask(outputs={"a": output}, mainoutput=Output())
|
|
58
|
+
output = Output.C().tag("hello", "world")
|
|
59
|
+
task = MyTask.C(outputs={"a": output}, mainoutput=Output.C())
|
|
65
60
|
task.submit(run_mode=RunMode.DRY_RUN)
|
|
66
61
|
assert output.tags() == {"hello": "world"}
|
|
67
62
|
|
|
68
|
-
evaluate = Evaluate(task=task).submit(run_mode=RunMode.DRY_RUN)
|
|
63
|
+
evaluate = Evaluate.C(task=task).submit(run_mode=RunMode.DRY_RUN)
|
|
69
64
|
assert evaluate.__xpm__.tags() == {"hello": "world"}
|
|
70
65
|
|
|
71
66
|
|
|
67
|
+
def test_tags_init_tasks():
|
|
68
|
+
"""Test tags within init tasks"""
|
|
69
|
+
|
|
70
|
+
class MyTask(Task):
|
|
71
|
+
pass
|
|
72
|
+
|
|
73
|
+
class InitTask(LightweightTask):
|
|
74
|
+
pass
|
|
75
|
+
|
|
76
|
+
class MyConfig(Config):
|
|
77
|
+
pass
|
|
78
|
+
|
|
79
|
+
class TaskWithOutput(Task):
|
|
80
|
+
x: Param[MyConfig]
|
|
81
|
+
|
|
82
|
+
def task_outputs(self, dep) -> MyConfig:
|
|
83
|
+
return dep(MyConfig.C())
|
|
84
|
+
|
|
85
|
+
init_task = InitTask.C().tag("hello", "world")
|
|
86
|
+
task = MyTask.C()
|
|
87
|
+
result = task.submit(run_mode=RunMode.DRY_RUN, init_tasks=[init_task])
|
|
88
|
+
assert result.tags() == {"hello": "world"}
|
|
89
|
+
|
|
90
|
+
other_task = TaskWithOutput.C(x=MyConfig.C().tag("hello", "world"))
|
|
91
|
+
assert other_task.tags() == {"hello": "world"}
|
|
92
|
+
|
|
93
|
+
result = other_task.submit(run_mode=RunMode.DRY_RUN)
|
|
94
|
+
assert isinstance(result, MyConfig)
|
|
95
|
+
assert result.tags() == {"hello": "world"}
|
|
96
|
+
|
|
97
|
+
result = MyTask.C().submit(run_mode=RunMode.DRY_RUN, init_tasks=[result])
|
|
98
|
+
assert result.tags() == {"hello": "world"}
|
|
99
|
+
|
|
100
|
+
|
|
72
101
|
class TaskDirectoryContext(DirectoryContext):
|
|
73
102
|
def __init__(self, task, path):
|
|
74
103
|
super().__init__(path)
|
|
@@ -86,6 +115,6 @@ def test_objects_tags():
|
|
|
86
115
|
x: Param[int]
|
|
87
116
|
|
|
88
117
|
context = DirectoryContext(Path("/__fakepath__"))
|
|
89
|
-
a = A(x=tag(1))
|
|
118
|
+
a = A.C(x=tag(1))
|
|
90
119
|
a.__xpm__.seal(context)
|
|
91
120
|
assert a.__xpm__.tags() == {"x": 1}
|