experimaestro 0.22.0__zip → 0.24.0__zip

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 (189) hide show
  1. {experimaestro-0.22.0 → experimaestro-0.24.0}/CHANGELOG.md +21 -0
  2. {experimaestro-0.22.0 → experimaestro-0.24.0}/PKG-INFO +22 -1
  3. {experimaestro-0.22.0 → experimaestro-0.24.0}/docs/experiments/task.md +45 -7
  4. {experimaestro-0.22.0 → experimaestro-0.24.0}/pyproject.toml +1 -1
  5. {experimaestro-0.22.0 → experimaestro-0.24.0}/requirements.txt +6 -3
  6. {experimaestro-0.22.0 → experimaestro-0.24.0}/setup.cfg +1 -1
  7. {experimaestro-0.22.0 → experimaestro-0.24.0}/src/experimaestro/__init__.py +7 -5
  8. {experimaestro-0.22.0 → experimaestro-0.24.0}/src/experimaestro/__main__.py +3 -3
  9. {experimaestro-0.22.0 → experimaestro-0.24.0}/src/experimaestro/commandline.py +0 -8
  10. {experimaestro-0.22.0 → experimaestro-0.24.0}/src/experimaestro/core/objects.py +218 -164
  11. {experimaestro-0.22.0 → experimaestro-0.24.0}/src/experimaestro/core/objects.pyi +25 -11
  12. experimaestro-0.24.0/src/experimaestro/core/serializers.py +52 -0
  13. {experimaestro-0.22.0 → experimaestro-0.24.0}/src/experimaestro/core/types.py +44 -2
  14. {experimaestro-0.22.0 → experimaestro-0.24.0}/src/experimaestro/generators.py +7 -6
  15. {experimaestro-0.22.0 → experimaestro-0.24.0}/src/experimaestro/huggingface.py +2 -2
  16. {experimaestro-0.22.0 → experimaestro-0.24.0}/src/experimaestro/launchers/__init__.py +19 -7
  17. {experimaestro-0.22.0 → experimaestro-0.24.0}/src/experimaestro/scheduler/base.py +21 -3
  18. {experimaestro-0.22.0 → experimaestro-0.24.0}/src/experimaestro/server/__init__.py +10 -2
  19. {experimaestro-0.22.0 → experimaestro-0.24.0}/src/experimaestro/tests/test_identifier.py +33 -6
  20. {experimaestro-0.22.0 → experimaestro-0.24.0}/src/experimaestro/tests/test_instance.py +18 -15
  21. experimaestro-0.24.0/src/experimaestro/tests/test_outputs.py +50 -0
  22. {experimaestro-0.22.0 → experimaestro-0.24.0}/src/experimaestro/tests/test_progress.py +7 -9
  23. experimaestro-0.24.0/src/experimaestro/tests/test_serializers.py +54 -0
  24. {experimaestro-0.22.0 → experimaestro-0.24.0}/src/experimaestro/utils/jobs.py +2 -2
  25. {experimaestro-0.22.0 → experimaestro-0.24.0}/src/experimaestro/version.py +2 -2
  26. {experimaestro-0.22.0 → experimaestro-0.24.0}/src/experimaestro/xpmutils.py +3 -3
  27. {experimaestro-0.22.0 → experimaestro-0.24.0}/src/experimaestro.egg-info/PKG-INFO +22 -1
  28. {experimaestro-0.22.0 → experimaestro-0.24.0}/src/experimaestro.egg-info/SOURCES.txt +2 -1
  29. {experimaestro-0.22.0 → experimaestro-0.24.0}/src/experimaestro.egg-info/requires.txt +6 -2
  30. experimaestro-0.22.0/src/experimaestro/tests/test_outputs.py +0 -88
  31. experimaestro-0.22.0/src/experimaestro/tests/test_serialization.py +0 -45
  32. {experimaestro-0.22.0 → experimaestro-0.24.0}/.circleci/config.yml +0 -0
  33. {experimaestro-0.22.0 → experimaestro-0.24.0}/.flake8 +0 -0
  34. {experimaestro-0.22.0 → experimaestro-0.24.0}/.github/release.yaml +0 -0
  35. {experimaestro-0.22.0 → experimaestro-0.24.0}/.github/workflows/pytest.yml +0 -0
  36. {experimaestro-0.22.0 → experimaestro-0.24.0}/.github/workflows/python-publish.yml +0 -0
  37. {experimaestro-0.22.0 → experimaestro-0.24.0}/.gitignore +0 -0
  38. {experimaestro-0.22.0 → experimaestro-0.24.0}/.gitmodules +0 -0
  39. {experimaestro-0.22.0 → experimaestro-0.24.0}/.pre-commit-config.yaml +0 -0
  40. {experimaestro-0.22.0 → experimaestro-0.24.0}/.prettierignore +0 -0
  41. {experimaestro-0.22.0 → experimaestro-0.24.0}/.readthedocs.yml +0 -0
  42. {experimaestro-0.22.0 → experimaestro-0.24.0}/LICENSE +0 -0
  43. {experimaestro-0.22.0 → experimaestro-0.24.0}/MANIFEST.in +0 -0
  44. {experimaestro-0.22.0 → experimaestro-0.24.0}/README.md +0 -0
  45. {experimaestro-0.22.0 → experimaestro-0.24.0}/app/.gitignore +0 -0
  46. {experimaestro-0.22.0 → experimaestro-0.24.0}/app/.nolluprc.js +0 -0
  47. {experimaestro-0.22.0 → experimaestro-0.24.0}/app/CHANGELOG.md +0 -0
  48. {experimaestro-0.22.0 → experimaestro-0.24.0}/app/README.md +0 -0
  49. {experimaestro-0.22.0 → experimaestro-0.24.0}/app/package-lock.json +0 -0
  50. {experimaestro-0.22.0 → experimaestro-0.24.0}/app/package.json +0 -0
  51. {experimaestro-0.22.0 → experimaestro-0.24.0}/app/postcss.config.js +0 -0
  52. {experimaestro-0.22.0 → experimaestro-0.24.0}/app/public/favicon.ico +0 -0
  53. {experimaestro-0.22.0 → experimaestro-0.24.0}/app/public/index.html +0 -0
  54. {experimaestro-0.22.0 → experimaestro-0.24.0}/app/public/login.html +0 -0
  55. {experimaestro-0.22.0 → experimaestro-0.24.0}/app/public/manifest.json +0 -0
  56. {experimaestro-0.22.0 → experimaestro-0.24.0}/app/src/App.tsx +0 -0
  57. {experimaestro-0.22.0 → experimaestro-0.24.0}/app/src/Experiments.tsx +0 -0
  58. {experimaestro-0.22.0 → experimaestro-0.24.0}/app/src/Services.tsx +0 -0
  59. {experimaestro-0.22.0 → experimaestro-0.24.0}/app/src/TaskDetail.tsx +0 -0
  60. {experimaestro-0.22.0 → experimaestro-0.24.0}/app/src/TaskJobs.tsx +0 -0
  61. {experimaestro-0.22.0 → experimaestro-0.24.0}/app/src/Tasks.tsx +0 -0
  62. {experimaestro-0.22.0 → experimaestro-0.24.0}/app/src/client.ts +0 -0
  63. {experimaestro-0.22.0 → experimaestro-0.24.0}/app/src/clipboard.ts +0 -0
  64. {experimaestro-0.22.0 → experimaestro-0.24.0}/app/src/index.css +0 -0
  65. {experimaestro-0.22.0 → experimaestro-0.24.0}/app/src/index.tsx +0 -0
  66. {experimaestro-0.22.0 → experimaestro-0.24.0}/app/src/logo.png +0 -0
  67. {experimaestro-0.22.0 → experimaestro-0.24.0}/app/src/logo.pxm +0 -0
  68. {experimaestro-0.22.0 → experimaestro-0.24.0}/app/src/reducers.ts +0 -0
  69. {experimaestro-0.22.0 → experimaestro-0.24.0}/app/src/store.ts +0 -0
  70. {experimaestro-0.22.0 → experimaestro-0.24.0}/app/src/theme/_jobs.scss +0 -0
  71. {experimaestro-0.22.0 → experimaestro-0.24.0}/app/src/theme/theme.scss +0 -0
  72. {experimaestro-0.22.0 → experimaestro-0.24.0}/app/src/ui/messages.tsx +0 -0
  73. {experimaestro-0.22.0 → experimaestro-0.24.0}/app/tsconfig.json +0 -0
  74. {experimaestro-0.22.0 → experimaestro-0.24.0}/app/webpack.config.ts +0 -0
  75. {experimaestro-0.22.0 → experimaestro-0.24.0}/app/xp/run.py +0 -0
  76. {experimaestro-0.22.0 → experimaestro-0.24.0}/docs/changelog.md +0 -0
  77. {experimaestro-0.22.0 → experimaestro-0.24.0}/docs/cli.md +0 -0
  78. {experimaestro-0.22.0 → experimaestro-0.24.0}/docs/concepts.md +0 -0
  79. {experimaestro-0.22.0 → experimaestro-0.24.0}/docs/configuration.md +0 -0
  80. {experimaestro-0.22.0 → experimaestro-0.24.0}/docs/connectors/index.md +0 -0
  81. {experimaestro-0.22.0 → experimaestro-0.24.0}/docs/documenting.md +0 -0
  82. {experimaestro-0.22.0 → experimaestro-0.24.0}/docs/experiments/config.md +0 -0
  83. {experimaestro-0.22.0 → experimaestro-0.24.0}/docs/experiments/overview.md +0 -0
  84. {experimaestro-0.22.0 → experimaestro-0.24.0}/docs/experiments/plan.md +0 -0
  85. {experimaestro-0.22.0 → experimaestro-0.24.0}/docs/faq.md +0 -0
  86. {experimaestro-0.22.0 → experimaestro-0.24.0}/docs/index.md +0 -0
  87. {experimaestro-0.22.0 → experimaestro-0.24.0}/docs/jupyter.md +0 -0
  88. {experimaestro-0.22.0 → experimaestro-0.24.0}/docs/launchers/index.md +0 -0
  89. {experimaestro-0.22.0 → experimaestro-0.24.0}/docs/requirements.txt +0 -0
  90. {experimaestro-0.22.0 → experimaestro-0.24.0}/docs/serialization.md +0 -0
  91. {experimaestro-0.22.0 → experimaestro-0.24.0}/docs/tutorial.md +0 -0
  92. {experimaestro-0.22.0 → experimaestro-0.24.0}/mkdocs.yml +0 -0
  93. {experimaestro-0.22.0 → experimaestro-0.24.0}/pytest.ini +0 -0
  94. {experimaestro-0.22.0 → experimaestro-0.24.0}/scripts/longtask.py +0 -0
  95. {experimaestro-0.22.0 → experimaestro-0.24.0}/setup.py +0 -0
  96. {experimaestro-0.22.0 → experimaestro-0.24.0}/src/experimaestro/annotations.py +0 -0
  97. {experimaestro-0.22.0 → experimaestro-0.24.0}/src/experimaestro/checkers.py +0 -0
  98. {experimaestro-0.22.0 → experimaestro-0.24.0}/src/experimaestro/click.py +0 -0
  99. {experimaestro-0.22.0 → experimaestro-0.24.0}/src/experimaestro/compat.py +0 -0
  100. {experimaestro-0.22.0 → experimaestro-0.24.0}/src/experimaestro/connectors/__init__.py +0 -0
  101. {experimaestro-0.22.0 → experimaestro-0.24.0}/src/experimaestro/connectors/local.py +0 -0
  102. {experimaestro-0.22.0 → experimaestro-0.24.0}/src/experimaestro/connectors/ssh.py +0 -0
  103. {experimaestro-0.22.0 → experimaestro-0.24.0}/src/experimaestro/core/__init__.py +0 -0
  104. {experimaestro-0.22.0 → experimaestro-0.24.0}/src/experimaestro/core/arguments.py +0 -0
  105. {experimaestro-0.22.0 → experimaestro-0.24.0}/src/experimaestro/core/context.py +0 -0
  106. {experimaestro-0.22.0 → experimaestro-0.24.0}/src/experimaestro/filter.py +0 -0
  107. {experimaestro-0.22.0 → experimaestro-0.24.0}/src/experimaestro/ipc.py +0 -0
  108. {experimaestro-0.22.0 → experimaestro-0.24.0}/src/experimaestro/launcherfinder/__init__.py +0 -0
  109. {experimaestro-0.22.0 → experimaestro-0.24.0}/src/experimaestro/launcherfinder/base.py +0 -0
  110. {experimaestro-0.22.0 → experimaestro-0.24.0}/src/experimaestro/launcherfinder/parser.py +0 -0
  111. {experimaestro-0.22.0 → experimaestro-0.24.0}/src/experimaestro/launcherfinder/registry.py +0 -0
  112. {experimaestro-0.22.0 → experimaestro-0.24.0}/src/experimaestro/launcherfinder/specs.py +0 -0
  113. {experimaestro-0.22.0 → experimaestro-0.24.0}/src/experimaestro/launchers/direct.py +0 -0
  114. {experimaestro-0.22.0 → experimaestro-0.24.0}/src/experimaestro/launchers/oar.py +0 -0
  115. {experimaestro-0.22.0 → experimaestro-0.24.0}/src/experimaestro/launchers/slurm.py +0 -0
  116. {experimaestro-0.22.0 → experimaestro-0.24.0}/src/experimaestro/locking.py +0 -0
  117. {experimaestro-0.22.0 → experimaestro-0.24.0}/src/experimaestro/mkdocs/__init__.py +0 -0
  118. {experimaestro-0.22.0 → experimaestro-0.24.0}/src/experimaestro/mkdocs/annotations.py +0 -0
  119. {experimaestro-0.22.0 → experimaestro-0.24.0}/src/experimaestro/mkdocs/base.py +0 -0
  120. {experimaestro-0.22.0 → experimaestro-0.24.0}/src/experimaestro/mkdocs/metaloader.py +0 -0
  121. {experimaestro-0.22.0 → experimaestro-0.24.0}/src/experimaestro/mkdocs/style.css +0 -0
  122. {experimaestro-0.22.0 → experimaestro-0.24.0}/src/experimaestro/mypy.py +0 -0
  123. {experimaestro-0.22.0 → experimaestro-0.24.0}/src/experimaestro/notifications.py +0 -0
  124. {experimaestro-0.22.0 → experimaestro-0.24.0}/src/experimaestro/rpyc.py +0 -0
  125. {experimaestro-0.22.0 → experimaestro-0.24.0}/src/experimaestro/run.py +0 -0
  126. {experimaestro-0.22.0 → experimaestro-0.24.0}/src/experimaestro/scheduler/__init__.py +0 -0
  127. {experimaestro-0.22.0 → experimaestro-0.24.0}/src/experimaestro/scheduler/dependencies.py +0 -0
  128. {experimaestro-0.22.0 → experimaestro-0.24.0}/src/experimaestro/scheduler/environment.py +0 -0
  129. {experimaestro-0.22.0 → experimaestro-0.24.0}/src/experimaestro/scheduler/services.py +0 -0
  130. {experimaestro-0.22.0 → experimaestro-0.24.0}/src/experimaestro/scheduler/workspace.py +0 -0
  131. {experimaestro-0.22.0 → experimaestro-0.24.0}/src/experimaestro/scriptbuilder.py +0 -0
  132. {experimaestro-0.22.0 → experimaestro-0.24.0}/src/experimaestro/settings.py +0 -0
  133. {experimaestro-0.22.0 → experimaestro-0.24.0}/src/experimaestro/sphinx/__init__.py +0 -0
  134. {experimaestro-0.22.0 → experimaestro-0.24.0}/src/experimaestro/sphinx/static/experimaestro.css +0 -0
  135. {experimaestro-0.22.0 → experimaestro-0.24.0}/src/experimaestro/taskglobals.py +0 -0
  136. {experimaestro-0.22.0 → experimaestro-0.24.0}/src/experimaestro/tests/__init__.py +0 -0
  137. {experimaestro-0.22.0 → experimaestro-0.24.0}/src/experimaestro/tests/conftest.py +0 -0
  138. {experimaestro-0.22.0 → experimaestro-0.24.0}/src/experimaestro/tests/connectors/bin/executable.py +0 -0
  139. {experimaestro-0.22.0 → experimaestro-0.24.0}/src/experimaestro/tests/connectors/test_local.py +0 -0
  140. {experimaestro-0.22.0 → experimaestro-0.24.0}/src/experimaestro/tests/connectors/utils.py +0 -0
  141. {experimaestro-0.22.0 → experimaestro-0.24.0}/src/experimaestro/tests/definitions_types.py +0 -0
  142. {experimaestro-0.22.0 → experimaestro-0.24.0}/src/experimaestro/tests/launchers/__init__.py +0 -0
  143. {experimaestro-0.22.0 → experimaestro-0.24.0}/src/experimaestro/tests/launchers/bin/sacct +0 -0
  144. {experimaestro-0.22.0 → experimaestro-0.24.0}/src/experimaestro/tests/launchers/bin/sbatch +0 -0
  145. {experimaestro-0.22.0 → experimaestro-0.24.0}/src/experimaestro/tests/launchers/bin/test.py +0 -0
  146. {experimaestro-0.22.0 → experimaestro-0.24.0}/src/experimaestro/tests/launchers/common.py +0 -0
  147. {experimaestro-0.22.0 → experimaestro-0.24.0}/src/experimaestro/tests/launchers/config_slurm/__init__.py +0 -0
  148. {experimaestro-0.22.0 → experimaestro-0.24.0}/src/experimaestro/tests/launchers/config_slurm/launchers.yaml +0 -0
  149. {experimaestro-0.22.0 → experimaestro-0.24.0}/src/experimaestro/tests/launchers/test_local.py +0 -0
  150. {experimaestro-0.22.0 → experimaestro-0.24.0}/src/experimaestro/tests/launchers/test_slurm.py +0 -0
  151. {experimaestro-0.22.0 → experimaestro-0.24.0}/src/experimaestro/tests/restart.py +0 -0
  152. {experimaestro-0.22.0 → experimaestro-0.24.0}/src/experimaestro/tests/restart_main.py +0 -0
  153. {experimaestro-0.22.0 → experimaestro-0.24.0}/src/experimaestro/tests/scripts/notifyandwait.py +0 -0
  154. {experimaestro-0.22.0 → experimaestro-0.24.0}/src/experimaestro/tests/scripts/waitforfile.py +0 -0
  155. {experimaestro-0.22.0 → experimaestro-0.24.0}/src/experimaestro/tests/task_tokens.py +0 -0
  156. {experimaestro-0.22.0 → experimaestro-0.24.0}/src/experimaestro/tests/tasks/__init__.py +0 -0
  157. {experimaestro-0.22.0 → experimaestro-0.24.0}/src/experimaestro/tests/tasks/all.py +0 -0
  158. {experimaestro-0.22.0 → experimaestro-0.24.0}/src/experimaestro/tests/tasks/foreign.py +0 -0
  159. {experimaestro-0.22.0 → experimaestro-0.24.0}/src/experimaestro/tests/tasks/subparams.py +0 -0
  160. {experimaestro-0.22.0 → experimaestro-0.24.0}/src/experimaestro/tests/test_checkers.py +0 -0
  161. {experimaestro-0.22.0 → experimaestro-0.24.0}/src/experimaestro/tests/test_findlauncher.py +0 -0
  162. {experimaestro-0.22.0 → experimaestro-0.24.0}/src/experimaestro/tests/test_forward.py +0 -0
  163. {experimaestro-0.22.0 → experimaestro-0.24.0}/src/experimaestro/tests/test_objects.py +0 -0
  164. {experimaestro-0.22.0 → experimaestro-0.24.0}/src/experimaestro/tests/test_param.py +0 -0
  165. {experimaestro-0.22.0 → experimaestro-0.24.0}/src/experimaestro/tests/test_snippets.py +0 -0
  166. {experimaestro-0.22.0 → experimaestro-0.24.0}/src/experimaestro/tests/test_ssh.py +0 -0
  167. {experimaestro-0.22.0 → experimaestro-0.24.0}/src/experimaestro/tests/test_tags.py +0 -0
  168. {experimaestro-0.22.0 → experimaestro-0.24.0}/src/experimaestro/tests/test_tasks.py +0 -0
  169. {experimaestro-0.22.0 → experimaestro-0.24.0}/src/experimaestro/tests/test_tokens.py +0 -0
  170. {experimaestro-0.22.0 → experimaestro-0.24.0}/src/experimaestro/tests/test_types.py +0 -0
  171. {experimaestro-0.22.0 → experimaestro-0.24.0}/src/experimaestro/tests/test_validation.py +0 -0
  172. {experimaestro-0.22.0 → experimaestro-0.24.0}/src/experimaestro/tests/token_reschedule.py +0 -0
  173. {experimaestro-0.22.0 → experimaestro-0.24.0}/src/experimaestro/tests/utils.py +0 -0
  174. {experimaestro-0.22.0 → experimaestro-0.24.0}/src/experimaestro/tokens.py +0 -0
  175. {experimaestro-0.22.0 → experimaestro-0.24.0}/src/experimaestro/tools/__init__.py +0 -0
  176. {experimaestro-0.22.0 → experimaestro-0.24.0}/src/experimaestro/tools/diff.py +0 -0
  177. {experimaestro-0.22.0 → experimaestro-0.24.0}/src/experimaestro/tools/jobs.py +0 -0
  178. {experimaestro-0.22.0 → experimaestro-0.24.0}/src/experimaestro/typingutils.py +0 -0
  179. {experimaestro-0.22.0 → experimaestro-0.24.0}/src/experimaestro/utils/__init__.py +0 -0
  180. {experimaestro-0.22.0 → experimaestro-0.24.0}/src/experimaestro/utils/asyncio.py +0 -0
  181. {experimaestro-0.22.0 → experimaestro-0.24.0}/src/experimaestro/utils/jupyter.py +0 -0
  182. {experimaestro-0.22.0 → experimaestro-0.24.0}/src/experimaestro/utils/resources.py +0 -0
  183. {experimaestro-0.22.0 → experimaestro-0.24.0}/src/experimaestro/utils/settings.py +0 -0
  184. {experimaestro-0.22.0 → experimaestro-0.24.0}/src/experimaestro/utils/yaml.py +0 -0
  185. {experimaestro-0.22.0 → experimaestro-0.24.0}/src/experimaestro.egg-info/dependency_links.txt +0 -0
  186. {experimaestro-0.22.0 → experimaestro-0.24.0}/src/experimaestro.egg-info/entry_points.txt +0 -0
  187. {experimaestro-0.22.0 → experimaestro-0.24.0}/src/experimaestro.egg-info/not-zip-safe +0 -0
  188. {experimaestro-0.22.0 → experimaestro-0.24.0}/src/experimaestro.egg-info/top_level.txt +0 -0
  189. {experimaestro-0.22.0 → experimaestro-0.24.0}/tox.ini +0 -0
