experimaestro 1.7.1__tar.gz → 1.8.0rc0__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 (155) hide show
  1. {experimaestro-1.7.1 → experimaestro-1.8.0rc0}/PKG-INFO +1 -1
  2. {experimaestro-1.7.1 → experimaestro-1.8.0rc0}/pyproject.toml +2 -2
  3. {experimaestro-1.7.1 → experimaestro-1.8.0rc0}/src/experimaestro/__init__.py +2 -1
  4. {experimaestro-1.7.1 → experimaestro-1.8.0rc0}/src/experimaestro/core/context.py +3 -9
  5. {experimaestro-1.7.1 → experimaestro-1.8.0rc0}/src/experimaestro/core/objects.py +2 -2
  6. {experimaestro-1.7.1 → experimaestro-1.8.0rc0}/src/experimaestro/core/serialization.py +9 -6
  7. {experimaestro-1.7.1 → experimaestro-1.8.0rc0}/src/experimaestro/experiments/cli.py +31 -9
  8. {experimaestro-1.7.1 → experimaestro-1.8.0rc0}/src/experimaestro/scheduler/base.py +9 -0
  9. experimaestro-1.8.0rc0/src/experimaestro/scheduler/state.py +75 -0
  10. experimaestro-1.8.0rc0/src/experimaestro/tests/test_experiment.py +50 -0
  11. {experimaestro-1.7.1 → experimaestro-1.8.0rc0}/src/experimaestro/typingutils.py +1 -8
  12. {experimaestro-1.7.1 → experimaestro-1.8.0rc0}/LICENSE +0 -0
  13. {experimaestro-1.7.1 → experimaestro-1.8.0rc0}/README.md +0 -0
  14. {experimaestro-1.7.1 → experimaestro-1.8.0rc0}/src/experimaestro/__main__.py +0 -0
  15. {experimaestro-1.7.1 → experimaestro-1.8.0rc0}/src/experimaestro/annotations.py +0 -0
  16. {experimaestro-1.7.1 → experimaestro-1.8.0rc0}/src/experimaestro/checkers.py +0 -0
  17. {experimaestro-1.7.1 → experimaestro-1.8.0rc0}/src/experimaestro/cli/__init__.py +0 -0
  18. {experimaestro-1.7.1 → experimaestro-1.8.0rc0}/src/experimaestro/cli/filter.py +0 -0
  19. {experimaestro-1.7.1 → experimaestro-1.8.0rc0}/src/experimaestro/cli/jobs.py +0 -0
  20. {experimaestro-1.7.1 → experimaestro-1.8.0rc0}/src/experimaestro/click.py +0 -0
  21. {experimaestro-1.7.1 → experimaestro-1.8.0rc0}/src/experimaestro/commandline.py +0 -0
  22. {experimaestro-1.7.1 → experimaestro-1.8.0rc0}/src/experimaestro/compat.py +0 -0
  23. {experimaestro-1.7.1 → experimaestro-1.8.0rc0}/src/experimaestro/connectors/__init__.py +0 -0
  24. {experimaestro-1.7.1 → experimaestro-1.8.0rc0}/src/experimaestro/connectors/local.py +0 -0
  25. {experimaestro-1.7.1 → experimaestro-1.8.0rc0}/src/experimaestro/connectors/ssh.py +0 -0
  26. {experimaestro-1.7.1 → experimaestro-1.8.0rc0}/src/experimaestro/core/__init__.py +0 -0
  27. {experimaestro-1.7.1 → experimaestro-1.8.0rc0}/src/experimaestro/core/arguments.py +0 -0
  28. {experimaestro-1.7.1 → experimaestro-1.8.0rc0}/src/experimaestro/core/objects.pyi +0 -0
  29. {experimaestro-1.7.1 → experimaestro-1.8.0rc0}/src/experimaestro/core/serializers.py +0 -0
  30. {experimaestro-1.7.1 → experimaestro-1.8.0rc0}/src/experimaestro/core/types.py +0 -0
  31. {experimaestro-1.7.1 → experimaestro-1.8.0rc0}/src/experimaestro/core/utils.py +0 -0
  32. {experimaestro-1.7.1 → experimaestro-1.8.0rc0}/src/experimaestro/exceptions.py +0 -0
  33. {experimaestro-1.7.1 → experimaestro-1.8.0rc0}/src/experimaestro/experiments/__init__.py +0 -0
  34. {experimaestro-1.7.1 → experimaestro-1.8.0rc0}/src/experimaestro/experiments/configuration.py +0 -0
  35. {experimaestro-1.7.1 → experimaestro-1.8.0rc0}/src/experimaestro/generators.py +0 -0
  36. {experimaestro-1.7.1 → experimaestro-1.8.0rc0}/src/experimaestro/huggingface.py +0 -0
  37. {experimaestro-1.7.1 → experimaestro-1.8.0rc0}/src/experimaestro/ipc.py +0 -0
  38. {experimaestro-1.7.1 → experimaestro-1.8.0rc0}/src/experimaestro/launcherfinder/__init__.py +0 -0
  39. {experimaestro-1.7.1 → experimaestro-1.8.0rc0}/src/experimaestro/launcherfinder/base.py +0 -0
  40. {experimaestro-1.7.1 → experimaestro-1.8.0rc0}/src/experimaestro/launcherfinder/parser.py +0 -0
  41. {experimaestro-1.7.1 → experimaestro-1.8.0rc0}/src/experimaestro/launcherfinder/registry.py +0 -0
  42. {experimaestro-1.7.1 → experimaestro-1.8.0rc0}/src/experimaestro/launcherfinder/specs.py +0 -0
  43. {experimaestro-1.7.1 → experimaestro-1.8.0rc0}/src/experimaestro/launchers/__init__.py +0 -0
  44. {experimaestro-1.7.1 → experimaestro-1.8.0rc0}/src/experimaestro/launchers/direct.py +0 -0
  45. {experimaestro-1.7.1 → experimaestro-1.8.0rc0}/src/experimaestro/launchers/oar.py +0 -0
  46. {experimaestro-1.7.1 → experimaestro-1.8.0rc0}/src/experimaestro/launchers/slurm/__init__.py +0 -0
  47. {experimaestro-1.7.1 → experimaestro-1.8.0rc0}/src/experimaestro/launchers/slurm/base.py +0 -0
  48. {experimaestro-1.7.1 → experimaestro-1.8.0rc0}/src/experimaestro/locking.py +0 -0
  49. {experimaestro-1.7.1 → experimaestro-1.8.0rc0}/src/experimaestro/mkdocs/__init__.py +0 -0
  50. {experimaestro-1.7.1 → experimaestro-1.8.0rc0}/src/experimaestro/mkdocs/annotations.py +0 -0
  51. {experimaestro-1.7.1 → experimaestro-1.8.0rc0}/src/experimaestro/mkdocs/base.py +0 -0
  52. {experimaestro-1.7.1 → experimaestro-1.8.0rc0}/src/experimaestro/mkdocs/metaloader.py +0 -0
  53. {experimaestro-1.7.1 → experimaestro-1.8.0rc0}/src/experimaestro/mkdocs/style.css +0 -0
  54. {experimaestro-1.7.1 → experimaestro-1.8.0rc0}/src/experimaestro/mypy.py +0 -0
  55. {experimaestro-1.7.1 → experimaestro-1.8.0rc0}/src/experimaestro/notifications.py +0 -0
  56. {experimaestro-1.7.1 → experimaestro-1.8.0rc0}/src/experimaestro/py.typed +0 -0
  57. {experimaestro-1.7.1 → experimaestro-1.8.0rc0}/src/experimaestro/rpyc.py +0 -0
  58. {experimaestro-1.7.1 → experimaestro-1.8.0rc0}/src/experimaestro/run.py +0 -0
  59. {experimaestro-1.7.1 → experimaestro-1.8.0rc0}/src/experimaestro/scheduler/__init__.py +0 -0
  60. {experimaestro-1.7.1 → experimaestro-1.8.0rc0}/src/experimaestro/scheduler/dependencies.py +0 -0
  61. {experimaestro-1.7.1 → experimaestro-1.8.0rc0}/src/experimaestro/scheduler/dynamic_outputs.py +0 -0
  62. {experimaestro-1.7.1 → experimaestro-1.8.0rc0}/src/experimaestro/scheduler/services.py +0 -0
  63. {experimaestro-1.7.1 → experimaestro-1.8.0rc0}/src/experimaestro/scheduler/workspace.py +0 -0
  64. {experimaestro-1.7.1 → experimaestro-1.8.0rc0}/src/experimaestro/scriptbuilder.py +0 -0
  65. {experimaestro-1.7.1 → experimaestro-1.8.0rc0}/src/experimaestro/server/__init__.py +0 -0
  66. {experimaestro-1.7.1 → experimaestro-1.8.0rc0}/src/experimaestro/server/data/016b4a6cdced82ab3aa1.ttf +0 -0
  67. {experimaestro-1.7.1 → experimaestro-1.8.0rc0}/src/experimaestro/server/data/0c35d18bf06992036b69.woff2 +0 -0
  68. {experimaestro-1.7.1 → experimaestro-1.8.0rc0}/src/experimaestro/server/data/1815e00441357e01619e.ttf +0 -0
  69. {experimaestro-1.7.1 → experimaestro-1.8.0rc0}/src/experimaestro/server/data/219aa9140e099e6c72ed.woff2 +0 -0
  70. {experimaestro-1.7.1 → experimaestro-1.8.0rc0}/src/experimaestro/server/data/2463b90d9a316e4e5294.woff2 +0 -0
  71. {experimaestro-1.7.1 → experimaestro-1.8.0rc0}/src/experimaestro/server/data/2582b0e4bcf85eceead0.ttf +0 -0
  72. {experimaestro-1.7.1 → experimaestro-1.8.0rc0}/src/experimaestro/server/data/3a4004a46a653d4b2166.woff +0 -0
  73. {experimaestro-1.7.1 → experimaestro-1.8.0rc0}/src/experimaestro/server/data/3baa5b8f3469222b822d.woff +0 -0
  74. {experimaestro-1.7.1 → experimaestro-1.8.0rc0}/src/experimaestro/server/data/4d73cb90e394b34b7670.woff +0 -0
  75. {experimaestro-1.7.1 → experimaestro-1.8.0rc0}/src/experimaestro/server/data/4ef4218c522f1eb6b5b1.woff2 +0 -0
  76. {experimaestro-1.7.1 → experimaestro-1.8.0rc0}/src/experimaestro/server/data/50701fbb8177c2dde530.ttf +0 -0
  77. {experimaestro-1.7.1 → experimaestro-1.8.0rc0}/src/experimaestro/server/data/5d681e2edae8c60630db.woff +0 -0
  78. {experimaestro-1.7.1 → experimaestro-1.8.0rc0}/src/experimaestro/server/data/6f420cf17cc0d7676fad.woff2 +0 -0
  79. {experimaestro-1.7.1 → experimaestro-1.8.0rc0}/src/experimaestro/server/data/878f31251d960bd6266f.woff2 +0 -0
  80. {experimaestro-1.7.1 → experimaestro-1.8.0rc0}/src/experimaestro/server/data/89999bdf5d835c012025.woff2 +0 -0
  81. {experimaestro-1.7.1 → experimaestro-1.8.0rc0}/src/experimaestro/server/data/914997e1bdfc990d0897.ttf +0 -0
  82. {experimaestro-1.7.1 → experimaestro-1.8.0rc0}/src/experimaestro/server/data/b041b1fa4fe241b23445.woff2 +0 -0
  83. {experimaestro-1.7.1 → experimaestro-1.8.0rc0}/src/experimaestro/server/data/b6879d41b0852f01ed5b.woff2 +0 -0
  84. {experimaestro-1.7.1 → experimaestro-1.8.0rc0}/src/experimaestro/server/data/c210719e60948b211a12.woff2 +0 -0
  85. {experimaestro-1.7.1 → experimaestro-1.8.0rc0}/src/experimaestro/server/data/c380809fd3677d7d6903.woff2 +0 -0
  86. {experimaestro-1.7.1 → experimaestro-1.8.0rc0}/src/experimaestro/server/data/d75e3fd1eb12e9bd6655.ttf +0 -0
  87. {experimaestro-1.7.1 → experimaestro-1.8.0rc0}/src/experimaestro/server/data/f882956fd323fd322f31.woff +0 -0
  88. {experimaestro-1.7.1 → experimaestro-1.8.0rc0}/src/experimaestro/server/data/favicon.ico +0 -0
  89. {experimaestro-1.7.1 → experimaestro-1.8.0rc0}/src/experimaestro/server/data/index.css +0 -0
  90. {experimaestro-1.7.1 → experimaestro-1.8.0rc0}/src/experimaestro/server/data/index.css.map +0 -0
  91. {experimaestro-1.7.1 → experimaestro-1.8.0rc0}/src/experimaestro/server/data/index.html +0 -0
  92. {experimaestro-1.7.1 → experimaestro-1.8.0rc0}/src/experimaestro/server/data/index.js +0 -0
  93. {experimaestro-1.7.1 → experimaestro-1.8.0rc0}/src/experimaestro/server/data/index.js.map +0 -0
  94. {experimaestro-1.7.1 → experimaestro-1.8.0rc0}/src/experimaestro/server/data/login.html +0 -0
  95. {experimaestro-1.7.1 → experimaestro-1.8.0rc0}/src/experimaestro/server/data/manifest.json +0 -0
  96. {experimaestro-1.7.1 → experimaestro-1.8.0rc0}/src/experimaestro/settings.py +0 -0
  97. {experimaestro-1.7.1 → experimaestro-1.8.0rc0}/src/experimaestro/sphinx/__init__.py +0 -0
  98. {experimaestro-1.7.1 → experimaestro-1.8.0rc0}/src/experimaestro/sphinx/static/experimaestro.css +0 -0
  99. {experimaestro-1.7.1 → experimaestro-1.8.0rc0}/src/experimaestro/taskglobals.py +0 -0
  100. {experimaestro-1.7.1 → experimaestro-1.8.0rc0}/src/experimaestro/tests/__init__.py +0 -0
  101. {experimaestro-1.7.1 → experimaestro-1.8.0rc0}/src/experimaestro/tests/conftest.py +0 -0
  102. {experimaestro-1.7.1 → experimaestro-1.8.0rc0}/src/experimaestro/tests/connectors/bin/executable.py +0 -0
  103. {experimaestro-1.7.1 → experimaestro-1.8.0rc0}/src/experimaestro/tests/connectors/test_local.py +0 -0
  104. {experimaestro-1.7.1 → experimaestro-1.8.0rc0}/src/experimaestro/tests/connectors/utils.py +0 -0
  105. {experimaestro-1.7.1 → experimaestro-1.8.0rc0}/src/experimaestro/tests/definitions_types.py +0 -0
  106. {experimaestro-1.7.1 → experimaestro-1.8.0rc0}/src/experimaestro/tests/launchers/__init__.py +0 -0
  107. {experimaestro-1.7.1 → experimaestro-1.8.0rc0}/src/experimaestro/tests/launchers/bin/sacct +0 -0
  108. {experimaestro-1.7.1 → experimaestro-1.8.0rc0}/src/experimaestro/tests/launchers/bin/sbatch +0 -0
  109. {experimaestro-1.7.1 → experimaestro-1.8.0rc0}/src/experimaestro/tests/launchers/bin/srun +0 -0
  110. {experimaestro-1.7.1 → experimaestro-1.8.0rc0}/src/experimaestro/tests/launchers/bin/test.py +0 -0
  111. {experimaestro-1.7.1 → experimaestro-1.8.0rc0}/src/experimaestro/tests/launchers/common.py +0 -0
  112. {experimaestro-1.7.1 → experimaestro-1.8.0rc0}/src/experimaestro/tests/launchers/config_slurm/__init__.py +0 -0
  113. {experimaestro-1.7.1 → experimaestro-1.8.0rc0}/src/experimaestro/tests/launchers/config_slurm/launchers.py +0 -0
  114. {experimaestro-1.7.1 → experimaestro-1.8.0rc0}/src/experimaestro/tests/launchers/test_local.py +0 -0
  115. {experimaestro-1.7.1 → experimaestro-1.8.0rc0}/src/experimaestro/tests/launchers/test_slurm.py +0 -0
  116. {experimaestro-1.7.1 → experimaestro-1.8.0rc0}/src/experimaestro/tests/restart.py +0 -0
  117. {experimaestro-1.7.1 → experimaestro-1.8.0rc0}/src/experimaestro/tests/restart_main.py +0 -0
  118. {experimaestro-1.7.1 → experimaestro-1.8.0rc0}/src/experimaestro/tests/scripts/notifyandwait.py +0 -0
  119. {experimaestro-1.7.1 → experimaestro-1.8.0rc0}/src/experimaestro/tests/scripts/waitforfile.py +0 -0
  120. {experimaestro-1.7.1 → experimaestro-1.8.0rc0}/src/experimaestro/tests/task_tokens.py +0 -0
  121. {experimaestro-1.7.1 → experimaestro-1.8.0rc0}/src/experimaestro/tests/tasks/__init__.py +0 -0
  122. {experimaestro-1.7.1 → experimaestro-1.8.0rc0}/src/experimaestro/tests/tasks/all.py +0 -0
  123. {experimaestro-1.7.1 → experimaestro-1.8.0rc0}/src/experimaestro/tests/tasks/foreign.py +0 -0
  124. {experimaestro-1.7.1 → experimaestro-1.8.0rc0}/src/experimaestro/tests/test_checkers.py +0 -0
  125. {experimaestro-1.7.1 → experimaestro-1.8.0rc0}/src/experimaestro/tests/test_dependencies.py +0 -0
  126. {experimaestro-1.7.1 → experimaestro-1.8.0rc0}/src/experimaestro/tests/test_findlauncher.py +0 -0
  127. {experimaestro-1.7.1 → experimaestro-1.8.0rc0}/src/experimaestro/tests/test_forward.py +0 -0
  128. {experimaestro-1.7.1 → experimaestro-1.8.0rc0}/src/experimaestro/tests/test_identifier.py +0 -0
  129. {experimaestro-1.7.1 → experimaestro-1.8.0rc0}/src/experimaestro/tests/test_instance.py +0 -0
  130. {experimaestro-1.7.1 → experimaestro-1.8.0rc0}/src/experimaestro/tests/test_objects.py +0 -0
  131. {experimaestro-1.7.1 → experimaestro-1.8.0rc0}/src/experimaestro/tests/test_outputs.py +0 -0
  132. {experimaestro-1.7.1 → experimaestro-1.8.0rc0}/src/experimaestro/tests/test_param.py +0 -0
  133. {experimaestro-1.7.1 → experimaestro-1.8.0rc0}/src/experimaestro/tests/test_progress.py +0 -0
  134. {experimaestro-1.7.1 → experimaestro-1.8.0rc0}/src/experimaestro/tests/test_serializers.py +0 -0
  135. {experimaestro-1.7.1 → experimaestro-1.8.0rc0}/src/experimaestro/tests/test_snippets.py +0 -0
  136. {experimaestro-1.7.1 → experimaestro-1.8.0rc0}/src/experimaestro/tests/test_ssh.py +0 -0
  137. {experimaestro-1.7.1 → experimaestro-1.8.0rc0}/src/experimaestro/tests/test_tags.py +0 -0
  138. {experimaestro-1.7.1 → experimaestro-1.8.0rc0}/src/experimaestro/tests/test_tasks.py +0 -0
  139. {experimaestro-1.7.1 → experimaestro-1.8.0rc0}/src/experimaestro/tests/test_tokens.py +0 -0
  140. {experimaestro-1.7.1 → experimaestro-1.8.0rc0}/src/experimaestro/tests/test_types.py +0 -0
  141. {experimaestro-1.7.1 → experimaestro-1.8.0rc0}/src/experimaestro/tests/test_validation.py +0 -0
  142. {experimaestro-1.7.1 → experimaestro-1.8.0rc0}/src/experimaestro/tests/token_reschedule.py +0 -0
  143. {experimaestro-1.7.1 → experimaestro-1.8.0rc0}/src/experimaestro/tests/utils.py +0 -0
  144. {experimaestro-1.7.1 → experimaestro-1.8.0rc0}/src/experimaestro/tokens.py +0 -0
  145. {experimaestro-1.7.1 → experimaestro-1.8.0rc0}/src/experimaestro/tools/__init__.py +0 -0
  146. {experimaestro-1.7.1 → experimaestro-1.8.0rc0}/src/experimaestro/tools/diff.py +0 -0
  147. {experimaestro-1.7.1 → experimaestro-1.8.0rc0}/src/experimaestro/tools/documentation.py +0 -0
  148. {experimaestro-1.7.1 → experimaestro-1.8.0rc0}/src/experimaestro/tools/jobs.py +0 -0
  149. {experimaestro-1.7.1 → experimaestro-1.8.0rc0}/src/experimaestro/utils/__init__.py +0 -0
  150. {experimaestro-1.7.1 → experimaestro-1.8.0rc0}/src/experimaestro/utils/asyncio.py +0 -0
  151. {experimaestro-1.7.1 → experimaestro-1.8.0rc0}/src/experimaestro/utils/jobs.py +0 -0
  152. {experimaestro-1.7.1 → experimaestro-1.8.0rc0}/src/experimaestro/utils/jupyter.py +0 -0
  153. {experimaestro-1.7.1 → experimaestro-1.8.0rc0}/src/experimaestro/utils/resources.py +0 -0
  154. {experimaestro-1.7.1 → experimaestro-1.8.0rc0}/src/experimaestro/utils/settings.py +0 -0
  155. {experimaestro-1.7.1 → experimaestro-1.8.0rc0}/src/experimaestro/xpmutils.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: experimaestro
