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/__init__.py
CHANGED
|
@@ -27,7 +27,15 @@ from .annotations import (
|
|
|
27
27
|
# Method
|
|
28
28
|
config_only,
|
|
29
29
|
)
|
|
30
|
-
from .core.serialization import
|
|
30
|
+
from .core.serialization import (
|
|
31
|
+
load,
|
|
32
|
+
save,
|
|
33
|
+
state_dict,
|
|
34
|
+
from_state_dict,
|
|
35
|
+
from_task_dir,
|
|
36
|
+
serialize,
|
|
37
|
+
deserialize,
|
|
38
|
+
)
|
|
31
39
|
from .core.arguments import (
|
|
32
40
|
# Types
|
|
33
41
|
Param,
|
|
@@ -36,15 +44,17 @@ from .core.arguments import (
|
|
|
36
44
|
DataPath,
|
|
37
45
|
Annotated,
|
|
38
46
|
Constant,
|
|
47
|
+
field,
|
|
39
48
|
# Annotations helpers
|
|
40
49
|
help,
|
|
41
50
|
default,
|
|
42
51
|
)
|
|
43
|
-
from .generators import pathgenerator
|
|
52
|
+
from .generators import pathgenerator, PathGenerator
|
|
44
53
|
from .core.objects import (
|
|
45
54
|
Config,
|
|
46
55
|
copyconfig,
|
|
47
56
|
setmeta,
|
|
57
|
+
DependentMarker,
|
|
48
58
|
Task,
|
|
49
59
|
LightweightTask,
|
|
50
60
|
ObjectStore,
|
|
@@ -53,9 +63,9 @@ from .core.context import SerializationContext
|
|
|
53
63
|
from .core.serializers import SerializationLWTask, PathSerializationLWTask
|
|
54
64
|
from .core.types import Any, SubmitHook
|
|
55
65
|
from .launchers import Launcher
|
|
56
|
-
from .scheduler.environment import Environment
|
|
57
|
-
from .scheduler.workspace import Workspace, RunMode
|
|
58
66
|
from .scheduler import Scheduler, experiment, FailedExperiment
|
|
67
|
+
from .scheduler.workspace import Workspace, RunMode
|
|
68
|
+
from .scheduler.state import get_experiment
|
|
59
69
|
from .notifications import progress, tqdm
|
|
60
70
|
from .checkers import Choices
|
|
61
71
|
from .xpmutils import DirectoryContext
|
experimaestro/__main__.py
CHANGED
|
@@ -1,427 +1,7 @@
|
|
|
1
|
-
|
|
2
|
-
import sys
|
|
3
|
-
from types import ModuleType
|
|
4
|
-
from typing import Set, Optional
|
|
5
|
-
import pkg_resources
|
|
6
|
-
from itertools import chain
|
|
7
|
-
from shutil import rmtree
|
|
8
|
-
import click
|
|
9
|
-
import logging
|
|
10
|
-
from functools import cached_property, update_wrapper
|
|
11
|
-
from pathlib import Path
|
|
12
|
-
import subprocess
|
|
13
|
-
from termcolor import colored, cprint
|
|
1
|
+
from experimaestro.cli import cli
|
|
14
2
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
import experimaestro.launcherfinder.registry as launcher_registry
|
|
18
|
-
|
|
19
|
-
# --- Command line main options
|
|
20
|
-
logging.basicConfig(level=logging.INFO)
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
def pass_cfg(f):
|
|
24
|
-
"""Pass configuration information"""
|
|
25
|
-
|
|
26
|
-
@click.pass_context
|
|
27
|
-
def new_func(ctx, *args, **kwargs):
|
|
28
|
-
return ctx.invoke(f, ctx.obj, *args, **kwargs)
|
|
29
|
-
|
|
30
|
-
return update_wrapper(new_func, f)
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
def check_xp_path(ctx, self, path: Path):
|
|
34
|
-
if not (path / ".__experimaestro__").is_file():
|
|
35
|
-
cprint(f"{path} is not an experimaestro working directory", "red")
|
|
36
|
-
for path in path.parents:
|
|
37
|
-
if (path / ".__experimaestro__").is_file():
|
|
38
|
-
cprint(f"{path} could be the folder you want", "green")
|
|
39
|
-
if click.confirm("Do you want to use this folder?"):
|
|
40
|
-
return path
|
|
41
|
-
sys.exit(1)
|
|
42
|
-
|
|
43
|
-
return path
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
class RunConfig:
|
|
47
|
-
def __init__(self):
|
|
48
|
-
self.traceback = False
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
def pass_cfg(f):
|
|
52
|
-
"""Pass configuration information"""
|
|
53
|
-
|
|
54
|
-
@click.pass_context
|
|
55
|
-
def new_func(ctx, *args, **kwargs):
|
|
56
|
-
return ctx.invoke(f, ctx.obj, *args, **kwargs)
|
|
57
|
-
|
|
58
|
-
return update_wrapper(new_func, f)
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
@click.group()
|
|
62
|
-
@click.option("--quiet", is_flag=True, help="Be quiet")
|
|
63
|
-
@click.option("--debug", is_flag=True, help="Be even more verbose (implies traceback)")
|
|
64
|
-
@click.option(
|
|
65
|
-
"--traceback", is_flag=True, help="Display traceback if an exception occurs"
|
|
66
|
-
)
|
|
67
|
-
@click.pass_context
|
|
68
|
-
def cli(ctx, quiet, debug, traceback):
|
|
69
|
-
if quiet:
|
|
70
|
-
logging.getLogger().setLevel(logging.WARN)
|
|
71
|
-
elif debug:
|
|
72
|
-
logging.getLogger().setLevel(logging.DEBUG)
|
|
73
|
-
|
|
74
|
-
ctx.obj = RunConfig()
|
|
75
|
-
ctx.obj.traceback = traceback
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
# Adds the run-experiment command
|
|
79
|
-
cli.add_command(experiments_cli, "run-experiment")
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
@cli.command(help="Get version")
|
|
83
|
-
def version():
|
|
84
|
-
print(experimaestro.__version__)
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
@click.argument("parameters", type=Path)
|
|
88
|
-
@cli.command(context_settings={"allow_extra_args": True})
|
|
89
|
-
def run(parameters):
|
|
90
|
-
"""Run a task"""
|
|
91
|
-
|
|
92
|
-
from experimaestro.run import run as do_run
|
|
93
|
-
|
|
94
|
-
do_run(parameters)
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
@click.argument("path2", type=Path)
|
|
98
|
-
@click.argument("path1", type=Path)
|
|
99
|
-
@cli.command(context_settings={"allow_extra_args": True})
|
|
100
|
-
def parameters_difference(path1, path2):
|
|
101
|
-
"""Compute the difference between two configurations"""
|
|
102
|
-
|
|
103
|
-
from experimaestro.tools.diff import diff
|
|
104
|
-
|
|
105
|
-
diff(path1, path2)
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
@click.option(
|
|
109
|
-
"--clean", is_flag=True, help="Remove the socket file and its enclosing directory"
|
|
110
|
-
)
|
|
111
|
-
@click.argument("unix-path", type=Path)
|
|
112
|
-
@cli.command()
|
|
113
|
-
def rpyc_server(unix_path, clean):
|
|
114
|
-
"""Start an rPyC server"""
|
|
115
|
-
from experimaestro.rpyc import start_server
|
|
116
|
-
|
|
117
|
-
start_server(unix_path, clean=clean)
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
@cli.group()
|
|
121
|
-
def deprecated():
|
|
122
|
-
"""Manage identifier changes"""
|
|
123
|
-
pass
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
@click.option("--fix", is_flag=True, help="Generate links to new IDs")
|
|
127
|
-
@click.option("--cleanup", is_flag=True, help="Remove symbolic links and move folders")
|
|
128
|
-
@click.argument("path", type=Path, callback=check_xp_path)
|
|
129
|
-
@deprecated.command(name="list")
|
|
130
|
-
def deprecated_list(path: Path, fix: bool, cleanup: bool):
|
|
131
|
-
"""List deprecated jobs"""
|
|
132
|
-
from experimaestro.tools.jobs import fix_deprecated
|
|
133
|
-
|
|
134
|
-
if cleanup and not fix:
|
|
135
|
-
logging.warning("Ignoring --cleanup since we are not fixing old IDs")
|
|
136
|
-
fix_deprecated(path, fix, cleanup)
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
@click.argument("path", type=Path, callback=check_xp_path)
|
|
140
|
-
@deprecated.command()
|
|
141
|
-
def diff(path: Path):
|
|
142
|
-
"""Show the reason of the identifier change for a job"""
|
|
143
|
-
from experimaestro.tools.jobs import load_job
|
|
144
|
-
from experimaestro import Config
|
|
145
|
-
from experimaestro.core.objects import ConfigWalkContext
|
|
146
|
-
|
|
147
|
-
_, job = load_job(path / "params.json", discard_id=False)
|
|
148
|
-
_, new_job = load_job(path / "params.json")
|
|
149
|
-
|
|
150
|
-
def check(path: str, value, new_value, done: Set[int]):
|
|
151
|
-
if isinstance(value, Config):
|
|
152
|
-
if id(value) in done:
|
|
153
|
-
return
|
|
154
|
-
done.add(id(value))
|
|
155
|
-
|
|
156
|
-
old_id = value.__xpm__.identifier.all.hex()
|
|
157
|
-
new_id = new_value.__xpm__.identifier.all.hex()
|
|
158
|
-
|
|
159
|
-
if new_id != old_id:
|
|
160
|
-
print(f"{path} differ: {new_id} vs {old_id}")
|
|
161
|
-
|
|
162
|
-
for arg in value.__xpmtype__.arguments.values():
|
|
163
|
-
arg_value = getattr(value, arg.name)
|
|
164
|
-
arg_newvalue = getattr(new_value, arg.name)
|
|
165
|
-
check(f"{path}/{arg.name}", arg_value, arg_newvalue, done)
|
|
166
|
-
|
|
167
|
-
elif isinstance(value, list):
|
|
168
|
-
for ix, (array_value, array_newvalue) in enumerate(zip(value, new_value)):
|
|
169
|
-
check(f"{path}.{ix}", array_value, array_newvalue, done)
|
|
170
|
-
|
|
171
|
-
elif isinstance(value, dict):
|
|
172
|
-
for key, dict_value in value.items():
|
|
173
|
-
check(f"{path}.{key}", dict_value, new_value[key], done)
|
|
174
|
-
|
|
175
|
-
check(".", job, new_job, set())
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
@click.argument("path", type=Path, callback=check_xp_path)
|
|
179
|
-
@click.option("--experiment", default=None, help="Restrict to this experiment")
|
|
180
|
-
@click.option("--tags", is_flag=True, help="Show tags")
|
|
181
|
-
@click.option("--ready", is_flag=True, help="Include tasks which are not yet scheduled")
|
|
182
|
-
@click.option("--filter", default="", help="Filter expression")
|
|
183
|
-
@click.option(
|
|
184
|
-
"--force",
|
|
185
|
-
is_flag=True,
|
|
186
|
-
help="Force operation even if experiment has not been completed yet",
|
|
187
|
-
)
|
|
188
|
-
@click.option("--kill", is_flag=True, help="Kill filtered tasks (when/before running)")
|
|
189
|
-
@click.option("--clean", is_flag=True, help="Remove finished tasks directories")
|
|
190
|
-
@cli.command()
|
|
191
|
-
def jobs(
|
|
192
|
-
path: Path,
|
|
193
|
-
experiment: str,
|
|
194
|
-
filter: str,
|
|
195
|
-
tags: bool,
|
|
196
|
-
ready: bool,
|
|
197
|
-
kill: bool,
|
|
198
|
-
clean: bool,
|
|
199
|
-
force: bool,
|
|
200
|
-
):
|
|
201
|
-
"""Job control: list, kill and clean
|
|
202
|
-
|
|
203
|
-
The job filter is a boolean expression where tags (alphanumeric)
|
|
204
|
-
and special job information (@state for job state, @name for job full
|
|
205
|
-
name) can be compared to a given value (using '~' for regex matching,
|
|
206
|
-
'=', 'not in', or 'in')
|
|
207
|
-
|
|
208
|
-
For instance,
|
|
209
|
-
|
|
210
|
-
model = "bm25" and mode in ["a", b"] and @state = "RUNNING"
|
|
211
|
-
|
|
212
|
-
selects jobs where the tag model is "bm25", the tag mode is either
|
|
213
|
-
"a" or "b", and the state is running.
|
|
214
|
-
|
|
215
|
-
"""
|
|
216
|
-
for p in (path / "xp").glob("*"):
|
|
217
|
-
if experiment and p.name != experiment:
|
|
218
|
-
continue
|
|
219
|
-
|
|
220
|
-
from .filter import createFilter, JobInformation
|
|
221
|
-
from experimaestro.scheduler import JobState
|
|
222
|
-
|
|
223
|
-
_filter = createFilter(filter) if filter else lambda x: True
|
|
224
|
-
|
|
225
|
-
print(f"* Experiment {p.name}")
|
|
226
|
-
if (p / "jobs.bak").is_dir():
|
|
227
|
-
cprint(" Experiment has not finished yet", "red")
|
|
228
|
-
if not force and (kill or clean):
|
|
229
|
-
cprint(" Preventing kill/clean (use --force if you want to)", "yellow")
|
|
230
|
-
kill = False
|
|
231
|
-
clean = False
|
|
232
|
-
print()
|
|
233
|
-
|
|
234
|
-
for job in p.glob("jobs/*/*"):
|
|
235
|
-
info = None
|
|
236
|
-
p = job.resolve()
|
|
237
|
-
if p.is_dir():
|
|
238
|
-
*_, scriptname = p.parent.name.rsplit(".", 1)
|
|
239
|
-
|
|
240
|
-
info = JobInformation(p, scriptname)
|
|
241
|
-
if filter:
|
|
242
|
-
if not _filter(info):
|
|
243
|
-
continue
|
|
244
|
-
|
|
245
|
-
if info.state is None:
|
|
246
|
-
print(
|
|
247
|
-
colored(f"NODIR {job.parent.name}/{job.name}", "red"), end=""
|
|
248
|
-
)
|
|
249
|
-
elif info.state.running():
|
|
250
|
-
if kill:
|
|
251
|
-
process = info.getprocess()
|
|
252
|
-
print("KILLING", process)
|
|
253
|
-
process.kill()
|
|
254
|
-
print(
|
|
255
|
-
colored(
|
|
256
|
-
f"{info.state.name:8}{job.parent.name}/{job.name}", "yellow"
|
|
257
|
-
),
|
|
258
|
-
end="",
|
|
259
|
-
)
|
|
260
|
-
elif info.state == JobState.DONE:
|
|
261
|
-
print(
|
|
262
|
-
colored(f"DONE {job.parent.name}/{job.name}", "green"),
|
|
263
|
-
end="",
|
|
264
|
-
)
|
|
265
|
-
elif info.state == JobState.ERROR:
|
|
266
|
-
print(
|
|
267
|
-
colored(f"FAIL {job.parent.name}/{job.name}", "red"), end=""
|
|
268
|
-
)
|
|
269
|
-
else:
|
|
270
|
-
print(
|
|
271
|
-
colored(
|
|
272
|
-
f"{info.state.name:8}{job.parent.name}/{job.name}", "red"
|
|
273
|
-
),
|
|
274
|
-
end="",
|
|
275
|
-
)
|
|
276
|
-
|
|
277
|
-
else:
|
|
278
|
-
if not ready:
|
|
279
|
-
continue
|
|
280
|
-
print(colored(f"READY {job.parent.name}/{job.name}", "yellow"), end="")
|
|
281
|
-
|
|
282
|
-
if tags:
|
|
283
|
-
print(f""" {" ".join(f"{k}={v}" for k, v in info.tags.items())}""")
|
|
284
|
-
else:
|
|
285
|
-
print()
|
|
286
|
-
|
|
287
|
-
if clean and info.state and info.state.finished():
|
|
288
|
-
cprint("Cleaning...", "red")
|
|
289
|
-
rmtree(p)
|
|
290
|
-
print()
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
@click.option("--show-all", is_flag=True, help="Show even not orphans")
|
|
294
|
-
@click.option(
|
|
295
|
-
"--ignore-old", is_flag=True, help="Ignore old jobs for unfinished experiments"
|
|
296
|
-
)
|
|
297
|
-
@click.option("--clean", is_flag=True, help="Prune the orphan folders")
|
|
298
|
-
@click.option("--size", is_flag=True, help="Show size of each folder")
|
|
299
|
-
@click.argument("path", type=Path, callback=check_xp_path)
|
|
300
|
-
@cli.command()
|
|
301
|
-
def orphans(path: Path, clean: bool, size: bool, show_all: bool, ignore_old: bool):
|
|
302
|
-
"""Check for tasks that are not part of an experimental plan"""
|
|
303
|
-
|
|
304
|
-
jobspath = path / "jobs"
|
|
305
|
-
|
|
306
|
-
def getjobs(path: Path):
|
|
307
|
-
return ((str(p.relative_to(path)), p) for p in path.glob("*/*") if p.is_dir())
|
|
308
|
-
|
|
309
|
-
def show(key: str, prefix=""):
|
|
310
|
-
if size:
|
|
311
|
-
print(
|
|
312
|
-
prefix,
|
|
313
|
-
subprocess.check_output(["du", "-hs", key], cwd=jobspath)
|
|
314
|
-
.decode("utf-8")
|
|
315
|
-
.strip(),
|
|
316
|
-
sep=None,
|
|
317
|
-
)
|
|
318
|
-
else:
|
|
319
|
-
print(prefix, key, sep=None)
|
|
320
|
-
|
|
321
|
-
for p in (path / "xp").glob("*/jobs.bak"):
|
|
322
|
-
logging.warning("Experiment %s has not completed successfully", p.parent.name)
|
|
323
|
-
|
|
324
|
-
# Retrieve the jobs within expedriments (jobs and jobs.bak folder within experiments)
|
|
325
|
-
xpjobs = set()
|
|
326
|
-
if ignore_old:
|
|
327
|
-
paths = (path / "xp").glob("*/jobs")
|
|
328
|
-
else:
|
|
329
|
-
paths = chain((path / "xp").glob("*/jobs"), (path / "xp").glob("*/jobs.bak"))
|
|
330
|
-
|
|
331
|
-
for p in paths:
|
|
332
|
-
if p.is_dir():
|
|
333
|
-
for relpath, path in getjobs(p):
|
|
334
|
-
xpjobs.add(relpath)
|
|
335
|
-
|
|
336
|
-
# Now, look at stored jobs
|
|
337
|
-
found = 0
|
|
338
|
-
for key, jobpath in getjobs(jobspath):
|
|
339
|
-
if key not in xpjobs:
|
|
340
|
-
show(key)
|
|
341
|
-
if clean:
|
|
342
|
-
logging.info("Removing data in %s", jobpath)
|
|
343
|
-
rmtree(jobpath)
|
|
344
|
-
else:
|
|
345
|
-
if show_all:
|
|
346
|
-
show(key, prefix="[not orphan] ")
|
|
347
|
-
found += 1
|
|
348
|
-
|
|
349
|
-
print(f"{found} jobs are not orphans")
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
def arg_split(ctx, param, value):
|
|
353
|
-
# split columns by ',' and remove whitespace
|
|
354
|
-
return set(c.strip() for c in value.split(","))
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
@click.option("--skip", default=set(), callback=arg_split)
|
|
358
|
-
@click.argument("package", type=str)
|
|
359
|
-
@click.argument("objects", type=Path)
|
|
360
|
-
@cli.command()
|
|
361
|
-
def check_documentation(objects, package, skip):
|
|
362
|
-
"""Check that all the configuration and tasks are documented within a
|
|
363
|
-
package, relying on the sphinx objects.inv file"""
|
|
364
|
-
from experimaestro.tools.documentation import documented_from_objects, undocumented
|
|
365
|
-
|
|
366
|
-
documented = documented_from_objects(objects)
|
|
367
|
-
errors, configs = undocumented([package], documented, skip)
|
|
368
|
-
for config in configs:
|
|
369
|
-
cprint(f"{config.__module__}.{config.__qualname__}", "red")
|
|
370
|
-
|
|
371
|
-
if errors > 0 or configs:
|
|
372
|
-
sys.exit(1)
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
@click.option("--config", type=Path, help="Show size of each folder")
|
|
376
|
-
@click.argument("spec", type=str)
|
|
377
|
-
@cli.command()
|
|
378
|
-
def find_launchers(config: Optional[Path], spec: str):
|
|
379
|
-
"""Find launchers matching a specification"""
|
|
380
|
-
if config is not None:
|
|
381
|
-
launcher_registry.LauncherRegistry.set_config_dir(config)
|
|
382
|
-
|
|
383
|
-
print(launcher_registry.find_launcher(spec))
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
class Launchers(click.MultiCommand):
|
|
387
|
-
"""Connectors commands"""
|
|
388
|
-
|
|
389
|
-
@cached_property
|
|
390
|
-
def commands(self):
|
|
391
|
-
map = {}
|
|
392
|
-
for ep in pkg_resources.iter_entry_points(f"experimaestro.{self.name}"):
|
|
393
|
-
if get_cli := getattr(ep.load(), "get_cli", None):
|
|
394
|
-
map[ep.name] = get_cli()
|
|
395
|
-
return map
|
|
396
|
-
|
|
397
|
-
def list_commands(self, ctx):
|
|
398
|
-
return self.commands.keys()
|
|
399
|
-
|
|
400
|
-
def get_command(self, ctx, name):
|
|
401
|
-
return self.commands[name]
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
cli.add_command(Launchers("launchers", help="Launcher specific commands"))
|
|
405
|
-
cli.add_command(Launchers("connectors", help="Connector specific commands"))
|
|
406
|
-
cli.add_command(Launchers("tokens", help="Token specific commands"))
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
@cli.group()
|
|
410
|
-
@click.argument("workdir", type=Path, callback=check_xp_path)
|
|
411
|
-
@click.pass_context
|
|
412
|
-
def experiments(ctx, workdir):
|
|
413
|
-
"""Manage experiments"""
|
|
414
|
-
ctx.obj = workdir
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
@experiments.command()
|
|
418
|
-
@pass_cfg
|
|
419
|
-
def list(workdir: Path):
|
|
420
|
-
for p in (workdir / "xp").iterdir():
|
|
421
|
-
if (p / "jobs.bak").exists():
|
|
422
|
-
cprint(f"[unfinished] {p.name}", "yellow")
|
|
423
|
-
else:
|
|
424
|
-
cprint(p.name, "cyan")
|
|
3
|
+
# flake8: noqa: F401
|
|
4
|
+
import experimaestro.cli.jobs
|
|
425
5
|
|
|
426
6
|
|
|
427
7
|
def main():
|
experimaestro/annotations.py
CHANGED
|
@@ -8,7 +8,7 @@ import experimaestro.core.objects as objects
|
|
|
8
8
|
import experimaestro.core.types as types
|
|
9
9
|
from experimaestro.generators import PathGenerator
|
|
10
10
|
|
|
11
|
-
from .core.arguments import Argument as CoreArgument
|
|
11
|
+
from .core.arguments import Argument as CoreArgument, field
|
|
12
12
|
from .core.objects import Config
|
|
13
13
|
from .core.types import Any, Identifier, TypeProxy, Type, ObjectType
|
|
14
14
|
from .utils import logger
|
|
@@ -71,7 +71,7 @@ class config:
|
|
|
71
71
|
assert inspect.isclass(tp), f"{tp} is not a class"
|
|
72
72
|
|
|
73
73
|
# Adds to xpminfo for on demand creation of information
|
|
74
|
-
return ObjectType(tp, identifier=self.identifier).
|
|
74
|
+
return ObjectType(tp, identifier=self.identifier).valuetype
|
|
75
75
|
|
|
76
76
|
|
|
77
77
|
class Array(TypeProxy):
|
|
@@ -134,12 +134,22 @@ class param:
|
|
|
134
134
|
self.type = Type.fromType(type) if type else None
|
|
135
135
|
self.help = help
|
|
136
136
|
self.ignored = ignored
|
|
137
|
-
self.default = default
|
|
138
137
|
self.required = required
|
|
139
|
-
self.generator = None
|
|
140
138
|
self.checker = checker
|
|
141
139
|
self.constant = constant
|
|
142
140
|
|
|
141
|
+
self.generator = None
|
|
142
|
+
self.default = None
|
|
143
|
+
|
|
144
|
+
# Set default or generator
|
|
145
|
+
if isinstance(default, field):
|
|
146
|
+
if default.default is not None:
|
|
147
|
+
self.default = default
|
|
148
|
+
elif default.default_factory is not None:
|
|
149
|
+
self.generator = default.default_factory
|
|
150
|
+
else:
|
|
151
|
+
self.default = default
|
|
152
|
+
|
|
143
153
|
def __call__(self, tp):
|
|
144
154
|
# Don't annotate in task mode
|
|
145
155
|
tp.__getxpmtype__().addAnnotation(self)
|