experimaestro 1.6.2__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.

Files changed (79) hide show
  1. experimaestro/__init__.py +3 -1
  2. experimaestro/annotations.py +13 -3
  3. experimaestro/cli/filter.py +3 -3
  4. experimaestro/cli/jobs.py +1 -1
  5. experimaestro/commandline.py +3 -7
  6. experimaestro/connectors/__init__.py +22 -10
  7. experimaestro/connectors/local.py +17 -8
  8. experimaestro/connectors/ssh.py +1 -1
  9. experimaestro/core/arguments.py +26 -3
  10. experimaestro/core/objects.py +90 -6
  11. experimaestro/core/objects.pyi +7 -1
  12. experimaestro/core/types.py +33 -2
  13. experimaestro/experiments/cli.py +7 -3
  14. experimaestro/generators.py +6 -1
  15. experimaestro/ipc.py +4 -1
  16. experimaestro/launcherfinder/registry.py +23 -5
  17. experimaestro/launchers/slurm/base.py +47 -9
  18. experimaestro/notifications.py +1 -1
  19. experimaestro/run.py +1 -1
  20. experimaestro/scheduler/base.py +98 -10
  21. experimaestro/scheduler/dynamic_outputs.py +184 -0
  22. experimaestro/scriptbuilder.py +3 -1
  23. experimaestro/server/data/016b4a6cdced82ab3aa1.ttf +0 -0
  24. experimaestro/server/data/0c35d18bf06992036b69.woff2 +0 -0
  25. experimaestro/server/data/1815e00441357e01619e.ttf +0 -0
  26. experimaestro/server/data/219aa9140e099e6c72ed.woff2 +0 -0
  27. experimaestro/server/data/2463b90d9a316e4e5294.woff2 +0 -0
  28. experimaestro/server/data/2582b0e4bcf85eceead0.ttf +0 -0
  29. experimaestro/server/data/3a4004a46a653d4b2166.woff +0 -0
  30. experimaestro/server/data/3baa5b8f3469222b822d.woff +0 -0
  31. experimaestro/server/data/4d73cb90e394b34b7670.woff +0 -0
  32. experimaestro/server/data/4ef4218c522f1eb6b5b1.woff2 +0 -0
  33. experimaestro/server/data/50701fbb8177c2dde530.ttf +0 -0
  34. experimaestro/server/data/5d681e2edae8c60630db.woff +0 -0
  35. experimaestro/server/data/6f420cf17cc0d7676fad.woff2 +0 -0
  36. experimaestro/server/data/878f31251d960bd6266f.woff2 +0 -0
  37. experimaestro/server/data/89999bdf5d835c012025.woff2 +0 -0
  38. experimaestro/server/data/914997e1bdfc990d0897.ttf +0 -0
  39. experimaestro/server/data/b041b1fa4fe241b23445.woff2 +0 -0
  40. experimaestro/server/data/b6879d41b0852f01ed5b.woff2 +0 -0
  41. experimaestro/server/data/c210719e60948b211a12.woff2 +0 -0
  42. experimaestro/server/data/c380809fd3677d7d6903.woff2 +0 -0
  43. experimaestro/server/data/d75e3fd1eb12e9bd6655.ttf +0 -0
  44. experimaestro/server/data/f882956fd323fd322f31.woff +0 -0
  45. experimaestro/server/data/favicon.ico +0 -0
  46. experimaestro/server/data/index.css +22963 -0
  47. experimaestro/server/data/index.css.map +1 -0
  48. experimaestro/server/data/index.html +27 -0
  49. experimaestro/server/data/index.js +101770 -0
  50. experimaestro/server/data/index.js.map +1 -0
  51. experimaestro/server/data/login.html +22 -0
  52. experimaestro/server/data/manifest.json +15 -0
  53. experimaestro/sphinx/__init__.py +7 -17
  54. experimaestro/taskglobals.py +7 -2
  55. experimaestro/tests/definitions_types.py +5 -3
  56. experimaestro/tests/launchers/bin/sbatch +34 -7
  57. experimaestro/tests/launchers/bin/srun +5 -0
  58. experimaestro/tests/launchers/common.py +16 -4
  59. experimaestro/tests/restart.py +6 -3
  60. experimaestro/tests/tasks/all.py +16 -10
  61. experimaestro/tests/tasks/foreign.py +2 -4
  62. experimaestro/tests/test_forward.py +5 -5
  63. experimaestro/tests/test_identifier.py +61 -66
  64. experimaestro/tests/test_instance.py +3 -6
  65. experimaestro/tests/test_param.py +40 -22
  66. experimaestro/tests/test_tags.py +5 -11
  67. experimaestro/tests/test_tokens.py +3 -2
  68. experimaestro/tests/test_types.py +17 -14
  69. experimaestro/tests/test_validation.py +48 -91
  70. experimaestro/tokens.py +16 -5
  71. experimaestro/typingutils.py +7 -0
  72. experimaestro/utils/asyncio.py +6 -2
  73. experimaestro/utils/resources.py +7 -3
  74. {experimaestro-1.6.2.dist-info → experimaestro-1.7.0.dist-info}/METADATA +3 -4
  75. experimaestro-1.7.0.dist-info/RECORD +154 -0
  76. {experimaestro-1.6.2.dist-info → experimaestro-1.7.0.dist-info}/WHEEL +1 -1
  77. experimaestro-1.6.2.dist-info/RECORD +0 -122
  78. {experimaestro-1.6.2.dist-info → experimaestro-1.7.0.dist-info}/LICENSE +0 -0
  79. {experimaestro-1.6.2.dist-info → experimaestro-1.7.0.dist-info}/entry_points.txt +0 -0
