experimaestro 1.11.1__tar.gz → 2.0.0a3__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 (162) hide show
  1. {experimaestro-1.11.1 → experimaestro-2.0.0a3}/PKG-INFO +1 -1
  2. {experimaestro-1.11.1 → experimaestro-2.0.0a3}/pyproject.toml +9 -3
  3. {experimaestro-1.11.1 → experimaestro-2.0.0a3}/src/experimaestro/annotations.py +1 -1
  4. {experimaestro-1.11.1 → experimaestro-2.0.0a3}/src/experimaestro/cli/__init__.py +10 -11
  5. experimaestro-2.0.0a3/src/experimaestro/cli/progress.py +269 -0
  6. {experimaestro-1.11.1 → experimaestro-2.0.0a3}/src/experimaestro/core/arguments.py +20 -1
  7. {experimaestro-1.11.1 → experimaestro-2.0.0a3}/src/experimaestro/core/identifier.py +11 -2
  8. {experimaestro-1.11.1 → experimaestro-2.0.0a3}/src/experimaestro/core/objects/config.py +119 -97
  9. {experimaestro-1.11.1 → experimaestro-2.0.0a3}/src/experimaestro/core/objects/config_walk.py +3 -1
  10. {experimaestro-1.11.1 → experimaestro-2.0.0a3}/src/experimaestro/core/types.py +35 -57
  11. {experimaestro-1.11.1 → experimaestro-2.0.0a3}/src/experimaestro/launcherfinder/registry.py +3 -3
  12. {experimaestro-1.11.1 → experimaestro-2.0.0a3}/src/experimaestro/mkdocs/base.py +6 -8
  13. {experimaestro-1.11.1 → experimaestro-2.0.0a3}/src/experimaestro/notifications.py +12 -3
  14. experimaestro-2.0.0a3/src/experimaestro/progress.py +406 -0
  15. {experimaestro-1.11.1 → experimaestro-2.0.0a3}/src/experimaestro/settings.py +4 -2
  16. {experimaestro-1.11.1 → experimaestro-2.0.0a3}/src/experimaestro/tests/launchers/common.py +2 -2
  17. {experimaestro-1.11.1 → experimaestro-2.0.0a3}/src/experimaestro/tests/restart.py +1 -1
  18. {experimaestro-1.11.1 → experimaestro-2.0.0a3}/src/experimaestro/tests/test_checkers.py +2 -2
  19. {experimaestro-1.11.1 → experimaestro-2.0.0a3}/src/experimaestro/tests/test_dependencies.py +12 -12
  20. {experimaestro-1.11.1 → experimaestro-2.0.0a3}/src/experimaestro/tests/test_experiment.py +3 -3
  21. experimaestro-2.0.0a3/src/experimaestro/tests/test_file_progress.py +425 -0
  22. experimaestro-2.0.0a3/src/experimaestro/tests/test_file_progress_integration.py +477 -0
  23. experimaestro-2.0.0a3/src/experimaestro/tests/test_generators.py +68 -0
  24. {experimaestro-1.11.1 → experimaestro-2.0.0a3}/src/experimaestro/tests/test_identifier.py +90 -81
  25. {experimaestro-1.11.1 → experimaestro-2.0.0a3}/src/experimaestro/tests/test_instance.py +16 -9
  26. {experimaestro-1.11.1 → experimaestro-2.0.0a3}/src/experimaestro/tests/test_objects.py +9 -32
  27. {experimaestro-1.11.1 → experimaestro-2.0.0a3}/src/experimaestro/tests/test_outputs.py +6 -6
  28. {experimaestro-1.11.1 → experimaestro-2.0.0a3}/src/experimaestro/tests/test_param.py +14 -14
  29. {experimaestro-1.11.1 → experimaestro-2.0.0a3}/src/experimaestro/tests/test_progress.py +4 -4
  30. {experimaestro-1.11.1 → experimaestro-2.0.0a3}/src/experimaestro/tests/test_serializers.py +5 -5
  31. {experimaestro-1.11.1 → experimaestro-2.0.0a3}/src/experimaestro/tests/test_tags.py +15 -15
  32. {experimaestro-1.11.1 → experimaestro-2.0.0a3}/src/experimaestro/tests/test_tasks.py +40 -36
  33. {experimaestro-1.11.1 → experimaestro-2.0.0a3}/src/experimaestro/tests/test_tokens.py +8 -6
  34. {experimaestro-1.11.1 → experimaestro-2.0.0a3}/src/experimaestro/tests/test_types.py +10 -10
  35. {experimaestro-1.11.1 → experimaestro-2.0.0a3}/src/experimaestro/tests/test_validation.py +19 -19
  36. {experimaestro-1.11.1 → experimaestro-2.0.0a3}/src/experimaestro/tests/token_reschedule.py +1 -1
  37. {experimaestro-1.11.1 → experimaestro-2.0.0a3}/LICENSE +0 -0
  38. {experimaestro-1.11.1 → experimaestro-2.0.0a3}/README.md +0 -0
  39. {experimaestro-1.11.1 → experimaestro-2.0.0a3}/src/experimaestro/__init__.py +0 -0
  40. {experimaestro-1.11.1 → experimaestro-2.0.0a3}/src/experimaestro/__main__.py +0 -0
  41. {experimaestro-1.11.1 → experimaestro-2.0.0a3}/src/experimaestro/checkers.py +0 -0
  42. {experimaestro-1.11.1 → experimaestro-2.0.0a3}/src/experimaestro/cli/filter.py +0 -0
  43. {experimaestro-1.11.1 → experimaestro-2.0.0a3}/src/experimaestro/cli/jobs.py +0 -0
  44. {experimaestro-1.11.1 → experimaestro-2.0.0a3}/src/experimaestro/click.py +0 -0
  45. {experimaestro-1.11.1 → experimaestro-2.0.0a3}/src/experimaestro/commandline.py +0 -0
  46. {experimaestro-1.11.1 → experimaestro-2.0.0a3}/src/experimaestro/compat.py +0 -0
  47. {experimaestro-1.11.1 → experimaestro-2.0.0a3}/src/experimaestro/connectors/__init__.py +0 -0
  48. {experimaestro-1.11.1 → experimaestro-2.0.0a3}/src/experimaestro/connectors/local.py +0 -0
  49. {experimaestro-1.11.1 → experimaestro-2.0.0a3}/src/experimaestro/connectors/ssh.py +0 -0
  50. {experimaestro-1.11.1 → experimaestro-2.0.0a3}/src/experimaestro/core/__init__.py +0 -0
  51. {experimaestro-1.11.1 → experimaestro-2.0.0a3}/src/experimaestro/core/callbacks.py +0 -0
  52. {experimaestro-1.11.1 → experimaestro-2.0.0a3}/src/experimaestro/core/context.py +0 -0
  53. {experimaestro-1.11.1 → experimaestro-2.0.0a3}/src/experimaestro/core/objects/__init__.py +0 -0
  54. {experimaestro-1.11.1 → experimaestro-2.0.0a3}/src/experimaestro/core/objects/config_utils.py +0 -0
  55. {experimaestro-1.11.1 → experimaestro-2.0.0a3}/src/experimaestro/core/objects.pyi +0 -0
  56. {experimaestro-1.11.1 → experimaestro-2.0.0a3}/src/experimaestro/core/serialization.py +0 -0
  57. {experimaestro-1.11.1 → experimaestro-2.0.0a3}/src/experimaestro/core/serializers.py +0 -0
  58. {experimaestro-1.11.1 → experimaestro-2.0.0a3}/src/experimaestro/core/utils.py +0 -0
  59. {experimaestro-1.11.1 → experimaestro-2.0.0a3}/src/experimaestro/exceptions.py +0 -0
  60. {experimaestro-1.11.1 → experimaestro-2.0.0a3}/src/experimaestro/experiments/__init__.py +0 -0
  61. {experimaestro-1.11.1 → experimaestro-2.0.0a3}/src/experimaestro/experiments/cli.py +0 -0
  62. {experimaestro-1.11.1 → experimaestro-2.0.0a3}/src/experimaestro/experiments/configuration.py +0 -0
  63. {experimaestro-1.11.1 → experimaestro-2.0.0a3}/src/experimaestro/generators.py +0 -0
  64. {experimaestro-1.11.1 → experimaestro-2.0.0a3}/src/experimaestro/huggingface.py +0 -0
  65. {experimaestro-1.11.1 → experimaestro-2.0.0a3}/src/experimaestro/ipc.py +0 -0
  66. {experimaestro-1.11.1 → experimaestro-2.0.0a3}/src/experimaestro/launcherfinder/__init__.py +0 -0
  67. {experimaestro-1.11.1 → experimaestro-2.0.0a3}/src/experimaestro/launcherfinder/base.py +0 -0
  68. {experimaestro-1.11.1 → experimaestro-2.0.0a3}/src/experimaestro/launcherfinder/parser.py +0 -0
  69. {experimaestro-1.11.1 → experimaestro-2.0.0a3}/src/experimaestro/launcherfinder/specs.py +0 -0
  70. {experimaestro-1.11.1 → experimaestro-2.0.0a3}/src/experimaestro/launchers/__init__.py +0 -0
  71. {experimaestro-1.11.1 → experimaestro-2.0.0a3}/src/experimaestro/launchers/direct.py +0 -0
  72. {experimaestro-1.11.1 → experimaestro-2.0.0a3}/src/experimaestro/launchers/oar.py +0 -0
  73. {experimaestro-1.11.1 → experimaestro-2.0.0a3}/src/experimaestro/launchers/slurm/__init__.py +0 -0
  74. {experimaestro-1.11.1 → experimaestro-2.0.0a3}/src/experimaestro/launchers/slurm/base.py +0 -0
  75. {experimaestro-1.11.1 → experimaestro-2.0.0a3}/src/experimaestro/locking.py +0 -0
  76. {experimaestro-1.11.1 → experimaestro-2.0.0a3}/src/experimaestro/mkdocs/__init__.py +0 -0
  77. {experimaestro-1.11.1 → experimaestro-2.0.0a3}/src/experimaestro/mkdocs/annotations.py +0 -0
  78. {experimaestro-1.11.1 → experimaestro-2.0.0a3}/src/experimaestro/mkdocs/metaloader.py +0 -0
  79. {experimaestro-1.11.1 → experimaestro-2.0.0a3}/src/experimaestro/mkdocs/style.css +0 -0
  80. {experimaestro-1.11.1 → experimaestro-2.0.0a3}/src/experimaestro/mypy.py +0 -0
  81. {experimaestro-1.11.1 → experimaestro-2.0.0a3}/src/experimaestro/py.typed +0 -0
  82. {experimaestro-1.11.1 → experimaestro-2.0.0a3}/src/experimaestro/rpyc.py +0 -0
  83. {experimaestro-1.11.1 → experimaestro-2.0.0a3}/src/experimaestro/run.py +0 -0
  84. {experimaestro-1.11.1 → experimaestro-2.0.0a3}/src/experimaestro/scheduler/__init__.py +0 -0
  85. {experimaestro-1.11.1 → experimaestro-2.0.0a3}/src/experimaestro/scheduler/base.py +0 -0
  86. {experimaestro-1.11.1 → experimaestro-2.0.0a3}/src/experimaestro/scheduler/dependencies.py +0 -0
  87. {experimaestro-1.11.1 → experimaestro-2.0.0a3}/src/experimaestro/scheduler/dynamic_outputs.py +0 -0
  88. {experimaestro-1.11.1 → experimaestro-2.0.0a3}/src/experimaestro/scheduler/services.py +0 -0
  89. {experimaestro-1.11.1 → experimaestro-2.0.0a3}/src/experimaestro/scheduler/state.py +0 -0
  90. {experimaestro-1.11.1 → experimaestro-2.0.0a3}/src/experimaestro/scheduler/workspace.py +0 -0
  91. {experimaestro-1.11.1 → experimaestro-2.0.0a3}/src/experimaestro/scriptbuilder.py +0 -0
  92. {experimaestro-1.11.1 → experimaestro-2.0.0a3}/src/experimaestro/server/__init__.py +0 -0
  93. {experimaestro-1.11.1 → experimaestro-2.0.0a3}/src/experimaestro/server/data/0c35d18bf06992036b69.woff2 +0 -0
  94. {experimaestro-1.11.1 → experimaestro-2.0.0a3}/src/experimaestro/server/data/1815e00441357e01619e.ttf +0 -0
  95. {experimaestro-1.11.1 → experimaestro-2.0.0a3}/src/experimaestro/server/data/219aa9140e099e6c72ed.woff2 +0 -0
  96. {experimaestro-1.11.1 → experimaestro-2.0.0a3}/src/experimaestro/server/data/2463b90d9a316e4e5294.woff2 +0 -0
  97. {experimaestro-1.11.1 → experimaestro-2.0.0a3}/src/experimaestro/server/data/2582b0e4bcf85eceead0.ttf +0 -0
  98. {experimaestro-1.11.1 → experimaestro-2.0.0a3}/src/experimaestro/server/data/3a4004a46a653d4b2166.woff +0 -0
  99. {experimaestro-1.11.1 → experimaestro-2.0.0a3}/src/experimaestro/server/data/3baa5b8f3469222b822d.woff +0 -0
  100. {experimaestro-1.11.1 → experimaestro-2.0.0a3}/src/experimaestro/server/data/4d73cb90e394b34b7670.woff +0 -0
  101. {experimaestro-1.11.1 → experimaestro-2.0.0a3}/src/experimaestro/server/data/4ef4218c522f1eb6b5b1.woff2 +0 -0
  102. {experimaestro-1.11.1 → experimaestro-2.0.0a3}/src/experimaestro/server/data/5d681e2edae8c60630db.woff +0 -0
  103. {experimaestro-1.11.1 → experimaestro-2.0.0a3}/src/experimaestro/server/data/6f420cf17cc0d7676fad.woff2 +0 -0
  104. {experimaestro-1.11.1 → experimaestro-2.0.0a3}/src/experimaestro/server/data/89999bdf5d835c012025.woff2 +0 -0
  105. {experimaestro-1.11.1 → experimaestro-2.0.0a3}/src/experimaestro/server/data/914997e1bdfc990d0897.ttf +0 -0
  106. {experimaestro-1.11.1 → experimaestro-2.0.0a3}/src/experimaestro/server/data/c210719e60948b211a12.woff2 +0 -0
  107. {experimaestro-1.11.1 → experimaestro-2.0.0a3}/src/experimaestro/server/data/c380809fd3677d7d6903.woff2 +0 -0
  108. {experimaestro-1.11.1 → experimaestro-2.0.0a3}/src/experimaestro/server/data/f882956fd323fd322f31.woff +0 -0
  109. {experimaestro-1.11.1 → experimaestro-2.0.0a3}/src/experimaestro/server/data/favicon.ico +0 -0
  110. {experimaestro-1.11.1 → experimaestro-2.0.0a3}/src/experimaestro/server/data/index.css +0 -0
  111. {experimaestro-1.11.1 → experimaestro-2.0.0a3}/src/experimaestro/server/data/index.css.map +0 -0
  112. {experimaestro-1.11.1 → experimaestro-2.0.0a3}/src/experimaestro/server/data/index.html +0 -0
  113. {experimaestro-1.11.1 → experimaestro-2.0.0a3}/src/experimaestro/server/data/index.js +0 -0
  114. {experimaestro-1.11.1 → experimaestro-2.0.0a3}/src/experimaestro/server/data/index.js.map +0 -0
  115. {experimaestro-1.11.1 → experimaestro-2.0.0a3}/src/experimaestro/server/data/login.html +0 -0
  116. {experimaestro-1.11.1 → experimaestro-2.0.0a3}/src/experimaestro/server/data/manifest.json +0 -0
  117. {experimaestro-1.11.1 → experimaestro-2.0.0a3}/src/experimaestro/sphinx/__init__.py +0 -0
  118. {experimaestro-1.11.1 → experimaestro-2.0.0a3}/src/experimaestro/sphinx/static/experimaestro.css +0 -0
  119. {experimaestro-1.11.1 → experimaestro-2.0.0a3}/src/experimaestro/taskglobals.py +0 -0
  120. {experimaestro-1.11.1 → experimaestro-2.0.0a3}/src/experimaestro/tests/__init__.py +0 -0
  121. {experimaestro-1.11.1 → experimaestro-2.0.0a3}/src/experimaestro/tests/conftest.py +0 -0
  122. {experimaestro-1.11.1 → experimaestro-2.0.0a3}/src/experimaestro/tests/connectors/bin/executable.py +0 -0
  123. {experimaestro-1.11.1 → experimaestro-2.0.0a3}/src/experimaestro/tests/connectors/test_local.py +0 -0
  124. {experimaestro-1.11.1 → experimaestro-2.0.0a3}/src/experimaestro/tests/connectors/utils.py +0 -0
  125. {experimaestro-1.11.1 → experimaestro-2.0.0a3}/src/experimaestro/tests/core/__init__.py +0 -0
  126. {experimaestro-1.11.1 → experimaestro-2.0.0a3}/src/experimaestro/tests/core/test_generics.py +0 -0
  127. {experimaestro-1.11.1 → experimaestro-2.0.0a3}/src/experimaestro/tests/definitions_types.py +0 -0
  128. {experimaestro-1.11.1 → experimaestro-2.0.0a3}/src/experimaestro/tests/launchers/__init__.py +0 -0
  129. {experimaestro-1.11.1 → experimaestro-2.0.0a3}/src/experimaestro/tests/launchers/bin/sacct +0 -0
  130. {experimaestro-1.11.1 → experimaestro-2.0.0a3}/src/experimaestro/tests/launchers/bin/sbatch +0 -0
  131. {experimaestro-1.11.1 → experimaestro-2.0.0a3}/src/experimaestro/tests/launchers/bin/srun +0 -0
  132. {experimaestro-1.11.1 → experimaestro-2.0.0a3}/src/experimaestro/tests/launchers/bin/test.py +0 -0
  133. {experimaestro-1.11.1 → experimaestro-2.0.0a3}/src/experimaestro/tests/launchers/config_slurm/__init__.py +0 -0
  134. {experimaestro-1.11.1 → experimaestro-2.0.0a3}/src/experimaestro/tests/launchers/config_slurm/launchers.py +0 -0
  135. {experimaestro-1.11.1 → experimaestro-2.0.0a3}/src/experimaestro/tests/launchers/test_local.py +0 -0
  136. {experimaestro-1.11.1 → experimaestro-2.0.0a3}/src/experimaestro/tests/launchers/test_slurm.py +0 -0
  137. {experimaestro-1.11.1 → experimaestro-2.0.0a3}/src/experimaestro/tests/restart_main.py +0 -0
  138. {experimaestro-1.11.1 → experimaestro-2.0.0a3}/src/experimaestro/tests/scripts/notifyandwait.py +0 -0
  139. {experimaestro-1.11.1 → experimaestro-2.0.0a3}/src/experimaestro/tests/scripts/waitforfile.py +0 -0
  140. {experimaestro-1.11.1 → experimaestro-2.0.0a3}/src/experimaestro/tests/task_tokens.py +0 -0
  141. {experimaestro-1.11.1 → experimaestro-2.0.0a3}/src/experimaestro/tests/tasks/__init__.py +0 -0
  142. {experimaestro-1.11.1 → experimaestro-2.0.0a3}/src/experimaestro/tests/tasks/all.py +0 -0
  143. {experimaestro-1.11.1 → experimaestro-2.0.0a3}/src/experimaestro/tests/tasks/foreign.py +0 -0
  144. {experimaestro-1.11.1 → experimaestro-2.0.0a3}/src/experimaestro/tests/test_findlauncher.py +0 -0
  145. {experimaestro-1.11.1 → experimaestro-2.0.0a3}/src/experimaestro/tests/test_forward.py +0 -0
  146. {experimaestro-1.11.1 → experimaestro-2.0.0a3}/src/experimaestro/tests/test_snippets.py +0 -0
  147. {experimaestro-1.11.1 → experimaestro-2.0.0a3}/src/experimaestro/tests/test_ssh.py +0 -0
  148. {experimaestro-1.11.1 → experimaestro-2.0.0a3}/src/experimaestro/tests/utils.py +0 -0
  149. {experimaestro-1.11.1 → experimaestro-2.0.0a3}/src/experimaestro/tokens.py +0 -0
  150. {experimaestro-1.11.1 → experimaestro-2.0.0a3}/src/experimaestro/tools/__init__.py +0 -0
  151. {experimaestro-1.11.1 → experimaestro-2.0.0a3}/src/experimaestro/tools/diff.py +0 -0
  152. {experimaestro-1.11.1 → experimaestro-2.0.0a3}/src/experimaestro/tools/documentation.py +0 -0
  153. {experimaestro-1.11.1 → experimaestro-2.0.0a3}/src/experimaestro/tools/jobs.py +0 -0
  154. {experimaestro-1.11.1 → experimaestro-2.0.0a3}/src/experimaestro/typingutils.py +0 -0
  155. {experimaestro-1.11.1 → experimaestro-2.0.0a3}/src/experimaestro/utils/__init__.py +0 -0
  156. {experimaestro-1.11.1 → experimaestro-2.0.0a3}/src/experimaestro/utils/asyncio.py +0 -0
  157. {experimaestro-1.11.1 → experimaestro-2.0.0a3}/src/experimaestro/utils/jobs.py +0 -0
  158. {experimaestro-1.11.1 → experimaestro-2.0.0a3}/src/experimaestro/utils/jupyter.py +0 -0
  159. {experimaestro-1.11.1 → experimaestro-2.0.0a3}/src/experimaestro/utils/multiprocessing.py +0 -0
  160. {experimaestro-1.11.1 → experimaestro-2.0.0a3}/src/experimaestro/utils/resources.py +0 -0
  161. {experimaestro-1.11.1 → experimaestro-2.0.0a3}/src/experimaestro/utils/settings.py +0 -0
  162. {experimaestro-1.11.1 → experimaestro-2.0.0a3}/src/experimaestro/xpmutils.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: experimaestro