3
- Version: 1.7.1
3
+ Version: 1.8.0rc0
4
4
  Summary: "Experimaestro is a computer science experiment manager"
5
5
  License: GPL-3
6
6
  Keywords: experiment manager
@@ -20,7 +20,7 @@ include = [
20
20
  "src/experimaestro/mkdocs/style.css",
21
21
  { path="src/experimaestro/server/data/*", format=['sdist', 'wheel']}
22
22
  ]
23
- version = "1.7.1"
23
+ version = "1.8.0-rc0"
24
24
  repository = "https://github.com/experimaestro/experimaestro-python"
25
25
  documentation = "https://experimaestro-python.readthedocs.io/"
26
26
 
@@ -122,7 +122,7 @@ warn_unused_ignores = true
122
122
 
123
123
  [tool.commitizen]
124
124
  name = "cz_conventional_commits"
125
- version = "1.7.1"
125
+ version = "1.8.0rc0"
126
126
  changelog_start_rev = "v1.0.0"
127
127
  tag_format = "v$major.$minor.$patch$prerelease"
128
128
  # update_changelog_on_bump = true
@@ -55,8 +55,9 @@ from .core.context import SerializationContext
55
55
  from .core.serializers import SerializationLWTask, PathSerializationLWTask
56
56
  from .core.types import Any, SubmitHook