@@ -4,16 +4,14 @@ import json
4
4
  from pathlib import Path
5
5
  from typing import Dict, List, Optional
6
6
  from experimaestro import (
7
- config,
8
7
  Param,
9
- param,
10
8
  deprecate,
11
9
  Config,
12
10
  Constant,
13
11
  Meta,
14
12
  Option,
15
- pathgenerator,
16
- Annotated,
13
+ PathGenerator,
14
+ field,
17
15
  Task,
18
16
  LightweightTask,
19
17
  )
@@ -48,8 +46,7 @@ class Float(Config):
48
46
  value: Param[float]
49
47
 
50
48
 
51
- @config()
52
- class Values:
49
+ class Values(Config):
53
50
  value1: Param[float]
54
51
  value2: Param[float]
55
52
 
@@ -66,50 +63,50 @@ def assert_notequal(a, b, message=""):
66
63
  assert getidentifier(a) != getidentifier(b), message
67
64
 
68
65
 
69
- def test_int():
66
+ def test_param_int():
70
67
  assert_equal(A(a=1), A(a=1))
71
68
 
72
69
 
73
- def test_different_type():
70
+ def test_param_different_type():
74
71
  assert_notequal(A(a=1), B(a=1))
75
72
 
76
73
 
77
- def test_order():
74
+ def test_param_order():
78
75
  assert_equal(Values(value1=1, value2=2), Values(value2=2, value1=1))
79
76
 
80
77
 
81
- def test_default():
78
+ def test_param_default():
82
79
  assert_equal(C(a=1, b=2), C(b=2))
83
80
 
84
81
 
85
- def test_inner_eq():
82
+ def test_param_inner_eq():
86
83
  assert_equal(D(a=A(a=1)), D(a=A(a=1)))
87
84
 
88
85
 
89
- def test_float():
86
+ def test_param_float():
90
87
  assert_equal(Float(value=1), Float(value=1))
91
88
 
92
89
 
93
- def test_float2():
90
+ def test_param_float2():
94
91
  assert_equal(Float(value=1.0), Float(value=1))
95
92
 
96
93
 
97
94
  # --- Argument name
98
95
 
99
96
 
100
- def test_name():
97
+ def test_param_name():
101
98
  """The identifier fully determines the hash code"""
102
99
 
103
- @config("test.identifier.argumentname")
104
- class Config0:
100
+ class Config0(Config):
101
+ __xpmid__ = "test.identifier.argumentname"
105
102
  a: Param[int]
106
103
 
107
- @config("test.identifier.argumentname")
108
- class Config1:
104
+ class Config1(Config):
105
+ __xpmid__ = "test.identifier.argumentname"
109
106
  b: Param[int]
110
107
 
