experimaestro 1.3.5__tar.gz → 1.4.0__tar.gz

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.

Files changed (146) hide show
  1. {experimaestro-1.3.5 → experimaestro-1.4.0}/PKG-INFO +1 -1
  2. {experimaestro-1.3.5 → experimaestro-1.4.0}/pyproject.toml +2 -2
  3. {experimaestro-1.3.5 → experimaestro-1.4.0}/src/experimaestro/__main__.py +5 -0
  4. {experimaestro-1.3.5 → experimaestro-1.4.0}/src/experimaestro/core/context.py +7 -2
  5. experimaestro-1.4.0/src/experimaestro/experiments/__init__.py +2 -0
  6. experimaestro-1.4.0/src/experimaestro/experiments/cli.py +220 -0
  7. experimaestro-1.4.0/src/experimaestro/experiments/configuration.py +32 -0
  8. {experimaestro-1.3.5 → experimaestro-1.4.0}/src/experimaestro/scheduler/base.py +66 -9
  9. {experimaestro-1.3.5 → experimaestro-1.4.0}/src/experimaestro/scheduler/services.py +18 -0
  10. {experimaestro-1.3.5 → experimaestro-1.4.0}/LICENSE +0 -0
  11. {experimaestro-1.3.5 → experimaestro-1.4.0}/README.md +0 -0
  12. {experimaestro-1.3.5 → experimaestro-1.4.0}/src/experimaestro/__init__.py +0 -0
  13. {experimaestro-1.3.5 → experimaestro-1.4.0}/src/experimaestro/annotations.py +0 -0
  14. {experimaestro-1.3.5 → experimaestro-1.4.0}/src/experimaestro/checkers.py +0 -0
  15. {experimaestro-1.3.5 → experimaestro-1.4.0}/src/experimaestro/click.py +0 -0
  16. {experimaestro-1.3.5 → experimaestro-1.4.0}/src/experimaestro/commandline.py +0 -0
  17. {experimaestro-1.3.5 → experimaestro-1.4.0}/src/experimaestro/compat.py +0 -0
  18. {experimaestro-1.3.5 → experimaestro-1.4.0}/src/experimaestro/connectors/__init__.py +0 -0
  19. {experimaestro-1.3.5 → experimaestro-1.4.0}/src/experimaestro/connectors/local.py +0 -0
  20. {experimaestro-1.3.5 → experimaestro-1.4.0}/src/experimaestro/connectors/ssh.py +0 -0
  21. {experimaestro-1.3.5 → experimaestro-1.4.0}/src/experimaestro/core/__init__.py +0 -0
  22. {experimaestro-1.3.5 → experimaestro-1.4.0}/src/experimaestro/core/arguments.py +0 -0
  23. {experimaestro-1.3.5 → experimaestro-1.4.0}/src/experimaestro/core/objects.py +0 -0
  24. {experimaestro-1.3.5 → experimaestro-1.4.0}/src/experimaestro/core/objects.pyi +0 -0
  25. {experimaestro-1.3.5 → experimaestro-1.4.0}/src/experimaestro/core/serialization.py +0 -0
  26. {experimaestro-1.3.5 → experimaestro-1.4.0}/src/experimaestro/core/serializers.py +0 -0
  27. {experimaestro-1.3.5 → experimaestro-1.4.0}/src/experimaestro/core/types.py +0 -0
  28. {experimaestro-1.3.5 → experimaestro-1.4.0}/src/experimaestro/core/utils.py +0 -0
  29. {experimaestro-1.3.5 → experimaestro-1.4.0}/src/experimaestro/filter.py +0 -0
  30. {experimaestro-1.3.5 → experimaestro-1.4.0}/src/experimaestro/generators.py +0 -0
  31. {experimaestro-1.3.5 → experimaestro-1.4.0}/src/experimaestro/huggingface.py +0 -0
  32. {experimaestro-1.3.5 → experimaestro-1.4.0}/src/experimaestro/ipc.py +0 -0
  33. {experimaestro-1.3.5 → experimaestro-1.4.0}/src/experimaestro/launcherfinder/__init__.py +0 -0
  34. {experimaestro-1.3.5 → experimaestro-1.4.0}/src/experimaestro/launcherfinder/base.py +0 -0
  35. {experimaestro-1.3.5 → experimaestro-1.4.0}/src/experimaestro/launcherfinder/parser.py +0 -0
  36. {experimaestro-1.3.5 → experimaestro-1.4.0}/src/experimaestro/launcherfinder/registry.py +0 -0
  37. {experimaestro-1.3.5 → experimaestro-1.4.0}/src/experimaestro/launcherfinder/specs.py +0 -0
  38. {experimaestro-1.3.5 → experimaestro-1.4.0}/src/experimaestro/launchers/__init__.py +0 -0
  39. {experimaestro-1.3.5 → experimaestro-1.4.0}/src/experimaestro/launchers/direct.py +0 -0
  40. {experimaestro-1.3.5 → experimaestro-1.4.0}/src/experimaestro/launchers/oar.py +0 -0
  41. {experimaestro-1.3.5 → experimaestro-1.4.0}/src/experimaestro/launchers/slurm/__init__.py +0 -0
  42. {experimaestro-1.3.5 → experimaestro-1.4.0}/src/experimaestro/launchers/slurm/base.py +0 -0
  43. {experimaestro-1.3.5 → experimaestro-1.4.0}/src/experimaestro/launchers/slurm/cli.py +0 -0
  44. {experimaestro-1.3.5 → experimaestro-1.4.0}/src/experimaestro/launchers/slurm/configuration.py +0 -0
  45. {experimaestro-1.3.5 → experimaestro-1.4.0}/src/experimaestro/locking.py +0 -0
  46. {experimaestro-1.3.5 → experimaestro-1.4.0}/src/experimaestro/mkdocs/__init__.py +0 -0
  47. {experimaestro-1.3.5 → experimaestro-1.4.0}/src/experimaestro/mkdocs/annotations.py +0 -0
  48. {experimaestro-1.3.5 → experimaestro-1.4.0}/src/experimaestro/mkdocs/base.py +0 -0
  49. {experimaestro-1.3.5 → experimaestro-1.4.0}/src/experimaestro/mkdocs/metaloader.py +0 -0
  50. {experimaestro-1.3.5 → experimaestro-1.4.0}/src/experimaestro/mkdocs/style.css +0 -0
  51. {experimaestro-1.3.5 → experimaestro-1.4.0}/src/experimaestro/mypy.py +0 -0
  52. {experimaestro-1.3.5 → experimaestro-1.4.0}/src/experimaestro/notifications.py +0 -0
  53. {experimaestro-1.3.5 → experimaestro-1.4.0}/src/experimaestro/py.typed +0 -0
  54. {experimaestro-1.3.5 → experimaestro-1.4.0}/src/experimaestro/rpyc.py +0 -0
  55. {experimaestro-1.3.5 → experimaestro-1.4.0}/src/experimaestro/run.py +0 -0
  56. {experimaestro-1.3.5 → experimaestro-1.4.0}/src/experimaestro/scheduler/__init__.py +0 -0
  57. {experimaestro-1.3.5 → experimaestro-1.4.0}/src/experimaestro/scheduler/dependencies.py +0 -0
  58. {experimaestro-1.3.5 → experimaestro-1.4.0}/src/experimaestro/scheduler/environment.py +0 -0
  59. {experimaestro-1.3.5 → experimaestro-1.4.0}/src/experimaestro/scheduler/workspace.py +0 -0
  60. {experimaestro-1.3.5 → experimaestro-1.4.0}/src/experimaestro/scriptbuilder.py +0 -0
  61. {experimaestro-1.3.5 → experimaestro-1.4.0}/src/experimaestro/server/__init__.py +0 -0
  62. {experimaestro-1.3.5 → experimaestro-1.4.0}/src/experimaestro/server/data/016b4a6cdced82ab3aa1.ttf +0 -0
  63. {experimaestro-1.3.5 → experimaestro-1.4.0}/src/experimaestro/server/data/0c35d18bf06992036b69.woff2 +0 -0
  64. {experimaestro-1.3.5 → experimaestro-1.4.0}/src/experimaestro/server/data/219aa9140e099e6c72ed.woff2 +0 -0
  65. {experimaestro-1.3.5 → experimaestro-1.4.0}/src/experimaestro/server/data/3a4004a46a653d4b2166.woff +0 -0
  66. {experimaestro-1.3.5 → experimaestro-1.4.0}/src/experimaestro/server/data/3baa5b8f3469222b822d.woff +0 -0
  67. {experimaestro-1.3.5 → experimaestro-1.4.0}/src/experimaestro/server/data/4d73cb90e394b34b7670.woff +0 -0
  68. {experimaestro-1.3.5 → experimaestro-1.4.0}/src/experimaestro/server/data/4ef4218c522f1eb6b5b1.woff2 +0 -0
  69. {experimaestro-1.3.5 → experimaestro-1.4.0}/src/experimaestro/server/data/50701fbb8177c2dde530.ttf +0 -0
  70. {experimaestro-1.3.5 → experimaestro-1.4.0}/src/experimaestro/server/data/5d681e2edae8c60630db.woff +0 -0
  71. {experimaestro-1.3.5 → experimaestro-1.4.0}/src/experimaestro/server/data/6f420cf17cc0d7676fad.woff2 +0 -0
  72. {experimaestro-1.3.5 → experimaestro-1.4.0}/src/experimaestro/server/data/878f31251d960bd6266f.woff2 +0 -0
  73. {experimaestro-1.3.5 → experimaestro-1.4.0}/src/experimaestro/server/data/b041b1fa4fe241b23445.woff2 +0 -0
  74. {experimaestro-1.3.5 → experimaestro-1.4.0}/src/experimaestro/server/data/b6879d41b0852f01ed5b.woff2 +0 -0
  75. {experimaestro-1.3.5 → experimaestro-1.4.0}/src/experimaestro/server/data/c380809fd3677d7d6903.woff2 +0 -0
  76. {experimaestro-1.3.5 → experimaestro-1.4.0}/src/experimaestro/server/data/d75e3fd1eb12e9bd6655.ttf +0 -0
  77. {experimaestro-1.3.5 → experimaestro-1.4.0}/src/experimaestro/server/data/f882956fd323fd322f31.woff +0 -0
  78. {experimaestro-1.3.5 → experimaestro-1.4.0}/src/experimaestro/server/data/favicon.ico +0 -0
  79. {experimaestro-1.3.5 → experimaestro-1.4.0}/src/experimaestro/server/data/index.css +0 -0
  80. {experimaestro-1.3.5 → experimaestro-1.4.0}/src/experimaestro/server/data/index.css.map +0 -0
  81. {experimaestro-1.3.5 → experimaestro-1.4.0}/src/experimaestro/server/data/index.html +0 -0
  82. {experimaestro-1.3.5 → experimaestro-1.4.0}/src/experimaestro/server/data/index.js +0 -0
  83. {experimaestro-1.3.5 → experimaestro-1.4.0}/src/experimaestro/server/data/index.js.map +0 -0
  84. {experimaestro-1.3.5 → experimaestro-1.4.0}/src/experimaestro/server/data/login.html +0 -0
  85. {experimaestro-1.3.5 → experimaestro-1.4.0}/src/experimaestro/server/data/manifest.json +0 -0
  86. {experimaestro-1.3.5 → experimaestro-1.4.0}/src/experimaestro/settings.py +0 -0
  87. {experimaestro-1.3.5 → experimaestro-1.4.0}/src/experimaestro/sphinx/__init__.py +0 -0
  88. {experimaestro-1.3.5 → experimaestro-1.4.0}/src/experimaestro/sphinx/static/experimaestro.css +0 -0
  89. {experimaestro-1.3.5 → experimaestro-1.4.0}/src/experimaestro/taskglobals.py +0 -0
  90. {experimaestro-1.3.5 → experimaestro-1.4.0}/src/experimaestro/tests/__init__.py +0 -0
  91. {experimaestro-1.3.5 → experimaestro-1.4.0}/src/experimaestro/tests/conftest.py +0 -0
  92. {experimaestro-1.3.5 → experimaestro-1.4.0}/src/experimaestro/tests/connectors/bin/executable.py +0 -0
  93. {experimaestro-1.3.5 → experimaestro-1.4.0}/src/experimaestro/tests/connectors/test_local.py +0 -0
  94. {experimaestro-1.3.5 → experimaestro-1.4.0}/src/experimaestro/tests/connectors/utils.py +0 -0
  95. {experimaestro-1.3.5 → experimaestro-1.4.0}/src/experimaestro/tests/definitions_types.py +0 -0
  96. {experimaestro-1.3.5 → experimaestro-1.4.0}/src/experimaestro/tests/launchers/__init__.py +0 -0
  97. {experimaestro-1.3.5 → experimaestro-1.4.0}/src/experimaestro/tests/launchers/bin/sacct +0 -0
  98. {experimaestro-1.3.5 → experimaestro-1.4.0}/src/experimaestro/tests/launchers/bin/sbatch +0 -0
  99. {experimaestro-1.3.5 → experimaestro-1.4.0}/src/experimaestro/tests/launchers/bin/test.py +0 -0
  100. {experimaestro-1.3.5 → experimaestro-1.4.0}/src/experimaestro/tests/launchers/common.py +0 -0
  101. {experimaestro-1.3.5 → experimaestro-1.4.0}/src/experimaestro/tests/launchers/config_slurm/__init__.py +0 -0
  102. {experimaestro-1.3.5 → experimaestro-1.4.0}/src/experimaestro/tests/launchers/config_slurm/launchers.yaml +0 -0
  103. {experimaestro-1.3.5 → experimaestro-1.4.0}/src/experimaestro/tests/launchers/test_local.py +0 -0
  104. {experimaestro-1.3.5 → experimaestro-1.4.0}/src/experimaestro/tests/launchers/test_slurm.py +0 -0
  105. {experimaestro-1.3.5 → experimaestro-1.4.0}/src/experimaestro/tests/restart.py +0 -0
  106. {experimaestro-1.3.5 → experimaestro-1.4.0}/src/experimaestro/tests/restart_main.py +0 -0
  107. {experimaestro-1.3.5 → experimaestro-1.4.0}/src/experimaestro/tests/scripts/notifyandwait.py +0 -0
  108. {experimaestro-1.3.5 → experimaestro-1.4.0}/src/experimaestro/tests/scripts/waitforfile.py +0 -0
  109. {experimaestro-1.3.5 → experimaestro-1.4.0}/src/experimaestro/tests/task_tokens.py +0 -0
  110. {experimaestro-1.3.5 → experimaestro-1.4.0}/src/experimaestro/tests/tasks/__init__.py +0 -0
  111. {experimaestro-1.3.5 → experimaestro-1.4.0}/src/experimaestro/tests/tasks/all.py +0 -0
  112. {experimaestro-1.3.5 → experimaestro-1.4.0}/src/experimaestro/tests/tasks/foreign.py +0 -0
  113. {experimaestro-1.3.5 → experimaestro-1.4.0}/src/experimaestro/tests/test_checkers.py +0 -0
  114. {experimaestro-1.3.5 → experimaestro-1.4.0}/src/experimaestro/tests/test_dependencies.py +0 -0
  115. {experimaestro-1.3.5 → experimaestro-1.4.0}/src/experimaestro/tests/test_findlauncher.py +0 -0
  116. {experimaestro-1.3.5 → experimaestro-1.4.0}/src/experimaestro/tests/test_forward.py +0 -0
  117. {experimaestro-1.3.5 → experimaestro-1.4.0}/src/experimaestro/tests/test_identifier.py +0 -0
  118. {experimaestro-1.3.5 → experimaestro-1.4.0}/src/experimaestro/tests/test_instance.py +0 -0
  119. {experimaestro-1.3.5 → experimaestro-1.4.0}/src/experimaestro/tests/test_objects.py +0 -0
  120. {experimaestro-1.3.5 → experimaestro-1.4.0}/src/experimaestro/tests/test_outputs.py +0 -0
  121. {experimaestro-1.3.5 → experimaestro-1.4.0}/src/experimaestro/tests/test_param.py +0 -0
  122. {experimaestro-1.3.5 → experimaestro-1.4.0}/src/experimaestro/tests/test_progress.py +0 -0
  123. {experimaestro-1.3.5 → experimaestro-1.4.0}/src/experimaestro/tests/test_serializers.py +0 -0
  124. {experimaestro-1.3.5 → experimaestro-1.4.0}/src/experimaestro/tests/test_snippets.py +0 -0
  125. {experimaestro-1.3.5 → experimaestro-1.4.0}/src/experimaestro/tests/test_ssh.py +0 -0
  126. {experimaestro-1.3.5 → experimaestro-1.4.0}/src/experimaestro/tests/test_tags.py +0 -0
  127. {experimaestro-1.3.5 → experimaestro-1.4.0}/src/experimaestro/tests/test_tasks.py +0 -0
  128. {experimaestro-1.3.5 → experimaestro-1.4.0}/src/experimaestro/tests/test_tokens.py +0 -0
  129. {experimaestro-1.3.5 → experimaestro-1.4.0}/src/experimaestro/tests/test_types.py +0 -0
  130. {experimaestro-1.3.5 → experimaestro-1.4.0}/src/experimaestro/tests/test_validation.py +0 -0
  131. {experimaestro-1.3.5 → experimaestro-1.4.0}/src/experimaestro/tests/token_reschedule.py +0 -0
  132. {experimaestro-1.3.5 → experimaestro-1.4.0}/src/experimaestro/tests/utils.py +0 -0
  133. {experimaestro-1.3.5 → experimaestro-1.4.0}/src/experimaestro/tokens.py +0 -0
  134. {experimaestro-1.3.5 → experimaestro-1.4.0}/src/experimaestro/tools/__init__.py +0 -0
  135. {experimaestro-1.3.5 → experimaestro-1.4.0}/src/experimaestro/tools/diff.py +0 -0
  136. {experimaestro-1.3.5 → experimaestro-1.4.0}/src/experimaestro/tools/documentation.py +0 -0
  137. {experimaestro-1.3.5 → experimaestro-1.4.0}/src/experimaestro/tools/jobs.py +0 -0
  138. {experimaestro-1.3.5 → experimaestro-1.4.0}/src/experimaestro/typingutils.py +0 -0
  139. {experimaestro-1.3.5 → experimaestro-1.4.0}/src/experimaestro/utils/__init__.py +0 -0
  140. {experimaestro-1.3.5 → experimaestro-1.4.0}/src/experimaestro/utils/asyncio.py +0 -0
  141. {experimaestro-1.3.5 → experimaestro-1.4.0}/src/experimaestro/utils/jobs.py +0 -0
  142. {experimaestro-1.3.5 → experimaestro-1.4.0}/src/experimaestro/utils/jupyter.py +0 -0
  143. {experimaestro-1.3.5 → experimaestro-1.4.0}/src/experimaestro/utils/resources.py +0 -0
  144. {experimaestro-1.3.5 → experimaestro-1.4.0}/src/experimaestro/utils/settings.py +0 -0
  145. {experimaestro-1.3.5 → experimaestro-1.4.0}/src/experimaestro/utils/yaml.py +0 -0
  146. {experimaestro-1.3.5 → experimaestro-1.4.0}/src/experimaestro/xpmutils.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: experimaestro
