experimaestro 1.6.2__py3-none-any.whl → 1.7.0rc0__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/connectors/__init__.py +17 -8
- experimaestro/connectors/local.py +8 -3
- 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/generators.py +6 -1
- experimaestro/ipc.py +4 -1
- experimaestro/launcherfinder/registry.py +7 -4
- experimaestro/notifications.py +1 -1
- experimaestro/run.py +1 -1
- experimaestro/scheduler/base.py +94 -10
- experimaestro/scheduler/dynamic_outputs.py +184 -0
- 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/taskglobals.py +7 -2
- experimaestro/tests/definitions_types.py +5 -3
- experimaestro/tests/launchers/bin/sbatch +18 -5
- experimaestro/tests/launchers/common.py +11 -3
- 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.2.dist-info → experimaestro-1.7.0rc0.dist-info}/METADATA +3 -4
- {experimaestro-1.6.2.dist-info → experimaestro-1.7.0rc0.dist-info}/RECORD +71 -40
- {experimaestro-1.6.2.dist-info → experimaestro-1.7.0rc0.dist-info}/WHEEL +1 -1
- {experimaestro-1.6.2.dist-info → experimaestro-1.7.0rc0.dist-info}/LICENSE +0 -0
- {experimaestro-1.6.2.dist-info → experimaestro-1.7.0rc0.dist-info}/entry_points.txt +0 -0
|
@@ -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]
|
|
@@ -179,8 +179,7 @@ def test_config_class():
|
|
|
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
185
|
a = A()
|
|
@@ -224,16 +223,14 @@ def test_inheritance():
|
|
|
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
|
|
|
@@ -258,18 +255,39 @@ 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().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()
|
|
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")
|
|
273
291
|
|
|
274
292
|
|
|
275
293
|
# --- Handling help annotations
|
experimaestro/tests/test_tags.py
CHANGED
|
@@ -3,8 +3,6 @@ from pathlib import Path
|
|
|
3
3
|
from experimaestro import (
|
|
4
4
|
tag,
|
|
5
5
|
LightweightTask,
|
|
6
|
-
config,
|
|
7
|
-
argument,
|
|
8
6
|
Config,
|
|
9
7
|
Task,
|
|
10
8
|
Param,
|
|
@@ -13,17 +11,13 @@ from experimaestro.scheduler.workspace import RunMode
|
|
|
13
11
|
from experimaestro.xpmutils import DirectoryContext
|
|
14
12
|
|
|
15
13
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
class Config1:
|
|
19
|
-
pass
|
|
14
|
+
class Config1(Config):
|
|
15
|
+
x: Param[int]
|
|
20
16
|
|
|
21
17
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
class Config2:
|
|
26
|
-
pass
|
|
18
|
+
class Config2(Config):
|
|
19
|
+
x: Param[int]
|
|
20
|
+
c: Param[Config1]
|
|
27
21
|
|
|
28
22
|
|
|
29
23
|
def test_tag():
|
|
@@ -7,7 +7,7 @@ import time
|
|
|
7
7
|
from pathlib import Path
|
|
8
8
|
|
|
9
9
|
import subprocess
|
|
10
|
-
from experimaestro import Task,
|
|
10
|
+
from experimaestro import Task, Param
|
|
11
11
|
from experimaestro.tokens import CounterToken, TokenFile
|
|
12
12
|
from experimaestro.scheduler import JobState
|
|
13
13
|
from .utils import (
|
|
@@ -74,8 +74,9 @@ def test_token_ok():
|
|
|
74
74
|
logging.info("Finished token_ok test")
|
|
75
75
|
|
|
76
76
|
|
|
77
|
-
@param("x", type=int)
|
|
78
77
|
class dummy_task(Task):
|
|
78
|
+
x: Param[int]
|
|
79
|
+
|
|
79
80
|
def execute(self):
|
|
80
81
|
pass
|
|
81
82
|
|
|
@@ -1,31 +1,26 @@
|
|
|
1
1
|
# --- Task and types definitions
|
|
2
2
|
|
|
3
3
|
import logging
|
|
4
|
-
from experimaestro import Config,
|
|
5
|
-
from
|
|
4
|
+
from experimaestro import Config, Param
|
|
5
|
+
from typing import Union
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
from experimaestro.
|
|
7
|
+
import pytest
|
|
8
|
+
from experimaestro.core.objects import TypeConfig
|
|
9
9
|
|
|
10
10
|
|
|
11
11
|
def test_multiple_inheritance():
|
|
12
|
-
|
|
13
|
-
class A:
|
|
12
|
+
class A(Config):
|
|
14
13
|
pass
|
|
15
14
|
|
|
16
|
-
|
|
17
|
-
class B:
|
|
15
|
+
class B(Config):
|
|
18
16
|
pass
|
|
19
17
|
|
|
20
|
-
@config()
|
|
21
18
|
class B1(B):
|
|
22
19
|
pass
|
|
23
20
|
|
|
24
|
-
@config()
|
|
25
21
|
class C1(B1, A):
|
|
26
22
|
pass
|
|
27
23
|
|
|
28
|
-
@config()
|
|
29
24
|
class C2(A, B1):
|
|
30
25
|
pass
|
|
31
26
|
|
|
@@ -45,14 +40,12 @@ def test_multiple_inheritance():
|
|
|
45
40
|
|
|
46
41
|
|
|
47
42
|
def test_missing_hierarchy():
|
|
48
|
-
|
|
49
|
-
class A:
|
|
43
|
+
class A(Config):
|
|
50
44
|
pass
|
|
51
45
|
|
|
52
46
|
class A1(A):
|
|
53
47
|
pass
|
|
54
48
|
|
|
55
|
-
@config()
|
|
56
49
|
class B(A1):
|
|
57
50
|
pass
|
|
58
51
|
|
|
@@ -60,3 +53,13 @@ def test_missing_hierarchy():
|
|
|
60
53
|
|
|
61
54
|
assert issubclass(B, A)
|
|
62
55
|
assert issubclass(B, A1)
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
def test_types_union():
|
|
59
|
+
class A(Config):
|
|
60
|
+
x: Param[Union[int, str]]
|
|
61
|
+
|
|
62
|
+
A(x=1)
|
|
63
|
+
A(x="hello")
|
|
64
|
+
with pytest.raises(ValueError):
|
|
65
|
+
A(x=[])
|
|
@@ -2,18 +2,9 @@
|
|
|
2
2
|
|
|
3
3
|
import pytest
|
|
4
4
|
from pathlib import Path
|
|
5
|
-
from experimaestro import
|
|
6
|
-
config,
|
|
7
|
-
Task,
|
|
8
|
-
Identifier,
|
|
9
|
-
argument,
|
|
10
|
-
pathoption,
|
|
11
|
-
ConstantParam,
|
|
12
|
-
Param,
|
|
13
|
-
Config,
|
|
14
|
-
)
|
|
5
|
+
from experimaestro import Task, field, Identifier, Constant, Param, Config, Meta
|
|
15
6
|
from enum import Enum
|
|
16
|
-
|
|
7
|
+
from experimaestro.generators import PathGenerator
|
|
17
8
|
from experimaestro.scheduler import Job, JobContext
|
|
18
9
|
from experimaestro.scheduler.workspace import RunMode
|
|
19
10
|
from .utils import TemporaryExperiment
|
|
@@ -31,57 +22,50 @@ def expect_notvalidate(value):
|
|
|
31
22
|
value.__xpm__.validate()
|
|
32
23
|
|
|
33
24
|
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
class A:
|
|
37
|
-
pass
|
|
25
|
+
class A(Config):
|
|
26
|
+
value: Param[int]
|
|
38
27
|
|
|
39
28
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
class B:
|
|
43
|
-
pass
|
|
29
|
+
class B(Config):
|
|
30
|
+
a: Param[A]
|
|
44
31
|
|
|
45
32
|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
class C:
|
|
33
|
+
class C(Config):
|
|
34
|
+
path: Meta[Path] = field(default_factory=PathGenerator("outdir"))
|
|
49
35
|
pass
|
|
50
36
|
|
|
51
37
|
|
|
52
|
-
def
|
|
38
|
+
def test_validation_simple():
|
|
53
39
|
expect_validate(A(value=1))
|
|
54
40
|
|
|
55
41
|
|
|
56
|
-
def
|
|
42
|
+
def test_validation_missing():
|
|
57
43
|
expect_notvalidate(A())
|
|
58
44
|
|
|
59
45
|
|
|
60
|
-
def
|
|
46
|
+
def test_validation_simple_nested():
|
|
61
47
|
b = B()
|
|
62
48
|
b.a = A(value=1)
|
|
63
49
|
expect_validate(b)
|
|
64
50
|
|
|
65
51
|
|
|
66
|
-
def
|
|
52
|
+
def test_validation_missing_nested():
|
|
67
53
|
b = B()
|
|
68
54
|
b.a = A()
|
|
69
55
|
expect_notvalidate(b)
|
|
70
56
|
|
|
71
57
|
|
|
72
|
-
def
|
|
73
|
-
|
|
74
|
-
|
|
58
|
+
def test_validation_type():
|
|
59
|
+
class A(Config):
|
|
60
|
+
__xpmid__ = valns.type.a
|
|
75
61
|
pass
|
|
76
62
|
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
pass
|
|
63
|
+
class B(Config):
|
|
64
|
+
__xpmid__ = valns.type.b
|
|
80
65
|
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
pass
|
|
66
|
+
class C(Config):
|
|
67
|
+
a: Param[A]
|
|
68
|
+
__xpmid__ = valns.type.c
|
|
85
69
|
|
|
86
70
|
with pytest.raises(ValueError):
|
|
87
71
|
C(a=B())
|
|
@@ -91,30 +75,26 @@ def test_type():
|
|
|
91
75
|
c.a = B()
|
|
92
76
|
|
|
93
77
|
|
|
94
|
-
def
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
pass
|
|
78
|
+
def test_validation_subtype():
|
|
79
|
+
class A(Config):
|
|
80
|
+
__xpmid__ = valns.subtype.a
|
|
98
81
|
|
|
99
|
-
@config(valns.subtype.a1)
|
|
100
82
|
class A1(A):
|
|
101
|
-
|
|
83
|
+
__xpmid__ = valns.subtype.a1
|
|
102
84
|
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
pass
|
|
85
|
+
class B(Config):
|
|
86
|
+
__xpmid__ = valns.subtype.b
|
|
87
|
+
a: Param[A]
|
|
107
88
|
|
|
108
89
|
expect_validate(B(a=A1()))
|
|
109
90
|
|
|
110
91
|
|
|
111
|
-
def
|
|
112
|
-
"""Test of
|
|
92
|
+
def test_validation_path_generator():
|
|
93
|
+
"""Test of path generator"""
|
|
113
94
|
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
pass
|
|
95
|
+
class A(Config):
|
|
96
|
+
__xpmid__ = valns.path.a
|
|
97
|
+
value: Meta[Path] = field(default_factory=PathGenerator("file.txt"))
|
|
118
98
|
|
|
119
99
|
a = A()
|
|
120
100
|
a.__xpm__.validate()
|
|
@@ -129,13 +109,12 @@ def test_path():
|
|
|
129
109
|
assert a.value.parents[3] == xp.workspace.path
|
|
130
110
|
|
|
131
111
|
|
|
132
|
-
def
|
|
133
|
-
"""Test of
|
|
112
|
+
def test_validation_constant():
|
|
113
|
+
"""Test of constant"""
|
|
134
114
|
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
pass
|
|
115
|
+
class A(Config):
|
|
116
|
+
__xpmid__ = valns.constant.a
|
|
117
|
+
value: Constant[int] = 1
|
|
139
118
|
|
|
140
119
|
a = A()
|
|
141
120
|
a.__xpm__.validate()
|
|
@@ -145,31 +124,26 @@ def test_constant():
|
|
|
145
124
|
assert a.value == 1
|
|
146
125
|
|
|
147
126
|
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
class Parent:
|
|
151
|
-
pass
|
|
127
|
+
class Parent(Config):
|
|
128
|
+
x: Param[int]
|
|
152
129
|
|
|
153
130
|
|
|
154
|
-
@config()
|
|
155
131
|
class Child(Parent):
|
|
156
132
|
pass
|
|
157
133
|
|
|
158
134
|
|
|
159
|
-
def
|
|
135
|
+
def test_validation_child():
|
|
160
136
|
expect_validate(Child(x=1))
|
|
161
137
|
|
|
162
138
|
|
|
163
139
|
# --- Path argument checks
|
|
164
140
|
|
|
165
141
|
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
class PathParent:
|
|
169
|
-
pass
|
|
142
|
+
class PathParent(Config):
|
|
143
|
+
x: Meta[Path] = field(default_factory=PathGenerator("x"))
|
|
170
144
|
|
|
171
145
|
|
|
172
|
-
def
|
|
146
|
+
def test_validation_path_option():
|
|
173
147
|
c = PathParent()
|
|
174
148
|
expect_validate(c)
|
|
175
149
|
|
|
@@ -177,28 +151,11 @@ def test_path_option():
|
|
|
177
151
|
# --- Default value
|
|
178
152
|
|
|
179
153
|
|
|
180
|
-
|
|
181
|
-
"value,apitype",
|
|
182
|
-
[(1.5, types.FloatType), (1, types.IntType), (False, types.BoolType)],
|
|
183
|
-
)
|
|
184
|
-
def test_default(value, apitype):
|
|
185
|
-
@argument("default", default=value)
|
|
186
|
-
@config(valns.default[str(type(value))])
|
|
187
|
-
class Default:
|
|
188
|
-
pass
|
|
189
|
-
|
|
190
|
-
value = Default()
|
|
191
|
-
expect_validate(value)
|
|
192
|
-
assert Default.__xpmtype__.arguments["default"].type.__class__ == apitype
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
def test_seal():
|
|
154
|
+
def test_validation_seal():
|
|
196
155
|
"""Test value sealing"""
|
|
197
156
|
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
class A:
|
|
201
|
-
pass
|
|
157
|
+
class A(Config):
|
|
158
|
+
a: Param[int]
|
|
202
159
|
|
|
203
160
|
a = A(a=2)
|
|
204
161
|
a.__xpm__.seal(EmptyContext())
|
|
@@ -207,7 +164,7 @@ def test_seal():
|
|
|
207
164
|
a.a = 1
|
|
208
165
|
|
|
209
166
|
|
|
210
|
-
def
|
|
167
|
+
def test_validation_validation_enum():
|
|
211
168
|
"""Path arguments should be ignored"""
|
|
212
169
|
|
|
213
170
|
class EnumParam(Enum):
|
|
@@ -241,7 +198,7 @@ class TaskConfigConsumer(Config):
|
|
|
241
198
|
x: Param[TaskParentConfig]
|
|
242
199
|
|
|
243
200
|
|
|
244
|
-
def
|
|
201
|
+
def test_validation_taskargument():
|
|
245
202
|
x = taskconfig()
|
|
246
203
|
with TemporaryExperiment("fake"):
|
|
247
204
|
x.submit(run_mode=RunMode.DRY_RUN)
|
experimaestro/tokens.py
CHANGED
|
@@ -59,6 +59,8 @@ class CounterTokenLock(Lock):
|
|
|
59
59
|
|
|
60
60
|
|
|
61
61
|
class CounterTokenDependency(Dependency):
|
|
62
|
+
"""A dependency onto a token"""
|
|
63
|
+
|
|
62
64
|
def __init__(self, token: "CounterToken", count: int):
|
|
63
65
|
super().__init__(token)
|
|
64
66
|
self._token = token
|
|
@@ -66,6 +68,7 @@ class CounterTokenDependency(Dependency):
|
|
|
66
68
|
|
|
67
69
|
@property
|
|
68
70
|
def name(self):
|
|
71
|
+
"""The (file) name for this dependency, when taken"""
|
|
69
72
|
return f"{self.target.identifier}.token"
|
|
70
73
|
|
|
71
74
|
def status(self) -> DependencyStatus:
|
|
@@ -165,7 +168,7 @@ class CounterToken(Token, FileSystemEventHandler):
|
|
|
165
168
|
- TIMESTAMP.token contains (1) the number of tokens (2) the job URI
|
|
166
169
|
"""
|
|
167
170
|
|
|
168
|
-
"""Maps
|
|
171
|
+
"""Maps token keys to CounterToken instances"""
|
|
169
172
|
TOKENS: Dict[str, "CounterToken"] = {}
|
|
170
173
|
|
|
171
174
|
@staticmethod
|
|
@@ -193,7 +196,7 @@ class CounterToken(Token, FileSystemEventHandler):
|
|
|
193
196
|
"""[summary]
|
|
194
197
|
|
|
195
198
|
Arguments:
|
|
196
|
-
path {Path} -- The file path of the token
|
|
199
|
+
path {Path} -- The file path of the token directory
|
|
197
200
|
count {int} -- Number of tokens (overrides previous definitions)
|
|
198
201
|
force -- If the token has already been created, force to write the maximum
|
|
199
202
|
number of tokens
|
|
@@ -298,6 +301,9 @@ class CounterToken(Token, FileSystemEventHandler):
|
|
|
298
301
|
tokenfile = TokenFile(path)
|
|
299
302
|
tokenfile.watch()
|
|
300
303
|
self.cache[path.name] = tokenfile
|
|
304
|
+
except FileNotFoundError:
|
|
305
|
+
# We did not find the token file... just ignore
|
|
306
|
+
pass
|
|
301
307
|
except Exception:
|
|
302
308
|
logger.exception("Uncaught exception in on_modified handler")
|
|
303
309
|
raise
|
|
@@ -336,13 +342,18 @@ class CounterToken(Token, FileSystemEventHandler):
|
|
|
336
342
|
for dependency in dependents:
|
|
337
343
|
dependency.check()
|
|
338
344
|
|
|
345
|
+
# A modified dependency not in cache
|
|
339
346
|
elif path.name.endswith(".token") and path.name not in self.cache:
|
|
340
347
|
with self.lock:
|
|
341
348
|
if path.name not in self.cache:
|
|
342
349
|
logger.debug("Token file not in cache %s", path.name)
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
350
|
+
try:
|
|
351
|
+
tokenfile = TokenFile(path)
|
|
352
|
+
tokenfile.watch()
|
|
353
|
+
self.cache[path.name] = tokenfile
|
|
354
|
+
except FileNotFoundError:
|
|
355
|
+
# Well, the file did not exist anymore...
|
|
356
|
+
pass
|
|
346
357
|
except Exception:
|
|
347
358
|
logger.exception("Uncaught exception in on_modified handler")
|
|
348
359
|
raise
|
experimaestro/typingutils.py
CHANGED
|
@@ -26,6 +26,13 @@ def isgenericalias(typehint):
|
|
|
26
26
|
return isinstance(typehint, GenericAlias)
|
|
27
27
|
|
|
28
28
|
|
|
29
|
+
def get_union(typehint):
|
|
30
|
+
"""Return the list of types of a union (or the type itself if it is not an union)"""
|
|
31
|
+
if isgenericalias(typehint) and typehint.__origin__ == typing.Union:
|
|
32
|
+
return typehint.__args__
|
|
33
|
+
return None
|
|
34
|
+
|
|
35
|
+
|
|
29
36
|
def get_optional(typehint):
|
|
30
37
|
if isgenericalias(typehint) and typehint.__origin__ == typing.Union:
|
|
31
38
|
if len(typehint.__args__) == 2:
|
experimaestro/utils/asyncio.py
CHANGED
|
@@ -10,8 +10,12 @@ def asyncThreadcheck(name, func, *args, **kwargs) -> asyncio.Future:
|
|
|
10
10
|
|
|
11
11
|
def dowait():
|
|
12
12
|
logging.debug("Running %s", func)
|
|
13
|
-
|
|
14
|
-
|
|
13
|
+
try:
|
|
14
|
+
result = func(*args, **kwargs)
|
|
15
|
+
logging.debug("Got result from %s", func)
|
|
16
|
+
except Exception:
|
|
17
|
+
logging.exception("Got an error in the thread")
|
|
18
|
+
raise
|
|
15
19
|
loop.call_soon_threadsafe(future.set_result, result)
|
|
16
20
|
|
|
17
21
|
# Start thread
|
experimaestro/utils/resources.py
CHANGED
|
@@ -24,15 +24,19 @@ class ResourcePathWrapper(PathLike):
|
|
|
24
24
|
parents = [s.name for s in reversed(self.path.parents)][1:]
|
|
25
25
|
return ".".join(parents)
|
|
26
26
|
|
|
27
|
-
@
|
|
27
|
+
@cached_property
|
|
28
28
|
def name(self):
|
|
29
29
|
return self.path.name
|
|
30
30
|
|
|
31
31
|
def is_file(self):
|
|
32
|
-
return
|
|
32
|
+
return any(
|
|
33
|
+
traversable.name == self.name and traversable.is_file()
|
|
34
|
+
for traversable in resources.files(self.package).iterdir()
|
|
35
|
+
)
|
|
33
36
|
|
|
34
37
|
def __fspath__(self):
|
|
35
|
-
|
|
38
|
+
"""Return the file system path representation of the object"""
|
|
39
|
+
return resources.as_file(resources.files(self.package) / self.name)
|
|
36
40
|
|
|
37
41
|
@contextmanager
|
|
38
42
|
def open(self, *args, **kwargs):
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
Metadata-Version: 2.3
|
|
2
2
|
Name: experimaestro
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.7.0rc0
|
|
4
4
|
Summary: "Experimaestro is a computer science experiment manager"
|
|
5
5
|
License: GPL-3
|
|
6
6
|
Keywords: experiment manager
|
|
7
7
|
Author: Benjamin Piwowarski
|
|
8
8
|
Author-email: benjamin@piwowarski.fr
|
|
9
|
-
Requires-Python: >=3.
|
|
9
|
+
Requires-Python: >=3.9,<4.0
|
|
10
10
|
Classifier: Development Status :: 4 - Beta
|
|
11
11
|
Classifier: Intended Audience :: Science/Research
|
|
12
12
|
Classifier: License :: OSI Approved :: GNU General Public License v3 (GPLv3)
|
|
@@ -14,7 +14,6 @@ Classifier: License :: Other/Proprietary License
|
|
|
14
14
|
Classifier: Operating System :: OS Independent
|
|
15
15
|
Classifier: Programming Language :: Python
|
|
16
16
|
Classifier: Programming Language :: Python :: 3
|
|
17
|
-
Classifier: Programming Language :: Python :: 3.8
|
|
18
17
|
Classifier: Programming Language :: Python :: 3.9
|
|
19
18
|
Classifier: Programming Language :: Python :: 3.10
|
|
20
19
|
Classifier: Programming Language :: Python :: 3.11
|
|
@@ -35,7 +34,7 @@ Requires-Dist: huggingface-hub (>0.17)
|
|
|
35
34
|
Requires-Dist: humanfriendly (>=10,<11)
|
|
36
35
|
Requires-Dist: marshmallow (>=3.20,<4.0)
|
|
37
36
|
Requires-Dist: omegaconf (>=2.3,<3.0)
|
|
38
|
-
Requires-Dist: psutil (>=
|
|
37
|
+
Requires-Dist: psutil (>=7)
|
|
39
38
|
Requires-Dist: pyparsing (>=3.1,<4.0)
|
|
40
39
|
Requires-Dist: pytools (>=2023.1.1,<2024.0.0)
|
|
41
40
|
Requires-Dist: pyyaml (>=6.0.1,<7.0.0)
|