111
- @config("test.identifier.argumentname")
112
- class Config3:
108
+ class Config3(Config):
109
+ __xpmid__ = "test.identifier.argumentname"
113
110
  a: Param[int]
114
111
 
115
112
  assert_notequal(Config0(a=2), Config1(b=2))
@@ -119,9 +116,9 @@ def test_name():
119
116
  # --- Test option
120
117
 
121
118
 
122
- def test_option():
123
- @config("test.identifier.option")
124
- class OptionConfig:
119
+ def test_param_option():
120
+ class OptionConfig(Config):
121
+ __xpmid__ = "test.identifier.option"
125
122
  a: Param[int]
126
123
  b: Option[int] = 1
127
124
 
@@ -133,7 +130,7 @@ def test_option():
133
130
  # --- Dictionnary
134
131
 
135
132
 
136
- def test_identifier_dict():
133
+ def test_param_identifier_dict():
137
134
  """Test identifiers of dictionary structures"""
138
135
 
139
136
  class B(Config):
@@ -152,13 +149,12 @@ def test_identifier_dict():
152
149
  # --- Ignore paths
153
150
 
154
151
 
155
- @config()
156
- class TypeWithPath:
152
+ class TypeWithPath(Config):
157
153
  a: Param[int]
158
154
  path: Param[Path]
159
155
 
160
156
 
161
- def test_path():
157
+ def test_param_identifier_path():
162
158
  """Path should be ignored"""
163
159
  assert_equal(TypeWithPath(a=1, path="/a/b"), TypeWithPath(a=1, path="/c/d"))
164
160
  assert_notequal(TypeWithPath(a=2, path="/a/b"), TypeWithPath(a=1, path="/c/d"))
@@ -167,23 +163,23 @@ def test_path():
167
163
  # --- Test with added arguments
168
164
 
169
165
 
170
- def test_pathoption():
166
+ def test_param_identifier_pathoption():
171
167
  """Path arguments should be ignored"""
172
168
 
173
- @config("pathoption_test")
174
- class A_with_path:
169
+ class A_with_path(Config):
170
+ __xpmid__ = "pathoption_test"
175
171
  a: Param[int]
176
- path: Annotated[Path, pathgenerator("path")]
172
+ path: Meta[Path] = field(default_factory=PathGenerator("path"))
177
173
 
178
- @config("pathoption_test")
179
- class A_without_path:
174
+ class A_without_path(Config):
175
+ __xpmid__ = "pathoption_test"
180
176
  a: Param[int]
181
177
 
182
178
  assert_equal(A_with_path(a=1), A_without_path(a=1))
183
179
 
184
180
 
185
- def test_identifier_enum():
186
- """Path arguments should be ignored"""
181
+ def test_param_identifier_enum():
182
+ """test enum parameters"""
187
183
  from enum import Enum
188
184
 
189
185
  class EnumParam(Enum):
@@ -197,7 +193,7 @@ def test_identifier_enum():
197
193
  assert_equal(EnumConfig(a=EnumParam.FIRST), EnumConfig(a=EnumParam.FIRST))
198
194
 
199
195
 
200
- def test_identifier_addnone():
196
+ def test_param_identifier_addnone():
201
197
  """Test the case of new parameter (with None default)"""
202
198
 
203
199
  class B(Config):
@@ -214,25 +210,24 @@ def test_identifier_addnone():
214
210
  assert_notequal(A_with_b(b=B(x=1)), A())
215
211
 
216
212
 
217
- def test_defaultnew():
213
+ def test_param_defaultnew():
218
214
  """Path arguments should be ignored"""
219
215
 
220
- @param("b", type=int, default=1)
221
- @param(name="a", type=int)
222
- @config("defaultnew")
223
- class A_with_b:
224
- pass
216
+ class A_with_b(Config):
217
+ __xpmid__ = "defaultnew"
225
218
 
226
- @param(name="a", type=int)
227
- @config("defaultnew")
228
- class A:
229
- pass
219
+ a: Param[int]
220
+ b: Param[int] = 1
221
+
222
+ class A(Config):
223
+ __xpmid__ = "defaultnew"
224
+ a: Param[int]
230
225
 
231
226
  assert_equal(A_with_b(a=1, b=1), A(a=1))