57
57
  from .launchers import Launcher
58
- from .scheduler.workspace import Workspace, RunMode
59
58
  from .scheduler import Scheduler, experiment, FailedExperiment
59
+ from .scheduler.workspace import Workspace, RunMode
60
+ from .scheduler.state import get_experiment
60
61
  from .notifications import progress, tqdm
61
62
  from .checkers import Choices
62
63
  from .xpmutils import DirectoryContext
@@ -1,22 +1,16 @@
1
1
  from contextlib import contextmanager
2
- from pathlib import Path
2
+ from pathlib import Path, UnsupportedOperation
3
3
  import shutil
4
4
  from typing import List, Optional, Protocol, Set, Union
5
5
  import os
6
- import sys
7
-
8
- has_hardlink_to = sys.version_info.major == 3 and sys.version_info.minor >= 10
9
6
 
10
7
 
11
8
  def shallow_copy(src_path: Path, dest_path: Path):
12
9
  """Copy a directory or file, trying to use hard links if possible"""
13
10
  if src_path.is_file():
14
11
  try:
15
- if has_hardlink_to:
16
- dest_path.hardlink_to(src_path)
17
- else:
18
- dest_path.link_to(src_path)
19
- except OSError:
12
+ dest_path.hardlink_to(src_path)
13
+ except (NotImplementedError, UnsupportedOperation, OSError):
20
14
  shutil.copy(src_path, dest_path)
