experimaestro 1.5.4__py3-none-any.whl → 1.5.5__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 CHANGED
@@ -53,7 +53,6 @@ from .core.context import SerializationContext
53
53
  from .core.serializers import SerializationLWTask, PathSerializationLWTask
54
54
  from .core.types import Any, SubmitHook
55
55
  from .launchers import Launcher
56
- from .scheduler.environment import Environment
57
56
  from .scheduler.workspace import Workspace, RunMode
58
57
  from .scheduler import Scheduler, experiment, FailedExperiment
59
58
  from .notifications import progress, tqdm
experimaestro/click.py CHANGED
@@ -1,6 +1,4 @@
1
1
  import click
2
- from experimaestro import Environment
3
- from experimaestro.run import parse_commandline
4
2
 
5
3
  """Defines the task command line argument prefix for experimaestro-handled command lines"""
6
4
 
@@ -45,36 +43,3 @@ class forwardoption(metaclass=forwardoptionMetaclass):
45
43
  def __getattr__(self, key):
46
44
  """Access to a class field"""
47
45
  return forwardoption([key])
48
-
49
-
50
- def environment(name: str):
51
- def annotate(f):
52
- def callback_env(ctx, name, value):
53
- if value:
54
- assert name not in ctx.params, "Environment has already been set"
55
- else:
56
- return ctx.params.get(name, None)
57
- return Environment.get(value)
58
-
59
- def callback(ctx, param, value):
60
- if value:
61
- if name not in ctx.params:
62
- ctx.params[name] = Environment()
63
- ctx.params[name].workdir = value
64
-
65
- f = click.option(
66
- f"--{name}-workdir",
67
- type=str,
68
- callback=callback,
69
- expose_value=False,
70
- help="Experimaestro environment",
71
- )(f)
72
- f = click.option(
73
- f"--{name}",
74
- type=str,
75
- callback=callback_env,
76
- help="Experimaestro environment",
77
- )(f)
78
- return f
79
-
80
- return annotate
@@ -1,5 +1,4 @@
1
1
  import inspect
2
- import itertools
3
2
  import json
4
3
  import logging
5
4
  import sys
@@ -12,7 +11,7 @@ import yaml
12
11
  from experimaestro import LauncherRegistry, RunMode, experiment
13
12
  from experimaestro.experiments.configuration import ConfigurationBase
14
13
  from experimaestro.exceptions import HandledException
15
- from experimaestro.settings import get_settings, get_workspace
14
+ from experimaestro.settings import get_workspace
16
15
  from omegaconf import OmegaConf, SCMode
17
16
  from termcolor import cprint
18
17
 
@@ -105,11 +104,17 @@ def load(yaml_file: Path):
105
104
  @click.option(
106
105
  "--file", "xp_file", help="The file containing the main experimental code"
107
106
  )
107
+ @click.option(
108
+ "--workspace",
109
+ type=str,
110
+ default=None,
111
+ help="Workspace ID (reads from settings.yaml in experimaestro config)",
112
+ )
108
113
  @click.option(
109
114
  "--workdir",
110
115
  type=str,
111
116
  default=None,
112
- help="Working directory - if None, uses the default XPM " "working directory",
117
+ help="Working environment",
113
118
  )
114
119
  @click.option("--conf", "-c", "extra_conf", type=str, multiple=True)