@@ -1,8 +1,10 @@
1
+ from abc import ABC
1
2
  import typing_extensions
2
3
 
3
4
  from experimaestro.core.types import ObjectType
4
5
  import experimaestro
5
6
  import io
7
+ from experimaestro.launchers import Launcher
6
8
  from experimaestro.scheduler.base import Job
7
9
 
8
10
  from experimaestro.scheduler.workspace import RunMode
@@ -20,7 +22,18 @@ from experimaestro.core.types import (
20
22
  from experimaestro.utils import logger as logger
21
23
  from functools import cached_property as cached_property
22
24
  from pathlib import Path
23
- from typing import Any, ClassVar, Dict, List, Optional, Set, TypeVar, Union, overload
25
+ from typing import (
26
+ Any,
27
+ Callable,
28
+ ClassVar,
29
+ Dict,
30
+ List,
31
+ Optional,
32
+ Set,
33
+ TypeVar,
34
+ Union,
35
+ overload,
36
+ )
24
37
 
25
38
  T = TypeVar("T", bound="Config")
26
39
 
@@ -62,7 +75,7 @@ class TaggedValue:
62
75
 
63
76
  def add_to_path(p) -> Generator[None, None, None]: ...
64
77
 
65
- class GenerationContext:
78
+ class ConfigWalkContext:
66
79
  @property
67
80
  def path(self) -> None: ...
68
81
  def __init__(self) -> None: ...
@@ -83,9 +96,9 @@ class ConfigProcessing:
83
96
  def map(self, k: str): ...
84
97
  def __call__(self, x): ...
85
98
 
86
- class GenerationConfigProcessing(ConfigProcessing):
99
+ class ConfigWalk(ConfigProcessing):
87
100
  context: Incomplete
88
- def __init__(self, context: GenerationContext) -> None: ...
101
+ def __init__(self, context: ConfigWalkContext) -> None: ...
89
102
  def list(self, i: int): ...
90
103
  def map(self, k: str): ...
91
104
 
@@ -108,7 +121,7 @@ class ConfigInformation:
108
121
  def xpmvalues(self, generated: bool = ...) -> Generator[Incomplete, None, None]: ...
109
122
  def tags(self): ...
110
123
  def validate(self) -> None: ...
111
- def seal(self, context: GenerationContext): ...
124
+ def seal(self, context: ConfigWalkContext): ...
112
125
  @property
113
126
  def identifier(self) -> Identifier: ...
114
127
  def dependency(self): ...
@@ -141,13 +154,13 @@ class ConfigInformation:
141
154
  save_directory: Optional[Path] = ...,
142
155
  ) -> Config: ...