21
15
  else:
22
16
  if dest_path.exists():
@@ -1079,7 +1079,7 @@ class ConfigInformation:
1079
1079
  if value is None:
1080
1080
  return None
1081
1081
 
1082
- elif isinstance(value, list):
1082
+ elif isinstance(value, (list, tuple)):
1083
1083
  return [ConfigInformation._outputjsonvalue(el, context) for el in value]
1084
1084
 
1085
1085
  elif isinstance(value, dict):
@@ -1196,7 +1196,7 @@ class ConfigInformation:
1196
1196
  configurations if necessary"""
1197
1197
  if isinstance(value, Config):
1198
1198
  value.__xpm__.__get_objects__(objects, context)
1199
- elif isinstance(value, list):
1199
+ elif isinstance(value, (list, tuple)):
1200
1200
  for el in value:
1201
1201
  ConfigInformation.__collect_objects__(el, objects, context)
1202
1202
  elif isinstance(value, dict):
@@ -1,6 +1,6 @@
1
1
  import json
2
2
  from pathlib import Path
3
- from typing import Any, Dict, List, Tuple, Union, TYPE_CHECKING
3
+ from typing import Any, Dict, List, Optional, Tuple, Union, TYPE_CHECKING
4
4
  from experimaestro.core.context import (
5
5
  SerializationContext,
6
6
  SerializedPath,
@@ -41,17 +41,20 @@ def state_dict(context: SerializationContext, obj: Any):
41
41
  return {"objects": objects, "data": data}
42
42
 
43
43
 
44
- def save(obj: Any, save_directory: Path):
44
+ def save_definition(obj: Any, context: SerializationContext, path: Path):
45
+ data = state_dict(context, obj)
46
+ with path.open("wt") as out:
47
+ json.dump(data, out)
48
+
49
+
50
+ def save(obj: Any, save_directory: Optional[Path]):
45
51
  """Saves an object into a disk file
