experimaestro 1.5.6__tar.gz → 1.5.8__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 (151) hide show
  1. {experimaestro-1.5.6 → experimaestro-1.5.8}/PKG-INFO +1 -1
  2. {experimaestro-1.5.6 → experimaestro-1.5.8}/pyproject.toml +2 -6
  3. experimaestro-1.5.8/src/experimaestro/__main__.py +12 -0
  4. experimaestro-1.5.6/src/experimaestro/__main__.py → experimaestro-1.5.8/src/experimaestro/cli/__init__.py +8 -128
  5. {experimaestro-1.5.6/src/experimaestro → experimaestro-1.5.8/src/experimaestro/cli}/filter.py +4 -4
  6. experimaestro-1.5.8/src/experimaestro/cli/jobs.py +251 -0
  7. {experimaestro-1.5.6 → experimaestro-1.5.8}/src/experimaestro/connectors/ssh.py +2 -2
  8. {experimaestro-1.5.6 → experimaestro-1.5.8}/src/experimaestro/core/objects.py +6 -3
  9. {experimaestro-1.5.6 → experimaestro-1.5.8}/src/experimaestro/core/types.py +8 -3
  10. {experimaestro-1.5.6 → experimaestro-1.5.8}/src/experimaestro/experiments/cli.py +20 -42
  11. {experimaestro-1.5.6 → experimaestro-1.5.8}/src/experimaestro/launcherfinder/__init__.py +1 -1
  12. experimaestro-1.5.8/src/experimaestro/launcherfinder/base.py +17 -0
  13. {experimaestro-1.5.6 → experimaestro-1.5.8}/src/experimaestro/launcherfinder/registry.py +22 -129
  14. experimaestro-1.5.8/src/experimaestro/launchers/direct.py +10 -0
  15. {experimaestro-1.5.6 → experimaestro-1.5.8}/src/experimaestro/launchers/slurm/base.py +3 -1
  16. {experimaestro-1.5.6 → experimaestro-1.5.8}/src/experimaestro/notifications.py +24 -8
  17. {experimaestro-1.5.6 → experimaestro-1.5.8}/src/experimaestro/run.py +0 -1
  18. {experimaestro-1.5.6 → experimaestro-1.5.8}/src/experimaestro/scheduler/base.py +0 -5
  19. {experimaestro-1.5.6 → experimaestro-1.5.8}/src/experimaestro/settings.py +29 -1
  20. experimaestro-1.5.8/src/experimaestro/tests/launchers/config_slurm/launchers.py +25 -0
  21. {experimaestro-1.5.6 → experimaestro-1.5.8}/src/experimaestro/tests/test_findlauncher.py +1 -1
  22. {experimaestro-1.5.6 → experimaestro-1.5.8}/src/experimaestro/tests/test_tags.py +35 -0
  23. {experimaestro-1.5.6 → experimaestro-1.5.8}/src/experimaestro/tokens.py +8 -8
  24. {experimaestro-1.5.6 → experimaestro-1.5.8}/src/experimaestro/utils/resources.py +5 -1
  25. experimaestro-1.5.6/src/experimaestro/launcherfinder/base.py +0 -33
  26. experimaestro-1.5.6/src/experimaestro/launchers/direct.py +0 -57
  27. experimaestro-1.5.6/src/experimaestro/launchers/slurm/cli.py +0 -29
  28. experimaestro-1.5.6/src/experimaestro/launchers/slurm/configuration.py +0 -597
  29. experimaestro-1.5.6/src/experimaestro/tests/launchers/config_slurm/launchers.yaml +0 -134
  30. experimaestro-1.5.6/src/experimaestro/utils/yaml.py +0 -202
  31. {experimaestro-1.5.6 → experimaestro-1.5.8}/LICENSE +0 -0
  32. {experimaestro-1.5.6 → experimaestro-1.5.8}/README.md +0 -0
  33. {experimaestro-1.5.6 → experimaestro-1.5.8}/src/experimaestro/__init__.py +0 -0
  34. {experimaestro-1.5.6 → experimaestro-1.5.8}/src/experimaestro/annotations.py +0 -0
  35. {experimaestro-1.5.6 → experimaestro-1.5.8}/src/experimaestro/checkers.py +0 -0
  36. {experimaestro-1.5.6 → experimaestro-1.5.8}/src/experimaestro/click.py +0 -0
  37. {experimaestro-1.5.6 → experimaestro-1.5.8}/src/experimaestro/commandline.py +0 -0
  38. {experimaestro-1.5.6 → experimaestro-1.5.8}/src/experimaestro/compat.py +0 -0
  39. {experimaestro-1.5.6 → experimaestro-1.5.8}/src/experimaestro/connectors/__init__.py +0 -0
  40. {experimaestro-1.5.6 → experimaestro-1.5.8}/src/experimaestro/connectors/local.py +0 -0
  41. {experimaestro-1.5.6 → experimaestro-1.5.8}/src/experimaestro/core/__init__.py +0 -0
  42. {experimaestro-1.5.6 → experimaestro-1.5.8}/src/experimaestro/core/arguments.py +0 -0
  43. {experimaestro-1.5.6 → experimaestro-1.5.8}/src/experimaestro/core/context.py +0 -0
  44. {experimaestro-1.5.6 → experimaestro-1.5.8}/src/experimaestro/core/objects.pyi +0 -0
  45. {experimaestro-1.5.6 → experimaestro-1.5.8}/src/experimaestro/core/serialization.py +0 -0
  46. {experimaestro-1.5.6 → experimaestro-1.5.8}/src/experimaestro/core/serializers.py +0 -0
  47. {experimaestro-1.5.6 → experimaestro-1.5.8}/src/experimaestro/core/utils.py +0 -0
  48. {experimaestro-1.5.6 → experimaestro-1.5.8}/src/experimaestro/exceptions.py +0 -0
  49. {experimaestro-1.5.6 → experimaestro-1.5.8}/src/experimaestro/experiments/__init__.py +0 -0
  50. {experimaestro-1.5.6 → experimaestro-1.5.8}/src/experimaestro/experiments/configuration.py +0 -0
  51. {experimaestro-1.5.6 → experimaestro-1.5.8}/src/experimaestro/generators.py +0 -0
  52. {experimaestro-1.5.6 → experimaestro-1.5.8}/src/experimaestro/huggingface.py +0 -0
  53. {experimaestro-1.5.6 → experimaestro-1.5.8}/src/experimaestro/ipc.py +0 -0
  54. {experimaestro-1.5.6 → experimaestro-1.5.8}/src/experimaestro/launcherfinder/parser.py +0 -0
  55. {experimaestro-1.5.6 → experimaestro-1.5.8}/src/experimaestro/launcherfinder/specs.py +0 -0
  56. {experimaestro-1.5.6 → experimaestro-1.5.8}/src/experimaestro/launchers/__init__.py +0 -0
  57. {experimaestro-1.5.6 → experimaestro-1.5.8}/src/experimaestro/launchers/oar.py +0 -0
  58. {experimaestro-1.5.6 → experimaestro-1.5.8}/src/experimaestro/launchers/slurm/__init__.py +0 -0
  59. {experimaestro-1.5.6 → experimaestro-1.5.8}/src/experimaestro/locking.py +0 -0
  60. {experimaestro-1.5.6 → experimaestro-1.5.8}/src/experimaestro/mkdocs/__init__.py +0 -0
  61. {experimaestro-1.5.6 → experimaestro-1.5.8}/src/experimaestro/mkdocs/annotations.py +0 -0
  62. {experimaestro-1.5.6 → experimaestro-1.5.8}/src/experimaestro/mkdocs/base.py +0 -0
  63. {experimaestro-1.5.6 → experimaestro-1.5.8}/src/experimaestro/mkdocs/metaloader.py +0 -0
  64. {experimaestro-1.5.6 → experimaestro-1.5.8}/src/experimaestro/mkdocs/style.css +0 -0
  65. {experimaestro-1.5.6 → experimaestro-1.5.8}/src/experimaestro/mypy.py +0 -0
  66. {experimaestro-1.5.6 → experimaestro-1.5.8}/src/experimaestro/py.typed +0 -0
  67. {experimaestro-1.5.6 → experimaestro-1.5.8}/src/experimaestro/rpyc.py +0 -0
  68. {experimaestro-1.5.6 → experimaestro-1.5.8}/src/experimaestro/scheduler/__init__.py +0 -0
  69. {experimaestro-1.5.6 → experimaestro-1.5.8}/src/experimaestro/scheduler/dependencies.py +0 -0
  70. {experimaestro-1.5.6 → experimaestro-1.5.8}/src/experimaestro/scheduler/services.py +0 -0
  71. {experimaestro-1.5.6 → experimaestro-1.5.8}/src/experimaestro/scheduler/workspace.py +0 -0
  72. {experimaestro-1.5.6 → experimaestro-1.5.8}/src/experimaestro/scriptbuilder.py +0 -0
  73. {experimaestro-1.5.6 → experimaestro-1.5.8}/src/experimaestro/server/__init__.py +0 -0
  74. {experimaestro-1.5.6 → experimaestro-1.5.8}/src/experimaestro/server/data/016b4a6cdced82ab3aa1.ttf +0 -0
  75. {experimaestro-1.5.6 → experimaestro-1.5.8}/src/experimaestro/server/data/0c35d18bf06992036b69.woff2 +0 -0
  76. {experimaestro-1.5.6 → experimaestro-1.5.8}/src/experimaestro/server/data/219aa9140e099e6c72ed.woff2 +0 -0
  77. {experimaestro-1.5.6 → experimaestro-1.5.8}/src/experimaestro/server/data/3a4004a46a653d4b2166.woff +0 -0
  78. {experimaestro-1.5.6 → experimaestro-1.5.8}/src/experimaestro/server/data/3baa5b8f3469222b822d.woff +0 -0
  79. {experimaestro-1.5.6 → experimaestro-1.5.8}/src/experimaestro/server/data/4d73cb90e394b34b7670.woff +0 -0
  80. {experimaestro-1.5.6 → experimaestro-1.5.8}/src/experimaestro/server/data/4ef4218c522f1eb6b5b1.woff2 +0 -0
  81. {experimaestro-1.5.6 → experimaestro-1.5.8}/src/experimaestro/server/data/50701fbb8177c2dde530.ttf +0 -0
  82. {experimaestro-1.5.6 → experimaestro-1.5.8}/src/experimaestro/server/data/5d681e2edae8c60630db.woff +0 -0
  83. {experimaestro-1.5.6 → experimaestro-1.5.8}/src/experimaestro/server/data/6f420cf17cc0d7676fad.woff2 +0 -0
  84. {experimaestro-1.5.6 → experimaestro-1.5.8}/src/experimaestro/server/data/878f31251d960bd6266f.woff2 +0 -0
  85. {experimaestro-1.5.6 → experimaestro-1.5.8}/src/experimaestro/server/data/b041b1fa4fe241b23445.woff2 +0 -0
  86. {experimaestro-1.5.6 → experimaestro-1.5.8}/src/experimaestro/server/data/b6879d41b0852f01ed5b.woff2 +0 -0
  87. {experimaestro-1.5.6 → experimaestro-1.5.8}/src/experimaestro/server/data/c380809fd3677d7d6903.woff2 +0 -0
  88. {experimaestro-1.5.6 → experimaestro-1.5.8}/src/experimaestro/server/data/d75e3fd1eb12e9bd6655.ttf +0 -0
  89. {experimaestro-1.5.6 → experimaestro-1.5.8}/src/experimaestro/server/data/f882956fd323fd322f31.woff +0 -0
  90. {experimaestro-1.5.6 → experimaestro-1.5.8}/src/experimaestro/server/data/favicon.ico +0 -0
  91. {experimaestro-1.5.6 → experimaestro-1.5.8}/src/experimaestro/server/data/index.css +0 -0
  92. {experimaestro-1.5.6 → experimaestro-1.5.8}/src/experimaestro/server/data/index.css.map +0 -0
  93. {experimaestro-1.5.6 → experimaestro-1.5.8}/src/experimaestro/server/data/index.html +0 -0
  94. {experimaestro-1.5.6 → experimaestro-1.5.8}/src/experimaestro/server/data/index.js +0 -0
  95. {experimaestro-1.5.6 → experimaestro-1.5.8}/src/experimaestro/server/data/index.js.map +0 -0
  96. {experimaestro-1.5.6 → experimaestro-1.5.8}/src/experimaestro/server/data/login.html +0 -0
  97. {experimaestro-1.5.6 → experimaestro-1.5.8}/src/experimaestro/server/data/manifest.json +0 -0
  98. {experimaestro-1.5.6 → experimaestro-1.5.8}/src/experimaestro/sphinx/__init__.py +0 -0
  99. {experimaestro-1.5.6 → experimaestro-1.5.8}/src/experimaestro/sphinx/static/experimaestro.css +0 -0
  100. {experimaestro-1.5.6 → experimaestro-1.5.8}/src/experimaestro/taskglobals.py +0 -0
  101. {experimaestro-1.5.6 → experimaestro-1.5.8}/src/experimaestro/tests/__init__.py +0 -0
  102. {experimaestro-1.5.6 → experimaestro-1.5.8}/src/experimaestro/tests/conftest.py +0 -0
  103. {experimaestro-1.5.6 → experimaestro-1.5.8}/src/experimaestro/tests/connectors/bin/executable.py +0 -0
  104. {experimaestro-1.5.6 → experimaestro-1.5.8}/src/experimaestro/tests/connectors/test_local.py +0 -0
  105. {experimaestro-1.5.6 → experimaestro-1.5.8}/src/experimaestro/tests/connectors/utils.py +0 -0
  106. {experimaestro-1.5.6 → experimaestro-1.5.8}/src/experimaestro/tests/definitions_types.py +0 -0
  107. {experimaestro-1.5.6 → experimaestro-1.5.8}/src/experimaestro/tests/launchers/__init__.py +0 -0
  108. {experimaestro-1.5.6 → experimaestro-1.5.8}/src/experimaestro/tests/launchers/bin/sacct +0 -0
  109. {experimaestro-1.5.6 → experimaestro-1.5.8}/src/experimaestro/tests/launchers/bin/sbatch +0 -0
  110. {experimaestro-1.5.6 → experimaestro-1.5.8}/src/experimaestro/tests/launchers/bin/test.py +0 -0
  111. {experimaestro-1.5.6 → experimaestro-1.5.8}/src/experimaestro/tests/launchers/common.py +0 -0
  112. {experimaestro-1.5.6 → experimaestro-1.5.8}/src/experimaestro/tests/launchers/config_slurm/__init__.py +0 -0
  113. {experimaestro-1.5.6 → experimaestro-1.5.8}/src/experimaestro/tests/launchers/test_local.py +0 -0
  114. {experimaestro-1.5.6 → experimaestro-1.5.8}/src/experimaestro/tests/launchers/test_slurm.py +0 -0
  115. {experimaestro-1.5.6 → experimaestro-1.5.8}/src/experimaestro/tests/restart.py +0 -0
  116. {experimaestro-1.5.6 → experimaestro-1.5.8}/src/experimaestro/tests/restart_main.py +0 -0
  117. {experimaestro-1.5.6 → experimaestro-1.5.8}/src/experimaestro/tests/scripts/notifyandwait.py +0 -0
  118. {experimaestro-1.5.6 → experimaestro-1.5.8}/src/experimaestro/tests/scripts/waitforfile.py +0 -0
  119. {experimaestro-1.5.6 → experimaestro-1.5.8}/src/experimaestro/tests/task_tokens.py +0 -0
  120. {experimaestro-1.5.6 → experimaestro-1.5.8}/src/experimaestro/tests/tasks/__init__.py +0 -0
  121. {experimaestro-1.5.6 → experimaestro-1.5.8}/src/experimaestro/tests/tasks/all.py +0 -0
  122. {experimaestro-1.5.6 → experimaestro-1.5.8}/src/experimaestro/tests/tasks/foreign.py +0 -0
  123. {experimaestro-1.5.6 → experimaestro-1.5.8}/src/experimaestro/tests/test_checkers.py +0 -0
  124. {experimaestro-1.5.6 → experimaestro-1.5.8}/src/experimaestro/tests/test_dependencies.py +0 -0
  125. {experimaestro-1.5.6 → experimaestro-1.5.8}/src/experimaestro/tests/test_forward.py +0 -0
  126. {experimaestro-1.5.6 → experimaestro-1.5.8}/src/experimaestro/tests/test_identifier.py +0 -0
  127. {experimaestro-1.5.6 → experimaestro-1.5.8}/src/experimaestro/tests/test_instance.py +0 -0
  128. {experimaestro-1.5.6 → experimaestro-1.5.8}/src/experimaestro/tests/test_objects.py +0 -0
  129. {experimaestro-1.5.6 → experimaestro-1.5.8}/src/experimaestro/tests/test_outputs.py +0 -0
  130. {experimaestro-1.5.6 → experimaestro-1.5.8}/src/experimaestro/tests/test_param.py +0 -0
  131. {experimaestro-1.5.6 → experimaestro-1.5.8}/src/experimaestro/tests/test_progress.py +0 -0
  132. {experimaestro-1.5.6 → experimaestro-1.5.8}/src/experimaestro/tests/test_serializers.py +0 -0
  133. {experimaestro-1.5.6 → experimaestro-1.5.8}/src/experimaestro/tests/test_snippets.py +0 -0
  134. {experimaestro-1.5.6 → experimaestro-1.5.8}/src/experimaestro/tests/test_ssh.py +0 -0
  135. {experimaestro-1.5.6 → experimaestro-1.5.8}/src/experimaestro/tests/test_tasks.py +0 -0
  136. {experimaestro-1.5.6 → experimaestro-1.5.8}/src/experimaestro/tests/test_tokens.py +0 -0
  137. {experimaestro-1.5.6 → experimaestro-1.5.8}/src/experimaestro/tests/test_types.py +0 -0
  138. {experimaestro-1.5.6 → experimaestro-1.5.8}/src/experimaestro/tests/test_validation.py +0 -0
  139. {experimaestro-1.5.6 → experimaestro-1.5.8}/src/experimaestro/tests/token_reschedule.py +0 -0
  140. {experimaestro-1.5.6 → experimaestro-1.5.8}/src/experimaestro/tests/utils.py +0 -0
  141. {experimaestro-1.5.6 → experimaestro-1.5.8}/src/experimaestro/tools/__init__.py +0 -0
  142. {experimaestro-1.5.6 → experimaestro-1.5.8}/src/experimaestro/tools/diff.py +0 -0
  143. {experimaestro-1.5.6 → experimaestro-1.5.8}/src/experimaestro/tools/documentation.py +0 -0
  144. {experimaestro-1.5.6 → experimaestro-1.5.8}/src/experimaestro/tools/jobs.py +0 -0
  145. {experimaestro-1.5.6 → experimaestro-1.5.8}/src/experimaestro/typingutils.py +0 -0
  146. {experimaestro-1.5.6 → experimaestro-1.5.8}/src/experimaestro/utils/__init__.py +0 -0
  147. {experimaestro-1.5.6 → experimaestro-1.5.8}/src/experimaestro/utils/asyncio.py +0 -0
  148. {experimaestro-1.5.6 → experimaestro-1.5.8}/src/experimaestro/utils/jobs.py +0 -0
  149. {experimaestro-1.5.6 → experimaestro-1.5.8}/src/experimaestro/utils/jupyter.py +0 -0
  150. {experimaestro-1.5.6 → experimaestro-1.5.8}/src/experimaestro/utils/settings.py +0 -0
  151. {experimaestro-1.5.6 → experimaestro-1.5.8}/src/experimaestro/xpmutils.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: experimaestro