143
156
 
144
- class FromPython(GenerationConfigProcessing):
157
+ class FromPython(ConfigWalk):
145
158
  objects: Incomplete
146
- def __init__(self, context: GenerationContext) -> None: ...
159
+ def __init__(self, context: ConfigWalkContext) -> None: ...
147
160
  def preprocess(self, config: Config): ...
148
161
  def postprocess(self, config: Config, values: Dict[str, Any]): ...
149
162
 
150
- def fromConfig(self, context: GenerationContext): ...
163
+ def fromConfig(self, context: ConfigWalkContext): ...
151
164
  def add_dependencies(self, *dependencies) -> None: ...
152
165
 
153
166
  def clone(v): ...
@@ -162,13 +175,14 @@ class TypeConfig:
162
175
  def __arguments__(self): ...
163
176
  def tags(self): ...
164
177
  def add_dependencies(self, *dependencies): ...
165
- def instance(self, context: GenerationContext = ...) -> T: ...
178
+ def instance(self, context: ConfigWalkContext = ...) -> T: ...
166
179
  def submit(
167
180
  self,
168
181
  *,
169
182
  workspace: Incomplete | None = ...,
170
183
  launcher: Incomplete | None = ...,
171
- run_mode: RunMode = ...
184
+ run_mode: RunMode = ...,
185
+ pre: List[Task] = []
172
186
  ): ...
