experimaestro 1.13.0__py3-none-any.whl → 1.14.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/core/arguments.py +10 -1
- experimaestro/core/objects/config.py +73 -16
- experimaestro/core/objects/config_walk.py +3 -1
- experimaestro/tests/test_generators.py +63 -3
- {experimaestro-1.13.0.dist-info → experimaestro-1.14.0.dist-info}/METADATA +1 -1
- {experimaestro-1.13.0.dist-info → experimaestro-1.14.0.dist-info}/RECORD +9 -9
- {experimaestro-1.13.0.dist-info → experimaestro-1.14.0.dist-info}/WHEEL +0 -0
- {experimaestro-1.13.0.dist-info → experimaestro-1.14.0.dist-info}/entry_points.txt +0 -0
- {experimaestro-1.13.0.dist-info → experimaestro-1.14.0.dist-info}/licenses/LICENSE +0 -0
experimaestro/core/arguments.py
CHANGED
|
@@ -80,10 +80,12 @@ class Argument:
|
|
|
80
80
|
|
|
81
81
|
self.generator = generator
|
|
82
82
|
self.default = None
|
|
83
|
+
self.ignore_generated = False
|
|
83
84
|
|
|
84
85
|
if default is not None:
|
|
85
86
|
assert self.generator is None, "generator and default are exclusive options"
|
|
86
87
|
if isinstance(default, field):
|
|
88
|
+
self.ignore_generated = default.ignore_generated
|
|
87
89
|
if default.default is not None:
|
|
88
90
|
self.default = default.default
|
|
89
91
|
elif default.default_factory is not None:
|
|
@@ -184,13 +186,20 @@ DataPath = Annotated[Path, dataHint]
|
|
|
184
186
|
class field:
|
|
185
187
|
"""Extra information for a given experimaestro field (param or meta)"""
|
|
186
188
|
|
|
187
|
-
def __init__(
|
|
189
|
+
def __init__(
|
|
190
|
+
self,
|
|
191
|
+
*,
|
|
192
|
+
default: Any = None,
|
|
193
|
+
default_factory: Callable = None,
|
|
194
|
+
ignore_generated=False,
|
|
195
|
+
):
|
|
188
196
|
assert not (
|
|
189
197
|
(default is not None) and (default_factory is not None)
|
|
190
198
|
), "default and default_factory are mutually exclusive options"
|
|
191
199
|
|
|
192
200
|
self.default_factory = default_factory
|
|
193
201
|
self.default = default
|
|
202
|
+
self.ignore_generated = ignore_generated
|
|
194
203
|
|
|
195
204
|
|
|
196
205
|
class help(TypeAnnotation):
|
|
@@ -170,10 +170,33 @@ class ConfigInformation:
|
|
|
170
170
|
self._sealed = False
|
|
171
171
|
self._meta = None
|
|
172
172
|
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
self.
|
|
173
|
+
# This contains the list of generated values (using context) in this
|
|
174
|
+
# configuration or any sub-configuration, is generated. This prevents
|
|
175
|
+
# problem when a configuration with generated values is re-used.
|
|
176
|
+
self._generated_values = []
|
|
177
|
+
|
|
178
|
+
def get_generated_paths(
|
|
179
|
+
self, path: list[str] = None, paths: list[str] = None
|
|
180
|
+
) -> list[str]:
|
|
181
|
+
"""Get the list of generated paths, useful to track down those
|
|
182
|
+
|
|
183
|
+
:param path: The current path
|
|
184
|
+
:param paths: The list of generated paths so far, defaults to None
|
|
185
|
+
:return: The full list of generated paths
|
|
186
|
+
"""
|
|
187
|
+
paths = [] if paths is None else paths
|
|
188
|
+
path = [] if path is None else path
|
|
189
|
+
|
|
190
|
+
for key in self._generated_values:
|
|
191
|
+
value = self.values[key]
|
|
192
|
+
if isinstance(value, ConfigMixin) and value.__xpm__._generated_values:
|
|
193
|
+
path.append(key)
|
|
194
|
+
value.__xpm__.get_generated_paths(path, paths)
|
|
195
|
+
path.pop()
|
|
196
|
+
else:
|
|
197
|
+
paths.append(".".join(path + [key]))
|
|
198
|
+
|
|
199
|
+
return paths
|
|
177
200
|
|
|
178
201
|
def set_meta(self, value: Optional[bool]):
|
|
179
202
|
"""Sets the meta flag"""
|
|
@@ -192,6 +215,31 @@ class ConfigInformation:
|
|
|
192
215
|
# Not an argument, bypass
|
|
193
216
|
return object.__getattribute__(self.pyobject, name)
|
|
194
217
|
|
|
218
|
+
@staticmethod
|
|
219
|
+
def is_generated_value(argument, value):
|
|
220
|
+
if argument.ignore_generated:
|
|
221
|
+
return False
|
|
222
|
+
|
|
223
|
+
if value is None:
|
|
224
|
+
return False
|
|
225
|
+
|
|
226
|
+
if isinstance(value, (int, str, float, bool, Path)):
|
|
227
|
+
return False
|
|
228
|
+
|
|
229
|
+
if isinstance(value, ConfigMixin):
|
|
230
|
+
return value.__xpm__._generated_values and value.__xpm__.task is None
|
|
231
|
+
|
|
232
|
+
if isinstance(value, list):
|
|
233
|
+
return any(ConfigInformation.is_generated_value(argument, x) for x in value)
|
|
234
|
+
|
|
235
|
+
if isinstance(value, dict):
|
|
236
|
+
return any(
|
|
237
|
+
ConfigInformation.is_generated_value(argument, x)
|
|
238
|
+
for x in value.values()
|
|
239
|
+
)
|
|
240
|
+
|
|
241
|
+
return False
|
|
242
|
+
|
|
195
243
|
def set(self, k, v, bypass=False):
|
|
196
244
|
from experimaestro.generators import Generator
|
|
197
245
|
|
|
@@ -208,18 +256,16 @@ class ConfigInformation:
|
|
|
208
256
|
"Configuration (and not objects) should be used. Consider using .C(...)"
|
|
209
257
|
)
|
|
210
258
|
|
|
211
|
-
if (
|
|
212
|
-
isinstance(v, ConfigMixin)
|
|
213
|
-
and v.__xpm__._has_generated_value
|
|
214
|
-
and v.__xpm__.task is None
|
|
215
|
-
):
|
|
216
|
-
raise AttributeError(
|
|
217
|
-
f"Cannot set {k} to a configuration with generated values"
|
|
218
|
-
)
|
|
219
|
-
|
|
220
259
|
try:
|
|
221
260
|
argument = self.xpmtype.arguments.get(k, None)
|
|
222
261
|
if argument:
|
|
262
|
+
if ConfigInformation.is_generated_value(argument, v):
|
|
263
|
+
raise AttributeError(
|
|
264
|
+
f"Cannot set {k} to a configuration with generated values. "
|
|
265
|
+
"Here is the list of paths to help you: "
|
|
266
|
+
f"""{', '.join(v.__xpm__.get_generated_paths([k]))}"""
|
|
267
|
+
)
|
|
268
|
+
|
|
223
269
|
if not bypass and (
|
|
224
270
|
(isinstance(argument.generator, Generator)) or argument.constant
|
|
225
271
|
):
|
|
@@ -345,6 +391,15 @@ class ConfigInformation:
|
|
|
345
391
|
Arguments:
|
|
346
392
|
- context: the generation context
|
|
347
393
|
"""
|
|
394
|
+
if generated_keys := [
|
|
395
|
+
k
|
|
396
|
+
for k, v in self.values.items()
|
|
397
|
+
if ConfigInformation.is_generated_value(self.xpmtype.arguments[k], v)
|
|
398
|
+
]:
|
|
399
|
+
raise AttributeError(
|
|
400
|
+
"Cannot seal a configuration with generated values:"
|
|
401
|
+
f"""{",".join(generated_keys)} in {context.currentpath}"""
|
|
402
|
+
)
|
|
348
403
|
|
|
349
404
|
class Sealer(ConfigWalk):
|
|
350
405
|
def preprocess(self, config: ConfigMixin):
|
|
@@ -368,13 +423,15 @@ class ConfigInformation:
|
|
|
368
423
|
if len(sig.parameters) == 0:
|
|
369
424
|
value = argument.generator()
|
|
370
425
|
elif len(sig.parameters) == 2:
|
|
426
|
+
# Only in that case do we need to flag this configuration
|
|
427
|
+
# as containing generated values
|
|
428
|
+
config.__xpm__._generated_values.append(k)
|
|
371
429
|
value = argument.generator(self.context, config)
|
|
372
430
|
else:
|
|
373
431
|
assert (
|
|
374
432
|
False
|
|
375
433
|
), "generator has either two parameters (context and config), or none"
|
|
376
434
|
config.__xpm__.set(k, value, bypass=True)
|
|
377
|
-
config.__xpm__._has_generated_value = True
|
|
378
435
|
else:
|
|
379
436
|
value = config.__xpm__.values.get(k)
|
|
380
437
|
except Exception:
|
|
@@ -387,9 +444,9 @@ class ConfigInformation:
|
|
|
387
444
|
if (
|
|
388
445
|
(value is not None)
|
|
389
446
|
and isinstance(value, ConfigMixin)
|
|
390
|
-
and value.__xpm__.
|
|
447
|
+
and value.__xpm__._generated_values
|
|
391
448
|
):
|
|
392
|
-
|
|
449
|
+
config.__xpm__._generated_values.append(k)
|
|
393
450
|
|
|
394
451
|
config.__xpm__._sealed = True
|
|
395
452
|
|
|
@@ -71,6 +71,7 @@ class ConfigWalk:
|
|
|
71
71
|
return self.context.push(str(i))
|
|
72
72
|
|
|
73
73
|
def map(self, k: str):
|
|
74
|
+
"""Provides a path context when processing a tree"""
|
|
74
75
|
return self.context.push(k)
|
|
75
76
|
|
|
76
77
|
def stub(self, config):
|
|
@@ -123,7 +124,8 @@ class ConfigWalk:
|
|
|
123
124
|
and self.recurse_task
|
|
124
125
|
and x.__xpm__.task is not x
|
|
125
126
|
):
|
|
126
|
-
self(
|
|
127
|
+
with self.map("__task__"):
|
|
128
|
+
self(x.__xpm__.task)
|
|
127
129
|
|
|
128
130
|
processed = self.postprocess(stub, x, result)
|
|
129
131
|
self.visited[xid] = processed
|
|
@@ -13,8 +13,35 @@ class Learner(Task):
|
|
|
13
13
|
validation: Param[Validation]
|
|
14
14
|
x: Param[int]
|
|
15
15
|
|
|
16
|
+
@staticmethod
|
|
17
|
+
def create(x: int, validation: Param[Validation]):
|
|
18
|
+
return Learner.C(x=x, validation=validation)
|
|
16
19
|
|
|
17
|
-
|
|
20
|
+
|
|
21
|
+
class LearnerList(Task):
|
|
22
|
+
validation: Param[list[Validation]]
|
|
23
|
+
x: Param[int]
|
|
24
|
+
|
|
25
|
+
@staticmethod
|
|
26
|
+
def create(x: int, validation: Param[Validation]):
|
|
27
|
+
return LearnerList.C(x=x, validation=[validation])
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
class LearnerDict(Task):
|
|
31
|
+
validation: Param[dict[str, Validation]]
|
|
32
|
+
x: Param[int]
|
|
33
|
+
|
|
34
|
+
@staticmethod
|
|
35
|
+
def create(x: int, validation: Param[Validation]):
|
|
36
|
+
return LearnerDict.C(x=x, validation={"key": validation})
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
class ModuleLoader(Task):
|
|
40
|
+
validation: Param[Validation] = field(ignore_generated=True)
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
@pytest.mark.parametrize("cls", [Learner, LearnerDict, LearnerList])
|
|
44
|
+
def test_generators_reuse_on_submit(cls):
|
|
18
45
|
# We have one way to select the best model
|
|
19
46
|
validation = Validation.C()
|
|
20
47
|
|
|
@@ -25,9 +52,42 @@ def test_generators_reuse():
|
|
|
25
52
|
)
|
|
26
53
|
|
|
27
54
|
# OK, the path is generated depending on Learner with x=1
|
|
28
|
-
|
|
55
|
+
cls.create(1, validation).submit(workspace=workspace)
|
|
29
56
|
|
|
30
57
|
with pytest.raises((AttributeError)):
|
|
31
58
|
# Here we have a problem...
|
|
32
59
|
# the path is still the previous one
|
|
33
|
-
|
|
60
|
+
cls.create(2, validation).submit(workspace=workspace)
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
@pytest.mark.parametrize("cls", [Learner, LearnerDict, LearnerList])
|
|
64
|
+
def test_generators_delayed_submit(cls):
|
|
65
|
+
workspace = Workspace(
|
|
66
|
+
Settings(),
|
|
67
|
+
WorkspaceSettings("test_generators_simple", path=Path("/tmp")),
|
|
68
|
+
run_mode=RunMode.DRY_RUN,
|
|
69
|
+
)
|
|
70
|
+
validation = Validation.C()
|
|
71
|
+
task1 = cls.create(1, validation)
|
|
72
|
+
task2 = cls.create(2, validation)
|
|
73
|
+
task1.submit(workspace=workspace)
|
|
74
|
+
with pytest.raises((AttributeError)):
|
|
75
|
+
task2.submit(workspace=workspace)
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
@pytest.mark.parametrize("cls", [Learner, LearnerDict, LearnerList])
|
|
79
|
+
def test_generators_reuse_on_set(cls):
|
|
80
|
+
workspace = Workspace(
|
|
81
|
+
Settings(),
|
|
82
|
+
WorkspaceSettings("test_generators_simple", path=Path("/tmp")),
|
|
83
|
+
run_mode=RunMode.DRY_RUN,
|
|
84
|
+
)
|
|
85
|
+
validation = Validation.C()
|
|
86
|
+
cls.create(1, validation).submit(workspace=workspace)
|
|
87
|
+
with pytest.raises((AttributeError)):
|
|
88
|
+
# We should not be able to *create* a second task with the same validation,
|
|
89
|
+
# even without submitting it
|
|
90
|
+
cls.create(2, validation)
|
|
91
|
+
|
|
92
|
+
# This should run OK
|
|
93
|
+
ModuleLoader.C(validation=validation)
|
|
@@ -12,14 +12,14 @@ experimaestro/connectors/__init__.py,sha256=UKhDU3uv9jFH37oUb0JiejrekA85xtEirn79
|
|
|
12
12
|
experimaestro/connectors/local.py,sha256=lCGIubqmUJZ1glLtLRXOgakTMfEaEmFtNkEcw9qV5vw,6143
|
|
13
13
|
experimaestro/connectors/ssh.py,sha256=5giqvv1y0QQKF-GI0IFUzI_Z5H8Bj9EuL_Szpvk899Q,8600
|
|
14
14
|
experimaestro/core/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
15
|
-
experimaestro/core/arguments.py,sha256=
|
|
15
|
+
experimaestro/core/arguments.py,sha256=jlK2_EILKLblLSomUDjlGmR-_xq3rAZPgAwbUjU4boc,6710
|
|
16
16
|
experimaestro/core/callbacks.py,sha256=59JfeUgWcCCdIQ3pvh-xNnoRp9BX8f4iOAkgm16wBzE,1660
|
|
17
17
|
experimaestro/core/context.py,sha256=1tLmX7WcgEKSbGw77vfziTzS8KNsoZJ02JBWMBCqqOk,2606
|
|
18
18
|
experimaestro/core/identifier.py,sha256=JadBAdW2fOIgoTyMRtKI_RY9SXQP2B1vq1rUcV465Hs,10214
|
|
19
19
|
experimaestro/core/objects/__init__.py,sha256=ucJY5e17QQ1Kc-GYXeL7g8GFj8rP0XB4g2vrl32uhxY,721
|
|
20
|
-
experimaestro/core/objects/config.py,sha256=
|
|
20
|
+
experimaestro/core/objects/config.py,sha256=ycZx6252pNmJadpSiu_1jd-LsiHw7kWWufkWH0vcOPc,60293
|
|
21
21
|
experimaestro/core/objects/config_utils.py,sha256=ZLECGkeIWdzunm8vwWsQhvcSgV1e064BgXbLiZnxSEM,1288
|
|
22
|
-
experimaestro/core/objects/config_walk.py,sha256=
|
|
22
|
+
experimaestro/core/objects/config_walk.py,sha256=b8u6oohf1gXyva4Y_Cyyl_3BNivzI2y-I2B6MUPV2aU,4353
|
|
23
23
|
experimaestro/core/objects.pyi,sha256=xvlsRj4u1xsJxbevJl5Ner_HwmxR8x1JlAeIVDJzuy0,6498
|
|
24
24
|
experimaestro/core/serialization.py,sha256=CSPEwOzlDsgAz6V2og-TgyU0RXDtzt_nXaoXFZleDZE,5775
|
|
25
25
|
experimaestro/core/serializers.py,sha256=R_CAMyjjfU1oi-eHU6VlEUixJpFayGqEPaYu7VsD9xA,1197
|
|
@@ -121,7 +121,7 @@ experimaestro/tests/test_dependencies.py,sha256=xfWrSkvjT45G4FSCL535m1huLT2ghmyW
|
|
|
121
121
|
experimaestro/tests/test_experiment.py,sha256=QWF9aHewL9hepagrKKFyfikKn3iiZ_lRRXl1LLttta0,1687
|
|
122
122
|
experimaestro/tests/test_findlauncher.py,sha256=KPy8ow--NXS1KFCIpxrmEJFRvjo-v-PwlVHVyoVKLPg,3134
|
|
123
123
|
experimaestro/tests/test_forward.py,sha256=9y1zYm7hT_Lx5citxnK7n20cMZ2WJbsaEeY5irCZ9U4,735
|
|
124
|
-
experimaestro/tests/test_generators.py,sha256=
|
|
124
|
+
experimaestro/tests/test_generators.py,sha256=R0UypTzxX0dPYvY4A_kozpLDHhGzQDNfLQyc0oRaSx8,2891
|
|
125
125
|
experimaestro/tests/test_identifier.py,sha256=zgfpz5UumQftQTHhdw3nmr044Xd7Ntrh7e2hIAoS_PA,13862
|
|
126
126
|
experimaestro/tests/test_instance.py,sha256=h-8UeoOlNsD-STWq5jbhmxw35CyiwJxtKAaUGmLPgB8,1521
|
|
127
127
|
experimaestro/tests/test_objects.py,sha256=zycxjvWuJAbPR8-q2T3zuBY9xfmlhf1YvtOcrImHxnc,2431
|
|
@@ -152,8 +152,8 @@ experimaestro/utils/multiprocessing.py,sha256=am3DkHP_kmWbpynbck2c9QystCUtPBoSAC
|
|
|
152
152
|
experimaestro/utils/resources.py,sha256=j-nvsTFwmgENMoVGOD2Ap-UD3WU85WkI0IgeSszMCX4,1328
|
|
153
153
|
experimaestro/utils/settings.py,sha256=jpFMqF0DLL4_P1xGal0zVR5cOrdD8O0Y2IOYvnRgN3k,793
|
|
154
154
|
experimaestro/xpmutils.py,sha256=S21eMbDYsHfvmZ1HmKpq5Pz5O-1HnCLYxKbyTBbASyQ,638
|
|
155
|
-
experimaestro-1.
|
|
156
|
-
experimaestro-1.
|
|
157
|
-
experimaestro-1.
|
|
158
|
-
experimaestro-1.
|
|
159
|
-
experimaestro-1.
|
|
155
|
+
experimaestro-1.14.0.dist-info/METADATA,sha256=qf7GOrTHf73pHUHBc2APqLSWvhCKbVhuPBOZmX53dpw,5705
|
|
156
|
+
experimaestro-1.14.0.dist-info/WHEEL,sha256=zp0Cn7JsFoX2ATtOhtaFYIiE2rmFAD4OcMhtUki8W3U,88
|
|
157
|
+
experimaestro-1.14.0.dist-info/entry_points.txt,sha256=TppTNiz5qm5xm1fhAcdLKdCLMrlL-eQggtCrCI00D9c,446
|
|
158
|
+
experimaestro-1.14.0.dist-info/licenses/LICENSE,sha256=OXLcl0T2SZ8Pmy2_dmlvKuetivmyPd5m1q-Gyd-zaYY,35149
|
|
159
|
+
experimaestro-1.14.0.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|