experimaestro 1.3.6__py3-none-any.whl → 1.4.0__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/__main__.py CHANGED
@@ -13,6 +13,7 @@ import subprocess
13
13
  from termcolor import colored, cprint
14
14
 
15
15
  import experimaestro
16
+ from experimaestro.experiments.cli import experiments_cli
16
17
  import experimaestro.launcherfinder.registry as launcher_registry
17
18
 
18
19
  # --- Command line main options
@@ -74,6 +75,10 @@ def cli(ctx, quiet, debug, traceback):
74
75
  ctx.obj.traceback = traceback
75
76
 
76
77
 
78
+ # Adds the run-experiment command
79
+ cli.add_command(experiments_cli, "run-experiment")
80
+
81
+
77
82
  @cli.command(help="Get version")
78
83
  def version():
79
84
  print(experimaestro.__version__)
@@ -0,0 +1,2 @@
1
+ from .cli import experiments_cli, ExperimentHelper, ExperimentCallable # noqa: F401
2
+ from .configuration import configuration, ConfigurationBase # noqa: F401
@@ -0,0 +1,220 @@
1
+ import inspect
2
+ import json
3
+ import logging
4
+ import sys
5
+ from pathlib import Path
6
+ from typing import Any, List, Optional, Protocol, Tuple, Dict
7
+
8
+ import click
9
+ import omegaconf
10
+ import yaml
11
+ from experimaestro import LauncherRegistry, RunMode, experiment
12
+ from experimaestro.experiments.configuration import ConfigurationBase
13
+ from experimaestro.settings import get_workspace
14
+ from omegaconf import OmegaConf, SCMode
15
+ from termcolor import cprint
16
+
17
+
18
+ class ExperimentHelper:
19
+ """Helper for experiments"""
20
+
21
+ # The experiment
22
+ xp: experiment
23
+
24
+ #: Run function
25
+ callable: "ExperimentCallable"
26
+
27
+ def __init__(self, callable: "ExperimentCallable"):
28
+ self.callable = callable
29
+
30
+ """Handles extra arguments"""
31
+
32
+ def run(self, args: List[str], configuration: ConfigurationBase):
33
+ assert len(args) == 0
34
+ self.callable(self, configuration)
35
+
36
+
37
+ class ExperimentCallable(Protocol):
38
+ """Protocol for the run function"""
39
+
40
+ def __call__(self, helper: ExperimentHelper, configuration: Any):
41
+ ...
42
+
43
+
44
+ def load(yaml_file: Path):
45
+ """Loads a YAML file, and parents one if they exist"""
46
+ if not yaml_file.exists() and yaml_file.suffix != ".yaml":
47
+ yaml_file = yaml_file.with_suffix(".yaml")
48
+
49
+ with yaml_file.open("rt") as fp:
50
+ _data = yaml.full_load(fp)
51
+ data = [_data]
52
+ if parent := _data.get("parent", None):
53
+ data.extend(load(yaml_file.parent / parent))
54
+
55
+ return data
56
+
57
+
58
+ @click.option("--debug", is_flag=True, help="Print debug information")
59
+ @click.option("--show", is_flag=True, help="Print configuration and exits")
60
+ @click.option(
61
+ "--env",
62
+ help="Define one environment variable",
63
+ type=(str, str),
64
+ multiple=True,
65
+ )
66
+ @click.option(
67
+ "--host",
68
+ type=str,
69
+ default=None,
70
+ help="Server hostname (default to localhost,"
71
+ " not suitable if your jobs are remote)",
72
+ )
73
+ @click.option(
74
+ "--run-mode",
75
+ type=click.Choice(RunMode),
76
+ default=RunMode.NORMAL,
77
+ help="Sets the run mode",
78
+ )
79
+ @click.option(
80
+ "--xpm-config-dir",
81
+ type=Path,
82
+ default=None,
83
+ help="Path for the experimaestro config directory "
84
+ "(if not specified, use $HOME/.config/experimaestro)",
85
+ )
86
+ @click.option(
87
+ "--port",
88
+ type=int,
89
+ default=None,
90
+ help="Port for monitoring (can be defined in the settings.yaml file)",
91
+ )
92
+ @click.option(
93
+ "--file", "xp_file", help="The file containing the main experimental code"
94
+ )
95
+ @click.option(
96
+ "--workdir",
97
+ type=str,
98
+ default=None,
99
+ help="Working directory - if None, uses the default XPM " "working directory",
100
+ )
101
+ @click.option("--conf", "-c", "extra_conf", type=str, multiple=True)
102
+ @click.argument("args", nargs=-1, type=click.UNPROCESSED)
103
+ @click.argument("yaml_file", metavar="YAML file", type=str)
104
+ @click.command()
105
+ def experiments_cli(
106
+ yaml_file: str,
107
+ xp_file: str,
108
+ host: str,
109
+ port: int,
110
+ xpm_config_dir: Path,
111
+ workdir: Optional[Path],
112
+ env: List[Tuple[str, str]],
113
+ run_mode: RunMode,
114
+ extra_conf: List[str],
115
+ args: List[str],
116
+ show: bool,
117
+ debug: bool,
118
+ ):
119
+ """Run an experiment"""
120
+ # --- Set the logger
121
+ logging.getLogger().setLevel(logging.DEBUG if debug else logging.INFO)
122
+ logging.getLogger("xpm.hash").setLevel(logging.INFO)
123
+
124
+ # --- Loads the YAML
125
+ yamls = load(Path(yaml_file))
126
+
127
+ # --- Get the XP file
128
+ if xp_file is None:
129
+ for data in yamls:
130
+ if xp_file := data.get("file"):
131
+ del data["file"]
132
+ break
133
+
134
+ if xp_file is None:
135
+ raise ValueError("No experiment file given")
136
+
137
+ # --- Set some options
138
+
139
+ if xpm_config_dir is not None:
140
+ assert xpm_config_dir.is_dir()
141
+ LauncherRegistry.set_config_dir(xpm_config_dir)
142
+
143
+ # --- Loads the XP file
144
+ xp_file = Path(xp_file)
145
+ if not xp_file.exists() and xp_file.suffix != ".py":
146
+ xp_file = xp_file.with_suffix(".py")
147
+ xp_file = Path(yaml_file).parent / xp_file
148
+
149
+ with open(xp_file, "r") as f:
150
+ source = f.read()
151
+ if sys.version_info < (3, 9):
152
+ the__file__ = str(xp_file)
153
+ else:
154
+ the__file__ = str(xp_file.absolute())
155
+
156
+ code = compile(source, filename=the__file__, mode="exec")
157
+ _locals: Dict[str, Any] = {}
158
+
159
+ sys.path.append(str(xp_file.parent.absolute()))
160
+ try:
161
+ exec(code, _locals, _locals)
162
+ finally:
163
+ sys.path.pop()
164
+
165
+ # --- ... and runs it
166
+ helper = _locals.get("run", None)
167
+ if helper is None:
168
+ raise ValueError(f"Could not find run function in {the__file__}")
169
+
170
+ if not isinstance(helper, ExperimentHelper):
171
+ helper = ExperimentHelper(helper)
172
+
173
+ parameters = inspect.signature(helper.callable).parameters
174
+ list_parameters = list(parameters.values())
175
+ assert len(list_parameters) == 2, (
176
+ "Callable function should only "
177
+ f"have two arguments (got {len(list_parameters)})"
178
+ )
179
+
180
+ schema = list_parameters[1].annotation
181
+ omegaconf_schema = OmegaConf.structured(schema())
182
+
183
+ configuration = OmegaConf.merge(*yamls)
184
+ if extra_conf:
185
+ configuration.merge_with(OmegaConf.from_dotlist(extra_conf))
186
+ if omegaconf_schema is not None:
187
+ try:
188
+ configuration = OmegaConf.merge(omegaconf_schema, configuration)
189
+ except omegaconf.errors.ConfigKeyError as e:
190
+ cprint(f"Error in configuration:\n\n{e}", "red", file=sys.stderr)
191
+ sys.exit(1)
192
+
193
+ # Move to an object container
194
+ configuration: schema = OmegaConf.to_container(
195
+ configuration, structured_config_mode=SCMode.INSTANTIATE
196
+ )
197
+
198
+ if show:
199
+ print(json.dumps(OmegaConf.to_container(configuration))) # noqa: T201
200
+ sys.exit(0)
201
+
202
+ # Get the working directory
203
+ if workdir is None or not Path(workdir).is_dir():
204
+ workdir = get_workspace(workdir).path.expanduser().resolve()
205
+ logging.info("Using working directory %s", workdir)
206
+
207
+ # --- Runs the experiment
208
+ with experiment(
209
+ workdir, configuration.id, host=host, port=port, run_mode=run_mode
210
+ ) as xp:
211
+ # Set up the environment
212
+ for key, value in env:
213
+ xp.setenv(key, value)
214
+
215
+ # Run the experiment
216
+ helper.xp = xp
217
+ helper.run(list(args), configuration)
218
+
219
+ # ... and wait
220
+ xp.wait()
@@ -0,0 +1,32 @@
1
+ from typing import Optional
2
+ import attr
3
+
4
+ try:
5
+ from typing import dataclass_transform
6
+ except ImportError:
7
+ from typing_extensions import dataclass_transform
8
+
9
+
10
+ @dataclass_transform(kw_only_default=True)
11
+ def configuration(*args, **kwargs):
12
+ """Method to define keyword only dataclasses
13
+
14
+ Configurations are keyword-only
15
+ """
16
+
17
+ return attr.define(*args, kw_only=True, slots=False, hash=True, eq=True, **kwargs)
18
+
19
+
20
+ @configuration()
21
+ class ConfigurationBase:
22
+ id: str
23
+ """ID of the experiment"""
24
+
25
+ description: str = ""
26
+ """Description of the experiment"""
27
+
28
+ file: str = "experiment"
29
+ """qualified name (relative to the module) for the file containing a run function"""
30
+
31
+ parent: Optional[str]
32
+ """Relative path of a YAML file that should be merged"""
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: experimaestro
3
- Version: 1.3.6
3
+ Version: 1.4.0
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
@@ -1,5 +1,5 @@
1
1
  experimaestro/__init__.py,sha256=TURptLVx9XunBd01lO4TYZACrP5rnMRnh_XE0T0_MwQ,1533