3
- Version: 1.5.6
3
+ Version: 1.5.8
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.5.6"
22
+ version = "1.5.8"
23
23
  repository = "https://github.com/experimaestro/experimaestro-python"
24
24
  documentation = "https://experimaestro-python.readthedocs.io/"
25
25
 
@@ -90,10 +90,6 @@ slurm = "experimaestro.launchers.slurm:BatchSlurmProcess"
90
90
  local = "experimaestro.connectors.local:LocalConnector"
91
91
  ssh = "experimaestro.connectors.ssh:SshConnector"
92
92
 
93
- [tool.poetry.plugins."experimaestro.launchers"]
94
- unix = "experimaestro.launchers.direct:DirectLauncher"
95
- slurm = "experimaestro.launchers.slurm:SlurmLauncher"
96
-
97
93
  [tool.poetry.plugins."experimaestro.tokens"]
98
94
  unix = "experimaestro.tokens:CounterToken"
99
95
 
@@ -123,7 +119,7 @@ warn_unused_ignores = true
123
119
 
124
120
  [tool.commitizen]
125
121
  name = "cz_conventional_commits"
126
- version = "1.5.6"
122
+ version = "1.5.8"
127
123
  changelog_start_rev = "0.15.0"
128
124
  tag_format = "v$version"