232
227
  assert_equal(A_with_b(a=1), A(a=1))
233
228
 
234
229
 
235
- def test_taskconfigidentifier():
230
+ def test_param_taskconfigidentifier():
236
231
  """Test whether the embedded task arguments make the configuration different"""
237
232
 
238
233
  class MyConfig(Config):
@@ -254,27 +249,27 @@ def test_taskconfigidentifier():
254
249
  )
255
250
 
256
251
 
257
- def test_constant():
252
+ def test_param_constant():
258
253
  """Test if constants are taken into account for signature computation"""
259
254
 
260
- @config("test.constant")
261
- class A1:
255
+ class A1(Config):
256
+ __xpmid__ = "test.constant"
262
257
  version: Constant[int] = 1
263
258
 
264
- @config("test.constant")
265
- class A1bis:
259
+ class A1bis(Config):
260
+ __xpmid__ = "test.constant"
266
261
  version: Constant[int] = 1
267
262
 
268
263
  assert_equal(A1(), A1bis())
269
264
 
270
- @config("test.constant")
271
- class A2:
265
+ class A2(Config):
266
+ __xpmid__ = "test.constant"
272
267
  version: Constant[int] = 2
273
268
 
274
269
  assert_notequal(A1(), A2())
275
270
 
276
271
 
277
- def test_identifier_deprecated_class():
272
+ def test_param_identifier_deprecated_class():
278
273
  """Test that when submitting the task, the computed identifier is the one of
279
274
  the new class"""
280
275
 
@@ -296,7 +291,7 @@ def test_identifier_deprecated_class():
296
291
  )
297
292
 
298
293
 
299
- def test_identifier_deprecated_attribute():
294
+ def test_param_identifier_deprecated_attribute():
300
295
  class Values(Config):
301
296
  values: Param[List[int]] = []
302
297
 
@@ -311,7 +306,7 @@ class MetaA(Config):
311
306
  x: Param[int]
312
307
 
313
308
 
314
- def test_identifier_meta():
309
+ def test_param_identifier_meta():
315
310
  """Test forced meta-parameter"""
316
311
 
317
312
  class B(Config):
@@ -350,7 +345,7 @@ def test_identifier_meta():
350
345
  )
351
346
 
352
347
 
353
- def test_identifier_meta_default_dict():
348
+ def test_param_identifier_meta_default_dict():
354
349
  class DictConfig(Config):
355
350
  params: Param[Dict[str, MetaA]] = {}
356
351
 
@@ -366,7 +361,7 @@ def test_identifier_meta_default_dict():
366
361
  )
367
362
 
368
363
 
369
- def test_identifier_meta_default_array():
364
+ def test_param_identifier_meta_default_array():
370
365
  class ArrayConfigWithDefault(Config):
371
366
  array: Param[List[MetaA]] = []
372
367
 
@@ -382,7 +377,7 @@ def test_identifier_meta_default_array():
382
377
  )
383
378
 
384
379
 
385
- def test_identifier_pre_task():
380
+ def test_param_identifier_pre_task():
386
381
  class MyConfig(Config):
387
382
  pass
388
383
 
@@ -412,7 +407,7 @@ def test_identifier_pre_task():
412
407
  assert_equal(task_with_pre, task_with_pre_3, "Pre-tasks are order-less")
413
408
 
414
409
 
415
- def test_identifier_init_task():
410
+ def test_param_identifier_init_task():
416
411
  class MyConfig(Config):
417
412
  pass
418
413
 
@@ -469,7 +464,7 @@ class IdentifierReloadConfig(Config):
469
464
  id: Param[str]
470
465
 
471
466
 
472
- def test_identifier_reload_config():
467
+ def test_param_identifier_reload_config():
473
468
  # Creates the configuration
474
469
  check_reload(IdentifierReloadConfig(id="123"))
475
470
 
@@ -485,7 +480,7 @@ class IdentifierReloadDerived(Config):
485
480
  task: Param[IdentifierReloadConfig]
486
481
 
487
482
 
488
- def test_identifier_reload_taskoutput():
483
+ def test_param_identifier_reload_taskoutput():
489
484
  """When using a task output, the identifier should not be different"""
490
485
 
491
486
  # Creates the configuration
