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.

@@ -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(path, WindowsPath)
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:
@@ -200,7 +200,7 @@ class SshProcessBuilder(ProcessBuilder):
200
200
  super().__init__()
201
201
  self.connector = connector
202
202
 
203
- def start(self):
203
+ def start(self, task_mode: bool = False):
204
204
  """Start the process"""
205
205
 
206
206
  trans = str.maketrans({'"': r"\"", "$": r"\$"})
@@ -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
- addstream(builder.command, "-e", self.stderr)
261
- addstream(builder.command, "-o", self.stdout)
262
- addstream(builder.command, "-i", self.stdin)
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
- builder = PythonScriptBuilder()
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
@@ -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
@@ -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
 
@@ -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
- [type] -- [description]
68
+ str -- The script path on disk
67
69
  """
68
70
  assert isinstance(
69
71
  job, CommandLineJob
@@ -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
- PyObject,
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(f" :task:", sourcename)
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(f" :generated:", source_name)
210
+ self.add_line(" :generated:", source_name)
218
211
  elif argument.constant:
219
- self.add_line(f" :constant:", source_name)
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="$(realpath "$(dirname "$0")"/..)/slurm"
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
- echo "Starting $@ ${args[@]} > $stdout" >&2
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 &
@@ -0,0 +1,5 @@
1
+ #!/bin/bash
2
+
3
+ # Just run the command
4
+
5
+ eval "$@"
@@ -80,10 +80,14 @@ def takeback(launcher, datapath, txp1, txp2):
80
80
  waiting = datapath / "waiting"
81
81
 
82
82
  with txp1:
83
- WaitUntilTouched(touching=touching, waiting=waiting).submit(launcher=launcher)
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.0rc2
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=NS1ubme8DTJtDS2uWwdHLQiZsl6TSK1LkNxu39c3-cw,9463
9
+ experimaestro/commandline.py,sha256=MJIJcfppGCjNA8ozxXUzbUSeDOlTExuzhxryGx3_lIo,9314
10
10
  experimaestro/compat.py,sha256=dQqE2ZNHLM2wtdfp7fBRYMfC33qNehVf9J6FGRBUQhs,171
11
- experimaestro/connectors/__init__.py,sha256=wXVUGWAK7f222KzwXUEP-2LOGdJP43iSXSygGye9EO8,5929
12
- experimaestro/connectors/local.py,sha256=5X22G1hg7EMAxWmtsk2wQ8Q6Cp4I7OtzzZ0Cu0fgRnk,5929
13
- experimaestro/connectors/ssh.py,sha256=P6XfCdC4mQTsFzgI-dEdx6AdVhwd0T6VrQpPmNpmiPM,8575
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=5KKElGd9aS1gegx-A6JVXL-LACk02wFVQxeUIEbZfUc,6105
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=syIC8Frfz-kWhpYkmPTfMjrskx7_bzvmRMQJ40lkF50,14185
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.proto,sha256=0bDb3DYeuP_69rUsBJxMA_2m-P9HznQampw7YGovLUU,363
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=qlduyqawKoO0FFcRMtaoPpp_MdNbkb2VB9HQX6l02Rg,35146
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=WGnPmSM5feNoKm4L72SmjzbzO8A0POQaY7bfVf6QlzQ,5719
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=nLtwZG136mcji3UhbUDNw4Wj8nbHcj1XysZ2f0_A8eQ,4947
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=heovvtwbYToZM-b6HNi4pJdBoo_97usdEawhMGSK3bk,9560
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=NMjBRvZ-TrpZCSLfMAirR5Vf1-IQFPNNNaDpIw56vPE,1350
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=ykTMdCLWsf6jmKzfqPoRMeHn93GCNSi755yOS5CYT_I,2832
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.0rc2.dist-info/LICENSE,sha256=OXLcl0T2SZ8Pmy2_dmlvKuetivmyPd5m1q-Gyd-zaYY,35149
156
- experimaestro-1.7.0rc2.dist-info/METADATA,sha256=61zmKYFsyGyj3HXvJATsPb3CFO3dn2ZXlR66pegSjic,6191
157
- experimaestro-1.7.0rc2.dist-info/WHEEL,sha256=fGIA9gx4Qxk2KDKeNJCbOEwSrmLtjWCwzBz351GyrPQ,88
158
- experimaestro-1.7.0rc2.dist-info/entry_points.txt,sha256=TppTNiz5qm5xm1fhAcdLKdCLMrlL-eQggtCrCI00D9c,446
159
- experimaestro-1.7.0rc2.dist-info/RECORD,,
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)
@@ -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,9 +0,0 @@
1
- syntax = "proto3";
2
-
3
- message PartialUpdate {
4
- int32 updated_field = 1;
5
- }
6
-
7
- message GlobalState {
8
- int32 field = 1;
9
- }
@@ -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)