115
120
  @click.option(
@@ -128,6 +133,7 @@ def experiments_cli( # noqa: C901
128
133
  port: int,
129
134
  xpm_config_dir: Path,
130
135
  workdir: Optional[Path],
136
+ workspace: Optional[str],
131
137
  env: List[Tuple[str, str]],
132
138
  run_mode: RunMode,
133
139
  extra_conf: List[str],
@@ -224,26 +230,38 @@ def experiments_cli( # noqa: C901
224
230
  configuration, structured_config_mode=SCMode.INSTANTIATE
225
231
  )
226
232
 
227
- # Get the working directory
228
- settings = get_settings()
229
- ws_env = {}
233
+ # Define the workspace
230
234
  workdir = Path(workdir) if workdir else None
231
- if (workdir is None) or (not workdir.is_dir()):
232
- logging.info("Searching for workspace %s", workdir)
233
- ws_settings = get_workspace(str(workdir))
234
- workdir = ws_settings.path.expanduser()
235
- ws_env = ws_settings.env
235
+
236
+ if workspace:
237
+ ws_env = get_workspace(workspace)
238
+ if ws_env is None:
239
+ raise RuntimeError("No workspace named %s", workspace)
240
+
241
+ logging.info("Using workspace %s", ws_env.id)
242
+ if workdir:
243
+ # Overrides working directory
244
+ logging.info(" override working directory: %s", workdir)
245
+ ws_env.path = workdir
246
+ else:
247
+ workdir = ws_env.path
248
+ elif workdir:
249
+ logging.info("Using workdir %s", workdir)
250
+ ws_env = workdir
251
+ else:
252
+ ws_env = get_workspace()
253
+ assert ws_env is not None, "No workdir or workspace defined, and no default"
254
+ logging.info("Using default workspace %s", ws_env.id)
236
255
 
237
256
  logging.info("Using working directory %s", str(workdir.resolve()))
238
257
 
239
258
  # --- Runs the experiment
240
259
  with experiment(
241
- workdir, configuration.id, host=host, port=port, run_mode=run_mode
260
+ ws_env, configuration.id, host=host, port=port, run_mode=run_mode
242
261
  ) as xp:
243
262
  # Set up the environment
244
263
  # (1) global settings (2) workspace settings and (3) command line settings
245
- for key, value in itertools.chain(settings.env.items(), ws_env.items(), env):
246
- logging.info("Setting environment: %s=%s", key, value)
264
+ for key, value in env:
247
265
  xp.setenv(key, value)
248
266
 
249
267
  try:
experimaestro/run.py CHANGED
@@ -92,7 +92,7 @@ class TaskRunner:
92
92
  logger.info("Finished cleanup")
93
93
 
94
94
  def handle_error(self, code, frame_type):
95
- logger.info("Finished with code %d", code)
95
+ logger.info("Error handler: finished with code %d", code)
96
96
  self.failedpath.write_text(str(code))
97
97
  self.cleanup()
98
98
  logger.info("Exiting")
@@ -100,8 +100,18 @@ class TaskRunner:
100
100
 
101
101
  def run(self):
102
102
  atexit.register(self.cleanup)
103
- signal.signal(signal.SIGTERM, self.handle_error)
104
- signal.signal(signal.SIGINT, self.handle_error)
103
+ sigterm_handler = signal.signal(signal.SIGTERM, self.handle_error)
104
+ sigint_handler = signal.signal(signal.SIGINT, self.handle_error)
105
+
106
+ def remove_signal_handlers(remove_cleanup=True):
107
+ """Removes cleanup in forked processes"""
108
+ signal.signal(signal.SIGTERM, sigterm_handler)
109
+ signal.signal(signal.SIGINT, sigint_handler)
110
+ atexit.unregister(self.cleanup)
111
+
112
+ if sys.platform != "win32":
113
+ os.register_at_fork(after_in_child=remove_signal_handlers)
114
+
105
115
  try:
106
116
  workdir = self.scriptpath.parent
107
117
  os.chdir(workdir)
@@ -129,6 +139,10 @@ class TaskRunner:
129
139
  self.started = True
130
140
  run(workdir / "params.json")
131
141
 
142
+ # ... remove the handlers
143
+ logger.info("Task ended successfully")
144
+ remove_signal_handlers(remove_cleanup=False)
145
+
132
146
  # Everything went OK
133
147
  sys.exit(0)
134
148
  except Exception:
@@ -137,6 +151,11 @@ class TaskRunner:
137
151
 
138
152
  except SystemExit as e:
139
153
  if e.code == 0:
154
+ # Normal exit, just create the ".done" file
140
155
  self.donepath.touch()
156
+
157
+ # ... and finish the exit process
158
+ logger.info("This is the end (TODO: remove this line)")
159
+ raise
141
160
  else:
142
161
  self.handle_error(e.code, None)
@@ -1,5 +1,6 @@
1
1
  from collections import ChainMap
2
2
  from functools import cached_property
3
+ import logging
3
4
  import os
4
5
  from pathlib import Path
5
6
  from shutil import rmtree
@@ -13,13 +14,12 @@ from experimaestro.exceptions import HandledException
13
14
  from experimaestro.notifications import LevelInformation, Reporter
14
15
  from typing import Dict
15
16
  from experimaestro.scheduler.services import Service
16
- from experimaestro.settings import get_settings
17
+ from experimaestro.settings import WorkspaceSettings, get_settings
17
18
 
18
19
 
19
20
  from experimaestro.core.objects import Config, ConfigWalkContext
20
21
  from experimaestro.utils import logger
21
22
  from experimaestro.locking import Locks, LockError, Lock
22
- from .environment import Environment
23
23
  from .workspace import RunMode, Workspace
24
24
  from .dependencies import Dependency, DependencyStatus, Resource
25
25
  import concurrent.futures
@@ -180,7 +180,7 @@ class Job(Resource):
180
180
  return ChainMap(
181
181
  {},
182
182
  self.launcher.environ if self.launcher else {},
183
- self.workspace.environment.environ if self.workspace else {},
183
+ self.workspace.env if self.workspace else {},
184
184
  )
185
185
 
186
186
  @property
@@ -508,6 +508,12 @@ class Scheduler:
508
508
  job.scheduler = self
509
509
  self.waitingjobs.add(job)
510
510
 
511
+ # Check that we don't have a completed job in
512
+ # alternate directories
513
+ for jobspath in experiment.current().alt_jobspaths:
514
+ # FIXME: check if done
515
+ pass
516
+
511
517
  # Creates a link into the experiment folder
512
518
  path = experiment.current().jobspath / job.relpath
513
519
  path.parent.mkdir(parents=True, exist_ok=True)
@@ -719,7 +725,7 @@ class experiment:
719
725
  ```
720
726
  """
721
727
 
722
- # Current experiment
728
+ #: Current experiment
723
729
  CURRENT: Optional["experiment"] = None
724
730
 
725
731
  @staticmethod
@@ -733,7 +739,7 @@ class experiment:
733
739
 
734
740
  def __init__(
735
741
  self,
736
- env: Union[Path, str, Environment],
742
+ env: Union[Path, str, WorkspaceSettings],
737
743
  name: str,
738
744
  *,
739
745
  host: Optional[str] = None,
@@ -761,16 +767,13 @@ class experiment:
761
767
 
762
768
  from experimaestro.server import Server
763
769
 
764
- if isinstance(env, Environment):
765
- self.environment = env
766
- else:
767
- self.environment = Environment(workdir=env)
770
+ settings = get_settings()
771
+ if not isinstance(env, WorkspaceSettings):
772
+ env = WorkspaceSettings(id=None, path=Path(env))
768
773
 
769
774
  # Creates the workspace
770
775
  run_mode = run_mode or RunMode.NORMAL
771
- self.workspace = Workspace(
772
- self.environment, launcher=launcher, run_mode=run_mode
773
- )
776
+ self.workspace = Workspace(settings, env, launcher=launcher, run_mode=run_mode)
774
777
 
775
778
  # Mark the directory has an experimaestro folder
776
779
  self.workdir = self.workspace.experimentspath / name
@@ -780,7 +783,7 @@ class experiment:
780
783
  self.old_experiment = None
781
784
  self.services: Dict[str, Service] = {}
782
785
 
783
- settings = get_settings()
786
+ # Get configuration settings
784
787
 
785
788
  if host is not None:
786
789
  settings.server.host = host
@@ -800,6 +803,11 @@ class experiment:
800
803
  else None
801
804
  )
802
805
 
806
+ # Copy environment variable from main (but do not
807
+ # override)
808
+ for key, value in settings.env.items():
809
+ self.environment.setenv(key, value, override=False)
810
+
803
811
  if os.environ.get("XPM_ENABLEFAULTHANDLER", "0") == "1":
804
812
  import faulthandler
805
813
 
@@ -832,6 +840,12 @@ class experiment:
832
840
  """Return the directory in which results can be stored for this experiment"""
833
841
  return self.workdir / "jobs"
834
842
 
843
+ @property
844
+ def alt_jobspaths(self):
845
+ """Return potential other directories"""
846
+ for alt_workdir in self.workspace.alt_workdirs:
847
+ yield alt_workdir / "jobs"
848
+
835
849
  @property
836
850
  def jobsbakpath(self):
837
851
  """Return the directory in which results can be stored for this experiment"""
@@ -876,9 +890,11 @@ class experiment:
876
890
  future = asyncio.run_coroutine_threadsafe(awaitcompletion(), self.loop)
877
891
  return future.result()
878
892
 
879
- def setenv(self, name, value):
893
+ def setenv(self, name, value, override=True):
880
894
  """Shortcut to set the environment value"""
881
- self.environment.setenv(name, value)
895
+ if override or name not in self.workspace.env:
896
+ logging.info("Setting environment: %s=%s", name, value)
897
+ self.worskpace.env[name] = value
882
898
 
883
899
  def token(self, name: str, count: int):
884
900
  """Returns a token for this experiment
@@ -1,9 +1,9 @@
1
+ from collections import ChainMap
1
2
  from enum import Enum
3
+ from functools import cached_property
2
4
  from pathlib import Path
3
- from typing import Optional, TYPE_CHECKING
4
-
5
- if TYPE_CHECKING:
6
- from experimaestro.scheduler.environment import Environment
5
+ from typing import Optional
6
+ from experimaestro.settings import WorkspaceSettings, Settings
7
7
 
8
8
 
9
9
  class RunMode(str, Enum):
@@ -25,15 +25,22 @@ class Workspace:
25
25
  """
26
26
 
27
27
  CURRENT = None
28
- environment: "Environment"
28
+ settings: "Settings"
29
+ worspace: "WorkspaceSettings"
29
30
 
30
31
  """Creates a workspace for experiments"""
31
32
 
32
33
  def __init__(
33
- self, environment: "Environment", launcher=None, run_mode: RunMode = None
34
+ self,
35
+ settings: "Settings",
36
+ workspace_settings: "WorkspaceSettings",
37
+ launcher=None,
38
+ run_mode: RunMode = None,
34
39
  ):
35
- self.environment = environment
36
- path = environment.workdir
40
+ self.settings = settings
41
+ self.workspace_settings = workspace_settings
42
+
43
+ path = self.workspace_settings.path
37
44
  self.notificationURL: Optional[str] = None
38
45
  if isinstance(path, Path):
39
46
  path = path.absolute()
@@ -43,6 +50,8 @@ class Workspace:
43
50
 
44
51
  self.launcher = launcher or Launcher.get(path)
45
52
 
53
+ self.env = ChainMap({}, workspace_settings.env, settings.env)
54
+
46
55
  def __enter__(self):
47
56
  self.old_workspace = Workspace.CURRENT
48
57
  Workspace.CURRENT = self
@@ -50,6 +59,15 @@ class Workspace:
50
59
  def __exit__(self, *args):
51
60
  Workspace.CURRENT = self.old_workspace
52
61
 
62
+ @cached_property
63
+ def alt_workspaces(self):
64
+ for ws_id in self.workspace_settings.alt_workspaces:
65
+ yield self.settings.workspaces[ws_id]
66
+
67
+ @property
68
+ def alt_workdirs(self):
69
+ yield from map(lambda ws: ws.path, self.workspace_settings.alt_workspaces)
70
+
53
71
  @property
54
72
  def connector(self):
55
73
  """Returns the default connector"""
@@ -92,7 +92,11 @@ class PythonScriptBuilder:
92
92
  with scriptpath.open("wt") as out:
93
93
  out.write("#!{}\n".format(self.pythonpath))
94
94
  out.write("# Experimaestro generated task\n\n")
95
- out.write("""import logging\nlogging.basicConfig(level=logging.INFO)\n\n""")
95
+ out.write(
96
+ """import logging\n"""
97
+ """logging.basicConfig(level=logging.INFO, """
98
+ """format='%(levelname)s:%(process)d:%(asctime)s [%(name)s] %(message)s', datefmt='%y-%m-%d %H:%M:%S')\n\n"""
99
+ )
96
100
 
97
101
  out.write("\nif __name__ == '__main__':\n\n" "")
98
102
 
experimaestro/settings.py CHANGED
@@ -1,5 +1,5 @@
1
1
  import os
2
- from omegaconf import OmegaConf
2
+ from omegaconf import OmegaConf, SCMode
3
3
  from dataclasses import field, dataclass
4
4
  from functools import lru_cache
5
5
  from pathlib import Path
@@ -23,15 +23,23 @@ class ServerSettings:
23
23
 
24
24
  @dataclass
25
25
  class WorkspaceSettings:
26
+ """Defines the workspace"""
27
+
26
28
  id: str
27
29
  """The workspace identifier"""
28
30
 
29
- path: Path
31
+ path: Path = field()
30
32
  """The workspace path"""
31
33
 
32
34
  env: Dict[str, str] = field(default_factory=dict)
33
35
  """Workspace specific environment variables"""
34
36
 
37
+ alt_workspaces: List[str] = field(default_factory=list)
38
+ """Alternative workspaces to find jobs or experiments"""
39
+
40
+ def __post_init__(self):
41
+ self.path = self.path.expanduser().resolve()
42
+
35
43
 
36
44
  @dataclass
37
45
  class Settings:
@@ -51,13 +59,15 @@ def get_settings(path: Optional[Path] = None) -> Settings:
51
59
 
52
60
  path = path or Path("~/.config/experimaestro/settings.yaml").expanduser()
53
61
  if not path.is_file():
54
- return schema
62
+ return schema.to_object()
55
63
 
56
64
  conf = OmegaConf.load(path)
57
- return OmegaConf.merge(schema, conf)
65
+ return OmegaConf.to_container(
66
+ OmegaConf.merge(schema, conf), structured_config_mode=SCMode.INSTANTIATE
67
+ )
58
68
 
59
69
 
60
- def get_workspace(id: Optional[str]) -> WorkspaceSettings:
70
+ def get_workspace(id: Optional[str] = None) -> Optional[WorkspaceSettings]:
61
71
  """Return the workspace settings given an id (or None for the default one)"""
62
72
  workspaces = get_settings().workspaces
63
73
  if workspaces:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: experimaestro
3
- Version: 1.5.4
3
+ Version: 1.5.5
4
4
  Summary: "Experimaestro is a computer science experiment manager"
5
5
  Home-page: https://github.com/experimaestro/experimaestro-python
6
6
  License: GPL-3
@@ -41,7 +41,7 @@ Requires-Dist: pyparsing (>=3.1,<4.0)
41
41
  Requires-Dist: pytools (>=2023.1.1,<2024.0.0)
42
42
  Requires-Dist: pyyaml (>=6.0.1,<7.0.0)
43
43
  Requires-Dist: requests (>=2.31,<3.0)
44
- Requires-Dist: rpyc (>=5,<6)
44
+ Requires-Dist: rpyc (>=5,<7)
45
45
  Requires-Dist: sortedcontainers (>=2.4,<3.0)
46
46
  Requires-Dist: termcolor (>=2.3)
47
47
  Requires-Dist: tqdm (>=4.66.1,<5.0.0)
@@ -1,8 +1,8 @@
1
- experimaestro/__init__.py,sha256=5T8NC2WvqlCRXV4qSW4-_V42owXQs_YpXbEP6vLZN7E,1548
1
+ experimaestro/__init__.py,sha256=btjhjJa9TofgAYpMEw8SLpVH6G-5kSelxsInQdrIu30,1501
2
2
  experimaestro/__main__.py,sha256=iRi6yRJVcthXRTVnHQB0TNOKTRmKjdnX6yIQaf-z79o,13528
3
3
  experimaestro/annotations.py,sha256=dcpFmo01T12S_y5nIBIQjiXsGsq5S80ZB-58o8tW9wA,8450
4
4
  experimaestro/checkers.py,sha256=ZCMbnE_GFC5compWjt-fuHhPImi9fCPjImF8Ow9NqK8,696
5
- experimaestro/click.py,sha256=HTfjm4poqfG69MIYztrPWEei5OVnd154UCr2pJm6yp8,2390
5
+ experimaestro/click.py,sha256=6BkeQHEgcxaxzq3xEvEEzwzuBj5-dkfrpOGikuA8L00,1377
6
6
  experimaestro/commandline.py,sha256=NS1ubme8DTJtDS2uWwdHLQiZsl6TSK1LkNxu39c3-cw,9463
7
7
  experimaestro/compat.py,sha256=dQqE2ZNHLM2wtdfp7fBRYMfC33qNehVf9J6FGRBUQhs,171
8
8
  experimaestro/connectors/__init__.py,sha256=hxcBSeVLk_7oyiIlS3l-9dGg1NGtShwvRD1tS7f8D2M,5461
@@ -19,7 +19,7 @@ experimaestro/core/types.py,sha256=oTXD4UjMVoYn_Usxn2C4h6IGhYDTtekKB3O3hfeOynQ,2
19
19
  experimaestro/core/utils.py,sha256=JfC3qGUS9b6FUHc2VxIYUI9ysNpXSQ1LjOBkjfZ8n7o,495
20
20
  experimaestro/exceptions.py,sha256=cUy83WHM3GeynxmMk6QRr5xsnpqUAdAoc-m3KQVrE2o,44
21
21
  experimaestro/experiments/__init__.py,sha256=GcpDUIbCvhnv6rxFdAp4wTffCVNTv-InY6fbQAlTy-o,159
22
- experimaestro/experiments/cli.py,sha256=MXJNRCvSuespStK8BSR82q-3NSPPXm6jq0Rsui4V2BU,7535
22
+ experimaestro/experiments/cli.py,sha256=5G78YkzZtNU6ygcp_pRJQXYvsMWlguB9_M35-8W_Ors,7916
23
23
  experimaestro/experiments/configuration.py,sha256=8GRqyLG1leF_NbvbFzqpm0yM24O0WjSNmQzvnuLnxxw,1150
24
24
  experimaestro/filter.py,sha256=DN1PrmS9yXoOa5Xnv001zbxzpdzvcVZFI9xZFKZ1-6g,5794
25
25
  experimaestro/generators.py,sha256=9NQ_TfDfASkArLnO4PF7s5Yoo9KWjlna2DCPzk5gJOI,1230
@@ -47,14 +47,13 @@ experimaestro/mypy.py,sha256=M39VFuDrab-ymlCDIF5jys9oKpTwnuBPzb1T8Un5J3s,285
47
47
  experimaestro/notifications.py,sha256=936tdH_KYJYBvPVu-ii4Sn_nso_TTs5GqAfO1FDYRgc,8710
48
48
  experimaestro/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
49
49
  experimaestro/rpyc.py,sha256=ZRKol-3tVoeoUITLNFenLF4dhWBLW_FvSV_GvsypmeI,3605
50
- experimaestro/run.py,sha256=_kljdp5ywHzfQnJAX-qZ6qtFXl4c2HotNNGur1ZPj4I,4499
50
+ experimaestro/run.py,sha256=pRTj7R7N0Tzm1lAjeWyn-ewM0kNFmpU0so8Yhv0BIYs,5294
51
51
  experimaestro/scheduler/__init__.py,sha256=ERmmOxz_9mUkIuccNbzUa5Y6gVLLVDdyc4cCxbCCUbY,20
52
- experimaestro/scheduler/base.py,sha256=1SkwjSbOKTHs_pAB44z63xmcsgYrXsxxluO2MhJW5uU,31450
52
+ experimaestro/scheduler/base.py,sha256=k2bwfC6cIwMYblu-krKsZV-CMm3mcVkOQ-nFpfaXl68,32132
53
53
  experimaestro/scheduler/dependencies.py,sha256=n9XegwrmjayOIxt3xhuTEBVEBGSq4oeVdzz-FviDGXo,1994
54
- experimaestro/scheduler/environment.py,sha256=ZaSHSgAcZBmIj7b_eS1OvNQOuVLFvuw-qvqtYrc3Vms,2393
55
54
  experimaestro/scheduler/services.py,sha256=aCKkNZMULlceabqf-kOs_-C7KPINnjU3Q-I00o5x6iY,2189
56
- experimaestro/scheduler/workspace.py,sha256=xATJi6-GJcpdwB4alliJnmAuvwt-URUiUKUfq5scUac,1731
57
- experimaestro/scriptbuilder.py,sha256=AYh2LdO__mrCWuFa1JWvdfKm_wnk5hHDr_OjHo1voIE,4357
55
+ experimaestro/scheduler/workspace.py,sha256=vyVbYZML28zvmgxfWc2PsKKHGlrAejY43UYlttbm9AU,2280
56
+ experimaestro/scriptbuilder.py,sha256=WokCsXjmsVsx9mD6-sJdsBI6lD6xNMXUz1YvAnb7e10,4533
58
57
  experimaestro/server/__init__.py,sha256=F2bzLf2q29Haj2OIbPA26r5WVbaipBNylIozg-As758,10854
59
58
  experimaestro/server/data/016b4a6cdced82ab3aa1.ttf,sha256=AD8RVBhWpkmmyCNcYmbIk2IkxdYJ5RRC2iTcVVbRT78,189684
60
59
  experimaestro/server/data/0c35d18bf06992036b69.woff2,sha256=gmX2R4Y5fWuDLRygqv3xSa2E5ydZ__qfcnLpGg-wFdE,128352
@@ -80,7 +79,7 @@ experimaestro/server/data/index.js,sha256=f0GvRsfsQ4ayP4en7Q-raZ6buwRXLCswCbzVax
80
79
  experimaestro/server/data/index.js.map,sha256=za3MUIjzyyGRI6F5KuBFMTgrFU55xgt0LBrw-4YPHag,3904832
81
80
  experimaestro/server/data/login.html,sha256=4dvhSOn6DHp_tbmzqIKrqq2uAo0sAUbgLVD0lTnPp4s,511
82
81
  experimaestro/server/data/manifest.json,sha256=EpzHQZzrGh9c1Kf63nrqvI33H1cm0nLYfdh5lDm8ijI,318
83
- experimaestro/settings.py,sha256=nZO-667XgM-aC6A-RHw1C4gglDlDeIIIJAxs83IgRyI,1807
82
+ experimaestro/settings.py,sha256=Sh-OefrMEtuJJA9tRe8TiS9-BIy0b41HmHXkf1cLLzM,2181
84
83
  experimaestro/sphinx/__init__.py,sha256=heovvtwbYToZM-b6HNi4pJdBoo_97usdEawhMGSK3bk,9560
85
84
  experimaestro/sphinx/static/experimaestro.css,sha256=0rEgt1LoDdD-a_R5rVfWZ19zD1gR-1L7q3f4UibIB58,294
86
85
  experimaestro/taskglobals.py,sha256=aBjPpo4HQp6E6M3GQ8L6PR4rK2Lu0kD5dS1WjnaGgDc,499
@@ -141,8 +140,8 @@ experimaestro/utils/resources.py,sha256=gDjkrRjo7GULWyXmNXm_u1uqzEIAoAvJydICk56n
141
140
  experimaestro/utils/settings.py,sha256=jpFMqF0DLL4_P1xGal0zVR5cOrdD8O0Y2IOYvnRgN3k,793
142
141
  experimaestro/utils/yaml.py,sha256=jEjqXqUtJ333wNUdIc0o3LGvdsTQ9AKW9a9CCd-bmGU,6766
143
142
  experimaestro/xpmutils.py,sha256=S21eMbDYsHfvmZ1HmKpq5Pz5O-1HnCLYxKbyTBbASyQ,638
144
- experimaestro-1.5.4.dist-info/LICENSE,sha256=OXLcl0T2SZ8Pmy2_dmlvKuetivmyPd5m1q-Gyd-zaYY,35149
145
- experimaestro-1.5.4.dist-info/METADATA,sha256=SgOUelTGKvHMk4RNsAAG7aoLgt1n1E_ls8MtP1okCbA,6265
146
- experimaestro-1.5.4.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
147
- experimaestro-1.5.4.dist-info/entry_points.txt,sha256=PhaEili_fDgn5q7rBJGip_uhGkRBq5l3Yuhg91zkcbk,574
148
- experimaestro-1.5.4.dist-info/RECORD,,
143
+ experimaestro-1.5.5.dist-info/LICENSE,sha256=OXLcl0T2SZ8Pmy2_dmlvKuetivmyPd5m1q-Gyd-zaYY,35149
144
+ experimaestro-1.5.5.dist-info/METADATA,sha256=sJ1eTuhmvZTLyHPBwFxzIgsZURveJQr25Dp6LcFRdCA,6265
145
+ experimaestro-1.5.5.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
146
+ experimaestro-1.5.5.dist-info/entry_points.txt,sha256=PhaEili_fDgn5q7rBJGip_uhGkRBq5l3Yuhg91zkcbk,574
147
+ experimaestro-1.5.5.dist-info/RECORD,,
@@ -1,94 +0,0 @@
1
- """Defines an experimental environment"""
2
-
3
- from pathlib import Path
4
- from typing import Dict
5
- import marshmallow as mm
6
- from experimaestro.utils.settings import JsonSettings
7
- from pytools import memoize
8
-
9
-
10
- def schema(schema_cls):
11
- def annotate(object_cls):
12
- schema_cls.OBJECT_CLS = object_cls
13
- object_cls.SCHEMA = schema_cls
14
- return object_cls
15
-
16
- return annotate
17
-
18
-
19
- class _Schema(mm.Schema):
20
- @mm.post_load
21
- def make_settings(self, data, **kwargs):
22
- settings = self.__class__.OBJECT_CLS()
23
- for key, value in data.items():
24
- setattr(settings, key, value)
25
- return settings
26
-
27
-
28
- class EnvironmentSchema(_Schema):
29
- hostname = mm.fields.Str()
30
- """The hostname (can be empty for localhost)"""
31
-
32
- pythonpath = mm.fields.Str()
33
- """Path to python executable"""
34
- workdir = mm.fields.Str()
35
- environ = mm.fields.Dict(keys=mm.fields.Str(), values=mm.fields.Str())
36
-
37
-
38
- class Schema(_Schema):
39
- environments = mm.fields.Dict(
40
- keys=mm.fields.Str(), values=mm.fields.Nested(EnvironmentSchema)
41
- )
42
-
43
-
44
- @schema(Schema)
45
- class Settings(JsonSettings):
46
- """User settings"""
47
-
48
- def __init__(self):
49
- self.environments: Dict[str, str] = {}
50
-
51
-
52
- @schema(EnvironmentSchema)
53
- class Environment:
54
- """This defines the environment for an experiment, and can be stored"""
55
-
56
- def __init__(self, workdir=None):
57
- self.hostname = None
58
- self._workdir = workdir
59
- self.pythonpath = None
60
- self.environ = {}
61
-
62
- @property
63
- def basepath(self):
64
- if self.hostname:
65
- from ..connectors.ssh import SshPath
66
-
67
- return SshPath(f"ssh://{self.hostname}")
68
- return Path()
69
-
70
- @property
71
- def workdir(self) -> Path:
72
- assert self._workdir, "The working directory has not been set"
73
- return self.basepath / self._workdir
74
-
75
- @workdir.setter
76
- def workdir(self, value):
77
- self._workdir = value
78
-
79
- def setenv(self, key: str, value: str):
80
- """Set the environment variable with key"""
81
- self.environ[key] = value
82
-
83
- @staticmethod
84
- @memoize()
85
- def _load():
86
- path = (
87
- Path("~").expanduser() / ".config" / "experimaestro" / "environments.json"
88
- )
89
- return Settings.load(path)
90
-
91
- @staticmethod
92
- def get(name: str):
93
- """Retrieve an environment by name"""
94
- return Environment._load().environments[name]