experimaestro 1.7.0rc2__py3-none-any.whl → 1.7.0rc3__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/commandline.py +3 -7
- experimaestro/connectors/__init__.py +5 -2
- experimaestro/connectors/local.py +9 -5
- experimaestro/connectors/ssh.py +1 -1
- experimaestro/launcherfinder/registry.py +5 -1
- experimaestro/launchers/slurm/base.py +47 -9
- experimaestro/notifications.py +1 -3
- experimaestro/scheduler/base.py +4 -0
- experimaestro/scheduler/dynamic_outputs.py +1 -1
- experimaestro/scriptbuilder.py +3 -1
- experimaestro/sphinx/__init__.py +6 -13
- experimaestro/tests/launchers/bin/sbatch +16 -2
- experimaestro/tests/launchers/bin/srun +5 -0
- experimaestro/tests/launchers/common.py +5 -1
- {experimaestro-1.7.0rc2.dist-info → experimaestro-1.7.0rc3.dist-info}/METADATA +1 -2
- {experimaestro-1.7.0rc2.dist-info → experimaestro-1.7.0rc3.dist-info}/RECORD +19 -24
- experimaestro/notifications.proto +0 -17
- experimaestro/notifications_pb2.py +0 -39
- experimaestro/streaming_state.py +0 -124
- experimaestro/tests/state_streamer.proto +0 -9
- experimaestro/tests/state_streamer_pb2.py +0 -38
- experimaestro/tests/test_state_streamer.py +0 -18
- {experimaestro-1.7.0rc2.dist-info → experimaestro-1.7.0rc3.dist-info}/LICENSE +0 -0
- {experimaestro-1.7.0rc2.dist-info → experimaestro-1.7.0rc3.dist-info}/WHEEL +0 -0
- {experimaestro-1.7.0rc2.dist-info → experimaestro-1.7.0rc3.dist-info}/entry_points.txt +0 -0
experimaestro/commandline.py
CHANGED
|
@@ -276,12 +276,6 @@ class CommandLineJob(Job):
|
|
|
276
276
|
|
|
277
277
|
scriptbuilder = self.launcher.scriptbuilder()
|
|
278
278
|
self.path.mkdir(parents=True, exist_ok=True)
|
|
279
|
-
donepath = self.donepath
|
|
280
|
-
|
|
281
|
-
# Check again if done (now that we have locked)
|
|
282
|
-
if not overwrite and donepath.is_file():
|
|
283
|
-
logger.info("Job %s is already done", self)
|
|
284
|
-
return JobState.DONE
|
|
285
279
|
|
|
286
280
|
# Now we can write the script
|
|
287
281
|
scriptbuilder.lockfiles.append(self.lockpath)
|
|
@@ -293,15 +287,17 @@ class CommandLineJob(Job):
|
|
|
293
287
|
if self._process:
|
|
294
288
|
return self._process
|
|
295
289
|
|
|
290
|
+
# Prepare the files to be run
|
|
296
291
|
scriptPath = self.prepare()
|
|
297
292
|
|
|
293
|
+
# OK, now starts the process
|
|
298
294
|
logger.info("Starting job %s", self.jobpath)
|
|
299
295
|
processbuilder = self.launcher.processbuilder()
|
|
300
296
|
processbuilder.environ = self.environ
|
|
301
297
|
processbuilder.command.append(self.launcher.connector.resolve(scriptPath))
|
|
302
298
|
processbuilder.stderr = Redirect.file(self.stderr)
|
|
303
299
|
processbuilder.stdout = Redirect.file(self.stdout)
|
|
304
|
-
self._process = processbuilder.start()
|
|
300
|
+
self._process = processbuilder.start(True)
|
|
305
301
|
|
|
306
302
|
with self.pidpath.open("w") as fp:
|
|
307
303
|
json.dump(self._process.tospec(), fp)
|
|
@@ -154,8 +154,11 @@ class ProcessBuilder:
|
|
|
154
154
|
self.environ: Mapping[str, str] = {}
|
|
155
155
|
self.command = []
|
|
156
156
|
|
|
157
|
-
def start(self) -> Process:
|
|
158
|
-
"""Start the process
|
|
157
|
+
def start(self, task_mode: bool = False) -> Process:
|
|
158
|
+
"""Start the process
|
|
159
|
+
|
|
160
|
+
:param task_mode: True if the process is a job script
|
|
161
|
+
"""
|
|
159
162
|
raise NotImplementedError("Method not implemented in %s" % self.__class__)
|
|
160
163
|
|
|
161
164
|
|
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
"""All classes related to localhost management
|
|
2
|
-
"""
|
|
1
|
+
"""All classes related to localhost management"""
|
|
3
2
|
|
|
4
3
|
import subprocess
|
|
5
4
|
from typing import Optional
|
|
@@ -107,8 +106,11 @@ def getstream(redirect: Redirect, write: bool):
|
|
|
107
106
|
|
|
108
107
|
|
|
109
108
|
class LocalProcessBuilder(ProcessBuilder):
|
|
110
|
-
def start(self):
|
|
111
|
-
"""Start the process
|
|
109
|
+
def start(self, task_mode=False):
|
|
110
|
+
"""Start the process
|
|
111
|
+
|
|
112
|
+
:param task_mode: just ignored
|
|
113
|
+
"""
|
|
112
114
|
stdin = getstream(self.stdin, False)
|
|
113
115
|
stdout = getstream(self.stdout, True)
|
|
114
116
|
stderr = getstream(self.stderr, True)
|
|
@@ -199,7 +201,9 @@ class LocalConnector(Connector):
|
|
|
199
201
|
return LocalProcessBuilder()
|
|
200
202
|
|
|
201
203
|
def resolve(self, path: Path, basepath: Path = None) -> str:
|
|
202
|
-
assert isinstance(path, PosixPath) or isinstance(
|
|
204
|
+
assert isinstance(path, PosixPath) or isinstance(
|
|
205
|
+
path, WindowsPath
|
|
206
|
+
), f"Unrecognized path {type(path)}"
|
|
203
207
|
if not basepath:
|
|
204
208
|
return str(path.absolute())
|
|
205
209
|
try:
|
experimaestro/connectors/ssh.py
CHANGED
|
@@ -8,7 +8,6 @@ import typing
|
|
|
8
8
|
from omegaconf import DictConfig, OmegaConf, SCMode
|
|
9
9
|
import pkg_resources
|
|
10
10
|
from experimaestro.utils import logger
|
|
11
|
-
|
|
12
11
|
from .base import ConnectorConfiguration, TokenConfiguration
|
|
13
12
|
from .specs import HostRequirement
|
|
14
13
|
|
|
@@ -158,9 +157,14 @@ class LauncherRegistry:
|
|
|
158
157
|
specs.append(spec)
|
|
159
158
|
|
|
160
159
|
# Use launcher function
|
|
160
|
+
from experimaestro.launchers import Launcher
|
|
161
|
+
|
|
161
162
|
if self.find_launcher_fn is not None:
|
|
162
163
|
for spec in specs:
|
|
163
164
|
if launcher := self.find_launcher_fn(spec, tags):
|
|
165
|
+
assert isinstance(
|
|
166
|
+
launcher, Launcher
|
|
167
|
+
), "f{self.find_launcher_fn} did not return a Launcher but {type(launcher)}"
|
|
164
168
|
return launcher
|
|
165
169
|
|
|
166
170
|
return None
|
|
@@ -11,6 +11,7 @@ from typing import (
|
|
|
11
11
|
)
|
|
12
12
|
from experimaestro.connectors.local import LocalConnector
|
|
13
13
|
import re
|
|
14
|
+
from shlex import quote as shquote
|
|
14
15
|
from contextlib import contextmanager
|
|
15
16
|
from dataclasses import dataclass
|
|
16
17
|
from experimaestro.launcherfinder.registry import (
|
|
@@ -235,15 +236,15 @@ class SlurmProcessBuilder(ProcessBuilder):
|
|
|
235
236
|
super().__init__()
|
|
236
237
|
self.launcher = launcher
|
|
237
238
|
|
|
238
|
-
def start(self) -> BatchSlurmProcess:
|
|
239
|
+
def start(self, task_mode: bool = False) -> BatchSlurmProcess:
|
|
239
240
|
"""Start the process"""
|
|
240
241
|
builder = self.launcher.connector.processbuilder()
|
|
241
|
-
builder.workingDirectory = self.workingDirectory
|
|
242
242
|
builder.environ = self.launcher.launcherenv
|
|
243
243
|
builder.detach = False
|
|
244
244
|
|
|
245
245
|
if not self.detach:
|
|
246
246
|
# Simplest case: we wait for the output
|
|
247
|
+
builder.workingDirectory = self.workingDirectory
|
|
247
248
|
builder.command = [f"{self.launcher.binpath}/srun"]
|
|
248
249
|
builder.command.extend(self.launcher.options.args())
|
|
249
250
|
builder.command.extend(self.command)
|
|
@@ -255,11 +256,17 @@ class SlurmProcessBuilder(ProcessBuilder):
|
|
|
255
256
|
return builder.start()
|
|
256
257
|
|
|
257
258
|
builder.command = [f"{self.launcher.binpath}/sbatch", "--parsable"]
|
|
258
|
-
builder.command.extend(self.launcher.options.args())
|
|
259
259
|
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
260
|
+
if not task_mode:
|
|
261
|
+
# Use command line parameters when not running a task
|
|
262
|
+
builder.command.extend(self.launcher.options.args())
|
|
263
|
+
|
|
264
|
+
if self.workingDirectory:
|
|
265
|
+
workdir = self.launcher.connector.resolve(self.workingDirectory)
|
|
266
|
+
builder.command.append(f"--chdir={workdir}")
|
|
267
|
+
addstream(builder.command, "-e", self.stderr)
|
|
268
|
+
addstream(builder.command, "-o", self.stdout)
|
|
269
|
+
addstream(builder.command, "-i", self.stdin)
|
|
263
270
|
|
|
264
271
|
builder.command.extend(self.command)
|
|
265
272
|
logger.info(
|
|
@@ -427,12 +434,43 @@ class SlurmLauncher(Launcher):
|
|
|
427
434
|
|
|
428
435
|
We assume *nix, but should be changed to PythonScriptBuilder when working
|
|
429
436
|
"""
|
|
430
|
-
|
|
431
|
-
builder.processtype = "slurm"
|
|
432
|
-
return builder
|
|
437
|
+
return SlurmScriptBuilder(self)
|
|
433
438
|
|
|
434
439
|
def processbuilder(self) -> SlurmProcessBuilder:
|
|
435
440
|
"""Returns the process builder for this launcher
|
|
436
441
|
|
|
437
442
|
By default, returns the associated connector builder"""
|
|
438
443
|
return SlurmProcessBuilder(self)
|
|
444
|
+
|
|
445
|
+
|
|
446
|
+
class SlurmScriptBuilder(PythonScriptBuilder):
|
|
447
|
+
def __init__(self, launcher: SlurmLauncher, pythonpath=None):
|
|
448
|
+
super().__init__(pythonpath)
|
|
449
|
+
self.launcher = launcher
|
|
450
|
+
self.processtype = "slurm"
|
|
451
|
+
|
|
452
|
+
def write(self, job):
|
|
453
|
+
py_path = super().write(job)
|
|
454
|
+
main_path = py_path.parent
|
|
455
|
+
|
|
456
|
+
def relpath(path: Path):
|
|
457
|
+
return shquote(self.launcher.connector.resolve(path, main_path))
|
|
458
|
+
|
|
459
|
+
# Writes the sbatch shell script containing all the options
|
|
460
|
+
sh_path = job.jobpath / ("%s.sh" % job.name)
|
|
461
|
+
with sh_path.open("wt") as out:
|
|
462
|
+
out.write("""#!/bin/sh\n\n""")
|
|
463
|
+
|
|
464
|
+
workdir = self.launcher.connector.resolve(main_path)
|
|
465
|
+
out.write(f"#SBATCH --chdir={shquote(workdir)}\n")
|
|
466
|
+
out.write(f"""#SBATCH --error={relpath(job.stderr)}\n""")
|
|
467
|
+
out.write(f"""#SBATCH --output={relpath(job.stdout)}\n""")
|
|
468
|
+
|
|
469
|
+
for arg in self.launcher.options.args():
|
|
470
|
+
out.write(f"""#SBATCH {arg}\n""")
|
|
471
|
+
|
|
472
|
+
# We finish by the call to srun
|
|
473
|
+
out.write(f"""\nsrun ./{relpath(py_path)}\n\n""")
|
|
474
|
+
|
|
475
|
+
self.launcher.connector.setExecutable(sh_path, True)
|
|
476
|
+
return sh_path
|
experimaestro/notifications.py
CHANGED
|
@@ -12,16 +12,15 @@ from tqdm.auto import tqdm as std_tqdm
|
|
|
12
12
|
|
|
13
13
|
from .utils import logger
|
|
14
14
|
from experimaestro.taskglobals import Env as TaskEnv
|
|
15
|
-
from experimaestro.notifications_pb2 import LevelInformation
|
|
16
15
|
|
|
17
16
|
# --- Progress and other notifications
|
|
18
17
|
|
|
19
18
|
T = TypeVar("T")
|
|
20
19
|
|
|
20
|
+
|
|
21
21
|
@dataclass
|
|
22
22
|
class LevelInformation:
|
|
23
23
|
level: int
|
|
24
|
-
|
|
25
24
|
desc: Optional[str]
|
|
26
25
|
progress: float
|
|
27
26
|
|
|
@@ -214,7 +213,6 @@ class Reporter(threading.Thread):
|
|
|
214
213
|
|
|
215
214
|
self.cv.notify_all()
|
|
216
215
|
|
|
217
|
-
#: The reporter instance
|
|
218
216
|
INSTANCE: ClassVar[Optional["Reporter"]] = None
|
|
219
217
|
|
|
220
218
|
@staticmethod
|
experimaestro/scheduler/base.py
CHANGED
|
@@ -740,6 +740,7 @@ class Scheduler:
|
|
|
740
740
|
code = await process.aio_code()
|
|
741
741
|
logger.debug("Got return code %s for %s", code, job)
|
|
742
742
|
|
|
743
|
+
# Check the file if there is no return code
|
|
743
744
|
if code is None:
|
|
744
745
|
# Case where we cannot retrieve the code right away
|
|
745
746
|
if job.donepath.is_file():
|
|
@@ -916,6 +917,7 @@ class experiment:
|
|
|
916
917
|
|
|
917
918
|
async def awaitcompletion():
|
|
918
919
|
assert self.central is not None
|
|
920
|
+
logger.debug("Waiting to exit scheduler...")
|
|
919
921
|
async with self.central.exitCondition:
|
|
920
922
|
while True:
|
|
921
923
|
if self.exitMode:
|
|
@@ -1011,6 +1013,7 @@ class experiment:
|
|
|
1011
1013
|
return self
|
|
1012
1014
|
|
|
1013
1015
|
def __exit__(self, exc_type, exc_value, traceback):
|
|
1016
|
+
logger.debug("Exiting scheduler context")
|
|
1014
1017
|
# If no exception and normal run mode, remove old "jobs"
|
|
1015
1018
|
if self.workspace.run_mode == RunMode.NORMAL:
|
|
1016
1019
|
if exc_type is None and self.jobsbakpath.is_dir():
|
|
@@ -1051,6 +1054,7 @@ class experiment:
|
|
|
1051
1054
|
# Put back old experiment as current one
|
|
1052
1055
|
experiment.CURRENT = self.old_experiment
|
|
1053
1056
|
if self.server:
|
|
1057
|
+
logger.info("Stopping web server")
|
|
1054
1058
|
self.server.stop()
|
|
1055
1059
|
|
|
1056
1060
|
async def update_task_output_count(self, delta: int):
|
|
@@ -135,7 +135,7 @@ class TaskOutputsWorker(threading.Thread):
|
|
|
135
135
|
"""This worker process dynamic output queue for one experiment"""
|
|
136
136
|
|
|
137
137
|
def __init__(self, xp: experiment):
|
|
138
|
-
super().__init__(name="task outputs worker")
|
|
138
|
+
super().__init__(name="task outputs worker", daemon=True)
|
|
139
139
|
self.queue = queue.Queue()
|
|
140
140
|
self.xp = xp
|
|
141
141
|
|
experimaestro/scriptbuilder.py
CHANGED
|
@@ -51,6 +51,8 @@ class PythonScriptBuilder:
|
|
|
51
51
|
self.lockfiles: List[Path] = []
|
|
52
52
|
self.notificationURL: Optional[str] = None
|
|
53
53
|
self.command: Optional[AbstractCommand] = None
|
|
54
|
+
|
|
55
|
+
# This is used to serialize the full process identifier on disk
|
|
54
56
|
self.processtype = "local"
|
|
55
57
|
|
|
56
58
|
def write(self, job: CommandLineJob):
|
|
@@ -63,7 +65,7 @@ class PythonScriptBuilder:
|
|
|
63
65
|
job {CommandLineJob} -- [description]
|
|
64
66
|
|
|
65
67
|
Returns:
|
|
66
|
-
|
|
68
|
+
str -- The script path on disk
|
|
67
69
|
"""
|
|
68
70
|
assert isinstance(
|
|
69
71
|
job, CommandLineJob
|
experimaestro/sphinx/__init__.py
CHANGED
|
@@ -10,19 +10,16 @@ from docutils import nodes
|
|
|
10
10
|
from sphinx.application import Sphinx
|
|
11
11
|
from sphinx import addnodes
|
|
12
12
|
from sphinx.ext.autodoc import ClassDocumenter, Documenter, restify
|
|
13
|
-
from sphinx.locale import _
|
|
13
|
+
from sphinx.locale import _
|
|
14
14
|
from sphinx.util import inspect, logging
|
|
15
15
|
from sphinx.domains.python import (
|
|
16
16
|
PyClasslike,
|
|
17
17
|
PyAttribute,
|
|
18
|
-
|
|
19
|
-
directives,
|
|
20
|
-
desc_signature,
|
|
21
|
-
_parse_annotation,
|
|
18
|
+
directives, # noqa: F401
|
|
22
19
|
)
|
|
20
|
+
from sphinx.addnodes import desc_signature
|
|
23
21
|
from sphinx.util.typing import OptionSpec
|
|
24
22
|
from docutils.statemachine import StringList
|
|
25
|
-
import logging
|
|
26
23
|
import re
|
|
27
24
|
|
|
28
25
|
from experimaestro import Config, Task
|
|
@@ -97,9 +94,6 @@ class ConfigDocumenter(ClassDocumenter):
|
|
|
97
94
|
can_document = inspect.isclass(member) and issubclass(member, Config)
|
|
98
95
|
return can_document
|
|
99
96
|
|
|
100
|
-
def add_directive_header(self, sig: str) -> None:
|
|
101
|
-
super().add_directive_header(sig)
|
|
102
|
-
|
|
103
97
|
def get_object_members(self, want_all: bool): # -> Tuple[bool, ObjectMembers]:
|
|
104
98
|
r = super().get_object_members(want_all)
|
|
105
99
|
return r
|
|
@@ -157,7 +151,7 @@ class ConfigDocumenter(ClassDocumenter):
|
|
|
157
151
|
|
|
158
152
|
# Our specific code
|
|
159
153
|
if issubclass(self.object, Task):
|
|
160
|
-
self.add_line(
|
|
154
|
+
self.add_line(" :task:", sourcename)
|
|
161
155
|
|
|
162
156
|
# add inheritance info, if wanted
|
|
163
157
|
if not self.doc_as_attr and self.options.show_inheritance:
|
|
@@ -196,7 +190,6 @@ class ConfigDocumenter(ClassDocumenter):
|
|
|
196
190
|
def add_content(
|
|
197
191
|
self, more_content: Optional[StringList], no_docstring: bool = False
|
|
198
192
|
) -> None:
|
|
199
|
-
|
|
200
193
|
xpminfo = getxpminfo(self.object)
|
|
201
194
|
source_name = self.get_sourcename()
|
|
202
195
|
|
|
@@ -214,9 +207,9 @@ class ConfigDocumenter(ClassDocumenter):
|
|
|
214
207
|
source_name,
|
|
215
208
|
)
|
|
216
209
|
if argument.generator:
|
|
217
|
-
self.add_line(
|
|
210
|
+
self.add_line(" :generated:", source_name)
|
|
218
211
|
elif argument.constant:
|
|
219
|
-
self.add_line(
|
|
212
|
+
self.add_line(" :constant:", source_name)
|
|
220
213
|
|
|
221
214
|
# self.add_line("", source_name)
|
|
222
215
|
if argument.help:
|
|
@@ -2,8 +2,11 @@
|
|
|
2
2
|
|
|
3
3
|
# slurm test suite
|
|
4
4
|
|
|
5
|
+
pwd 1>&2
|
|
6
|
+
CURDIR="$(realpath "$(dirname "$0")"/..)"
|
|
7
|
+
|
|
5
8
|
# Where we store the jobs
|
|
6
|
-
XPM_SLURM_DIR="$
|
|
9
|
+
XPM_SLURM_DIR="${CURDIR}/slurm"
|
|
7
10
|
if ! test -d "$XPM_SLURM_DIR"; then
|
|
8
11
|
echo "Directory $XPM_SLURM_DIR does not exist" 1>&2
|
|
9
12
|
exit 1
|
|
@@ -51,8 +54,19 @@ while true; do
|
|
|
51
54
|
esac
|
|
52
55
|
done
|
|
53
56
|
|
|
54
|
-
|
|
57
|
+
chdir=$(pwd)
|
|
58
|
+
while IFS= read -r line; do
|
|
59
|
+
case "$line" in
|
|
60
|
+
"#SBATCH --output="*)stdout=${line#*#SBATCH --output=};;
|
|
61
|
+
"#SBATCH --error="*) stderr=${line#*#SBATCH --error=};;
|
|
62
|
+
"#SBATCH --chdir="*) chdir=${line#*#SBATCH --chdir=};;
|
|
63
|
+
esac
|
|
64
|
+
done < "$1"
|
|
65
|
+
|
|
66
|
+
echo "Starting $@ ${args[@]} > $stdout 2> $stderr" >&2
|
|
55
67
|
(
|
|
68
|
+
if test "$chdir"; then cd "$chdir"; fi
|
|
69
|
+
export PATH="${CURDIR}/bin:$PATH"
|
|
56
70
|
eval "$@" "${args[@]}"
|
|
57
71
|
echo $? > "$XPM_SLURM_DIR/jobs/$$.status"
|
|
58
72
|
) > $stdout 2> $stderr &
|
|
@@ -80,10 +80,14 @@ def takeback(launcher, datapath, txp1, txp2):
|
|
|
80
80
|
waiting = datapath / "waiting"
|
|
81
81
|
|
|
82
82
|
with txp1:
|
|
83
|
-
WaitUntilTouched
|
|
83
|
+
task: WaitUntilTouched = WaitUntilTouched(
|
|
84
|
+
touching=touching, waiting=waiting
|
|
85
|
+
).submit(launcher=launcher)
|
|
84
86
|
|
|
85
87
|
logger.debug("Waiting for task to create 'touching' file")
|
|
86
88
|
while not touching.is_file():
|
|
89
|
+
if task.__xpm__.job.state.finished():
|
|
90
|
+
raise Exception("Job has finished... too early")
|
|
87
91
|
time.sleep(0.01)
|
|
88
92
|
|
|
89
93
|
with txp2:
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.3
|
|
2
2
|
Name: experimaestro
|
|
3
|
-
Version: 1.7.
|
|
3
|
+
Version: 1.7.0rc3
|
|
4
4
|
Summary: "Experimaestro is a computer science experiment manager"
|
|
5
5
|
License: GPL-3
|
|
6
6
|
Keywords: experiment manager
|
|
@@ -34,7 +34,6 @@ Requires-Dist: huggingface-hub (>0.17)
|
|
|
34
34
|
Requires-Dist: humanfriendly (>=10,<11)
|
|
35
35
|
Requires-Dist: marshmallow (>=3.20,<4.0)
|
|
36
36
|
Requires-Dist: omegaconf (>=2.3,<3.0)
|
|
37
|
-
Requires-Dist: protobuf (>5)
|
|
38
37
|
Requires-Dist: psutil (>=7)
|
|
39
38
|
Requires-Dist: pyparsing (>=3.1,<4.0)
|
|
40
39
|
Requires-Dist: pytools (>=2023.1.1,<2024.0.0)
|
|
@@ -6,11 +6,11 @@ experimaestro/cli/__init__.py,sha256=mzc-qqTFtZnFwCQl7IiwlYXEx08kLGwdntWayCerZ6E
|
|
|
6
6
|
experimaestro/cli/filter.py,sha256=sdP651rnvMfzdwRdv0AiG1BqK8Z6Mia8l-D-qatGB04,5781
|
|
7
7
|
experimaestro/cli/jobs.py,sha256=QJNno0r6aKylol9jsGRgEPRmehEIyl7pv7CZk-DfvJg,7771
|
|
8
8
|
experimaestro/click.py,sha256=6BkeQHEgcxaxzq3xEvEEzwzuBj5-dkfrpOGikuA8L00,1377
|
|
9
|
-
experimaestro/commandline.py,sha256=
|
|
9
|
+
experimaestro/commandline.py,sha256=MJIJcfppGCjNA8ozxXUzbUSeDOlTExuzhxryGx3_lIo,9314
|
|
10
10
|
experimaestro/compat.py,sha256=dQqE2ZNHLM2wtdfp7fBRYMfC33qNehVf9J6FGRBUQhs,171
|
|
11
|
-
experimaestro/connectors/__init__.py,sha256=
|
|
12
|
-
experimaestro/connectors/local.py,sha256=
|
|
13
|
-
experimaestro/connectors/ssh.py,sha256=
|
|
11
|
+
experimaestro/connectors/__init__.py,sha256=Pe8CZ73IedTIb51-zbdMMk-JFFkZ0t1cOL6QuvdSJjg,6026
|
|
12
|
+
experimaestro/connectors/local.py,sha256=348akOw69t7LgiTBMPG5McCg821I8qfR5GvYNU14sv4,6051
|
|
13
|
+
experimaestro/connectors/ssh.py,sha256=5giqvv1y0QQKF-GI0IFUzI_Z5H8Bj9EuL_Szpvk899Q,8600
|
|
14
14
|
experimaestro/core/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
15
15
|
experimaestro/core/arguments.py,sha256=7hpkU1f8LJ7JL8kQaD514h9CFSfMotYLsVfMsMmdpWk,6487
|
|
16
16
|
experimaestro/core/context.py,sha256=Q8_ngiHRBZ0laavXRJNiDvdCprrnROVTWaHfrwMdlG4,2638
|
|
@@ -30,13 +30,13 @@ experimaestro/ipc.py,sha256=Xn3tYME83jLEB0nFak3DwEIhpL5IRZpCl3jirBF_jl4,1570
|
|
|
30
30
|
experimaestro/launcherfinder/__init__.py,sha256=qRUDyv3B9UsAM8Q31mRrZrTZox0AptwdmOY4f2K-TUo,279
|
|
31
31
|
experimaestro/launcherfinder/base.py,sha256=q47SsF_cXdo5O6ZhFKn5385WVFcx8Wd-BcEpd6tRpbs,515
|
|
32
32
|
experimaestro/launcherfinder/parser.py,sha256=pYbfEJw7osnqZWm7fkVhQawhpNU8dLU_6vEjtXdc8E8,2279
|
|
33
|
-
experimaestro/launcherfinder/registry.py,sha256=
|
|
33
|
+
experimaestro/launcherfinder/registry.py,sha256=4Ix86rrooRucOlKtiCufmdvrebT-FRrzpqQu0qHpJJg,6337
|
|
34
34
|
experimaestro/launcherfinder/specs.py,sha256=G8za6mEmkVxuZY_ab3OhWJIpONpcBMO_iXeB30sUbhI,6448
|
|
35
35
|
experimaestro/launchers/__init__.py,sha256=lXn544sgJExr6uirILWzAXu_IfmfyqFZOt4OzRnjHXg,2525
|
|
36
36
|
experimaestro/launchers/direct.py,sha256=JZh6WOPnO6ED_xlOs8pL4MRFmnRhmXzpVxTl-ByaD2A,258
|
|
37
37
|
experimaestro/launchers/oar.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
38
38
|
experimaestro/launchers/slurm/__init__.py,sha256=R1Zwd4phZaXV8FwCYhzfB44n0V4cf-hBQzOc6NkFQ0s,41
|
|
39
|
-
experimaestro/launchers/slurm/base.py,sha256=
|
|
39
|
+
experimaestro/launchers/slurm/base.py,sha256=s6rE_wQBmFuW_Ee75F4iG2ktpWHdKY_SXQsZs3qObB8,15678
|
|
40
40
|
experimaestro/locking.py,sha256=hPT-LuDGZTijpbme8O0kEoB9a3WjdVzI2h31OT44UxE,1477
|
|
41
41
|
experimaestro/mkdocs/__init__.py,sha256=u3AT-uBu3PqyZZXBr6U_ffioEoSZngDdw85005DbyDA,34
|
|
42
42
|
experimaestro/mkdocs/annotations.py,sha256=qpDw8lzrxpsOShXcpcP_LAeR3UhiIXAybG8UvS64-OU,263
|
|
@@ -44,19 +44,17 @@ experimaestro/mkdocs/base.py,sha256=SwLh9s7BZfrTAZdBaealSqVeLAroDSwLLMOHmLCxMPQ,
|
|
|
44
44
|
experimaestro/mkdocs/metaloader.py,sha256=qCqnTWhlgxql-oe46E8AbvYdoM311-lQh-msmPnbllQ,1481
|
|
45
45
|
experimaestro/mkdocs/style.css,sha256=42kJ6Ozq_n4Iw5UfJ4-nO1u-HN3ELvV7Vhvj1Xkn7rQ,66
|
|
46
46
|
experimaestro/mypy.py,sha256=M39VFuDrab-ymlCDIF5jys9oKpTwnuBPzb1T8Un5J3s,285
|
|
47
|
-
experimaestro/notifications.
|
|
48
|
-
experimaestro/notifications.py,sha256=j5BbUkQ9JkRvAJCH60MtrOPbaZ1iWTafc-6bFKYrod8,9368
|
|
49
|
-
experimaestro/notifications_pb2.py,sha256=nkUsLXGOXtKIxdASePIFPgLEH6DuGXUKVX1TpxjMS4M,1847
|
|
47
|
+
experimaestro/notifications.py,sha256=UOcCai8wPGzAI_Zu1-KUr9a8Ns1eHUym3gwjUHXUtSE,9274
|
|
50
48
|
experimaestro/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
51
49
|
experimaestro/rpyc.py,sha256=ZRKol-3tVoeoUITLNFenLF4dhWBLW_FvSV_GvsypmeI,3605
|
|
52
50
|
experimaestro/run.py,sha256=58ZlIZ2dQ7a0un2iGiyHJhK14zc18BnpEFDis7OyTPA,5222
|
|
53
51
|
experimaestro/scheduler/__init__.py,sha256=ERmmOxz_9mUkIuccNbzUa5Y6gVLLVDdyc4cCxbCCUbY,20
|
|
54
|
-
experimaestro/scheduler/base.py,sha256=
|
|
52
|
+
experimaestro/scheduler/base.py,sha256=R40hKI_2cpyZ8F4GTwEulTWczPAaGYi1UaaELh9etGM,35368
|
|
55
53
|
experimaestro/scheduler/dependencies.py,sha256=n9XegwrmjayOIxt3xhuTEBVEBGSq4oeVdzz-FviDGXo,1994
|
|
56
|
-
experimaestro/scheduler/dynamic_outputs.py,sha256=
|
|
54
|
+
experimaestro/scheduler/dynamic_outputs.py,sha256=yYPL98I0nSgDjlE3Sk9dtvovh2PZ6rUDnKjDNnAg1dc,5732
|
|
57
55
|
experimaestro/scheduler/services.py,sha256=aCKkNZMULlceabqf-kOs_-C7KPINnjU3Q-I00o5x6iY,2189
|
|
58
56
|
experimaestro/scheduler/workspace.py,sha256=KNdxPBwUk7gO8h2utMCrlIVKB-f2Ylqg_IxLc4okp_8,2320
|
|
59
|
-
experimaestro/scriptbuilder.py,sha256=
|
|
57
|
+
experimaestro/scriptbuilder.py,sha256=6GKUkgixLqSEy41sNr-_HNcrjKb8uxaoQ65DywRYsC0,5027
|
|
60
58
|
experimaestro/server/__init__.py,sha256=F2bzLf2q29Haj2OIbPA26r5WVbaipBNylIozg-As758,10854
|
|
61
59
|
experimaestro/server/data/016b4a6cdced82ab3aa1.ttf,sha256=AD8RVBhWpkmmyCNcYmbIk2IkxdYJ5RRC2iTcVVbRT78,189684
|
|
62
60
|
experimaestro/server/data/0c35d18bf06992036b69.woff2,sha256=gmX2R4Y5fWuDLRygqv3xSa2E5ydZ__qfcnLpGg-wFdE,128352
|
|
@@ -89,9 +87,8 @@ experimaestro/server/data/index.js.map,sha256=cv7KF28Uq5dy7Ux3yRnoSVztrOFVid359f
|
|
|
89
87
|
experimaestro/server/data/login.html,sha256=4dvhSOn6DHp_tbmzqIKrqq2uAo0sAUbgLVD0lTnPp4s,511
|
|
90
88
|
experimaestro/server/data/manifest.json,sha256=EpzHQZzrGh9c1Kf63nrqvI33H1cm0nLYfdh5lDm8ijI,318
|
|
91
89
|
experimaestro/settings.py,sha256=U6gTVBL5Z4Rk0_7BAVoavVJKN2sQNRpspE-601Elfys,3170
|
|
92
|
-
experimaestro/sphinx/__init__.py,sha256=
|
|
90
|
+
experimaestro/sphinx/__init__.py,sha256=xac4NmlxvciVONXfr3CChHH7AxZeAuQoC1MYL4Hwu3g,9440
|
|
93
91
|
experimaestro/sphinx/static/experimaestro.css,sha256=0rEgt1LoDdD-a_R5rVfWZ19zD1gR-1L7q3f4UibIB58,294
|
|
94
|
-
experimaestro/streaming_state.py,sha256=NEneDTRWRYfvhqButJCg8rzmVb5coy-sTazljub7umQ,3927
|
|
95
92
|
experimaestro/taskglobals.py,sha256=Lp0bqobVLndR7fOtF9qPI7utTKQXXwTdVN6l5Av9Dc4,660
|
|
96
93
|
experimaestro/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
97
94
|
experimaestro/tests/conftest.py,sha256=CtC6TvUS9sbgSc3pZYyTyEvfilnNGPpOUJvi-jn6twI,695
|
|
@@ -101,9 +98,10 @@ experimaestro/tests/connectors/utils.py,sha256=9sM3Pwy2nRfSr7pwQoOoSCDhBrEcDsEEl
|
|
|
101
98
|
experimaestro/tests/definitions_types.py,sha256=nMoQxZxhTJAYV87Ce9F2dAITxXGHf7Uw5j-MKsZ3LmQ,399
|
|
102
99
|
experimaestro/tests/launchers/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
103
100
|
experimaestro/tests/launchers/bin/sacct,sha256=9mmRAYCE4RBSBOf7aanhFw9hzujOUlcw3OJmZJ3K-Wc,639
|
|
104
|
-
experimaestro/tests/launchers/bin/sbatch,sha256=
|
|
101
|
+
experimaestro/tests/launchers/bin/sbatch,sha256=MGzCDw6ywc973nMViPijwLzJcAW-ghJynMJA00h9AoQ,1746
|
|
102
|
+
experimaestro/tests/launchers/bin/srun,sha256=3oE3qq0UFpVtTvXfR1kH3tovFYX74fp1Fk-o8zgsaJA,47
|
|
105
103
|
experimaestro/tests/launchers/bin/test.py,sha256=MbxdAd2Sf7T-Hj3ldmrtngbQuBdNOkXjMcICJTf39wI,477
|
|
106
|
-
experimaestro/tests/launchers/common.py,sha256=
|
|
104
|
+
experimaestro/tests/launchers/common.py,sha256=E09KhrQfNQNZbaeVkXBgHk2906DvaCEU7CqEQvLbZTE,2994
|
|
107
105
|
experimaestro/tests/launchers/config_slurm/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
108
106
|
experimaestro/tests/launchers/config_slurm/launchers.py,sha256=DohwQVv1eXWfDpAYFg7KJekEm7q7G-lMou2lPg-PKOk,838
|
|
109
107
|
experimaestro/tests/launchers/test_local.py,sha256=4oGgWH2YgkEm-Muu6s4cwlgriXtYr5xAd72DVoIw_Yk,717
|
|
@@ -112,8 +110,6 @@ experimaestro/tests/restart.py,sha256=wivq06i3SoxL_cUKti9J_o3MykynmLic6czuCZSHc_
|
|
|
112
110
|
experimaestro/tests/restart_main.py,sha256=iAFzw0H1q9Aq7t6TrSAj236QBnYU52qx0VF-2dz6tx4,295
|
|
113
111
|
experimaestro/tests/scripts/notifyandwait.py,sha256=3BLXLI5XgP3BnKf6sQ56oKoMVqR80VMHmploJ3JO6Ko,407
|
|
114
112
|
experimaestro/tests/scripts/waitforfile.py,sha256=EDT-TuFi2fU_qt51K5EmAxjw_OnJKkBW7UCfhrtDbVw,120
|
|
115
|
-
experimaestro/tests/state_streamer.proto,sha256=KopoTOt0VpRKbZ_ed9boQyaWB7oNOlDDKhqX-cj9pZs,121
|
|
116
|
-
experimaestro/tests/state_streamer_pb2.py,sha256=5mbmirhDMAOU09Kx9Wo_YrKF1PK6Ebr2TNBELQ2dfMQ,1541
|
|
117
113
|
experimaestro/tests/task_tokens.py,sha256=vgqUa-S_YC2Id9pGOSv40qFTwq1WGZkFhr556Z5o678,477
|
|
118
114
|
experimaestro/tests/tasks/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
119
115
|
experimaestro/tests/tasks/all.py,sha256=OMkHsWZkErCmTajiNO7hNhvnk9eKzJC-VatWgabWlsI,1955
|
|
@@ -131,7 +127,6 @@ experimaestro/tests/test_progress.py,sha256=wtIGQzlV3ldd_wMng11LinVESchW-1J954mC
|
|
|
131
127
|
experimaestro/tests/test_serializers.py,sha256=xSCezAM9yH_Ix1wr7j0au9SyBv9DtZ7b0zs2-Ynt-VM,2338
|
|
132
128
|
experimaestro/tests/test_snippets.py,sha256=rojnyDjtmAMnSuDUj6Bv9XEgdP8oQf2nVc132JF8vsM,3081
|
|
133
129
|
experimaestro/tests/test_ssh.py,sha256=KS1NWltiXrJBSStY9d4mwrexeqgNGWmhxuAU_WLQDAU,1449
|
|
134
|
-
experimaestro/tests/test_state_streamer.py,sha256=T1_Mz-dkHEcpPjCGcUsat6s4cOizxHt1so4bw7VkypM,609
|
|
135
130
|
experimaestro/tests/test_tags.py,sha256=v_8_7LuEHfY_gfa0JRCUkmgJh8h6RMya_nd5NcPAJPw,2852
|
|
136
131
|
experimaestro/tests/test_tasks.py,sha256=bUSB_UT1MTN2P_RPHd4AT5NK-DFsgCVeFKSiXu3bEz8,9429
|
|
137
132
|
experimaestro/tests/test_tokens.py,sha256=U3nKBL1KftWhmk3dQZZLQd-MLJL_SscMjI3zMieMHfc,7823
|
|
@@ -152,8 +147,8 @@ experimaestro/utils/jupyter.py,sha256=JcEo2yQK7x3Cr1tNl5FqGMZOICxCv9DwMvL5xsWdQP
|
|
|
152
147
|
experimaestro/utils/resources.py,sha256=j-nvsTFwmgENMoVGOD2Ap-UD3WU85WkI0IgeSszMCX4,1328
|
|
153
148
|
experimaestro/utils/settings.py,sha256=jpFMqF0DLL4_P1xGal0zVR5cOrdD8O0Y2IOYvnRgN3k,793
|
|
154
149
|
experimaestro/xpmutils.py,sha256=S21eMbDYsHfvmZ1HmKpq5Pz5O-1HnCLYxKbyTBbASyQ,638
|
|
155
|
-
experimaestro-1.7.
|
|
156
|
-
experimaestro-1.7.
|
|
157
|
-
experimaestro-1.7.
|
|
158
|
-
experimaestro-1.7.
|
|
159
|
-
experimaestro-1.7.
|
|
150
|
+
experimaestro-1.7.0rc3.dist-info/LICENSE,sha256=OXLcl0T2SZ8Pmy2_dmlvKuetivmyPd5m1q-Gyd-zaYY,35149
|
|
151
|
+
experimaestro-1.7.0rc3.dist-info/METADATA,sha256=-fuyq-zQ7nqyCb4NJ0fbTWp5YTZKgFDk6o26noSDrJQ,6162
|
|
152
|
+
experimaestro-1.7.0rc3.dist-info/WHEEL,sha256=fGIA9gx4Qxk2KDKeNJCbOEwSrmLtjWCwzBz351GyrPQ,88
|
|
153
|
+
experimaestro-1.7.0rc3.dist-info/entry_points.txt,sha256=TppTNiz5qm5xm1fhAcdLKdCLMrlL-eQggtCrCI00D9c,446
|
|
154
|
+
experimaestro-1.7.0rc3.dist-info/RECORD,,
|
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
syntax = "proto3";
|
|
2
|
-
import "google/protobuf/any.proto";
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
message Envelope {
|
|
6
|
-
google.protobuf.Any payload = 1;
|
|
7
|
-
}
|
|
8
|
-
|
|
9
|
-
message LevelInformation {
|
|
10
|
-
int32 level = 1;
|
|
11
|
-
|
|
12
|
-
optional string desc = 2; // Optional in proto3 is handled by default unless you want to use 'optional'
|
|
13
|
-
float progress = 3;
|
|
14
|
-
|
|
15
|
-
float previous_progress = 4;
|
|
16
|
-
string previous_desc = 5;
|
|
17
|
-
}
|
|
@@ -1,39 +0,0 @@
|
|
|
1
|
-
# -*- coding: utf-8 -*-
|
|
2
|
-
# Generated by the protocol buffer compiler. DO NOT EDIT!
|
|
3
|
-
# NO CHECKED-IN PROTOBUF GENCODE
|
|
4
|
-
# source: src/experimaestro/notifications.proto
|
|
5
|
-
# Protobuf Python Version: 5.29.3
|
|
6
|
-
"""Generated protocol buffer code."""
|
|
7
|
-
from google.protobuf import descriptor as _descriptor
|
|
8
|
-
from google.protobuf import descriptor_pool as _descriptor_pool
|
|
9
|
-
from google.protobuf import runtime_version as _runtime_version
|
|
10
|
-
from google.protobuf import symbol_database as _symbol_database
|
|
11
|
-
from google.protobuf.internal import builder as _builder
|
|
12
|
-
_runtime_version.ValidateProtobufRuntimeVersion(
|
|
13
|
-
_runtime_version.Domain.PUBLIC,
|
|
14
|
-
5,
|
|
15
|
-
29,
|
|
16
|
-
3,
|
|
17
|
-
'',
|
|
18
|
-
'src/experimaestro/notifications.proto'
|
|
19
|
-
)
|
|
20
|
-
# @@protoc_insertion_point(imports)
|
|
21
|
-
|
|
22
|
-
_sym_db = _symbol_database.Default()
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
from google.protobuf import any_pb2 as google_dot_protobuf_dot_any__pb2
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n%src/experimaestro/notifications.proto\x1a\x19google/protobuf/any.proto\"1\n\x08\x45nvelope\x12%\n\x07payload\x18\x01 \x01(\x0b\x32\x14.google.protobuf.Any\"\x81\x01\n\x10LevelInformation\x12\r\n\x05level\x18\x01 \x01(\x05\x12\x11\n\x04\x64\x65sc\x18\x02 \x01(\tH\x00\x88\x01\x01\x12\x10\n\x08progress\x18\x03 \x01(\x02\x12\x19\n\x11previous_progress\x18\x04 \x01(\x02\x12\x15\n\rprevious_desc\x18\x05 \x01(\tB\x07\n\x05_descb\x06proto3')
|
|
29
|
-
|
|
30
|
-
_globals = globals()
|
|
31
|
-
_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals)
|
|
32
|
-
_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'src.experimaestro.notifications_pb2', _globals)
|
|
33
|
-
if not _descriptor._USE_C_DESCRIPTORS:
|
|
34
|
-
DESCRIPTOR._loaded_options = None
|
|
35
|
-
_globals['_ENVELOPE']._serialized_start=68
|
|
36
|
-
_globals['_ENVELOPE']._serialized_end=117
|
|
37
|
-
_globals['_LEVELINFORMATION']._serialized_start=120
|
|
38
|
-
_globals['_LEVELINFORMATION']._serialized_end=249
|
|
39
|
-
# @@protoc_insertion_point(module_scope)
|
experimaestro/streaming_state.py
DELETED
|
@@ -1,124 +0,0 @@
|
|
|
1
|
-
"""Handles streaming states
|
|
2
|
-
|
|
3
|
-
Allows to track a global state via a file
|
|
4
|
-
|
|
5
|
-
- partial states can be output
|
|
6
|
-
- every K bytes, a global state is output
|
|
7
|
-
|
|
8
|
-
The reader can then recover quickly by seeking the last global state output.
|
|
9
|
-
|
|
10
|
-
"""
|
|
11
|
-
|
|
12
|
-
import struct
|
|
13
|
-
from io import BufferedWriter, BufferedReader, SEEK_END
|
|
14
|
-
from typing import Type, Generator, Union
|
|
15
|
-
|
|
16
|
-
from google.protobuf.message import Message
|
|
17
|
-
|
|
18
|
-
# Constants
|
|
19
|
-
PARTIAL_UPDATE = 1
|
|
20
|
-
GLOBAL_STATE = 2
|
|
21
|
-
HEADER_FORMAT = ">BI" # 1-byte type tag, 4-byte message size
|
|
22
|
-
HEADER_SIZE = struct.calcsize(HEADER_FORMAT)
|
|
23
|
-
MAGIC_NUMBER = b"\xab\xcd\xef\x00"
|
|
24
|
-
MAGIC_SIZE = len(MAGIC_NUMBER)
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
class StreamingStateWriter:
|
|
28
|
-
def __init__(
|
|
29
|
-
self,
|
|
30
|
-
file: BufferedWriter,
|
|
31
|
-
partial_cls: Type[Message],
|
|
32
|
-
snapshot_cls: Type[Message],
|
|
33
|
-
chunk_size: int = 8192,
|
|
34
|
-
):
|
|
35
|
-
self.file = file
|
|
36
|
-
self.partial_cls = partial_cls
|
|
37
|
-
self.snapshot_cls = snapshot_cls
|
|
38
|
-
self.chunk_size = chunk_size
|
|
39
|
-
self.bytes_since_last_snapshot = 0
|
|
40
|
-
|
|
41
|
-
def write_partial(self, message: Message):
|
|
42
|
-
assert isinstance(message, self.partial_cls)
|
|
43
|
-
self._write_message(PARTIAL_UPDATE, message)
|
|
44
|
-
|
|
45
|
-
def write_snapshot_if_needed(self, message: Message):
|
|
46
|
-
assert isinstance(message, self.snapshot_cls)
|
|
47
|
-
if self.bytes_since_last_snapshot >= self.chunk_size:
|
|
48
|
-
self._write_message(GLOBAL_STATE, message, with_magic=True)
|
|
49
|
-
self.bytes_since_last_snapshot = 0
|
|
50
|
-
|
|
51
|
-
def _write_message(self, type_tag: int, message: Message, with_magic: bool = False):
|
|
52
|
-
data = message.SerializeToString()
|
|
53
|
-
header = struct.pack(HEADER_FORMAT, type_tag, len(data))
|
|
54
|
-
if with_magic:
|
|
55
|
-
self.file.write(MAGIC_NUMBER)
|
|
56
|
-
self.bytes_since_last_snapshot += MAGIC_SIZE
|
|
57
|
-
self.file.write(header + data)
|
|
58
|
-
self.file.flush()
|
|
59
|
-
self.bytes_since_last_snapshot += HEADER_SIZE + len(data)
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
class StreamingStateReader:
|
|
63
|
-
def __init__(
|
|
64
|
-
self,
|
|
65
|
-
file: BufferedReader,
|
|
66
|
-
partial_cls: Type[Message],
|
|
67
|
-
snapshot_cls: Type[Message],
|
|
68
|
-
):
|
|
69
|
-
self.file = file
|
|
70
|
-
self.partial_cls = partial_cls
|
|
71
|
-
self.snapshot_cls = snapshot_cls
|
|
72
|
-
|
|
73
|
-
def __iter__(self) -> Generator[Union[Message, Message], None, None]:
|
|
74
|
-
while True:
|
|
75
|
-
peek = self.file.peek(MAGIC_SIZE)[:MAGIC_SIZE]
|
|
76
|
-
if peek == MAGIC_NUMBER:
|
|
77
|
-
self.file.read(MAGIC_SIZE)
|
|
78
|
-
|
|
79
|
-
header = self.file.read(HEADER_SIZE)
|
|
80
|
-
if len(header) < HEADER_SIZE:
|
|
81
|
-
break
|
|
82
|
-
|
|
83
|
-
type_tag, length = struct.unpack(HEADER_FORMAT, header)
|
|
84
|
-
payload = self.file.read(length)
|
|
85
|
-
|
|
86
|
-
if type_tag == PARTIAL_UPDATE:
|
|
87
|
-
msg = self.partial_cls()
|
|
88
|
-
elif type_tag == GLOBAL_STATE:
|
|
89
|
-
msg = self.snapshot_cls()
|
|
90
|
-
else:
|
|
91
|
-
raise ValueError(f"Unknown type tag: {type_tag}")
|
|
92
|
-
|
|
93
|
-
msg.ParseFromString(payload)
|
|
94
|
-
yield msg
|
|
95
|
-
|
|
96
|
-
def seek_last_snapshot(self) -> Union[Message, None]:
|
|
97
|
-
self.file.seek(0, SEEK_END)
|
|
98
|
-
file_size = self.file.tell()
|
|
99
|
-
|
|
100
|
-
window = 4096
|
|
101
|
-
pos = file_size
|
|
102
|
-
|
|
103
|
-
while pos > 0:
|
|
104
|
-
read_size = min(window, pos)
|
|
105
|
-
pos -= read_size
|
|
106
|
-
self.file.seek(pos)
|
|
107
|
-
data = self.file.read(read_size)
|
|
108
|
-
|
|
109
|
-
idx = data.rfind(MAGIC_NUMBER)
|
|
110
|
-
if idx != -1:
|
|
111
|
-
snapshot_pos = pos + idx + MAGIC_SIZE
|
|
112
|
-
self.file.seek(snapshot_pos)
|
|
113
|
-
header = self.file.read(HEADER_SIZE)
|
|
114
|
-
if len(header) < HEADER_SIZE:
|
|
115
|
-
break
|
|
116
|
-
type_tag, length = struct.unpack(HEADER_FORMAT, header)
|
|
117
|
-
if type_tag != GLOBAL_STATE:
|
|
118
|
-
continue
|
|
119
|
-
payload = self.file.read(length)
|
|
120
|
-
msg = self.snapshot_cls()
|
|
121
|
-
msg.ParseFromString(payload)
|
|
122
|
-
return msg
|
|
123
|
-
|
|
124
|
-
return None
|
|
@@ -1,38 +0,0 @@
|
|
|
1
|
-
# -*- coding: utf-8 -*-
|
|
2
|
-
# Generated by the protocol buffer compiler. DO NOT EDIT!
|
|
3
|
-
# NO CHECKED-IN PROTOBUF GENCODE
|
|
4
|
-
# source: src/experimaestro/tests/state_streamer.proto
|
|
5
|
-
# Protobuf Python Version: 5.29.3
|
|
6
|
-
"""Generated protocol buffer code."""
|
|
7
|
-
from google.protobuf import descriptor as _descriptor
|
|
8
|
-
from google.protobuf import descriptor_pool as _descriptor_pool
|
|
9
|
-
from google.protobuf import runtime_version as _runtime_version
|
|
10
|
-
from google.protobuf import symbol_database as _symbol_database
|
|
11
|
-
from google.protobuf.internal import builder as _builder
|
|
12
|
-
_runtime_version.ValidateProtobufRuntimeVersion(
|
|
13
|
-
_runtime_version.Domain.PUBLIC,
|
|
14
|
-
5,
|
|
15
|
-
29,
|
|
16
|
-
3,
|
|
17
|
-
'',
|
|
18
|
-
'src/experimaestro/tests/state_streamer.proto'
|
|
19
|
-
)
|
|
20
|
-
# @@protoc_insertion_point(imports)
|
|
21
|
-
|
|
22
|
-
_sym_db = _symbol_database.Default()
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n,src/experimaestro/tests/state_streamer.proto\"&\n\rPartialUpdate\x12\x15\n\rupdated_field\x18\x01 \x01(\x05\"\x1c\n\x0bGlobalState\x12\r\n\x05\x66ield\x18\x01 \x01(\x05\x62\x06proto3')
|
|
28
|
-
|
|
29
|
-
_globals = globals()
|
|
30
|
-
_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals)
|
|
31
|
-
_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'src.experimaestro.tests.state_streamer_pb2', _globals)
|
|
32
|
-
if not _descriptor._USE_C_DESCRIPTORS:
|
|
33
|
-
DESCRIPTOR._loaded_options = None
|
|
34
|
-
_globals['_PARTIALUPDATE']._serialized_start=48
|
|
35
|
-
_globals['_PARTIALUPDATE']._serialized_end=86
|
|
36
|
-
_globals['_GLOBALSTATE']._serialized_start=88
|
|
37
|
-
_globals['_GLOBALSTATE']._serialized_end=116
|
|
38
|
-
# @@protoc_insertion_point(module_scope)
|
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
from .state_streamer_pb2 import PartialUpdate, GlobalState
|
|
2
|
-
|
|
3
|
-
# Writing
|
|
4
|
-
with open("log.bin", "wb") as f:
|
|
5
|
-
writer = StreamingStateWriter(f, PartialUpdate, GlobalState)
|
|
6
|
-
for i in range(1000):
|
|
7
|
-
writer.write_partial(PartialUpdate(updated_field=i))
|
|
8
|
-
if i % 100 == 0:
|
|
9
|
-
writer.write_snapshot_if_needed(GlobalState(field=i))
|
|
10
|
-
|
|
11
|
-
# Reading
|
|
12
|
-
with open("log.bin", "rb") as f:
|
|
13
|
-
reader = StreamingStateReader(f, PartialUpdate, GlobalState)
|
|
14
|
-
last_snapshot = reader.seek_last_snapshot()
|
|
15
|
-
print("Last Snapshot:", last_snapshot)
|
|
16
|
-
|
|
17
|
-
for msg in reader:
|
|
18
|
-
print(type(msg).__name__, msg)
|
|
File without changes
|
|
File without changes
|
|
File without changes
|