173
187
  def stdout(self): ...
174
188
  def stderr(self): ...
@@ -183,7 +197,7 @@ class Config:
183
197
  __xpmtype__: ClassVar[ObjectType]
184
198
  __xpm__: ConfigInformation
185
199
  @classmethod
186
- def __getxpmtype__(cls): ...
200
+ def __getxpmtype__(cls) -> ObjectType: ...
187
201
  def __getnewargs_ex__(self): ...
188
202
  @classmethod
189
203
  def c(cls, **kwargs) -> T: ...
@@ -0,0 +1,52 @@
1
+ from abc import ABC, abstractmethod
2
+ from pathlib import Path
3
+ from typing import List, TypeVar
4
+
5
+ from experimaestro import Param
6
+
7
+ from .objects import Config, Proxy
8
+ from .arguments import DataPath
9
+
10
+ T = TypeVar("T")
11
+
12
+
13
+ class SerializedConfig(Config, Proxy, ABC):
14
+ """A serializable configuration
15
+
16
+ This can be used to define a loading mechanism when instanciating the
17
+ configuration
18
+ """
19
+
20
+ config: Param[Config]
21
+ """The configuration that will be serialized"""
22
+
23
+ registered: List[Config]
24
+ """(execution only) List of configurations that use this serialized config"""
25
+
26
+ def __post_init__(self):
27
+ super().__post_init__()
28
+ self.registered = []
29
+
30
+ def register(self, config: Config):
31
+ self.registered.append(config)
32
+
33
+ def __unwrap__(self):
34
+ return self.config
35
+
36
+ @abstractmethod
37
+ def initialize(self):
38
+ """Initialize the object
39
+
40
+ This might imply loading saved data (e.g. learned models)
41
+ """
42
+ ...
43
+
44
+
45
+ class PathBasedSerializedConfig(SerializedConfig):
46
+ """A path based serialized configuration
47
+
48
+ The most common case it to have
49
+ """
50
+
51
+ path: DataPath[Path]
52
+ """Path containing the data"""
@@ -1,6 +1,7 @@
1
+ from abc import ABC, abstractmethod
1
2
  import inspect