129
125
  update_changelog_on_bump = true
@@ -0,0 +1,12 @@
1
+ from experimaestro.cli import cli
2
+
3
+ # flake8: noqa: F401
4
+ import experimaestro.cli.jobs
5
+
6
+
7
+ def main():
8
+ cli(obj=None)
9
+
10
+
11
+ if __name__ == "__main__":
12
+ main()
@@ -1,6 +1,5 @@
1
1
  # flake8: noqa: T201
2
2
  import sys
3
- from types import ModuleType
4
3
  from typing import Set, Optional
5
4
  import pkg_resources
6
5
  from itertools import chain
@@ -10,11 +9,12 @@ import logging
10
9
  from functools import cached_property, update_wrapper
11
10
  from pathlib import Path
12
11
  import subprocess
13
- from termcolor import colored, cprint
12
+ from termcolor import cprint
14
13
 
15
14
  import experimaestro
16
15
  from experimaestro.experiments.cli import experiments_cli
17
16
  import experimaestro.launcherfinder.registry as launcher_registry
17
+ from experimaestro.settings import find_workspace
18
18
 
19
19
  # --- Command line main options
20
20
  logging.basicConfig(level=logging.INFO)
@@ -175,121 +175,6 @@ def diff(path: Path):
175
175
  check(".", job, new_job, set())