2
- experimaestro/__main__.py,sha256=9TIUAidbzgZF84qU-ZSWztycpPwqSfrKuA8B18r4-bA,13383
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
5
  experimaestro/click.py,sha256=HTfjm4poqfG69MIYztrPWEei5OVnd154UCr2pJm6yp8,2390
@@ -17,6 +17,9 @@ experimaestro/core/serialization.py,sha256=nLlSSqerH9lqiWvqkCxz0YRRW8M1y56ainZ9_
17
17
  experimaestro/core/serializers.py,sha256=R_CAMyjjfU1oi-eHU6VlEUixJpFayGqEPaYu7VsD9xA,1197
18
18
  experimaestro/core/types.py,sha256=jQGlyC1xnsZ5NKct3FELIcXcXQMvNQvauCEuumyfBz8,19253
19
19
  experimaestro/core/utils.py,sha256=jKmZqovj5BhmWQ0rXd3YFToSbIFTylC48NhCwxo3QfU,493
20
+ experimaestro/experiments/__init__.py,sha256=GcpDUIbCvhnv6rxFdAp4wTffCVNTv-InY6fbQAlTy-o,159
21
+ experimaestro/experiments/cli.py,sha256=QJ04wB0O3O0X0SEHM-Y8peA74lE1WIDesbk9_RsghL0,6216
22
+ experimaestro/experiments/configuration.py,sha256=bwoszuFWFT2fmaL7RNefNschBGpydDQkSgcSmLJ8gek,787
20
23
  experimaestro/filter.py,sha256=DN1PrmS9yXoOa5Xnv001zbxzpdzvcVZFI9xZFKZ1-6g,5794