2
3
  import sys
3
- from typing import Union, Dict, Iterator, List
4
+ from typing import Set, Union, Dict, Iterator, List
4
5
  from collections import ChainMap
5
6
  from pathlib import Path
6
7
  import typing
@@ -17,6 +18,11 @@ if sys.version_info.major == 3 and sys.version_info.minor < 9:
17
18
  else:
18
19
  from typing import _AnnotatedAlias, get_type_hints
19
20
 
21
+ if typing.TYPE_CHECKING:
22
+ from experimaestro.scheduler.base import Job
23
+ from experimaestro.launchers import Launcher
24
+ from experimaestro.core.objects import Config
25
+
20
26
 
21
27
  class Identifier:
22
28
  def __init__(self, name: str):
@@ -143,7 +149,42 @@ class DeprecatedAttribute:
143
149
  self.fn(instance, value)
144
150
 
145
151
 
152
+ class SubmitHook(ABC):
153
+ """Hook called before the job is submitted to the scheduler
154
+
155
+ This allows modifying e.g. the run environnement
156
+ """
157
+
158
+ @abstractmethod
159
+ def __call__(self, job: "Job", launcher: "Launcher"):
160
+ ...
161
+
162
+ @abstractmethod
163
+ def __spec__(self):
164
+ """Returns an identifier tuple for hashing/equality"""
165
+ ...
166
+
167
+ def __eq__(self, other):
168
+ if other.__class__ is not self.__class__:
169
+ return False
170
+ return self.__spec__ == other.__spec__
171
+
172
+ def __hash__(self):
173
+ return hash((self.__class__, self.__spec__))
174
+
175
+
176
+ def submit_hook_decorator(hook: SubmitHook):
177
+ def decorator(cls: typing.Type["Config"]):
178
+ cls.__getxpmtype__().submit_hooks.add(hook)
179
+ return cls
180
+
181
+ return decorator
182
+
183
+
146
184
  class ObjectType(Type):