3
- Version: 1.11.1
3
+ Version: 2.0.0a3
4
4
  Summary: "Experimaestro is a computer science experiment manager"
5
5
  License: GPL-3
6
6
  Keywords: experiment manager
@@ -48,7 +48,7 @@ dependencies = [
48
48
  "typing-extensions >=4.2; python_version < \"3.12\"",
49
49
  "watchdog >=2"
50
50
  ]
51
- version = "1.11.1"
51
+ version = "2.0.0-a3"
52
52
 
53
53
  [tool.poetry-dynamic-versioning]
54
54
  enable = false
@@ -56,7 +56,7 @@ vcs = "git"
56
56
  style = "semver"
57
57
  dirty = true
58
58
  format-jinja = """{%- set pre = [] -%}{%- set metadata = [] -%}
59
- {%- if revision is not none -%}{{ pre.append("rc" + revision|string) or "" }}{%- endif -%}
59
+ {%- if revision is not none -%}{{ pre.append(stage|string + revision|string) or "" }}{%- endif -%}
60
60
  {%- if distance > 0 -%}{{ metadata.append(distance|string) or "" }}{%- endif -%}
61
61
  {{ serialize_semver(base, pre, metadata)}}"""
62
62
 
@@ -83,6 +83,12 @@ poetry-dynamic-versioning = { version = ">=1.0.0,<2.0.0", extras = ["plugin"] }
83
83
  requires = ["poetry-core>=1.0.0", "poetry-dynamic-versioning>=1.0.0,<2.0.0"]
