experimaestro 2.0.0b4__py3-none-any.whl → 2.0.0b17__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 +12 -5
- experimaestro/cli/__init__.py +393 -134
- experimaestro/cli/filter.py +48 -23
- experimaestro/cli/jobs.py +253 -71
- experimaestro/cli/refactor.py +1 -2
- experimaestro/commandline.py +7 -4
- experimaestro/connectors/__init__.py +9 -1
- experimaestro/connectors/local.py +43 -3
- experimaestro/core/arguments.py +18 -18
- experimaestro/core/identifier.py +11 -11
- experimaestro/core/objects/config.py +96 -39
- experimaestro/core/objects/config_walk.py +3 -3
- experimaestro/core/{subparameters.py → partial.py} +16 -16
- experimaestro/core/partial_lock.py +394 -0
- experimaestro/core/types.py +12 -15
- experimaestro/dynamic.py +290 -0
- experimaestro/experiments/__init__.py +6 -2
- experimaestro/experiments/cli.py +223 -52
- experimaestro/experiments/configuration.py +24 -0
- experimaestro/generators.py +5 -5
- experimaestro/ipc.py +118 -1
- experimaestro/launcherfinder/__init__.py +2 -2
- experimaestro/launcherfinder/registry.py +6 -7
- experimaestro/launcherfinder/specs.py +2 -9
- experimaestro/launchers/slurm/__init__.py +2 -2
- experimaestro/launchers/slurm/base.py +62 -0
- experimaestro/locking.py +957 -1
- experimaestro/notifications.py +89 -201
- experimaestro/progress.py +63 -366
- experimaestro/rpyc.py +0 -2
- experimaestro/run.py +29 -2
- experimaestro/scheduler/__init__.py +8 -1
- experimaestro/scheduler/base.py +650 -53
- experimaestro/scheduler/dependencies.py +20 -16
- experimaestro/scheduler/experiment.py +764 -169
- experimaestro/scheduler/interfaces.py +338 -96
- experimaestro/scheduler/jobs.py +58 -20
- experimaestro/scheduler/remote/__init__.py +31 -0
- experimaestro/scheduler/remote/adaptive_sync.py +265 -0
- experimaestro/scheduler/remote/client.py +928 -0
- experimaestro/scheduler/remote/protocol.py +282 -0
- experimaestro/scheduler/remote/server.py +447 -0
- experimaestro/scheduler/remote/sync.py +144 -0
- experimaestro/scheduler/services.py +186 -35
- experimaestro/scheduler/state_provider.py +811 -2157
- experimaestro/scheduler/state_status.py +1247 -0
- experimaestro/scheduler/transient.py +31 -0
- experimaestro/scheduler/workspace.py +1 -1
- experimaestro/scheduler/workspace_state_provider.py +1273 -0
- experimaestro/scriptbuilder.py +4 -4
- experimaestro/settings.py +36 -0
- experimaestro/tests/conftest.py +33 -5
- experimaestro/tests/connectors/bin/executable.py +1 -1
- experimaestro/tests/fixtures/pre_experiment/experiment_check_env.py +16 -0
- experimaestro/tests/fixtures/pre_experiment/experiment_check_mock.py +14 -0
- experimaestro/tests/fixtures/pre_experiment/experiment_simple.py +12 -0
- experimaestro/tests/fixtures/pre_experiment/pre_setup_env.py +5 -0
- experimaestro/tests/fixtures/pre_experiment/pre_setup_error.py +3 -0
- experimaestro/tests/fixtures/pre_experiment/pre_setup_mock.py +8 -0
- experimaestro/tests/launchers/bin/test.py +1 -0
- experimaestro/tests/launchers/test_slurm.py +9 -9
- experimaestro/tests/partial_reschedule.py +46 -0
- experimaestro/tests/restart.py +3 -3
- experimaestro/tests/restart_main.py +1 -0
- experimaestro/tests/scripts/notifyandwait.py +1 -0
- experimaestro/tests/task_partial.py +38 -0
- experimaestro/tests/task_tokens.py +2 -2
- experimaestro/tests/tasks/test_dynamic.py +6 -6
- experimaestro/tests/test_dependencies.py +3 -3
- experimaestro/tests/test_deprecated.py +15 -15
- experimaestro/tests/test_dynamic_locking.py +317 -0
- experimaestro/tests/test_environment.py +24 -14
- experimaestro/tests/test_experiment.py +171 -36
- experimaestro/tests/test_identifier.py +25 -25
- experimaestro/tests/test_identifier_stability.py +3 -5
- experimaestro/tests/test_multitoken.py +2 -4
- experimaestro/tests/{test_subparameters.py → test_partial.py} +25 -25
- experimaestro/tests/test_partial_paths.py +81 -138
- experimaestro/tests/test_pre_experiment.py +219 -0
- experimaestro/tests/test_progress.py +2 -8
- experimaestro/tests/test_remote_state.py +1132 -0
- experimaestro/tests/test_stray_jobs.py +261 -0
- experimaestro/tests/test_tasks.py +1 -2
- experimaestro/tests/test_token_locking.py +52 -67
- experimaestro/tests/test_tokens.py +5 -6
- experimaestro/tests/test_transient.py +225 -0
- experimaestro/tests/test_workspace_state_provider.py +768 -0
- experimaestro/tests/token_reschedule.py +1 -3
- experimaestro/tests/utils.py +2 -7
- experimaestro/tokens.py +227 -372
- experimaestro/tools/diff.py +1 -0
- experimaestro/tools/documentation.py +4 -5
- experimaestro/tools/jobs.py +1 -2
- experimaestro/tui/app.py +459 -1895
- experimaestro/tui/app.tcss +162 -0
- experimaestro/tui/dialogs.py +172 -0
- experimaestro/tui/log_viewer.py +253 -3
- experimaestro/tui/messages.py +137 -0
- experimaestro/tui/utils.py +54 -0
- experimaestro/tui/widgets/__init__.py +23 -0
- experimaestro/tui/widgets/experiments.py +468 -0
- experimaestro/tui/widgets/global_services.py +238 -0
- experimaestro/tui/widgets/jobs.py +972 -0
- experimaestro/tui/widgets/log.py +156 -0
- experimaestro/tui/widgets/orphans.py +363 -0
- experimaestro/tui/widgets/runs.py +185 -0
- experimaestro/tui/widgets/services.py +314 -0
- experimaestro/tui/widgets/stray_jobs.py +528 -0
- experimaestro/utils/__init__.py +1 -1
- experimaestro/utils/environment.py +105 -22
- experimaestro/utils/fswatcher.py +124 -0
- experimaestro/utils/jobs.py +1 -2
- experimaestro/utils/jupyter.py +1 -2
- experimaestro/utils/logging.py +72 -0
- experimaestro/version.py +2 -2
- experimaestro/webui/__init__.py +9 -0
- experimaestro/webui/app.py +117 -0
- experimaestro/{server → webui}/data/index.css +66 -11
- experimaestro/webui/data/index.css.map +1 -0
- experimaestro/{server → webui}/data/index.js +82763 -87217
- experimaestro/webui/data/index.js.map +1 -0
- experimaestro/webui/routes/__init__.py +5 -0
- experimaestro/webui/routes/auth.py +53 -0
- experimaestro/webui/routes/proxy.py +117 -0
- experimaestro/webui/server.py +200 -0
- experimaestro/webui/state_bridge.py +152 -0
- experimaestro/webui/websocket.py +413 -0
- {experimaestro-2.0.0b4.dist-info → experimaestro-2.0.0b17.dist-info}/METADATA +8 -9
- experimaestro-2.0.0b17.dist-info/RECORD +219 -0
- experimaestro/cli/progress.py +0 -269
- experimaestro/scheduler/state.py +0 -75
- experimaestro/scheduler/state_db.py +0 -388
- experimaestro/scheduler/state_sync.py +0 -834
- experimaestro/server/__init__.py +0 -467
- experimaestro/server/data/index.css.map +0 -1
- experimaestro/server/data/index.js.map +0 -1
- experimaestro/tests/test_cli_jobs.py +0 -615
- experimaestro/tests/test_file_progress.py +0 -425
- experimaestro/tests/test_file_progress_integration.py +0 -477
- experimaestro/tests/test_state_db.py +0 -434
- experimaestro-2.0.0b4.dist-info/RECORD +0 -181
- /experimaestro/{server → webui}/data/1815e00441357e01619e.ttf +0 -0
- /experimaestro/{server → webui}/data/2463b90d9a316e4e5294.woff2 +0 -0
- /experimaestro/{server → webui}/data/2582b0e4bcf85eceead0.ttf +0 -0
- /experimaestro/{server → webui}/data/89999bdf5d835c012025.woff2 +0 -0
- /experimaestro/{server → webui}/data/914997e1bdfc990d0897.ttf +0 -0
- /experimaestro/{server → webui}/data/c210719e60948b211a12.woff2 +0 -0
- /experimaestro/{server → webui}/data/favicon.ico +0 -0
- /experimaestro/{server → webui}/data/index.html +0 -0
- /experimaestro/{server → webui}/data/login.html +0 -0
- /experimaestro/{server → webui}/data/manifest.json +0 -0
- {experimaestro-2.0.0b4.dist-info → experimaestro-2.0.0b17.dist-info}/WHEEL +0 -0
- {experimaestro-2.0.0b4.dist-info → experimaestro-2.0.0b17.dist-info}/entry_points.txt +0 -0
- {experimaestro-2.0.0b4.dist-info → experimaestro-2.0.0b17.dist-info}/licenses/LICENSE +0 -0
|
@@ -0,0 +1,219 @@
|
|
|
1
|
+
"""Tests for pre_experiment feature in run-experiment CLI"""
|
|
2
|
+
|
|
3
|
+
import pytest
|
|
4
|
+
from pathlib import Path
|
|
5
|
+
from click.testing import CliRunner
|
|
6
|
+
|
|
7
|
+
from experimaestro.cli import cli
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
# --- Test fixture files as separate modules ---
|
|
11
|
+
|
|
12
|
+
# Pre-experiment scripts
|
|
13
|
+
PRE_SETUP_ENV = (
|
|
14
|
+
Path(__file__).parent / "fixtures" / "pre_experiment" / "pre_setup_env.py"
|
|
15
|
+
)
|
|
16
|
+
PRE_SETUP_MOCK = (
|
|
17
|
+
Path(__file__).parent / "fixtures" / "pre_experiment" / "pre_setup_mock.py"
|
|
18
|
+
)
|
|
19
|
+
PRE_SETUP_ERROR = (
|
|
20
|
+
Path(__file__).parent / "fixtures" / "pre_experiment" / "pre_setup_error.py"
|
|
21
|
+
)
|
|
22
|
+
|
|
23
|
+
# Experiment files
|
|
24
|
+
EXP_CHECK_ENV = (
|
|
25
|
+
Path(__file__).parent / "fixtures" / "pre_experiment" / "experiment_check_env.py"
|
|
26
|
+
)
|
|
27
|
+
EXP_CHECK_MOCK = (
|
|
28
|
+
Path(__file__).parent / "fixtures" / "pre_experiment" / "experiment_check_mock.py"
|
|
29
|
+
)
|
|
30
|
+
EXP_SIMPLE = (
|
|
31
|
+
Path(__file__).parent / "fixtures" / "pre_experiment" / "experiment_simple.py"
|
|
32
|
+
)
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
@pytest.fixture
|
|
36
|
+
def experiment_dir(tmp_path):
|
|
37
|
+
"""Create a directory with experiment files"""
|
|
38
|
+
exp_dir = tmp_path / "experiment"
|
|
39
|
+
exp_dir.mkdir()
|
|
40
|
+
|
|
41
|
+
workdir = tmp_path / "workdir"
|
|
42
|
+
workdir.mkdir()
|
|
43
|
+
|
|
44
|
+
return exp_dir, workdir
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
def _create_yaml(exp_dir, experiment_id, file_path, pre_experiment_path=None):
|
|
48
|
+
"""Helper to create a YAML config file"""
|
|
49
|
+
yaml_file = exp_dir / "config.yaml"
|
|
50
|
+
content = f"id: {experiment_id}\nfile: {file_path}\n"
|
|
51
|
+
if pre_experiment_path:
|
|
52
|
+
content += f"pre_experiment: {pre_experiment_path}\n"
|
|
53
|
+
yaml_file.write_text(content)
|
|
54
|
+
return yaml_file
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
def test_pre_experiment_sets_env_var(experiment_dir):
|
|
58
|
+
"""Test that pre_experiment script can set environment variables"""
|
|
59
|
+
exp_dir, workdir = experiment_dir
|
|
60
|
+
|
|
61
|
+
# Copy fixture files
|
|
62
|
+
import shutil
|
|
63
|
+
|
|
64
|
+
shutil.copy(PRE_SETUP_ENV, exp_dir / "pre_setup.py")
|
|
65
|
+
shutil.copy(EXP_CHECK_ENV, exp_dir / "experiment.py")
|
|
66
|
+
|
|
67
|
+
yaml_file = _create_yaml(
|
|
68
|
+
exp_dir, "test-pre-experiment", "experiment", "pre_setup.py"
|
|
69
|
+
)
|
|
70
|
+
|
|
71
|
+
runner = CliRunner(env={"XPM_TEST_PRE_EXPERIMENT": ""})
|
|
72
|
+
result = runner.invoke(
|
|
73
|
+
cli,
|
|
74
|
+
[
|
|
75
|
+
"run-experiment",
|
|
76
|
+
"--workdir",
|
|
77
|
+
str(workdir),
|
|
78
|
+
"--run-mode",
|
|
79
|
+
"DRY_RUN",
|
|
80
|
+
str(yaml_file),
|
|
81
|
+
],
|
|
82
|
+
)
|
|
83
|
+
|
|
84
|
+
assert "PRE_EXPERIMENT_TEST_PASSED" in result.output, (
|
|
85
|
+
f"Pre-experiment did not execute correctly. Output: {result.output}"
|
|
86
|
+
)
|
|
87
|
+
assert result.exit_code == 0, f"CLI failed with: {result.output}"
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
def test_pre_experiment_file_not_found(experiment_dir):
|
|
91
|
+
"""Test error handling when pre_experiment file doesn't exist"""
|
|
92
|
+
exp_dir, workdir = experiment_dir
|
|
93
|
+
|
|
94
|
+
import shutil
|
|
95
|
+
|
|
96
|
+
shutil.copy(EXP_SIMPLE, exp_dir / "experiment.py")
|
|
97
|
+
|
|
98
|
+
yaml_file = _create_yaml(
|
|
99
|
+
exp_dir, "test-pre-experiment-missing", "experiment", "nonexistent.py"
|
|
100
|
+
)
|
|
101
|
+
|
|
102
|
+
runner = CliRunner()
|
|
103
|
+
result = runner.invoke(
|
|
104
|
+
cli,
|
|
105
|
+
[
|
|
106
|
+
"run-experiment",
|
|
107
|
+
"--workdir",
|
|
108
|
+
str(workdir),
|
|
109
|
+
"--run-mode",
|
|
110
|
+
"DRY_RUN",
|
|
111
|
+
str(yaml_file),
|
|
112
|
+
],
|
|
113
|
+
)
|
|
114
|
+
|
|
115
|
+
assert result.exit_code != 0, "Should fail when pre_experiment file doesn't exist"
|
|
116
|
+
assert "not found" in result.output.lower(), (
|
|
117
|
+
f"Should mention file not found. Output: {result.output}"
|
|
118
|
+
)
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
def test_pre_experiment_execution_error(experiment_dir):
|
|
122
|
+
"""Test error handling when pre_experiment script has an error"""
|
|
123
|
+
exp_dir, workdir = experiment_dir
|
|
124
|
+
|
|
125
|
+
import shutil
|
|
126
|
+
|
|
127
|
+
shutil.copy(PRE_SETUP_ERROR, exp_dir / "pre_setup.py")
|
|
128
|
+
shutil.copy(EXP_SIMPLE, exp_dir / "experiment.py")
|
|
129
|
+
|
|
130
|
+
yaml_file = _create_yaml(
|
|
131
|
+
exp_dir, "test-pre-experiment-error", "experiment", "pre_setup.py"
|
|
132
|
+
)
|
|
133
|
+
|
|
134
|
+
runner = CliRunner()
|
|
135
|
+
result = runner.invoke(
|
|
136
|
+
cli,
|
|
137
|
+
[
|
|
138
|
+
"run-experiment",
|
|
139
|
+
"--workdir",
|
|
140
|
+
str(workdir),
|
|
141
|
+
"--run-mode",
|
|
142
|
+
"DRY_RUN",
|
|
143
|
+
str(yaml_file),
|
|
144
|
+
],
|
|
145
|
+
)
|
|
146
|
+
|
|
147
|
+
assert result.exit_code != 0, "Should fail when pre_experiment has an error"
|
|
148
|
+
assert (
|
|
149
|
+
"failed to execute" in result.output.lower()
|
|
150
|
+
or "intentional error" in result.output.lower()
|
|
151
|
+
), f"Should show execution error. Output: {result.output}"
|
|
152
|
+
|
|
153
|
+
|
|
154
|
+
def test_pre_experiment_relative_path(experiment_dir):
|
|
155
|
+
"""Test that pre_experiment relative paths are resolved from YAML file location"""
|
|
156
|
+
exp_dir, workdir = experiment_dir
|
|
157
|
+
|
|
158
|
+
# Create a subdirectory for the pre_experiment script
|
|
159
|
+
setup_dir = exp_dir / "setup"
|
|
160
|
+
setup_dir.mkdir()
|
|
161
|
+
|
|
162
|
+
import shutil
|
|
163
|
+
|
|
164
|
+
shutil.copy(PRE_SETUP_ENV, setup_dir / "init.py")
|
|
165
|
+
shutil.copy(EXP_CHECK_ENV, exp_dir / "experiment.py")
|
|
166
|
+
|
|
167
|
+
yaml_file = _create_yaml(
|
|
168
|
+
exp_dir, "test-pre-experiment-relative", "experiment", "setup/init.py"
|
|
169
|
+
)
|
|
170
|
+
|
|
171
|
+
runner = CliRunner(env={"XPM_TEST_PRE_EXPERIMENT": ""})
|
|
172
|
+
result = runner.invoke(
|
|
173
|
+
cli,
|
|
174
|
+
[
|
|
175
|
+
"run-experiment",
|
|
176
|
+
"--workdir",
|
|
177
|
+
str(workdir),
|
|
178
|
+
"--run-mode",
|
|
179
|
+
"DRY_RUN",
|
|
180
|
+
str(yaml_file),
|
|
181
|
+
],
|
|
182
|
+
)
|
|
183
|
+
|
|
184
|
+
assert "PRE_EXPERIMENT_TEST_PASSED" in result.output, (
|
|
185
|
+
f"Relative path resolution failed. Output: {result.output}"
|
|
186
|
+
)
|
|
187
|
+
assert result.exit_code == 0, f"CLI failed with: {result.output}"
|
|
188
|
+
|
|
189
|
+
|
|
190
|
+
def test_pre_experiment_mocking_modules(experiment_dir):
|
|
191
|
+
"""Test that pre_experiment can mock modules before experiment import"""
|
|
192
|
+
exp_dir, workdir = experiment_dir
|
|
193
|
+
|
|
194
|
+
import shutil
|
|
195
|
+
|
|
196
|
+
shutil.copy(PRE_SETUP_MOCK, exp_dir / "pre_setup.py")
|
|
197
|
+
shutil.copy(EXP_CHECK_MOCK, exp_dir / "experiment.py")
|
|
198
|
+
|
|
199
|
+
yaml_file = _create_yaml(
|
|
200
|
+
exp_dir, "test-pre-experiment-mock", "experiment", "pre_setup.py"
|
|
201
|
+
)
|
|
202
|
+
|
|
203
|
+
runner = CliRunner()
|
|
204
|
+
result = runner.invoke(
|
|
205
|
+
cli,
|
|
206
|
+
[
|
|
207
|
+
"run-experiment",
|
|
208
|
+
"--workdir",
|
|
209
|
+
str(workdir),
|
|
210
|
+
"--run-mode",
|
|
211
|
+
"DRY_RUN",
|
|
212
|
+
str(yaml_file),
|
|
213
|
+
],
|
|
214
|
+
)
|
|
215
|
+
|
|
216
|
+
assert "MOCK_MODULE_TEST_PASSED" in result.output, (
|
|
217
|
+
f"Module mocking failed. Output: {result.output}"
|
|
218
|
+
)
|
|
219
|
+
assert result.exit_code == 0, f"CLI failed with: {result.output}"
|
|
@@ -65,10 +65,7 @@ class ProgressListener(Listener):
|
|
|
65
65
|
|
|
66
66
|
def test_progress_basic():
|
|
67
67
|
"""Test that we get all the progress reports"""
|
|
68
|
-
with TemporaryExperiment("progress-basic", maxwait=5
|
|
69
|
-
assert xp.server is not None
|
|
70
|
-
assert xp.server.port > 0
|
|
71
|
-
|
|
68
|
+
with TemporaryExperiment("progress-basic", maxwait=5) as xp:
|
|
72
69
|
listener = ProgressListener()
|
|
73
70
|
xp.scheduler.addlistener(listener)
|
|
74
71
|
|
|
@@ -162,10 +159,7 @@ def check_nested(
|
|
|
162
159
|
|
|
163
160
|
def test_progress_nested():
|
|
164
161
|
"""Test that we get all the progress reports"""
|
|
165
|
-
with TemporaryExperiment("progress-nested", maxwait=20
|
|
166
|
-
assert xp.server is not None
|
|
167
|
-
assert xp.server.port > 0
|
|
168
|
-
|
|
162
|
+
with TemporaryExperiment("progress-nested", maxwait=20) as xp:
|
|
169
163
|
listener = ProgressListener()
|
|
170
164
|
xp.scheduler.addlistener(listener)
|
|
171
165
|
|