46
52
 
47
53
  :param save_directory: The directory in which the object and its data will
48
54
  be saved (by default, the object is saved in "definition.json")
49
55
  """
50
56
  context = SerializationContext(save_directory=save_directory)
51
-
52
- data = state_dict(context, obj)
53
- with (save_directory / "definition.json").open("wt") as out:
54
- json.dump(data, out)
57
+ save_definition(obj, context, save_directory / "definition.json")
55
58
 
56
59
 
57
60
  def get_data_loader(path: Union[str, Path, SerializedPathLoader]):
@@ -59,6 +59,7 @@ class ConfigurationLoader:
59
59
  def __init__(self):
60
60
  self.yamls = []
61
61
  self.python_path = set()
62
+ self.yaml_module_file: None | Path = None
62
63
 
63
64
  def load(self, yaml_file: Path):
64
65
  """Loads a YAML file, and parents one if they exist"""
@@ -73,6 +74,10 @@ class ConfigurationLoader:
73
74
  if not path.is_absolute():
74
75
  _data["file"] = str((yaml_file.parent / path).resolve())
75
76
 
77
+ if "module" in _data:
78
+ # Keeps track of the YAML file where the module was defined
79
+ self.yaml_module_file = yaml_file
80
+
76
81
  if parent := _data.get("parent", None):
77
82
  self.load(yaml_file.parent / parent)
78
83
 
@@ -121,7 +126,10 @@ class ConfigurationLoader:
121
126
  help="Port for monitoring (can be defined in the settings.yaml file)",
122
127
  )
123
128
  @click.option(
124
- "--file", "xp_file", help="The file containing the main experimental code"
129
+ "--file",
130
+ "xp_file",
131
+ type=Path,
132
+ help="The file containing the main experimental code",
125
133
  )
126
134
  @click.option(
127
135
  "--module-name", "module_name", help="Module containing the experimental code"
@@ -202,8 +210,6 @@ def experiments_cli( # noqa: C901
202
210
  logging.info(
203
211
  "Using python path: %s", ", ".join(str(s) for s in python_path)
204
212
  )
205
- else:
206
- xp_file = Path(xp_file)
207
213
 
208
214
  assert (
209
215
  module_name or xp_file
@@ -221,6 +227,19 @@ def experiments_cli( # noqa: C901
221
227
  for path in python_path:
222
228
  sys.path.append(str(path))
223
229
 
230
+ # --- Adds automatically the experiment module if not found
231
+ if module_name and conf_loader.yaml_module_file:
232
+ try:
233
+ importlib.import_module(module_name)
234
+ except ModuleNotFoundError:
235
+ # Try to setup a path
236
+ path = conf_loader.yaml_module_file.resolve()
237
+ for _ in range(len(module_name.split("."))):
238
+ path = path.parent
239
+
240
+ logging.info("Appending %s to python path", path)
241
+ sys.path.append(str(path))
242
+
224
243
  if xp_file:
225
244
  if not xp_file.exists() and xp_file.suffix != ".py":
226
245
  xp_file = xp_file.with_suffix(".py")
@@ -243,17 +262,20 @@ def experiments_cli( # noqa: C901
243
262
 
244
263
  # --- ... and runs it
245
264
  if helper is None:
246
- raise ValueError(f"Could not find run function in {xp_file}")
265
+ raise click.ClickException(
266
+ f"Could not find run function in {xp_file if xp_file else module_name}"
267
+ )
247
268
 
248
269
  if not isinstance(helper, ExperimentHelper):
249
270
  helper = ExperimentHelper(helper)
250
271
 
251
272
  parameters = inspect.signature(helper.callable).parameters
252
273
  list_parameters = list(parameters.values())
253
- assert len(list_parameters) == 2, (
254
- "Callable function should only "
255
- f"have two arguments (got {len(list_parameters)})"
256
- )
274
+ if len(list_parameters) != 2:
275
+ raise click.ClickException(
276
+ f"run in {xp_file if xp_file else module_name} function should only "
277
+ f"have two arguments (got {len(list_parameters)}), "
278
+ )
257
279
 
258
280
  schema = list_parameters[1].annotation
259
281
  omegaconf_schema = OmegaConf.structured(schema())
@@ -263,7 +285,7 @@ def experiments_cli( # noqa: C901
263
285
  configuration = OmegaConf.merge(omegaconf_schema, configuration)
264
286
  except omegaconf.errors.ConfigKeyError as e:
265
287
  cprint(f"Error in configuration:\n\n{e}", "red", file=sys.stderr)
266
- sys.exit(1)
288
+ raise click.ClickException("Error in configuration")
267
289
 
268
290
  if show:
269
291
  print(json.dumps(OmegaConf.to_container(configuration))) # noqa: T201
@@ -1057,6 +1057,15 @@ class experiment:
1057
1057
  logger.info("Stopping web server")
1058
1058
  self.server.stop()
1059
1059
 
1060
+ if self.workspace.run_mode == RunMode.NORMAL:
1061
+ # Write the state
1062
+ logging.info("Saving the experiment state")
1063
+ from experimaestro.scheduler.state import ExperimentState
1064
+
1065
+ ExperimentState.save(
1066
+ self.workdir / "state.json", self.scheduler.jobs.values()
1067
+ )
1068
+
1060
1069
  async def update_task_output_count(self, delta: int):
1061
1070
  """Change in the number of task outputs to process"""
1062
1071
  async with self.central.exitCondition:
@@ -0,0 +1,75 @@
1
+ from dataclasses import dataclass, field
2
+ import json
3
+ from pathlib import Path
4
+ from typing import Iterable, Optional, Type
5
+ from experimaestro import Task
6
+
7
+ from experimaestro.core.context import SerializationContext
8
+ from experimaestro.scheduler.base import Job, JobDependency
9
+ from experimaestro.settings import find_workspace
10
+ from experimaestro.core.serialization import from_state_dict, save_definition
11
+
12
+
13
+ @dataclass
14
+ class JobInformation:
15
+ id: str
16
+ path: Path
17
+ task: Task
18
+ tags: dict[str, str]
19
+ depends_on: list["JobInformation"] = field(default_factory=list)
20
+
21
+ def __post_init__(self):
22
+ self.path = Path(self.path)
23
+
24
+
25
+ class ExperimentState:
26
+ def __init__(self, workdir: Path, name: str):
27
+ path = workdir / "xp" / name / "state.json"
28
+ with path.open("rt") as fh:
29
+ content = json.load(fh)
30
+
31
+ self.states: dict[str, JobInformation] = {
32
+ state_dict["id"]: JobInformation(**state_dict)
33
+ for state_dict in from_state_dict(content, as_instance=False)
34
+ }
35
+
36
+ for state in self.states.values():
37
+ state.depends_on = [self.states[key] for key in state.depends_on]
38
+
39
+ def get_jobs(self, task_class: Type[Task]) -> list[JobInformation]:
40
+ if task_class is None:
41
+ return list(self.data.values())
42
+
43
+ tasks = []
44
+ for job_state in self.states.values():
45
+ if isinstance(job_state.task, task_class):
46
+ tasks.append(job_state)
47
+ return tasks
48
+
49
+ @staticmethod
50
+ def save(path: Path, jobs: Iterable[Job]):
51
+ save_definition(
52
+ [
53
+ {
54
+ "id": job.identifier,
55
+ "path": str(job.path),
56
+ "task": job.config,
57
+ "tags": job.config.__xpm__.tags(),
58
+ "depends_on": list(
59
+ dep.origin.identifier
60
+ for dep in job.dependencies
61
+ if isinstance(dep, JobDependency)
62
+ ),
63
+ }
64
+ for job in jobs
65
+ ],
66
+ SerializationContext(),
67
+ path,
68
+ )
69
+
70
+
71
+ def get_experiment(
72
+ name: str, *, workspace: Optional[str] = None, workdir: Optional[Path] = None
73
+ ) -> ExperimentState:
74
+ ws = find_workspace(workspace=workspace, workdir=workdir)
75
+ return ExperimentState(ws.path, name)
@@ -0,0 +1,50 @@
1
+ from experimaestro import Task, Param, get_experiment, tag
2
+ from experimaestro.tests.utils import TemporaryDirectory, TemporaryExperiment
3
+
4
+
5
+ class TaskA(Task):
6
+ def execute(self):
7
+ pass
8
+
9
+
10
+ class TaskB(Task):
11
+ task_a: Param[TaskA]
12
+ x: Param[int]
13
+
14
+ def execute(self):
15
+ pass
16
+
17
+
18
+ # xp = get_experiment(id="my-xp-1")
19
+
20
+ # # Returns a list of tasks which were submitted and successful
21
+ # tasks = xp.get_tasks(myxps.evaluation.Evaluation, status=Job.DONE)
22
+
23
+ # for task in tasks:
24
+ # # Look at the tags
25
+ # print(task.tags)
26
+
27
+ # # Get some information
28
+ # print("Task ran in {task.workdir}")
29
+
30
+ # # Look at the parent jobs
31
+ # print(task.depends_on)
32
+
33
+ # # Look at the dependant
34
+ # print(task.dependents)
35
+
36
+
37
+ def test_experiment_history():
38
+ """Test retrieving experiment history"""
39
+ with TemporaryDirectory() as workdir:
40
+ with TemporaryExperiment("experiment", workdir=workdir):
41
+ task_a = TaskA().submit()
42
+ TaskB(task_a=task_a, x=tag(1)).submit()
43
+
44
+ # Look at the experiment
45
+ xp = get_experiment("experiment", workdir=workdir)
46
+
47
+ (task_a_info,) = xp.get_jobs(TaskA)
48
+ (task_b_info,) = xp.get_jobs(TaskB)
49
+ assert task_b_info.tags == {"x": 1}
50
+ assert task_b_info.depends_on == [task_a_info]
@@ -8,14 +8,7 @@ if sys.version_info.major == 3:
8
8
  else:
9
9
  from typing import _collect_parameters
10
10
 
11
- if sys.version_info.minor < 9:
12
- from typing_extensions import (
13
- _AnnotatedAlias as AnnotatedAlias,
14
- get_args,
15
- get_origin,
16
- )
17
- else:
18
- from typing import _AnnotatedAlias as AnnotatedAlias, get_args, get_origin
11
+ from typing import _AnnotatedAlias as AnnotatedAlias, get_args, get_origin
19
12
 
20
13
  GenericAlias = typing._GenericAlias
21
14
 
File without changes