84
84
  build-backend = "poetry_dynamic_versioning.backend"
85
85
 
86
+ [dependency-groups]
87
+ dev = [
88
+ "pytest>=8.4.1",
89
+ "pytest-timeout>=2.4.0",
90
+ ]
91
+
86
92
  [tool.poetry.group.ssh]
87
93
  optional = true
88
94
 
@@ -140,7 +146,7 @@ warn_unused_ignores = true
140
146
 
141
147
  [tool.commitizen]
142
148
  name = "cz_conventional_commits"
143
- version = "1.11.1"
149
+ version = "2.0.0a3"
144
150
  changelog_start_rev = "v1.0.0"
145
151
  tag_format = "v$major.$minor.$patch$prerelease"
146
152
  # update_changelog_on_bump = true
@@ -71,7 +71,7 @@ class config:
71
71
  assert inspect.isclass(tp), f"{tp} is not a class"
72
72
 
73
73
  # Adds to xpminfo for on demand creation of information
74
- return ObjectType(tp, identifier=self.identifier).basetype
74
+ return ObjectType(tp, identifier=self.identifier).valuetype
75
75
 
76
76
 
77
77
  class Array(TypeProxy):
@@ -20,16 +20,6 @@ from experimaestro.settings import find_workspace
20
20
  logging.basicConfig(level=logging.INFO)