185
+ submit_hooks: Set[SubmitHook]
186
+ """Hooks associated with this configuration"""
187
+
147
188
  """ObjectType contains class-level information about
148
189
  experimaestro configurations and tasks
149
190
 
@@ -167,6 +208,7 @@ class ObjectType(Type):
167
208
  self.taskcommandfactory = None
168
209
  self.task = None
169
210
  self._title = None
211
+ self.submit_hooks = set()
170
212
 
171
213
  # Get the identifier
172
214
  if identifier is None and hasattr(tp, "__xpmid__"):
@@ -219,7 +261,7 @@ class ObjectType(Type):
219
261
  self.configtype.__module__ = tp.__module__
220
262
 
221
263
  # Create the type-specific object class
222
- # (now, the same as basetype - TODO: remove references)
264
+ # (now, the same as basetype - but in the future, remove references)
223
265
  self.objecttype = self.basetype # type: type
224
266
  self.basetype._ = self.configtype
225
267
 
@@ -1,15 +1,16 @@
1
1
  import inspect
2
2
  from pathlib import Path
3
- from typing import Callable, List, Tuple, Union
3
+ from typing import Callable, Union
4
4
  from experimaestro.core.arguments import ArgumentOptions, TypeAnnotation
5
- from experimaestro.core.objects import GenerationContext, Config
5
+ from experimaestro.core.objects import ConfigWalkContext, Config
6
6
 
7
7
 
8
8
  class Generator:
9
9
  """Base class for all generators"""
10
10
 
11
11
  def isoutput(self):
12
- """Returns True if this generator is a task output (e.g. generates a path within the job folder)"""
12
+ """Returns True if this generator is a task output (e.g. generates a
13
+ path within the job folder)"""
13
14
  return False
14
15
 
15
16
 
@@ -17,11 +18,11 @@ class PathGenerator(Generator):
17
18
  """Generates a path"""
18
19
 
19
20
  def __init__(
20
- self, path: Union[str, Path, Callable[[GenerationContext, Config], Path]]
21
+ self, path: Union[str, Path, Callable[[ConfigWalkContext, Config], Path]]
21
22
  ):
22
23
  self.path = path
23
24
 
24
- def __call__(self, context: GenerationContext, config: Config):
25
+ def __call__(self, context: ConfigWalkContext, config: Config):
25
26
  if inspect.isfunction(self.path):
26
27
  path = context.currentpath() / self.path(context, config) # type: Path
27
28
  else:
@@ -34,7 +35,7 @@ class PathGenerator(Generator):
34
35
 
35
36
 
36
37
  class pathgenerator(TypeAnnotation):
37
- def __init__(self, value: Union[str, Callable[[GenerationContext, Config], str]]):
38
+ def __init__(self, value: Union[str, Callable[[ConfigWalkContext, Config], str]]):
38
39
  self.value = value
39
40
 
40
41
  def annotate(self, options: ArgumentOptions):
@@ -1,6 +1,6 @@
1
1
  from pathlib import Path
2
2
  from typing import Optional, Union
3
- from experimaestro import Config, TaskOutput
3
+ from experimaestro import Config, ConfigWrapper
4
4
  from experimaestro.core.context import SerializedPath
5
5
  from experimaestro.core.objects import ConfigInformation
6
6
  from huggingface_hub import ModelHubMixin, hf_hub_download, snapshot_download
@@ -11,7 +11,7 @@ class ExperimaestroHFHub(ModelHubMixin):
11
11
  """Defines models that can be uploaded/downloaded from the Hub"""
12
12
 
13
13
  def __init__(
14
- self, config: Union[Config, TaskOutput], variant: Optional[str] = None
14
+ self, config: Union[Config, ConfigWrapper], variant: Optional[str] = None
15
15
  ):
16
16
  self.config = config if isinstance(config, Config) else config.__unwrap__()
17
17
  self.variant = variant
@@ -1,25 +1,36 @@
1
1
  from pathlib import Path, PosixPath
2
- from typing import Callable, Dict, List, Optional, Protocol
2
+ from typing import Callable, Dict, List, Optional
3
3
  from experimaestro.commandline import AbstractCommand, Job, CommandLineJob
4
4
  from experimaestro.connectors import Connector
5
5
  from experimaestro.connectors.local import ProcessBuilder, LocalConnector
6
6
  from experimaestro.connectors.ssh import SshPath, SshConnector
7
+ from abc import ABC, abstractmethod
7
8
 
8
9
 
9
- class ScriptBuilder:
10
- lockfiles: List[Path]
11
- command: "AbstractCommand"
10
+ class ScriptBuilder(ABC):
12
11
  """A script builder is responsible for generating the script