21
24
  experimaestro/generators.py,sha256=9NQ_TfDfASkArLnO4PF7s5Yoo9KWjlna2DCPzk5gJOI,1230
22
25
  experimaestro/huggingface.py,sha256=gnVlr6SZnbutYz4PLH0Q77n1TRF-uk-dR-3UFzFqAY0,2956
@@ -137,8 +140,8 @@ experimaestro/utils/resources.py,sha256=gDjkrRjo7GULWyXmNXm_u1uqzEIAoAvJydICk56n
137
140
  experimaestro/utils/settings.py,sha256=jpFMqF0DLL4_P1xGal0zVR5cOrdD8O0Y2IOYvnRgN3k,793
138
141
  experimaestro/utils/yaml.py,sha256=jEjqXqUtJ333wNUdIc0o3LGvdsTQ9AKW9a9CCd-bmGU,6766
139
142
  experimaestro/xpmutils.py,sha256=S21eMbDYsHfvmZ1HmKpq5Pz5O-1HnCLYxKbyTBbASyQ,638
140
- experimaestro-1.3.6.dist-info/LICENSE,sha256=OXLcl0T2SZ8Pmy2_dmlvKuetivmyPd5m1q-Gyd-zaYY,35149
141
- experimaestro-1.3.6.dist-info/METADATA,sha256=oIMCcjrUWC-mHhVFI2K2aj5d-cw3JXLMqlmID1LD4Bo,5336
142
- experimaestro-1.3.6.dist-info/WHEEL,sha256=FMvqSimYX_P7y0a7UY-_Mc83r5zkBZsCYPm7Lr0Bsq4,88
143
- experimaestro-1.3.6.dist-info/entry_points.txt,sha256=PhaEili_fDgn5q7rBJGip_uhGkRBq5l3Yuhg91zkcbk,574
144
- experimaestro-1.3.6.dist-info/RECORD,,
143
+ experimaestro-1.4.0.dist-info/LICENSE,sha256=OXLcl0T2SZ8Pmy2_dmlvKuetivmyPd5m1q-Gyd-zaYY,35149
144
+ experimaestro-1.4.0.dist-info/METADATA,sha256=XWQCmuYMI4mGENdtGlEA7p0D4enym4BV3laNFmW8Kc0,5336
145
+ experimaestro-1.4.0.dist-info/WHEEL,sha256=FMvqSimYX_P7y0a7UY-_Mc83r5zkBZsCYPm7Lr0Bsq4,88
146
+ experimaestro-1.4.0.dist-info/entry_points.txt,sha256=PhaEili_fDgn5q7rBJGip_uhGkRBq5l3Yuhg91zkcbk,574
147
+ experimaestro-1.4.0.dist-info/RECORD,,