176
176
 
177
177
 
178
- @click.argument("path", type=Path, callback=check_xp_path)
179
- @click.option("--experiment", default=None, help="Restrict to this experiment")
180
- @click.option("--tags", is_flag=True, help="Show tags")
181
- @click.option("--ready", is_flag=True, help="Include tasks which are not yet scheduled")
182
- @click.option("--filter", default="", help="Filter expression")
183
- @click.option(
184
- "--force",
185
- is_flag=True,
186
- help="Force operation even if experiment has not been completed yet",
187
- )
188
- @click.option("--kill", is_flag=True, help="Kill filtered tasks (when/before running)")
189
- @click.option("--clean", is_flag=True, help="Remove finished tasks directories")
190
- @cli.command()
191
- def jobs(
192
- path: Path,
193
- experiment: str,
194
- filter: str,
195
- tags: bool,
196
- ready: bool,
197
- kill: bool,
198
- clean: bool,
199
- force: bool,
200
- ):
201
- """Job control: list, kill and clean
202
-
203
- The job filter is a boolean expression where tags (alphanumeric)
204
- and special job information (@state for job state, @name for job full
205
- name) can be compared to a given value (using '~' for regex matching,
206
- '=', 'not in', or 'in')
207
-
208
- For instance,
209
-
210
- model = "bm25" and mode in ["a", b"] and @state = "RUNNING"
211
-
212
- selects jobs where the tag model is "bm25", the tag mode is either
213
- "a" or "b", and the state is running.
214
-
215
- """
216
- for p in (path / "xp").glob("*"):
217
- if experiment and p.name != experiment:
218
- continue
219
-
220
- from .filter import createFilter, JobInformation
221
- from experimaestro.scheduler import JobState
222
-
223
- _filter = createFilter(filter) if filter else lambda x: True
224
-
225
- print(f"* Experiment {p.name}")
226
- if (p / "jobs.bak").is_dir():
227
- cprint(" Experiment has not finished yet", "red")
228
- if not force and (kill or clean):
229
- cprint(" Preventing kill/clean (use --force if you want to)", "yellow")
230
- kill = False
231
- clean = False
232
- print()
233
-
234
- for job in p.glob("jobs/*/*"):
235
- info = None
236
- p = job.resolve()
237
- if p.is_dir():
238
- *_, scriptname = p.parent.name.rsplit(".", 1)
239
-
240
- info = JobInformation(p, scriptname)
241
- if filter:
242
- if not _filter(info):
243
- continue
244
-
245
- if info.state is None:
246
- print(
247
- colored(f"NODIR {job.parent.name}/{job.name}", "red"), end=""
248
- )
249
- elif info.state.running():
250
- if kill:
251
- process = info.getprocess()
252
- print("KILLING", process)
253
- process.kill()
254
- print(
255
- colored(
256
- f"{info.state.name:8}{job.parent.name}/{job.name}", "yellow"
257
- ),
258
- end="",
259
- )
260
- elif info.state == JobState.DONE:
261
- print(
262
- colored(f"DONE {job.parent.name}/{job.name}", "green"),
263
- end="",
264
- )
265
- elif info.state == JobState.ERROR:
266
- print(
267
- colored(f"FAIL {job.parent.name}/{job.name}", "red"), end=""
268
- )
269
- else:
270
- print(
271
- colored(
272
- f"{info.state.name:8}{job.parent.name}/{job.name}", "red"
273
- ),
274
- end="",
275
- )
276
-
277
- else:
278
- if not ready:
279
- continue
280
- print(colored(f"READY {job.parent.name}/{job.name}", "yellow"), end="")
281
-
282
- if tags:
283
- print(f""" {" ".join(f"{k}={v}" for k, v in info.tags.items())}""")
284
- else:
285
- print()
286
-
287
- if clean and info.state and info.state.finished():
288
- cprint("Cleaning...", "red")
289
- rmtree(p)
290
- print()
291
-
292
-
293
178
  @click.option("--show-all", is_flag=True, help="Show even not orphans")