3
- Version: 1.3.5
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
@@ -19,7 +19,7 @@ include = [
19
19
  "src/experimaestro/sphinx/static/experimaestro.css",
20
20
  "src/experimaestro/mkdocs/style.css"
21
21
  ]
22
- version = "1.3.5"
22
+ version = "1.4.0"
23
23
  repository = "https://github.com/experimaestro/experimaestro-python"
24
24
  documentation = "https://experimaestro-python.readthedocs.io/"
25
25
 
@@ -124,7 +124,7 @@ warn_unused_ignores = true
124
124
 
125
125
  [tool.commitizen]
126
126
  name = "cz_conventional_commits"
127
- version = "1.3.5"
127
+ version = "1.4.0"
128
128
  changelog_start_rev = "0.15.0"
129
129
  tag_format = "v$version"
130
130
  update_changelog_on_bump = true
@@ -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__)
@@ -36,13 +36,18 @@ class SerializedPath:
36
36
 
37
37
 
38
38
  class SerializationContext:
39
+ """Context when serializing experimaestro configurations"""
40
+
39
41
  save_directory: Optional[Path]
40
42
  var_path: List[str]
41
43
  serialized: Set[int]
42
44
 
43
- """Context when serializing experimaestro configurations"""
44
-
45
45
  def __init__(self, *, save_directory: Optional[Path] = None):