21
21
 
22
22
 
23
- def pass_cfg(f):
24
- """Pass configuration information"""
25
-
26
- @click.pass_context
27
- def new_func(ctx, *args, **kwargs):
28
- return ctx.invoke(f, ctx.obj, *args, **kwargs)
29
-
30
- return update_wrapper(new_func, f)
31
-
32
-
33
23
  def check_xp_path(ctx, self, path: Path):
34
24
  if not (path / ".__experimaestro__").is_file():
35
25
  cprint(f"{path} is not an experimaestro working directory", "red")
@@ -142,7 +132,6 @@ def diff(path: Path):
142
132
  """Show the reason of the identifier change for a job"""
143
133
  from experimaestro.tools.jobs import load_job
144
134
  from experimaestro import Config
145
- from experimaestro.core.objects import ConfigWalkContext
146
135
 
147
136
  _, job = load_job(path / "params.json", discard_id=False)
148
137
  _, new_job = load_job(path / "params.json")
@@ -290,6 +279,16 @@ cli.add_command(Launchers("launchers", help="Launcher specific commands"))
290
279
  cli.add_command(Launchers("connectors", help="Connector specific commands"))
291
280
  cli.add_command(Launchers("tokens", help="Token specific commands"))
292
281
 
282
+ # Import and add progress commands
283
+ from .progress import progress as progress_cli
284
+
285
+ cli.add_command(progress_cli)
286
+
287
+ # Import and add jobs commands
288
+ from .jobs import jobs as jobs_cli
289
+
290
+ cli.add_command(jobs_cli)
291
+
293
292
 
