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
experimaestro/experiments/cli.py
CHANGED
|
@@ -1,27 +1,29 @@
|
|
|
1
|
+
import datetime
|
|
2
|
+
import importlib
|
|
1
3
|
import inspect
|
|
2
|
-
import itertools
|
|
3
4
|
import json
|
|
4
5
|
import logging
|
|
5
6
|
import sys
|
|
6
7
|
from pathlib import Path
|
|
7
|
-
from typing import Any, List, Optional, Protocol, Tuple
|
|
8
|
+
from typing import Any, List, Optional, Protocol, Tuple
|
|
8
9
|
|
|
9
10
|
import click
|
|
10
11
|
import omegaconf
|
|
11
12
|
import yaml
|
|
12
|
-
from experimaestro import LauncherRegistry, RunMode, experiment
|
|
13
|
-
from experimaestro.experiments.configuration import ConfigurationBase
|
|
14
|
-
from experimaestro.exceptions import HandledException
|
|
15
|
-
from experimaestro.settings import get_settings, get_workspace
|
|
16
13
|
from omegaconf import OmegaConf, SCMode
|
|
17
14
|
from termcolor import cprint
|
|
18
15
|
|
|
16
|
+
from experimaestro import LauncherRegistry, RunMode, experiment
|
|
17
|
+
from experimaestro.exceptions import HandledException
|
|
18
|
+
from experimaestro.experiments.configuration import ConfigurationBase
|
|
19
|
+
from experimaestro.settings import find_workspace
|
|
20
|
+
|
|
19
21
|
|
|
20
22
|
class ExperimentHelper:
|
|
21
23
|
"""Helper for experiments"""
|
|
22
24
|
|
|
23
|
-
# The experiment
|
|
24
25
|
xp: experiment
|
|
26
|
+
"""The experiment object"""
|
|
25
27
|
|
|
26
28
|
#: Run function
|
|
27
29
|
callable: "ExperimentCallable"
|
|
@@ -54,18 +56,40 @@ class ExperimentCallable(Protocol):
|
|
|
54
56
|
...
|
|
55
57
|
|
|
56
58
|
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
59
|
+
class ConfigurationLoader:
|
|
60
|
+
def __init__(self):
|
|
61
|
+
self.yamls = []
|
|
62
|
+
self.python_path = set()
|
|
63
|
+
self.yaml_module_file: None | Path = None
|
|
64
|
+
|
|
65
|
+
def load(self, yaml_file: Path):
|
|
66
|
+
"""Loads a YAML file, and parents one if they exist"""
|
|
67
|
+
if not yaml_file.exists() and yaml_file.suffix != ".yaml":
|
|
68
|
+
yaml_file = yaml_file.with_suffix(".yaml")
|
|
69
|
+
|
|
70
|
+
with yaml_file.open("rt") as fp:
|
|
71
|
+
_data = yaml.full_load(fp)
|
|
72
|
+
|
|
73
|
+
if "file" in _data:
|
|
74
|
+
path = Path(_data["file"])
|
|
75
|
+
if not path.is_absolute():
|
|
76
|
+
_data["file"] = str((yaml_file.parent / path).resolve())
|
|
61
77
|
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
if parent := _data.get("parent", None):
|
|
66
|
-
data.extend(load(yaml_file.parent / parent))
|
|
78
|
+
if "module" in _data:
|
|
79
|
+
# Keeps track of the YAML file where the module was defined
|
|
80
|
+
self.yaml_module_file = yaml_file
|
|
67
81
|
|
|
68
|
-
|
|
82
|
+
if parent := _data.get("parent", None):
|
|
83
|
+
self.load(yaml_file.parent / parent)
|
|
84
|
+
|
|
85
|
+
self.yamls.append(_data)
|
|
86
|
+
|
|
87
|
+
for path in _data.get("pythonpath", []):
|
|
88
|
+
path = Path(path)
|
|
89
|
+
if path.is_absolute():
|
|
90
|
+
self.python_path.add(path.resolve())
|
|
91
|
+
else:
|
|
92
|
+
self.python_path.add((yaml_file.parent / path).resolve())
|
|
69
93
|
|
|
70
94
|
|
|
71
95
|
@click.option("--debug", is_flag=True, help="Print debug information")
|
|
@@ -103,13 +127,25 @@ def load(yaml_file: Path):
|
|
|
103
127
|
help="Port for monitoring (can be defined in the settings.yaml file)",
|
|
104
128
|
)
|
|
105
129
|
@click.option(
|
|
106
|
-
"--file",
|
|
130
|
+
"--file",
|
|
131
|
+
"xp_file",
|
|
132
|
+
type=Path,
|
|
133
|
+
help="The file containing the main experimental code",
|
|
134
|
+
)
|
|
135
|
+
@click.option(
|
|
136
|
+
"--module-name", "module_name", help="Module containing the experimental code"
|
|
137
|
+
)
|
|
138
|
+
@click.option(
|
|
139
|
+
"--workspace",
|
|
140
|
+
type=str,
|
|
141
|
+
default=None,
|
|
142
|
+
help="Workspace ID (reads from settings.yaml in experimaestro config)",
|
|
107
143
|
)
|
|
108
144
|
@click.option(
|
|
109
145
|
"--workdir",
|
|
110
146
|
type=str,
|
|
111
147
|
default=None,
|
|
112
|
-
help="Working
|
|
148
|
+
help="Working environment",
|
|
113
149
|
)
|
|
114
150
|
@click.option("--conf", "-c", "extra_conf", type=str, multiple=True)
|
|
115
151
|
@click.option(
|
|
@@ -128,36 +164,57 @@ def experiments_cli( # noqa: C901
|
|
|
128
164
|
port: int,
|
|
129
165
|
xpm_config_dir: Path,
|
|
130
166
|
workdir: Optional[Path],
|
|
167
|
+
workspace: Optional[str],
|
|
131
168
|
env: List[Tuple[str, str]],
|
|
132
169
|
run_mode: RunMode,
|
|
133
170
|
extra_conf: List[str],
|
|
134
171
|
pre_yaml: List[str],
|
|
135
172
|
post_yaml: List[str],
|
|
173
|
+
module_name: Optional[str],
|
|
136
174
|
args: List[str],
|
|
137
175
|
show: bool,
|
|
138
176
|
debug: bool,
|
|
139
177
|
):
|
|
140
178
|
"""Run an experiment"""
|
|
179
|
+
|
|
141
180
|
# --- Set the logger
|
|
142
181
|
logging.getLogger().setLevel(logging.DEBUG if debug else logging.INFO)
|
|
143
182
|
logging.getLogger("xpm.hash").setLevel(logging.INFO)
|
|
144
183
|
|
|
145
184
|
# --- Loads the YAML
|
|
146
|
-
|
|
185
|
+
conf_loader = ConfigurationLoader()
|
|
147
186
|
for y in pre_yaml:
|
|
148
|
-
|
|
149
|
-
|
|
187
|
+
conf_loader.load(Path(y))
|
|
188
|
+
conf_loader.load(Path(yaml_file))
|
|
150
189
|
for y in post_yaml:
|
|
151
|
-
|
|
190
|
+
conf_loader.load(Path(y))
|
|
191
|
+
|
|
192
|
+
# --- Merge the YAMLs
|
|
193
|
+
configuration = OmegaConf.merge(*conf_loader.yamls)
|
|
194
|
+
if extra_conf:
|
|
195
|
+
configuration.merge_with(OmegaConf.from_dotlist(extra_conf))
|
|
152
196
|
|
|
153
197
|
# --- Get the XP file
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
break
|
|
198
|
+
python_path = list(conf_loader.python_path)
|
|
199
|
+
if module_name is None:
|
|
200
|
+
module_name = configuration.get("module", None)
|
|
158
201
|
|
|
159
|
-
|
|
160
|
-
|
|
202
|
+
if xp_file is None:
|
|
203
|
+
xp_file = configuration.get("file", None)
|
|
204
|
+
if xp_file:
|
|
205
|
+
assert (
|
|
206
|
+
not module_name
|
|
207
|
+
), "Module name and experiment file are mutually exclusive options"
|
|
208
|
+
xp_file = Path(xp_file)
|
|
209
|
+
if not python_path:
|
|
210
|
+
python_path.append(xp_file.parent.absolute())
|
|
211
|
+
logging.info(
|
|
212
|
+
"Using python path: %s", ", ".join(str(s) for s in python_path)
|
|
213
|
+
)
|
|
214
|
+
|
|
215
|
+
assert (
|
|
216
|
+
module_name or xp_file
|
|
217
|
+
), "Either the module name or experiment file should be given"
|
|
161
218
|
|
|
162
219
|
# --- Set some options
|
|
163
220
|
|
|
@@ -165,91 +222,116 @@ def experiments_cli( # noqa: C901
|
|
|
165
222
|
assert xpm_config_dir.is_dir()
|
|
166
223
|
LauncherRegistry.set_config_dir(xpm_config_dir)
|
|
167
224
|
|
|
168
|
-
# ---
|
|
169
|
-
xp_file = Path(xp_file)
|
|
170
|
-
if not xp_file.exists() and xp_file.suffix != ".py":
|
|
171
|
-
xp_file = xp_file.with_suffix(".py")
|
|
172
|
-
xp_file = Path(yaml_file).parent / xp_file
|
|
225
|
+
# --- Finds the "run" function
|
|
173
226
|
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
the__file__ = str(xp_file)
|
|
178
|
-
else:
|
|
179
|
-
the__file__ = str(xp_file.absolute())
|
|
227
|
+
# Modifies the Python path
|
|
228
|
+
for path in python_path:
|
|
229
|
+
sys.path.append(str(path))
|
|
180
230
|
|
|
181
|
-
|
|
182
|
-
|
|
231
|
+
# --- Adds automatically the experiment module if not found
|
|
232
|
+
if module_name and conf_loader.yaml_module_file:
|
|
233
|
+
try:
|
|
234
|
+
importlib.import_module(module_name)
|
|
235
|
+
except ModuleNotFoundError:
|
|
236
|
+
# Try to setup a path
|
|
237
|
+
path = conf_loader.yaml_module_file.resolve()
|
|
238
|
+
for _ in range(len(module_name.split("."))):
|
|
239
|
+
path = path.parent
|
|
240
|
+
|
|
241
|
+
logging.info("Appending %s to python path", path)
|
|
242
|
+
sys.path.append(str(path))
|
|
243
|
+
python_path.append(path)
|
|
244
|
+
|
|
245
|
+
if xp_file:
|
|
246
|
+
if not xp_file.exists() and xp_file.suffix != ".py":
|
|
247
|
+
xp_file = xp_file.with_suffix(".py")
|
|
248
|
+
xp_file: Path = Path(yaml_file).parent / xp_file
|
|
249
|
+
module_name = xp_file.with_suffix("").name
|
|
250
|
+
spec = importlib.util.spec_from_file_location(
|
|
251
|
+
module_name, str(xp_file.absolute())
|
|
252
|
+
)
|
|
253
|
+
mod = importlib.util.module_from_spec(spec)
|
|
254
|
+
spec.loader.exec_module(mod)
|
|
255
|
+
else:
|
|
256
|
+
# Module
|
|
257
|
+
try:
|
|
258
|
+
mod = importlib.import_module(module_name)
|
|
259
|
+
except ModuleNotFoundError as e:
|
|
260
|
+
logging.error("Module not found: %s with python path %s", e, sys.path)
|
|
261
|
+
raise
|
|
183
262
|
|
|
184
|
-
|
|
185
|
-
try:
|
|
186
|
-
exec(code, _locals, _locals)
|
|
187
|
-
finally:
|
|
188
|
-
sys.path.pop()
|
|
263
|
+
helper = getattr(mod, "run", None)
|
|
189
264
|
|
|
190
265
|
# --- ... and runs it
|
|
191
|
-
helper = _locals.get("run", None)
|
|
192
266
|
if helper is None:
|
|
193
|
-
raise
|
|
267
|
+
raise click.ClickException(
|
|
268
|
+
f"Could not find run function in {xp_file if xp_file else module_name}"
|
|
269
|
+
)
|
|
194
270
|
|
|
195
271
|
if not isinstance(helper, ExperimentHelper):
|
|
196
272
|
helper = ExperimentHelper(helper)
|
|
197
273
|
|
|
198
274
|
parameters = inspect.signature(helper.callable).parameters
|
|
199
275
|
list_parameters = list(parameters.values())
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
276
|
+
if len(list_parameters) != 2:
|
|
277
|
+
raise click.ClickException(
|
|
278
|
+
f"run in {xp_file if xp_file else module_name} function should only "
|
|
279
|
+
f"have two arguments (got {len(list_parameters)}), "
|
|
280
|
+
)
|
|
204
281
|
|
|
205
282
|
schema = list_parameters[1].annotation
|
|
206
283
|
omegaconf_schema = OmegaConf.structured(schema())
|
|
207
284
|
|
|
208
|
-
configuration = OmegaConf.merge(*yamls)
|
|
209
|
-
if extra_conf:
|
|
210
|
-
configuration.merge_with(OmegaConf.from_dotlist(extra_conf))
|
|
211
285
|
if omegaconf_schema is not None:
|
|
212
286
|
try:
|
|
213
287
|
configuration = OmegaConf.merge(omegaconf_schema, configuration)
|
|
214
288
|
except omegaconf.errors.ConfigKeyError as e:
|
|
215
289
|
cprint(f"Error in configuration:\n\n{e}", "red", file=sys.stderr)
|
|
216
|
-
|
|
290
|
+
raise click.ClickException("Error in configuration")
|
|
217
291
|
|
|
218
292
|
if show:
|
|
219
293
|
print(json.dumps(OmegaConf.to_container(configuration))) # noqa: T201
|
|
220
294
|
sys.exit(0)
|
|
221
295
|
|
|
222
296
|
# Move to an object container
|
|
223
|
-
|
|
297
|
+
xp_configuration: ConfigurationBase = OmegaConf.to_container(
|
|
224
298
|
configuration, structured_config_mode=SCMode.INSTANTIATE
|
|
225
299
|
)
|
|
226
300
|
|
|
227
|
-
#
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
workdir =
|
|
231
|
-
if (workdir is None) or (not workdir.is_dir()):
|
|
232
|
-
logging.info("Searching for workspace %s", workdir)
|
|
233
|
-
ws_settings = get_workspace(str(workdir))
|
|
234
|
-
workdir = ws_settings.path.expanduser()
|
|
235
|
-
ws_env = ws_settings.env
|
|
301
|
+
# Define the workspace
|
|
302
|
+
ws_env = find_workspace(workdir=workdir, workspace=workspace)
|
|
303
|
+
|
|
304
|
+
workdir = ws_env.path
|
|
236
305
|
|
|
237
|
-
|
|
306
|
+
# --- Sets up the experiment ID
|
|
238
307
|
|
|
239
308
|
# --- Runs the experiment
|
|
309
|
+
if xp_configuration.add_timestamp:
|
|
310
|
+
timestamp = datetime.datetime.now().strftime("%Y%m%d-%H%M")
|
|
311
|
+
experiment_id = f"""{xp_configuration.id}-{timestamp}"""
|
|
312
|
+
else:
|
|
313
|
+
experiment_id = xp_configuration.id
|
|
314
|
+
|
|
315
|
+
logging.info(
|
|
316
|
+
"Running experiment %s working directory %s",
|
|
317
|
+
experiment_id,
|
|
318
|
+
str(workdir.resolve()),
|
|
319
|
+
)
|
|
240
320
|
with experiment(
|
|
241
|
-
|
|
321
|
+
ws_env, experiment_id, host=host, port=port, run_mode=run_mode
|
|
242
322
|
) as xp:
|
|
243
323
|
# Set up the environment
|
|
244
324
|
# (1) global settings (2) workspace settings and (3) command line settings
|
|
245
|
-
for key, value in
|
|
246
|
-
logging.info("Setting environment: %s=%s", key, value)
|
|
325
|
+
for key, value in env:
|
|
247
326
|
xp.setenv(key, value)
|
|
248
327
|
|
|
328
|
+
# Sets the python path
|
|
329
|
+
xp.workspace.python_path.extend(python_path)
|
|
330
|
+
|
|
249
331
|
try:
|
|
250
332
|
# Run the experiment
|
|
251
333
|
helper.xp = xp
|
|
252
|
-
helper.run(list(args),
|
|
334
|
+
helper.run(list(args), xp_configuration)
|
|
253
335
|
|
|
254
336
|
# ... and wait
|
|
255
337
|
xp.wait()
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
from omegaconf import MISSING
|
|
2
|
-
from typing import Optional
|
|
2
|
+
from typing import Optional, List
|
|
3
3
|
import attr
|
|
4
4
|
|
|
5
5
|
try:
|
|
@@ -31,6 +31,12 @@ class ConfigurationBase:
|
|
|
31
31
|
file: str = "experiment"
|
|
32
32
|
"""Relative path of the file containing a run function"""
|
|
33
33
|
|
|
34
|
+
module: Optional[str] = None
|
|
35
|
+
"""Relative path of the file containing a run function"""
|
|
36
|
+
|
|
37
|
+
pythonpath: Optional[List[str]] = None
|
|
38
|
+
"""Python path relative to the parent directory of the YAML file"""
|
|
39
|
+
|
|
34
40
|
parent: Optional[str] = None
|
|
35
41
|
"""Relative path of a YAML file that should be merged"""
|
|
36
42
|
|
|
@@ -45,3 +51,6 @@ class ConfigurationBase:
|
|
|
45
51
|
|
|
46
52
|
description: str = ""
|
|
47
53
|
"""Description of the experiment"""
|
|
54
|
+
|
|
55
|
+
add_timestamp: bool = False
|
|
56
|
+
"""Adds a timestamp YYYY_MM_DD-HH_MM to the experiment ID"""
|
experimaestro/generators.py
CHANGED
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
import inspect
|
|
2
2
|
from pathlib import Path
|
|
3
|
+
from abc import ABC, abstractmethod
|
|
3
4
|
from typing import Callable, Union
|
|
4
5
|
from experimaestro.core.arguments import ArgumentOptions, TypeAnnotation
|
|
5
6
|
from experimaestro.core.objects import ConfigWalkContext, Config
|
|
6
7
|
|
|
7
8
|
|
|
8
|
-
class Generator:
|
|
9
|
+
class Generator(ABC):
|
|
9
10
|
"""Base class for all generators"""
|
|
10
11
|
|
|
11
12
|
def isoutput(self):
|
|
@@ -13,6 +14,10 @@ class Generator:
|
|
|
13
14
|
path within the job folder)"""
|
|
14
15
|
return False
|
|
15
16
|
|
|
17
|
+
@abstractmethod
|
|
18
|
+
def __call__(self, context: ConfigWalkContext, config: Config):
|
|
19
|
+
...
|
|
20
|
+
|
|
16
21
|
|
|
17
22
|
class PathGenerator(Generator):
|
|
18
23
|
"""Generates a path"""
|
experimaestro/ipc.py
CHANGED
|
@@ -7,6 +7,7 @@ import sys
|
|
|
7
7
|
import logging
|
|
8
8
|
from .utils import logger
|
|
9
9
|
from watchdog.observers import Observer
|
|
10
|
+
from watchdog.observers.api import ObservedWatch
|
|
10
11
|
from watchdog.events import FileSystemEventHandler
|
|
11
12
|
|
|
12
13
|
|
|
@@ -20,7 +21,9 @@ class IPCom:
|
|
|
20
21
|
self.observer.start()
|
|
21
22
|
self.pid = os.getpid()
|
|
22
23
|
|
|
23
|
-
def fswatch(
|
|
24
|
+
def fswatch(
|
|
25
|
+
self, watcher: FileSystemEventHandler, path: Path, recursive=False
|
|
26
|
+
) -> ObservedWatch:
|
|
24
27
|
if not self.observer.is_alive():
|
|
25
28
|
logging.error("Observer is not alive")
|
|
26
29
|
|
|
@@ -1,33 +1,17 @@
|
|
|
1
|
-
from
|
|
2
|
-
from typing import TYPE_CHECKING, List, Optional
|
|
1
|
+
from typing import TYPE_CHECKING
|
|
3
2
|
|
|
4
|
-
from experimaestro.utils.yaml import YAMLDataClass
|
|
5
|
-
from .specs import HostRequirement
|
|
6
3
|
|
|
7
4
|
if TYPE_CHECKING:
|
|
8
|
-
from experimaestro.launchers import Launcher
|
|
9
5
|
from experimaestro.connectors import Connector
|
|
10
6
|
from experimaestro.tokens import Token
|
|
11
7
|
from .registry import LauncherRegistry
|
|
12
8
|
|
|
13
9
|
|
|
14
|
-
class LauncherConfiguration:
|
|
15
|
-
tags: List[str]
|
|
16
|
-
weight: int
|
|
17
|
-
|
|
18
|
-
"""Generic class for a launcher configuration"""
|
|
19
|
-
|
|
20
|
-
def get(
|
|
21
|
-
self, registry: "LauncherRegistry", requirement: HostRequirement
|
|
22
|
-
) -> Optional["Launcher"]:
|
|
23
|
-
raise NotImplementedError(f"For {self.__class__}")
|
|
24
|
-
|
|
25
|
-
|
|
26
10
|
class ConnectorConfiguration:
|
|
27
11
|
def create(self, registry: "LauncherRegistry") -> "Connector":
|
|
28
12
|
raise NotImplementedError(f"For {self.__class__}")
|
|
29
13
|
|
|
30
14
|
|
|
31
|
-
class TokenConfiguration
|
|
15
|
+
class TokenConfiguration:
|
|
32
16
|
def create(self, registry: "LauncherRegistry", identifier: str) -> "Token":
|
|
33
17
|
raise NotImplementedError(f"For {self.__class__}")
|
|
@@ -23,7 +23,7 @@ class SuppressStrMatch(StrMatch):
|
|
|
23
23
|
|
|
24
24
|
|
|
25
25
|
def mem_spec():
|
|
26
|
-
return "mem", "=", RegExMatch(r"\d+(G|M)?")
|
|
26
|
+
return "mem", "=", RegExMatch(r"\d+(GiB|MiB|G|M)?")
|
|
27
27
|
|
|
28
28
|
|
|
29
29
|
def cores_spec():
|
|
@@ -51,7 +51,12 @@ def cpu():
|
|
|
51
51
|
|
|
52
52
|
|
|
53
53
|
def duration():
|
|
54
|
-
return
|
|
54
|
+
return (
|
|
55
|
+
"duration",
|
|
56
|
+
"=",
|
|
57
|
+
RegExMatch(r"\d+"),
|
|
58
|
+
RegExMatch(r"h(ours?)?|d(ays?)?|m(ins?)?"),
|
|
59
|
+
)
|
|
55
60
|
|
|
56
61
|
|
|
57
62
|
def one_spec():
|
|
@@ -67,7 +72,7 @@ def grammar():
|
|
|
67
72
|
|
|
68
73
|
class Visitor(PTNodeVisitor):
|
|
69
74
|
def visit_grammar(self, node, children):
|
|
70
|
-
return [child for child in children]
|
|
75
|
+
return specs.RequirementUnion(*[child for child in children])
|
|
71
76
|
|
|
72
77
|
def visit_one_spec(self, node, children):
|
|
73
78
|
return reduce(lambda x, el: x & el, children)
|