46
+ """Creates a new serialization context
47
+
48
+ :param save_directory: Defines the directory where `SerializedPath` are
49
+ stored
50
+ """
46
51
  self.save_directory = save_directory
47
52
  self.var_path = []
48
53
  self.serialized = set()
@@ -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"""
@@ -5,7 +5,7 @@ from pathlib import Path
5
5
  from shutil import rmtree
6
6
  import threading
7
7
  import time
8
- from typing import List, Optional, Set, TypeVar, Union, TYPE_CHECKING
8
+ from typing import Any, List, Optional, Set, TypeVar, Union, TYPE_CHECKING
9
9
  import enum
10
10
  import signal
11
11
  import asyncio
@@ -482,7 +482,7 @@ class Scheduler:
482
482
 
483
483
  return None
484
484
 
485
- async def aio_submit(self, job: Job) -> JobState:
485
+ async def aio_submit(self, job: Job) -> JobState: # noqa: C901
486
486
  """Main scheduler function: submit a job, run it (if needed), and returns
487
487
  the status code
488
488
  """
@@ -693,14 +693,25 @@ ServiceClass = TypeVar("ServiceClass", bound=Service)
693
693
 
694
694
 
695
695
  class experiment:
696
- """Experiment context"""
696
+ """Main experiment object
697
+
698
+ It is a context object, i.e. experiments is run with
699
+
700
+ ```py
701
+ with experiment(...) as xp:
702
+ ...
703
+ ```
704
+ """
697
705
 
698
706
  # Current experiment
699
707
  CURRENT: Optional["experiment"] = None
700
708
 
701
709
  @staticmethod
702
710
  def current() -> "experiment":
703
- """Returns the current experiment, but checking first if set"""
711
+ """Returns the current experiment, but checking first if set
712
+
713
+ If there is no current experiment, raises an AssertError
714
+ """
704
715
  assert experiment.CURRENT is not None, "No current experiment defined"
705
716
  return experiment.CURRENT
706
717
 
@@ -718,9 +729,18 @@ class experiment:
718
729
  """