294
293
  @cli.group()
295
294
  @click.option("--workdir", type=Path, default=None)
@@ -0,0 +1,269 @@
1
+ """Simplified CLI commands for managing and viewing progress files"""
2
+
3
+ import time
4
+ from datetime import datetime
5
+ from pathlib import Path
6
+ from typing import Optional, Dict
7
+
8
+ import click
9
+ from termcolor import colored
10
+
11
+ try:
12
+ from tqdm import tqdm
13
+
14
+ TQDM_AVAILABLE = True
15
+ except ImportError:
16
+ TQDM_AVAILABLE = False
17
+
18
+ from experimaestro.progress import ProgressEntry, ProgressFileReader
19
+ from experimaestro.settings import find_workspace
20
+ from . import cli
21
+
22
+
23
+ @click.option("--workspace", default="", help="Experimaestro workspace")
24
+ @click.option("--workdir", type=Path, default=None)
25
+ @cli.group()
26
+ @click.pass_context
27
+ def progress(
28
+ ctx,
29
+ workdir: Optional[Path],
30
+ workspace: Optional[str],
31
+ ):
32
+ """Progress tracking commands"""
33
+ ctx.obj.workspace = find_workspace(workdir=workdir, workspace=workspace)
34
+
35
+
36
+ def format_timestamp(timestamp: float) -> str:
37
+ """Format timestamp for display"""
38
+ dt = datetime.fromtimestamp(timestamp)
39
+ return dt.strftime("%Y-%m-%d %H:%M:%S")
40
+
41
+
42
+ @click.argument("jobid", type=str)
43
+ @progress.command()
44
+ @click.pass_context
45
+ def show(ctx, jobid: str):
46
+ """Show current progress state (default command)
47
+
48
+ JOBID format: task_name/task_hash
49
+ """
50
+ try:
51
+ task_name, task_hash = jobid.split("/")
52
+ except ValueError:
53
+ raise click.ClickException("JOBID must be in format task_name/task_hash")
54
+
55
+ workspace = ctx.obj.workspace
56
+ task_path = workspace.path / "jobs" / task_name / task_hash
57
+
58
+ if not task_path.exists():
59
+ raise click.ClickException(f"Job directory not found: {task_path}")
60
+
61
+ reader = ProgressFileReader(task_path)
62
+ current_progress = reader.get_current_progress()
63
+
64
+ if not current_progress:
65
+ click.echo("No progress information available")
66
+ return
67
+
68
+ # Filter out EOJ markers
69
+ current_progress = {k: v for k, v in current_progress.items() if k != -1}
70
+
71
+ if not current_progress:
72
+ click.echo("No progress information available")
73
+ return
74
+
75
+ click.echo(f"Progress for job {jobid}")
76
+ click.echo("=" * 80)
77
+
78
+ # Show simple text-based progress for each level
79
+ for level in sorted(current_progress.keys()):
80
+ entry = current_progress[level]
81
+ indent = " " * level
82
+ progress_pct = f"{entry.progress * 100:5.1f}%"
83
+ desc = entry.desc or f"Level {level}"
84
+ timestamp = format_timestamp(entry.timestamp)
85
+
86
+ color = "green" if entry.progress >= 1.0 else "yellow"
87
+ click.echo(colored(f"{indent}L{level}: {progress_pct} - {desc}", color))
88
+ click.echo(colored(f"{indent} Last updated: {timestamp}", "cyan"))
89
+
90
+
91
+ def create_progress_bar(
92
+ level: int,
93
+ desc: str,
94
+ progress: float = 0.0,
95
+ ) -> tqdm:
96
+ """Create a properly aligned progress bar like dashboard style"""
97
+ if level > 0:
98
+ indent = " " * (level - 1) + "└─ "
99
+ else:
100
+ indent = ""
101
+ label = f"{indent}L{level}"
102
+
103
+ colors = ["blue", "yellow", "magenta", "cyan", "white"]
104
+ bar_color = colors[level % len(colors)]
105
+
106
+ unit = desc[:50] if desc else f"Level {level}"
107
+ ncols = 100
108
+ wbar = 50
109
+
110
+ return tqdm(
111
+ total=100,
112
+ desc=label,
113
+ position=level,
114
+ leave=True,
115
+ bar_format=f"{{desc}}: {{percentage:3.0f}}%|{{bar:{wbar - len(indent)}}}| {{unit}}", # noqa: F541
116
+ ncols=ncols, # Adjust width based on level
117
+ unit=unit,
118
+ colour=bar_color,
119
+ initial=progress * 100,
120
+ )
121
+
122
+
123
+ def _update_progress_display(
124
+ reader: ProgressFileReader, progress_bars: Dict[int, tqdm]
125
+ ) -> bool:
126
+ """Update the tqdm progress bars in dashboard style"""
127
+ current_state: Dict[int, ProgressEntry] = {
128
+ k: v for k, v in reader.get_current_state().items() if k != -1
129
+ }
130
+
131
+ if not current_state:
132
+ click.echo("No progress information available yet...")
133
+ return False
134
+
135
+ # Update existing bars and create new ones
136
+ for _level, entry in current_state.items():
137
+ progress_val = entry.progress * 100
138
+ desc = entry.desc or f"Level {entry.level}"
139
+
140
+ if entry.level not in progress_bars:
141
+ progress_bars[entry.level] = create_progress_bar(
142
+ entry.level, desc, progress_val
143
+ )
144
+
145
+ bar = progress_bars[entry.level]
146
+ bar.unit = desc[:50]
147
+ bar.n = progress_val
148
+
149
+ bar.refresh()
150
+
151
+ # Remove bars for levels that no longer exist
152
+ levels_to_remove = set(progress_bars.keys()) - set(current_state.keys())
153
+ for level in levels_to_remove:
154
+ progress_bars[level].close()
155
+ del progress_bars[level]
156
+
157
+ return True
158
+
159
+
160
+ @click.argument("jobid", type=str)
161
+ @click.option("--refresh-rate", "-r", default=0.5, help="Refresh rate in seconds")
162
+ @progress.command()
163
+ @click.pass_context
164
+ def live(ctx, jobid: str, refresh_rate: float):
165
+ """Show live progress with tqdm-style bars
166
+
167
+ JOBID format: task_name/task_hash
168
+ """
169
+ if not TQDM_AVAILABLE:
170
+ click.echo("tqdm is not available. Install with: pip install tqdm")
171
+ click.echo("Falling back to basic display...")
172
+ ctx.invoke(show, jobid=jobid)
173
+ return
174
+
175
+ try:
176
+ task_name, task_hash = jobid.split("/")
177
+ except ValueError:
178
+ raise click.ClickException("JOBID must be in format task_name/task_hash")
179
+
180
+ workspace = ctx.obj.workspace
181
+ task_path = workspace.path / "jobs" / task_name / task_hash
182
+
183
+ if not task_path.exists():
184
+ raise click.ClickException(f"Job directory not found: {task_path}")
185
+
186
+ reader = ProgressFileReader(task_path)
187
+ progress_bars: Dict[int, tqdm] = {}
188
+
189
+ def cleanup_bars():
190
+ """Clean up all progress bars"""
191
+ for bar in progress_bars.values():
192
+ bar.close()
193
+ progress_bars.clear()
194
+
195
+ click.echo(f"Live progress for job {jobid}")
196
+ click.echo("Press Ctrl+C to stop")
197
+ click.echo("=" * 80)
198
+
199
+ try:
200
+ if not _update_progress_display(reader, progress_bars):
201
+ click.echo("No progress information available yet...")
202
+
203
+ while True:
204
+ time.sleep(refresh_rate)
205
+
206
+ if not _update_progress_display(reader, progress_bars):
207
+ # Check if job is complete
208
+ if reader.is_done():
209
+ click.echo("\nJob completed!")
210
+ break
211
+
212
+ # Check if all progress bars are at 100%
213
+ if progress_bars and all(bar.n >= 100 for bar in progress_bars.values()):
214
+ cleanup_bars()
215
+ click.echo("\nAll progress completed!")
216
+ break
217
+
218
+ except KeyboardInterrupt:
219
+ click.echo("\nStopped monitoring progress")
220
+ finally:
221
+ cleanup_bars()
222
+
223
+
224
+ @progress.command(name="list")
225
+ @click.pass_context
226
+ def list_jobs(ctx):
227
+ """List all jobs with progress information"""
228
+ ws = ctx.obj.workspace
229
+ jobs_path = ws.path / "jobs"
230
+
231
+ if not jobs_path.exists():
232
+ click.echo("No jobs directory found")
233
+ return
234
+
235
+ for task_dir in jobs_path.iterdir():
236
+ if not task_dir.is_dir():
237
+ continue
238
+
239
+ for job_dir in task_dir.iterdir():
240
+ if not job_dir.is_dir():
241
+ continue
242
+
243
+ progress_dir = job_dir / ".experimaestro"
244
+ if not progress_dir.exists():
245
+ continue
246
+
247
+ # Check if there are progress files
248
+ progress_files = list(progress_dir.glob("progress-*.jsonl"))
249
+ if not progress_files:
250
+ continue
251
+
252
+ job_id = f"{task_dir.name}/{job_dir.name}"
253
+ reader = ProgressFileReader(job_dir)
254
+ current_state = reader.get_current_state()
255
+
256
+ # if current_progress:
257
+ if current_state:
258
+ # Get overall progress (level 0)
259
+ level_0 = current_state.get(0)
260
+ if level_0:
261
+ color = "green" if level_0.progress >= 1.0 else "yellow"
262
+ desc = f"{level_0.desc}" if level_0.desc else ""
263
+ progress_pct = f"{level_0.progress * 100:5.1f}%"
264
+ click.echo(colored(f"{job_id:50} - {progress_pct} - {desc}", color))
265
+
266
+ else:
267
+ click.echo(f"{job_id:50} No level 0 progress")
268
+ else:
269
+ click.echo(f"{job_id:50} No progress data")
@@ -80,10 +80,13 @@ class Argument:
80
80
 