@@ -507,7 +502,7 @@ class IdentifierReloadTaskDerived(Config):
507
502
  other: Param[IdentifierReloadTaskConfig]
508
503
 
509
504
 
510
- def test_identifier_reload_task_direct():
505
+ def test_param_identifier_reload_task_direct():
511
506
  """When using a direct task output, the identifier should not be different"""
512
507
 
513
508
  # Creates the configuration
@@ -518,7 +513,7 @@ def test_identifier_reload_task_direct():
518
513
  check_reload(config)
519
514
 
520
515
 
521
- def test_identifier_reload_meta():
516
+ def test_param_identifier_reload_meta():
522
517
  """Test identifier don't change when using meta"""
523
518
  # Creates the configuration
524
519
  task = IdentifierReloadTask(id="123").submit(run_mode=RunMode.DRY_RUN)
@@ -541,7 +536,7 @@ class LoopC(Config):
541
536
  param_b: Param["LoopB"]
542
537
 
543
538
 
544
- def test_identifier_loop():
539
+ def test_param_identifier_loop():
545
540
  c = LoopC()
546
541
  b = LoopB(param_c=c)
547
542
  a = LoopA(param_b=b)
@@ -1,21 +1,18 @@
1
1
  from typing import Optional
2
- from experimaestro import config, Param, Config
2
+ from experimaestro import Param, Config
3
3
  from experimaestro.core.objects import TypeConfig
4
4
  from experimaestro.core.serializers import SerializationLWTask
5
5
 
6
6
 
7
- @config()
8
- class A:
7
+ class A(Config):
9
8
  x: Param[int] = 1
10
9
 
11
10
 
12
- @config()
13
11
  class A1(A):
14
12
  pass
15
13
 
16
14
 
17
- @config()
18
- class B:
15
+ class B(Config):
19
16
  a: Param[A]
20
17
 
21
18
 
@@ -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
- @config("annotations.b")
33
- class B:
34
+ class B(Config):
35
+ __xpmid__ = "annotations.b"
34
36
  pass
35
37
 
36
38
 
37
39
  def test_fullname():
38
- assert str(B.__xpmtype__.identifier) == "annotations.b"
40
+ assert str(B.__getxpmtype__().identifier) == "annotations.b"
39
41
 
40
42
 
41
43
  # --- Automatic name for configuration
42
44
 
43
45
 
44
- @config()
45
- class A:
46
+ class A(Config):
46
47
  pass
47
48
 
48
49
 
49
50
  def test_noname():
50
- assert str(A.__xpmtype__.identifier) == "experimaestro.tests.test_param.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
- @config()
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.__xpmtype__
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: Annotated[Path, pathgenerator("test.txt")]
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
- @config()
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
- @config()
228
- class A:
226
+ class A(Config):
229
227
  x: Param[int]
230
228
 
231
- @config()
232
- class B:
229
+ class B(Config):
233
230
  x: Param[int] = 3
234
231
 
235
- atx = A.C.__xpmtype__.getArgument("x")
236
- btx = B.C.__xpmtype__.getArgument("x")
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
- @config()
268
- class A:
286
+ class A(Config):
269
287
  x: Param[int] = 0.2
270
288
 
271
289
  with pytest.raises(TypeError):
272
- A.__xpmtype__.getArgument("x")
290
+ A.__getxpmtype__().getArgument("x")
273
291
 
274
292
 
275
293
  # --- Handling help annotations
@@ -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
- @argument("x", type=int)
17
- @config()
18
- class Config1:
19
- pass
14
+ class Config1(Config):
15
+ x: Param[int]
20
16
 
21
17
 
22
- @argument("x", type=int)
23
- @argument("c", type=Config1)
24
- @config()
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, param
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, config
5
- from experimaestro.core.objects import TypeConfig
4
+ from experimaestro import Config, Param
5
+ from typing import Union
6
6
 
7
- from .utils import TemporaryExperiment
8
- from experimaestro.scheduler import JobState
7
+ import pytest
8
+ from experimaestro.core.objects import TypeConfig
9
9
 
10
10
 
11
11
  def test_multiple_inheritance():
12
- @config()
13
- class A:
12
+ class A(Config):
14
13
  pass
15
14
 
16
- @config()
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
- @config()
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=[])