294
179
  @click.option(
295
180
  "--ignore-old", is_flag=True, help="Ignore old jobs for unfinished experiments"
@@ -407,11 +292,14 @@ cli.add_command(Launchers("tokens", help="Token specific commands"))
407
292
 
408
293
 
409
294
  @cli.group()
410
- @click.argument("workdir", type=Path, callback=check_xp_path)
295
+ @click.option("--workdir", type=Path, default=None)
296
+ @click.option("--workspace", type=str, default=None)
411
297
  @click.pass_context
412
- def experiments(ctx, workdir):
298
+ def experiments(ctx, workdir, workspace):
413
299
  """Manage experiments"""
414
- ctx.obj = workdir
300
+ ws = find_workspace(workdir=workdir, workspace=workspace)
301
+ path = check_xp_path(None, None, ws.path)
302
+ ctx.obj = path
415
303
 
416
304
 
417
305
  @experiments.command()
@@ -422,11 +310,3 @@ def list(workdir: Path):
422
310
  cprint(f"[unfinished] {p.name}", "yellow")
423
311
  else:
424
312
  cprint(p.name, "cyan")
425
-
426
-
427
- def main():
428
- cli(obj=None)
429
-
430
-
431
- if __name__ == "__main__":
432
- main()
@@ -22,12 +22,12 @@ class JobInformation:
22
22
 
23
23
  @cached_property
24
24
  def state(self) -> Optional[JobState]:
25
- if (self.path / f"{self.scriptname}.pid").is_file():
26
- return JobState.RUNNING
27
- elif (self.path / f"{self.scriptname}.done").is_file():
25
+ if (self.path / f"{self.scriptname}.done").is_file():
28
26
  return JobState.DONE
29
- elif (self.path / f"{self.scriptname}.failed").is_file():
27
+ if (self.path / f"{self.scriptname}.failed").is_file():
30
28
  return JobState.ERROR
29
+ if (self.path / f"{self.scriptname}.pid").is_file():
30
+ return JobState.RUNNING
31
31
  else:
32
32
  return None
33
33
 
@@ -0,0 +1,251 @@
1
+ # flake8: noqa: T201
2
+ import subprocess
3
+ from typing import Optional
4
+ from shutil import rmtree
5
+ import click
6
+ from pathlib import Path
7
+ from termcolor import colored, cprint
8
+
9
+ from experimaestro.settings import find_workspace
10
+ from . import check_xp_path, cli
11
+
12
+
13
+ @click.option("--workspace", default="", help="Experimaestro workspace")
14
+ @click.option("--workdir", type=Path, default=None)
15
+ @cli.group()
16
+ @click.pass_context
17
+ def jobs(
18
+ ctx,
19
+ workdir: Optional[Path],
20
+ workspace: Optional[str],
21
+ ):
22
+ """Job control: list, kill and clean
23
+
24
+ The job filter is a boolean expression where tags (alphanumeric)
25
+ and special job information (@state for job state, @name for job full
26
+ name) can be compared to a given value (using '~' for regex matching,
27
+ '=', 'not in', or 'in')
28
+
29
+ For instance,
30
+
31
+ model = "bm25" and mode in ["a", b"] and @state = "RUNNING"
32
+
33
+ selects jobs where the tag model is "bm25", the tag mode is either
34
+ "a" or "b", and the state is running.
35
+
36
+ """
37
+ ws = ctx.obj.workspace = find_workspace(workdir=workdir, workspace=workspace)
38
+ check_xp_path(ctx, None, ws.path)
39
+
40
+
41
+ def process(
42
+ workspace,
43
+ *,
44
+ experiment="",
45
+ tags="",
46
+ ready=False,
47
+ clean=False,
48
+ kill=False,
49
+ filter="",
50
+ perform=False,
51
+ fullpath=False,
52
+ ):
53
+ path = workspace.path
54
+ for p in (path / "xp").glob("*"):
55
+ if experiment and p.name != experiment:
56
+ continue
57
+
58
+ from .filter import createFilter, JobInformation
59
+ from experimaestro.scheduler import JobState
60
+
61
+ _filter = createFilter(filter) if filter else lambda x: True
62
+
63
+ print(f"* Experiment {p.name}")
64
+ if (p / "jobs.bak").is_dir():
65
+ cprint(" Experiment has not finished yet", "red")
66
+ if not perform and (kill or clean):
67
+ cprint(" Preventing kill/clean (use --force if you want to)", "yellow")
68
+ kill = False
69
+ clean = False
70
+ print()
71
+
72
+ for job in p.glob("jobs/*/*"):
73
+ info = None
74
+ p = job.resolve()
75
+ if p.is_dir():
76
+ *_, scriptname = p.parent.name.rsplit(".", 1)
77
+
78
+ info = JobInformation(p, scriptname)
79
+ job_path = (
80
+ str(job.resolve()) if fullpath else f"{job.parent.name}/{job.name}"
81
+ )
82
+
83
+ if filter:
84
+ if not _filter(info):
85
+ continue
86
+
87
+ if info.state is None:
88
+ print(colored(f"NODIR {job_path}", "red"), end="")
89
+ elif info.state.running():
90
+ if kill:
91
+ if perform:
92
+ process = info.getprocess()
93
+ if process is None:
94
+ cprint(
95
+ "internal error – no process could be retrieved",
96
+ "red",
97
+ )
98
+ else:
99
+ cprint(f"KILLING {process}", "light_red")
100
+ process.kill()
101
+ else:
102
+ print("KILLING (not performing)", process)
103
+ print(
104
+ colored(f"{info.state.name:8}{job_path}", "yellow"),
105
+ end="",
106
+ )
107
+ elif info.state == JobState.DONE:
108
+ print(
109
+ colored(f"DONE {job_path}", "green"),
110
+ end="",
111
+ )
112
+ elif info.state == JobState.ERROR:
113
+ print(colored(f"FAIL {job_path}", "red"), end="")
114
+ else:
115
+ print(
116
+ colored(f"{info.state.name:8}{job_path}", "red"),
117
+ end="",
118
+ )
119
+
120
+ else:
121
+ if not ready:
122
+ continue
123
+ print(colored(f"READY {job_path}", "yellow"), end="")
124
+
125
+ if tags:
126
+ print(f""" {" ".join(f"{k}={v}" for k, v in info.tags.items())}""")
127
+ else:
128
+ print()
129
+
130
+ if clean and info.state and info.state.finished():
131
+ if perform:
132
+ cprint("Cleaning...", "red")
133
+ rmtree(p)
134
+ else:
135
+ cprint("Cleaning... (not performed)", "red")
136
+ print()
137
+
138
+
139
+ @click.option("--experiment", default=None, help="Restrict to this experiment")
140
+ @click.option("--tags", is_flag=True, help="Show tags")
141
+ @click.option("--ready", is_flag=True, help="Include tasks which are not yet scheduled")
142
+ @click.option("--filter", default="", help="Filter expression")
143
+ @click.option("--fullpath", is_flag=True, help="Prints full paths")
144
+ @jobs.command()
145
+ @click.pass_context
146
+ def list(
147
+ ctx,
148
+ experiment: str,
149
+ filter: str,
150
+ tags: bool,
151
+ ready: bool,
152
+ fullpath: bool,
153
+ ):
154
+ process(
155
+ ctx.obj.workspace,
156
+ experiment=experiment,
157
+ filter=filter,
158
+ tags=tags,
159
+ ready=ready,
160
+ fullpath=fullpath,
161
+ )
162
+
163
+
164
+ @click.option("--experiment", default=None, help="Restrict to this experiment")
165
+ @click.option("--tags", is_flag=True, help="Show tags")
166
+ @click.option("--ready", is_flag=True, help="Include tasks which are not yet scheduled")
167
+ @click.option("--filter", default="", help="Filter expression")
168
+ @click.option("--perform", is_flag=True, help="Really perform the killing")
169
+ @click.option("--fullpath", is_flag=True, help="Prints full paths")
170
+ @jobs.command()
171
+ @click.pass_context
172
+ def kill(
173
+ ctx,
174
+ experiment: str,
175
+ filter: str,
176
+ tags: bool,
177
+ ready: bool,
178
+ fullpath: bool,
179
+ perform: bool,
180
+ ):
181
+ process(
182
+ ctx.obj.workspace,
183
+ experiment=experiment,
184
+ filter=filter,
185
+ tags=tags,
186
+ ready=ready,
187
+ kill=True,
188
+ perform=perform,
189
+ fullpath=fullpath,
190
+ )
191
+
192
+
193
+ @click.option("--experiment", default=None, help="Restrict to this experiment")
194
+ @click.option("--tags", is_flag=True, help="Show tags")
195
+ @click.option("--ready", is_flag=True, help="Include tasks which are not yet scheduled")
196
+ @click.option("--filter", default="", help="Filter expression")
197
+ @click.option("--perform", is_flag=True, help="Really perform the cleaning")
198
+ @click.option("--fullpath", is_flag=True, help="Prints full paths")
199
+ @jobs.command()
200
+ @click.pass_context
201
+ def clean(
202
+ ctx,
203
+ experiment: str,
204
+ filter: str,
205
+ tags: bool,
206
+ ready: bool,
207
+ fullpath: bool,
208
+ perform: bool,
209
+ ):
210
+ process(
211
+ ctx.obj.workspace,
212
+ experiment=experiment,
213
+ filter=filter,
214
+ tags=tags,
215
+ ready=ready,
216
+ clean=True,
217
+ perform=perform,
218
+ fullpath=fullpath,
219
+ )
220
+
221
+
222
+ @click.argument("jobid", type=str)
223
+ @click.option(
224
+ "--follow", "-f", help="Use tail instead of less to follow changes", is_flag=True
225
+ )
226
+ @click.option("--std", help="Follow stdout instead of stderr", is_flag=True)
227
+ @jobs.command()
228
+ @click.pass_context
229
+ def log(ctx, jobid: str, follow: bool, std: bool):
230
+ task_name, task_hash = jobid.split("/")
231
+ _, name = task_name.rsplit(".", 1)
232
+ path = (
233
+ ctx.obj.workspace.path
234
+ / "jobs"
235
+ / task_name
236
+ / task_hash
237
+ / f"""{name}.{'out' if std else 'err'}"""
238
+ )
239
+ if follow:
240
+ subprocess.run(["tail", "-f", path])
241
+ else:
242
+ subprocess.run(["less", "-r", path])
243
+
244
+
245
+ @click.argument("jobid", type=str)
246
+ @jobs.command()
247
+ @click.pass_context
248
+ def path(ctx, jobid: str):
249
+ task_name, task_hash = jobid.split("/")
250
+ path = ctx.obj.workspace.path / "jobs" / task_name / task_hash
251
+ print(path)
@@ -3,7 +3,7 @@ from pathlib import Path, _posix_flavour
3
3
  import io
4
4
  import os
5
5
  import re
6
- from experimaestro.launcherfinder import LauncherRegistry, YAMLDataClass
6
+ from experimaestro.launcherfinder import LauncherRegistry
7
7
  from fabric import Connection
8
8
  from invoke import Promise
9
9
  import invoke.exceptions
@@ -132,7 +132,7 @@ class SshPath(Path):
132
132
 
133
133
 
134
134
  @dataclass
135
- class SshConfiguration(YAMLDataClass):
135
+ class SshConfiguration:
136
136
  hostname: str
137
137
 
138
138
  def create(self, registry: LauncherRegistry):
@@ -916,14 +916,14 @@ class ConfigInformation:
916
916
 
917
917
  # --- Submit the job
918
918
 
919
+ # Sets the init tasks
920
+ self.init_tasks = init_tasks
921
+
919
922
  # Creates a new job
920
923
  self.job = self.xpmtype.task(
921
924
  self.pyobject, launcher=launcher, workspace=workspace, run_mode=run_mode
922
925
  )
923
926
 
924
- # Sets the init tasks
925
- self.init_tasks = init_tasks
926
-
927
927
  # Validate the object
928
928
  job_context = JobContext(self.job)
929
929
  self.validate_and_seal(job_context)
@@ -979,6 +979,9 @@ class ConfigInformation:
979
979
  elif self.job.failedpath.is_file():
980
980
  color = "light_red"
981
981
  cprint(f"[failed] {s}", color, file=sys.stderr)
982
+ elif self.job.pidpath.is_file():
983
+ color = "blue"
984
+ cprint(f"[running] {s}", color, file=sys.stderr)
982
985
  else:
983
986
  color = "light_blue"
984
987
  cprint(f"[not run] {s}", color, file=sys.stderr)
@@ -325,12 +325,17 @@ class ObjectType(Type):
325
325
 
326
326
  # Get the module
327
327
  module = inspect.getmodule(self.originaltype)
328
- if getattr(module, "__file__", None) is None:
328
+ self._module = module.__name__
329
+ self._package = module.__package__
330
+
331
+ if self._module and self._package:
329
332
  self._file = None
330
333
  else:
331
334
  self._file = Path(inspect.getfile(self.originaltype)).absolute()
332
- self._module = module.__name__
333
- self._package = module.__package__
335
+
336
+ assert (
337
+ self._module and self._package
338
+ ) or self._file, f"Could not detect module/file for {self.originaltype}"
334
339
 
335
340
  # The class of the object
336
341
 
@@ -1,26 +1,28 @@
1
+ import imp
1
2
  import inspect
2
3
  import json
3
4
  import logging
4
5
  import sys
5
6
  from pathlib import Path
6
- from typing import Any, List, Optional, Protocol, Tuple, Dict
7
+ from typing import Any, List, Optional, Protocol, Tuple
7
8
 
8
9
  import click
9
10
  import omegaconf
10
11
  import yaml
11
- from experimaestro import LauncherRegistry, RunMode, experiment
12
- from experimaestro.experiments.configuration import ConfigurationBase
13
- from experimaestro.exceptions import HandledException
14
- from experimaestro.settings import get_workspace
15
12
  from omegaconf import OmegaConf, SCMode
16
13
  from termcolor import cprint
17
14
 
15
+ from experimaestro import LauncherRegistry, RunMode, experiment
16
+ from experimaestro.exceptions import HandledException
17
+ from experimaestro.experiments.configuration import ConfigurationBase
18
+ from experimaestro.settings import find_workspace
19
+
18
20
 
19
21
  class ExperimentHelper:
20
22
  """Helper for experiments"""
21
23
 
22
- # The experiment
23
24
  xp: experiment
25
+ """The experiment object"""
24
26
 
25
27
  #: Run function
26
28
  callable: "ExperimentCallable"
@@ -175,28 +177,23 @@ def experiments_cli( # noqa: C901
175
177
  xp_file = Path(xp_file)
176
178
  if not xp_file.exists() and xp_file.suffix != ".py":
177
179
  xp_file = xp_file.with_suffix(".py")
178
- xp_file = Path(yaml_file).parent / xp_file
179
-
180
- with open(xp_file, "r") as f:
181
- source = f.read()
182
- if sys.version_info < (3, 9):
183
- the__file__ = str(xp_file)
184
- else:
185
- the__file__ = str(xp_file.absolute())
186
-
187
- code = compile(source, filename=the__file__, mode="exec")
188
- _locals: Dict[str, Any] = {}
180
+ xp_file: Path = Path(yaml_file).parent / xp_file
189
181
 
190
- sys.path.append(str(xp_file.parent.absolute()))
182
+ # --- Finds the "run" function
191
183
  try:
192
- exec(code, _locals, _locals)
184
+ sys.path.append(str(xp_file.parent.absolute()))
185
+ with open(xp_file) as src:
186
+ module_name = xp_file.with_suffix("").name
187
+ mod = imp.load_module(
188
+ module_name, src, str(xp_file.absolute()), (".py", "r", imp.PY_SOURCE)
189
+ )
190
+ helper = getattr(mod, "run", None)
193
191
  finally:
194
192
  sys.path.pop()
195
193
 
196
194
  # --- ... and runs it
197
- helper = _locals.get("run", None)
198
195
  if helper is None:
199
- raise ValueError(f"Could not find run function in {the__file__}")
196
+ raise ValueError(f"Could not find run function in {xp_file}")
200
197
 
201
198
  if not isinstance(helper, ExperimentHelper):
202
199
  helper = ExperimentHelper(helper)
@@ -231,27 +228,8 @@ def experiments_cli( # noqa: C901
231
228
  )
232
229
 
233
230
  # Define the workspace
234
- workdir = Path(workdir) if workdir else None
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)
231
+ ws_env = find_workspace(workdir=workdir, workspace=workspace)
232
+ workdir = ws_env.path
255
233
 
256
234
  logging.info("Using working directory %s", str(workdir.resolve()))
257
235
 
@@ -10,5 +10,5 @@ from .specs import (
10
10
  HostRequirement,
11
11
  MatchRequirement,
12
12
  )
13
- from .registry import find_launcher, LauncherRegistry, YAMLDataClass
13
+ from .registry import find_launcher, LauncherRegistry
14
14
  from .parser import parse
@@ -0,0 +1,17 @@
1
+ from typing import TYPE_CHECKING
2
+
3
+
4
+ if TYPE_CHECKING:
5
+ from experimaestro.connectors import Connector
6
+ from experimaestro.tokens import Token
7
+ from .registry import LauncherRegistry
8
+
9
+
10
+ class ConnectorConfiguration:
11
+ def create(self, registry: "LauncherRegistry") -> "Connector":
12
+ raise NotImplementedError(f"For {self.__class__}")
13
+
14
+
15
+ class TokenConfiguration:
16
+ def create(self, registry: "LauncherRegistry", identifier: str) -> "Token":
17
+ raise NotImplementedError(f"For {self.__class__}")