81
81
  self.generator = generator
82
82
  self.default = None
83
+ self.ignore_generated = False
83
84
 
84
85
  if default is not None:
85
86
  assert self.generator is None, "generator and default are exclusive options"
86
87
  if isinstance(default, field):
88
+ self.ignore_generated = default.ignore_generated
89
+
87
90
  if default.default is not None:
88
91
  self.default = default.default
89
92
  elif default.default_factory is not None:
@@ -184,13 +187,29 @@ DataPath = Annotated[Path, dataHint]
184
187
  class field:
185
188
  """Extra information for a given experimaestro field (param or meta)"""
186
189
 
187
- def __init__(self, *, default: Any = None, default_factory: Callable = None):
190
+ def __init__(
191
+ self,
192
+ *,
193
+ default: Any = None,
194
+ default_factory: Callable = None,
195
+ ignore_generated=False,
196
+ ):
197
+ """Gives some extra per-field information
198
+
199
+ :param default: a default value, defaults to None
200
+ :param default_factory: a default factory for values, defaults to None
201
+ :param ignore_generated: True if the value is hidden – it won't be accessible in
202
+ tasks, defaults to False. The interest of hidden is to add a
203
+ configuration field that changes the identifier, but will not be
204
+ used.
205
+ """
188
206
  assert not (
189
207
  (default is not None) and (default_factory is not None)
190
208
  ), "default and default_factory are mutually exclusive options"