13
12
  used to launch a command line job"""
14
13
 
14
+ lockfiles: List[Path]
15
+ """The files that must be locked before starting the job"""
16
+
17
+ command: "AbstractCommand"
18
+ """Command to be run"""
19
+
20
+ @abstractmethod
15
21
  def write(self, job: CommandLineJob) -> Path:
16
- raise NotImplementedError()
22
+ """Write the commmand line job
23
+
24
+ :params job: The job to be written
25
+ """
26
+ ...
17
27
 
18
28
 
19
29
  SubmitListener = Callable[[Job], None]
30
+ """Listen to job submissions"""
20
31
 
21
32
 
22
- class Launcher:
33
+ class Launcher(ABC):
23
34
  """A launcher"""
24
35
 
25
36
  submit_listeners: List[SubmitListener]
@@ -36,9 +47,10 @@ class Launcher:
36
47
  def setNotificationURL(self, url: Optional[str]):
37
48
  self.notificationURL = url
38
49
 
50
+ @abstractmethod
39
51
  def scriptbuilder(self) -> ScriptBuilder:
40
52
  """Returns a script builder"""
41
- raise NotImplementedError(f"For class {self.__class__}")
53
+ ...
42
54
 
43
55
  def addListener(self, listener: SubmitListener):
44
56
  self.submit_listeners.append(listener)
@@ -1,4 +1,4 @@
1
- from collections import defaultdict
1
+ from collections import ChainMap, defaultdict
2
2
  import os
3
3
  from pathlib import Path
4
4
  from shutil import rmtree
@@ -14,7 +14,7 @@ from experimaestro.scheduler.services import Service
14
14
  from experimaestro.settings import get_settings
15
15
 
16
16
 
17
- from experimaestro.core.objects import Config, GenerationContext
17
+ from experimaestro.core.objects import Config, ConfigWalkContext
18
18
  from experimaestro.utils import logger
19
19
  from experimaestro.locking import Locks, LockError, Lock
20
20
  from experimaestro.tokens import ProcessCounterToken
@@ -151,6 +151,23 @@ class Job(Resource):
151
151
  assert self._future, "Cannot wait a not submitted job"
152
152
  return self._future.result()
153
153
 
154
+ @property
155
+ def environ(self):
156
+ """Returns the job environment
157
+
158
+ It is made of (by order of priority):
159
+
160
+ 1. The job environment
161
+ 1. The launcher environment
162
+ 1. The workspace environment
163
+
164
+ """
165
+ return ChainMap(
166
+ {},
167
+ self.launcher.environ if self.launcher else {},
168
+ self.workspace.environment.environ,
169
+ )
170
+
154
171
  @property
155
172
  def progress(self):
156
173
  return self._progress
@@ -292,7 +309,7 @@ class Job(Resource):
292
309
  return self._future
293
310
 
294
311
 
295
- class JobContext(GenerationContext):
312
+ class JobContext(ConfigWalkContext):
296
313
  def __init__(self, job: Job):
297
314
  super().__init__()
298
315
  self.job = job
@@ -839,6 +856,7 @@ class experiment:
839
856
  def __enter__(self):
840
857
  logger.info("Locking experiment %s", self.xplockpath)
841
858
  self.xplock = self.workspace.connector.lock(self.xplockpath, 0).__enter__()
859
+ logger.info("Experiment locked")
842
860
 
843
861
  # Move old jobs into "jobs.bak"
844
862
  if self.workspace.run_mode == RunMode.NORMAL:
@@ -143,10 +143,15 @@ def proxy_response(base_url: str, request: Request, path: str):
143
143
 
144
144
 
145
145
  def start_app(server: "Server"):
146
+ logging.debug("Starting Flask server...")
146
147
  app = Flask("experimaestro")
147
- socketio = SocketIO(app, path="/api", async_mode="eventlet")
148
+
149
+ logging.debug("Starting Flask server (SocketIO)...")
150
+ socketio = SocketIO(app, path="/api", async_mode="gevent")
148
151
  listener = Listener(server.scheduler, socketio)
149
152
 
153
+ logging.debug("Starting Flask server (setting up socketio)...")
154
+
150
155
  @socketio.on("connect")
151
156
  def handle_connect():
152
157
  if server.token != request.cookies.get("experimaestro_token", None):
@@ -183,6 +188,8 @@ def start_app(server: "Server"):
183
188
  if process is not None:
184
189
  process.kill()
185
190
 
191
+ logging.debug("Starting Flask server (setting up routes)...")
192
+
186
193
  @app.route("/services/<path:path>", methods=["GET", "POST"])
187
194
  def route_service(path):
188
195
  service, *path = path.split("/", 1)
@@ -256,8 +263,9 @@ def start_app(server: "Server"):
256
263
 
257
264
  # Start the app
258
265
  if server.port is None or server.port == 0:
266
+ logging.info("Searching for an available port")
259
267
  sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
260
- sock.bind(("localhost", 0))
268
+ sock.bind(("", 0))
261
269
  server.port = sock.getsockname()[1]
262
270
  sock.close()
263
271
 
@@ -17,7 +17,8 @@ from experimaestro import (
17
17
  Annotated,
18
18
  Task,
19
19
  )
20
- from experimaestro.core.objects import ConfigInformation, TaskOutput, setmeta
20
+ from experimaestro.core.objects import ConfigInformation, ConfigWrapper, setmeta
21
+ from experimaestro.core.serializers import SerializedConfig
21
22
  from experimaestro.scheduler.workspace import RunMode
22
23
 
23
24
 
@@ -56,7 +57,7 @@ class Values:
56
57
 
57
58
 
58
59
  def getidentifier(x):
59
- if isinstance(x, TaskOutput):
60
+ if isinstance(x, ConfigWrapper):
60
61
  return x.__xpm__.identifier.all
61
62
  return x.__xpm__.identifier.all
62
63
 
@@ -387,6 +388,32 @@ def test_identifier_meta_default_array():
387
388
  )
388
389
 
389
390
 
391
+ # --- Check ConfigWrapper
392
+
393
+
394
+ class Model(Config):
395
+ def __post_init__(self):
396
+ self.initialized = False
397
+
398
+
399
+ class Trainer(Config):
400
+ model: Param[Model]
401
+
402
+
403
+ class SerializedModel(SerializedConfig):
404
+ def initialize(self):
405
+ self.config.initialized = True
406
+
407
+
408
+ def test_identifier_serialized_config():
409
+ trainer1 = Trainer(model=Model())
410
+ trainer2 = Trainer(model=SerializedModel(config=Model()))
411
+ assert_notequal(trainer1, trainer2)
412
+
413
+
414
+ # --- Check configuration reloads
415
+
416
+
390
417
  def check_reload(config):
391
418
  old_identifier = config.__xpm__.identifier.all
392
419
 
@@ -412,14 +439,14 @@ def test_identifier_reload_config():
412
439
  check_reload(IdentifierReloadConfig(id="123"))
413
440
 
414
441
 
415
- class IdentifierReloadTaskOutput(Task):
442
+ class IdentifierReloadConfigWrapper(Task):
416
443
  id: Param[str]
417
444
 
418
445
  def taskoutputs(self):
419
446
  return IdentifierReloadConfig(id=self.id)
420
447
 
421
448
 
422
- class IdentifierReloadTaskOutputDerived(Config):
449
+ class IdentifierReloadConfigWrapperDerived(Config):
423
450
  task: Param[IdentifierReloadConfig]
424
451
 
425
452
 
@@ -427,8 +454,8 @@ def test_identifier_reload_taskoutput():
427
454
  """When using a task output, the identifier should not be different"""
428
455
 
429
456
  # Creates the configuration
430
- task = IdentifierReloadTaskOutput(id="123").submit(run_mode=RunMode.DRY_RUN)
431
- config = IdentifierReloadTaskOutputDerived(task=task)
457
+ task = IdentifierReloadConfigWrapper(id="123").submit(run_mode=RunMode.DRY_RUN)
458
+ config = IdentifierReloadConfigWrapperDerived(task=task)
432
459
  check_reload(config)
433
460
 
434
461
 
@@ -1,6 +1,7 @@
1
1
  from typing import Optional
2
2
  from experimaestro import config, Param, Config
3
3
  from experimaestro.core.objects import TypeConfig
4
+ from experimaestro.core.serializers import SerializedConfig
4
5
 
5
6
 
6
7
  @config()
@@ -31,27 +32,29 @@ def test_simple_instance():
31
32
  assert isinstance(b.a, A.__xpmtype__.basetype)
32
33
 
33
34
 
34
- @config()
35
- class SerializedConfig:
36
- x: Param[int] = 1
37
- pass
35
+ class Model(Config):
36
+ def __post_init__(self):
37
+ self.initialized = False
38
+
38
39
 
40
+ class Trainer(Config):
41
+ model: Param[Model]
39
42
 
40
- class TestSerialization:
41
- """Test that a config can be serialized during execution"""
42
43
 
43
- def test_instance(self):
44
- import pickle
44
+ class SerializedModel(SerializedConfig):
45
+ def initialize(self):
46
+ self.config.initialized = True
45
47
 
46
- a = SerializedConfig(x=2).instance()
47
- assert not isinstance(a, TypeConfig)
48
- assert isinstance(a, SerializedConfig)
49
48
 
50
- s_a = pickle.dumps(a)
49
+ def test_instance_serialized():
50
+ model = SerializedModel(config=Model())
51
+ trainer = Trainer(model=model)
52
+ instance = trainer.instance()
51
53
 
52
- deserialized = pickle.loads(s_a)
53
- assert not isinstance(deserialized, TypeConfig)
54
- assert deserialized.x == 2
54
+ assert isinstance(
55
+ instance.model, Model
56
+ ), f"The model is not a Model but a {type(instance.model).__qualname__}"
57
+ assert instance.model.initialized, "The model was not initialized"
55
58
 
56
59
 
57
60
  class ConfigWithOptional(Config):
@@ -0,0 +1,50 @@
1
+ """Test for task outputs"""
2
+
3
+ from experimaestro import Config, Task, Param, ConfigWrapper
4
+ from experimaestro.scheduler.workspace import RunMode
5
+
6
+
7
+ class B(Config):
8
+ x: Param[int] = 1
9
+
10
+
11
+ class A(Config):
12
+ b: Param[B]
13
+
14
+
15
+ class Main(Task):
16
+ a: Param[A]
17
+
18
+ def taskoutputs(self):
19
+ return self.a, {
20
+ "a": self.a,
21
+ }
22
+
23
+ def execute(self):
24
+ print(self.a.b.x) # noqa: T201
25
+
26
+
27
+ class MainB(Task):
28
+ b: Param[B]
29
+
30
+ def execute(self):
31
+ pass
32
+
33
+
34
+ def test_output_taskoutput():
35
+ a = A(b=B())
36
+ output, ioutput = Main(a=a).submit(run_mode=RunMode.DRY_RUN)
37
+
38
+ assert isinstance(output, ConfigWrapper), "outputs should be task proxies"
39
+
40
+ # Direct
41
+ Main(a=output)
42
+
43
+ # Via getattr
44
+ Main(a=A(b=output.b))
45
+
46
+ # Via getitem
47
+ Main(a=ioutput["a"])
48
+
49
+ # Now, submits
50
+ Main(a=output).submit(run_mode=RunMode.DRY_RUN)
@@ -1,13 +1,11 @@
1
1
  # Test that progress notification work
2
2
  from copy import copy
3
- import logging
4
3
  from pathlib import Path
5
4
  import time
6
5
  import fasteners
7
6
  from typing import List, Tuple, Union
8
- from experimaestro.commandline import CommandLineJob
9
7
  from experimaestro import Task, Annotated, pathgenerator, progress, tqdm
10
- from experimaestro.core.objects import TaskOutput, logger
8
+ from experimaestro.core.objects import ConfigWrapper, logger
11
9
  from experimaestro.notifications import LevelInformation
12
10
  from experimaestro.scheduler import Job, Listener
13
11
  from queue import Queue
@@ -74,7 +72,7 @@ def test_progress_basic():
74
72
  listener = ProgressListener()
75
73
  xp.scheduler.addlistener(listener)
76
74
 
77
- out = ProgressingTask().submit() # type: TaskOutput
75
+ out: ConfigWrapper = ProgressingTask().submit()
78
76
  path = out.path # type: Path
79
77
  job = out.__xpm__.job
80
78
 
@@ -87,9 +85,9 @@ def test_progress_basic():
87
85
  for v in progresses:
88
86
  writeprogress(path, v)
89
87
  if v < 1:
90
- l = listener.progresses.get()[0]
91
- logger.info("Got %s", l)
92
- assert l.progress == v
88
+ info = listener.progresses.get()[0]
89
+ logger.info("Got %s", info)
90
+ assert info.progress == v
93
91
 
94
92
 
95
93
  def test_progress_multiple():
@@ -105,7 +103,7 @@ def test_progress_multiple():
105
103
  listener1 = ProgressListener()
106
104
  xp1.scheduler.addlistener(listener1)
107
105
 
108
- out = ProgressingTask().submit() # type: TaskOutput
106
+ out = ProgressingTask().submit() # type: ConfigWrapper
109
107
  path = out.path # type: Path
110
108
  job = out.__xpm__.job
111
109
 
@@ -219,7 +217,7 @@ def test_progress_nested():
219
217
  listener = ProgressListener()
220
218
  xp.scheduler.addlistener(listener)
221
219
 
222
- out = NestedProgressingTask().submit() # type: TaskOutput
220
+ out = NestedProgressingTask().submit() # type: ConfigWrapper
223
221
  job = out.__xpm__.job
224
222
  path = out.path # type: Path
225
223