719
730
  :param env: an environment -- or a working directory for a local
720
731
  environment
721
- :param port: the port for the web server (overrides environment port if
722
- any). Use None to avoid running a web server (default when dry run).
732
+
733
+ :param name: the identifier of the experiment
734
+
723
735
  :param launcher: The launcher (if not provided, inferred from path)
736
+
737
+ :param host: The host for the web server (overrides the environment if
738
+ set)
739
+ :param port: the port for the web server (overrides the environment if
740
+ set). Use negative number to avoid running a web server (default when dry run).
741
+
742
+ :param run_mode: The run mode for the experiment (normal, generate run
743
+ files, dry run)
724
744
  """
725
745
 
726
746
  from experimaestro.server import Server
@@ -759,7 +779,7 @@ class experiment:
759
779
  self.scheduler = Scheduler(self, name)
760
780
  self.server = (
761
781
  Server(self.scheduler, settings.server)
762
- if settings.server.port is not None
782
+ if (settings.server.port is not None and settings.server.port >= 0)
763
783
  and self.workspace.run_mode == RunMode.NORMAL
764
784
  else None
765
785
  )
@@ -842,7 +862,9 @@ class experiment:
842
862
  self.environment.setenv(name, value)
843
863
 
844
864
  def token(self, name: str, count: int):
845
- """Returns a token for this experiment depending on the host"""
865
+ """Returns a token for this experiment
866
+
867
+ The token is the default token of the workspace connector"""
846
868
  return self.workspace.connector.createtoken(name, count)
847
869
 
848
870
  def __enter__(self):
@@ -928,8 +950,43 @@ class experiment:
928
950
  self.server.stop()
929
951
 
930
952
  def add_service(self, service: ServiceClass) -> ServiceClass:
931
- """Adds a service (e.g. tensorboard viewer) to the experiment"""
953
+ """Adds a service (e.g. tensorboard viewer) to the experiment
954
+
955
+ :param service: A service instance
956
+ :type service: Service
957
+ :return: The same service instance
958
+ """
932
959
  self.services[service.id] = service
933
960
  for listener in self.scheduler.listeners:
934
961
  listener.service_add(service)
935
962
  return service
963
+
964
+ def save(self, obj: Any, name: str = "default"):
965
+ """Serializes configurations.
966
+
967
+ Saves configuration objects within the experimental directory
968
+
969
+ :param obj: The object to save
970
+ :param name: The name of the saving directory (default to `default`)
971
+ """
972
+
973
+ if self.workspace.run_mode == RunMode.NORMAL:
974
+ from experimaestro import save
975
+
976
+ save_dir = self.workdir / "data" / name
977
+ save_dir.mkdir(exist_ok=True, parents=True)
978
+
979
+ save(obj, save_dir)
980
+
981
+ def load(self, reference: str, name: str = "default"):
982
+ """Serializes configurations.
983
+
984
+ Loads configuration objects from an experimental directory
985
+
986
+ :param reference: The name of the experiment
987
+ :param name: The name of the saving directory (default to `default`)
988
+ """
989
+ from experimaestro import load
990
+
991
+ path = self.workspace.experimentspath / reference / "data" / name
992
+ return load(path)
@@ -6,6 +6,8 @@ from typing import Set
6
6
 
7
7
 
8
8
  class ServiceListener:
9
+ """A service listener"""
10
+
9
11
  def service_state_changed(service):
10
12
  pass
11
13
 
@@ -18,6 +20,12 @@ class ServiceState(Enum):
18
20
 
19
21
 
20
22
  class Service:
23
+ """An experiment service
24
+
25
+ Services can be associated with an experiment. They send
26
+ notifications to service listeners.
27
+ """
28
+
21
29
  id: str
22
30
  _state: ServiceState = ServiceState.STOPPED
23
31
 
@@ -25,9 +33,17 @@ class Service:
25
33
  self.listeners: Set[ServiceListener] = set()
26
34
 
27
35
  def add_listener(self, listener: ServiceListener):
36
+ """Adds a listener
37
+
38
+ :param listener: The listener to add
39
+ """
28
40
  self.listeners.add(listener)
29
41
 
30
42
  def remove_listener(self, listener: ServiceListener):
43
+ """Removes a listener
44
+
45
+ :param listener: The listener to remove
46
+ """
31
47
  self.listeners.remove(listener)
32
48
 
33
49
  def description(self):
@@ -47,6 +63,8 @@ class Service:
47
63
 
48
64
 
49
65
  class WebService(Service):
66
+ """Web service"""
67
+
50
68
  def __init__(self):
51
69
  super().__init__()
52
70
  self.url = None
File without changes
File without changes