191
209
 
192
210
  self.default_factory = default_factory
193
211
  self.default = default
212
+ self.ignore_generated = ignore_generated
194
213
 
195
214
 
196
215
  class help(TypeAnnotation):
@@ -170,7 +170,7 @@ class IdentifierComputer:
170
170
  self._hashupdate(IdentifierComputer.ENUM_ID)
171
171
  k = value.__class__
172
172
  self._hashupdate(
173
- f"{k.__module__}.{k.__qualname__ }:{value.name}".encode("utf-8"),
173
+ f"{k.__module__}.{k.__qualname__}:{value.name}".encode("utf-8"),
174
174
  )
175
175
  elif isinstance(value, dict):
176
176
  self._hashupdate(IdentifierComputer.DICT_ID)
@@ -235,7 +235,16 @@ class IdentifierComputer:
235
235
  # Skip if the argument is not a constant, and
236
236
  # - optional argument: both value and default are None
237
237
  # - the argument value is equal to the default value
238
- argvalue = getattr(value, argument.name, None)
238
+ try:
239
+ argvalue = getattr(value, argument.name, None)
240
+ except KeyError:
241
+ logging.warning(
242
+ "Parameter %s has not been set in %s created at %s",
243
+ argument.name,
244
+ value,
245
+ value.__xpm__._initinfo,
246
+ )
247
+ raise
239
248
  if not argument.constant and (
240
249
  (
241
250
  not argument.required