experimaestro 1.8.0rc6__tar.gz → 1.8.0rc7__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.8.0rc6 → experimaestro-1.8.0rc7}/PKG-INFO +5 -5
  2. {experimaestro-1.8.0rc6 → experimaestro-1.8.0rc7}/README.md +3 -3
  3. {experimaestro-1.8.0rc6 → experimaestro-1.8.0rc7}/pyproject.toml +3 -3
  4. experimaestro-1.8.0rc7/src/experimaestro/core/identifier.py +296 -0
  5. experimaestro-1.8.0rc7/src/experimaestro/core/objects/__init__.py +44 -0
  6. experimaestro-1.8.0rc6/src/experimaestro/core/objects.py → experimaestro-1.8.0rc7/src/experimaestro/core/objects/config.py +157 -549
  7. experimaestro-1.8.0rc7/src/experimaestro/core/objects/config_utils.py +58 -0
  8. experimaestro-1.8.0rc7/src/experimaestro/core/objects/config_walk.py +150 -0
  9. {experimaestro-1.8.0rc6 → experimaestro-1.8.0rc7}/src/experimaestro/core/objects.pyi +6 -38
  10. {experimaestro-1.8.0rc6 → experimaestro-1.8.0rc7}/src/experimaestro/core/types.py +28 -4
  11. {experimaestro-1.8.0rc6 → experimaestro-1.8.0rc7}/src/experimaestro/notifications.py +1 -0
  12. {experimaestro-1.8.0rc6 → experimaestro-1.8.0rc7}/src/experimaestro/scheduler/base.py +1 -0
  13. experimaestro-1.8.0rc7/src/experimaestro/tests/core/test_generics.py +206 -0
  14. {experimaestro-1.8.0rc6 → experimaestro-1.8.0rc7}/src/experimaestro/tests/restart.py +3 -1
  15. {experimaestro-1.8.0rc6 → experimaestro-1.8.0rc7}/src/experimaestro/tests/test_instance.py +3 -3
  16. {experimaestro-1.8.0rc6 → experimaestro-1.8.0rc7}/src/experimaestro/tests/test_objects.py +20 -4
  17. {experimaestro-1.8.0rc6 → experimaestro-1.8.0rc7}/src/experimaestro/tests/test_serializers.py +3 -3
  18. {experimaestro-1.8.0rc6 → experimaestro-1.8.0rc7}/src/experimaestro/tests/test_types.py +2 -2
  19. experimaestro-1.8.0rc7/src/experimaestro/tools/__init__.py +0 -0
  20. {experimaestro-1.8.0rc6 → experimaestro-1.8.0rc7}/LICENSE +0 -0
  21. {experimaestro-1.8.0rc6 → experimaestro-1.8.0rc7}/src/experimaestro/__init__.py +0 -0
  22. {experimaestro-1.8.0rc6 → experimaestro-1.8.0rc7}/src/experimaestro/__main__.py +0 -0
  23. {experimaestro-1.8.0rc6 → experimaestro-1.8.0rc7}/src/experimaestro/annotations.py +0 -0
  24. {experimaestro-1.8.0rc6 → experimaestro-1.8.0rc7}/src/experimaestro/checkers.py +0 -0
  25. {experimaestro-1.8.0rc6 → experimaestro-1.8.0rc7}/src/experimaestro/cli/__init__.py +0 -0
  26. {experimaestro-1.8.0rc6 → experimaestro-1.8.0rc7}/src/experimaestro/cli/filter.py +0 -0
  27. {experimaestro-1.8.0rc6 → experimaestro-1.8.0rc7}/src/experimaestro/cli/jobs.py +0 -0
  28. {experimaestro-1.8.0rc6 → experimaestro-1.8.0rc7}/src/experimaestro/click.py +0 -0
  29. {experimaestro-1.8.0rc6 → experimaestro-1.8.0rc7}/src/experimaestro/commandline.py +0 -0
  30. {experimaestro-1.8.0rc6 → experimaestro-1.8.0rc7}/src/experimaestro/compat.py +0 -0
  31. {experimaestro-1.8.0rc6 → experimaestro-1.8.0rc7}/src/experimaestro/connectors/__init__.py +0 -0
  32. {experimaestro-1.8.0rc6 → experimaestro-1.8.0rc7}/src/experimaestro/connectors/local.py +0 -0
  33. {experimaestro-1.8.0rc6 → experimaestro-1.8.0rc7}/src/experimaestro/connectors/ssh.py +0 -0
  34. {experimaestro-1.8.0rc6 → experimaestro-1.8.0rc7}/src/experimaestro/core/__init__.py +0 -0
  35. {experimaestro-1.8.0rc6 → experimaestro-1.8.0rc7}/src/experimaestro/core/arguments.py +0 -0
  36. {experimaestro-1.8.0rc6 → experimaestro-1.8.0rc7}/src/experimaestro/core/callbacks.py +0 -0
  37. {experimaestro-1.8.0rc6 → experimaestro-1.8.0rc7}/src/experimaestro/core/context.py +0 -0
  38. {experimaestro-1.8.0rc6 → experimaestro-1.8.0rc7}/src/experimaestro/core/serialization.py +0 -0
  39. {experimaestro-1.8.0rc6 → experimaestro-1.8.0rc7}/src/experimaestro/core/serializers.py +0 -0
  40. {experimaestro-1.8.0rc6 → experimaestro-1.8.0rc7}/src/experimaestro/core/utils.py +0 -0
  41. {experimaestro-1.8.0rc6 → experimaestro-1.8.0rc7}/src/experimaestro/exceptions.py +0 -0
  42. {experimaestro-1.8.0rc6 → experimaestro-1.8.0rc7}/src/experimaestro/experiments/__init__.py +0 -0
  43. {experimaestro-1.8.0rc6 → experimaestro-1.8.0rc7}/src/experimaestro/experiments/cli.py +0 -0
  44. {experimaestro-1.8.0rc6 → experimaestro-1.8.0rc7}/src/experimaestro/experiments/configuration.py +0 -0
  45. {experimaestro-1.8.0rc6 → experimaestro-1.8.0rc7}/src/experimaestro/generators.py +0 -0
  46. {experimaestro-1.8.0rc6 → experimaestro-1.8.0rc7}/src/experimaestro/huggingface.py +0 -0
  47. {experimaestro-1.8.0rc6 → experimaestro-1.8.0rc7}/src/experimaestro/ipc.py +0 -0
  48. {experimaestro-1.8.0rc6 → experimaestro-1.8.0rc7}/src/experimaestro/launcherfinder/__init__.py +0 -0
  49. {experimaestro-1.8.0rc6 → experimaestro-1.8.0rc7}/src/experimaestro/launcherfinder/base.py +0 -0
  50. {experimaestro-1.8.0rc6 → experimaestro-1.8.0rc7}/src/experimaestro/launcherfinder/parser.py +0 -0
  51. {experimaestro-1.8.0rc6 → experimaestro-1.8.0rc7}/src/experimaestro/launcherfinder/registry.py +0 -0
  52. {experimaestro-1.8.0rc6 → experimaestro-1.8.0rc7}/src/experimaestro/launcherfinder/specs.py +0 -0
  53. {experimaestro-1.8.0rc6 → experimaestro-1.8.0rc7}/src/experimaestro/launchers/__init__.py +0 -0
  54. {experimaestro-1.8.0rc6 → experimaestro-1.8.0rc7}/src/experimaestro/launchers/direct.py +0 -0
  55. {experimaestro-1.8.0rc6 → experimaestro-1.8.0rc7}/src/experimaestro/launchers/oar.py +0 -0
  56. {experimaestro-1.8.0rc6 → experimaestro-1.8.0rc7}/src/experimaestro/launchers/slurm/__init__.py +0 -0
  57. {experimaestro-1.8.0rc6 → experimaestro-1.8.0rc7}/src/experimaestro/launchers/slurm/base.py +0 -0
  58. {experimaestro-1.8.0rc6 → experimaestro-1.8.0rc7}/src/experimaestro/locking.py +0 -0
  59. {experimaestro-1.8.0rc6 → experimaestro-1.8.0rc7}/src/experimaestro/mkdocs/__init__.py +0 -0
  60. {experimaestro-1.8.0rc6 → experimaestro-1.8.0rc7}/src/experimaestro/mkdocs/annotations.py +0 -0
  61. {experimaestro-1.8.0rc6 → experimaestro-1.8.0rc7}/src/experimaestro/mkdocs/base.py +0 -0
  62. {experimaestro-1.8.0rc6 → experimaestro-1.8.0rc7}/src/experimaestro/mkdocs/metaloader.py +0 -0
  63. {experimaestro-1.8.0rc6 → experimaestro-1.8.0rc7}/src/experimaestro/mkdocs/style.css +0 -0
  64. {experimaestro-1.8.0rc6 → experimaestro-1.8.0rc7}/src/experimaestro/mypy.py +0 -0
  65. {experimaestro-1.8.0rc6 → experimaestro-1.8.0rc7}/src/experimaestro/py.typed +0 -0
  66. {experimaestro-1.8.0rc6 → experimaestro-1.8.0rc7}/src/experimaestro/rpyc.py +0 -0
  67. {experimaestro-1.8.0rc6 → experimaestro-1.8.0rc7}/src/experimaestro/run.py +0 -0
  68. {experimaestro-1.8.0rc6 → experimaestro-1.8.0rc7}/src/experimaestro/scheduler/__init__.py +0 -0
  69. {experimaestro-1.8.0rc6 → experimaestro-1.8.0rc7}/src/experimaestro/scheduler/dependencies.py +0 -0
  70. {experimaestro-1.8.0rc6 → experimaestro-1.8.0rc7}/src/experimaestro/scheduler/dynamic_outputs.py +0 -0
  71. {experimaestro-1.8.0rc6 → experimaestro-1.8.0rc7}/src/experimaestro/scheduler/services.py +0 -0
  72. {experimaestro-1.8.0rc6 → experimaestro-1.8.0rc7}/src/experimaestro/scheduler/state.py +0 -0
  73. {experimaestro-1.8.0rc6 → experimaestro-1.8.0rc7}/src/experimaestro/scheduler/workspace.py +0 -0
  74. {experimaestro-1.8.0rc6 → experimaestro-1.8.0rc7}/src/experimaestro/scriptbuilder.py +0 -0
  75. {experimaestro-1.8.0rc6 → experimaestro-1.8.0rc7}/src/experimaestro/server/__init__.py +0 -0
  76. {experimaestro-1.8.0rc6 → experimaestro-1.8.0rc7}/src/experimaestro/server/data/016b4a6cdced82ab3aa1.ttf +0 -0
  77. {experimaestro-1.8.0rc6 → experimaestro-1.8.0rc7}/src/experimaestro/server/data/0c35d18bf06992036b69.woff2 +0 -0
  78. {experimaestro-1.8.0rc6 → experimaestro-1.8.0rc7}/src/experimaestro/server/data/1815e00441357e01619e.ttf +0 -0
  79. {experimaestro-1.8.0rc6 → experimaestro-1.8.0rc7}/src/experimaestro/server/data/219aa9140e099e6c72ed.woff2 +0 -0
  80. {experimaestro-1.8.0rc6 → experimaestro-1.8.0rc7}/src/experimaestro/server/data/2463b90d9a316e4e5294.woff2 +0 -0
  81. {experimaestro-1.8.0rc6 → experimaestro-1.8.0rc7}/src/experimaestro/server/data/2582b0e4bcf85eceead0.ttf +0 -0
  82. {experimaestro-1.8.0rc6 → experimaestro-1.8.0rc7}/src/experimaestro/server/data/3a4004a46a653d4b2166.woff +0 -0
  83. {experimaestro-1.8.0rc6 → experimaestro-1.8.0rc7}/src/experimaestro/server/data/3baa5b8f3469222b822d.woff +0 -0
  84. {experimaestro-1.8.0rc6 → experimaestro-1.8.0rc7}/src/experimaestro/server/data/4d73cb90e394b34b7670.woff +0 -0
  85. {experimaestro-1.8.0rc6 → experimaestro-1.8.0rc7}/src/experimaestro/server/data/4ef4218c522f1eb6b5b1.woff2 +0 -0
  86. {experimaestro-1.8.0rc6 → experimaestro-1.8.0rc7}/src/experimaestro/server/data/50701fbb8177c2dde530.ttf +0 -0
  87. {experimaestro-1.8.0rc6 → experimaestro-1.8.0rc7}/src/experimaestro/server/data/5d681e2edae8c60630db.woff +0 -0
  88. {experimaestro-1.8.0rc6 → experimaestro-1.8.0rc7}/src/experimaestro/server/data/6f420cf17cc0d7676fad.woff2 +0 -0
  89. {experimaestro-1.8.0rc6 → experimaestro-1.8.0rc7}/src/experimaestro/server/data/878f31251d960bd6266f.woff2 +0 -0
  90. {experimaestro-1.8.0rc6 → experimaestro-1.8.0rc7}/src/experimaestro/server/data/89999bdf5d835c012025.woff2 +0 -0
  91. {experimaestro-1.8.0rc6 → experimaestro-1.8.0rc7}/src/experimaestro/server/data/914997e1bdfc990d0897.ttf +0 -0
  92. {experimaestro-1.8.0rc6 → experimaestro-1.8.0rc7}/src/experimaestro/server/data/b041b1fa4fe241b23445.woff2 +0 -0
  93. {experimaestro-1.8.0rc6 → experimaestro-1.8.0rc7}/src/experimaestro/server/data/b6879d41b0852f01ed5b.woff2 +0 -0
  94. {experimaestro-1.8.0rc6 → experimaestro-1.8.0rc7}/src/experimaestro/server/data/c210719e60948b211a12.woff2 +0 -0
  95. {experimaestro-1.8.0rc6 → experimaestro-1.8.0rc7}/src/experimaestro/server/data/c380809fd3677d7d6903.woff2 +0 -0
  96. {experimaestro-1.8.0rc6 → experimaestro-1.8.0rc7}/src/experimaestro/server/data/d75e3fd1eb12e9bd6655.ttf +0 -0
  97. {experimaestro-1.8.0rc6 → experimaestro-1.8.0rc7}/src/experimaestro/server/data/f882956fd323fd322f31.woff +0 -0
  98. {experimaestro-1.8.0rc6 → experimaestro-1.8.0rc7}/src/experimaestro/server/data/favicon.ico +0 -0
  99. {experimaestro-1.8.0rc6 → experimaestro-1.8.0rc7}/src/experimaestro/server/data/index.css +0 -0
  100. {experimaestro-1.8.0rc6 → experimaestro-1.8.0rc7}/src/experimaestro/server/data/index.css.map +0 -0
  101. {experimaestro-1.8.0rc6 → experimaestro-1.8.0rc7}/src/experimaestro/server/data/index.html +0 -0
  102. {experimaestro-1.8.0rc6 → experimaestro-1.8.0rc7}/src/experimaestro/server/data/index.js +0 -0
  103. {experimaestro-1.8.0rc6 → experimaestro-1.8.0rc7}/src/experimaestro/server/data/index.js.map +0 -0
  104. {experimaestro-1.8.0rc6 → experimaestro-1.8.0rc7}/src/experimaestro/server/data/login.html +0 -0
  105. {experimaestro-1.8.0rc6 → experimaestro-1.8.0rc7}/src/experimaestro/server/data/manifest.json +0 -0
  106. {experimaestro-1.8.0rc6 → experimaestro-1.8.0rc7}/src/experimaestro/settings.py +0 -0
  107. {experimaestro-1.8.0rc6 → experimaestro-1.8.0rc7}/src/experimaestro/sphinx/__init__.py +0 -0
  108. {experimaestro-1.8.0rc6 → experimaestro-1.8.0rc7}/src/experimaestro/sphinx/static/experimaestro.css +0 -0
  109. {experimaestro-1.8.0rc6 → experimaestro-1.8.0rc7}/src/experimaestro/taskglobals.py +0 -0
  110. {experimaestro-1.8.0rc6 → experimaestro-1.8.0rc7}/src/experimaestro/tests/__init__.py +0 -0
  111. {experimaestro-1.8.0rc6 → experimaestro-1.8.0rc7}/src/experimaestro/tests/conftest.py +0 -0
  112. {experimaestro-1.8.0rc6 → experimaestro-1.8.0rc7}/src/experimaestro/tests/connectors/bin/executable.py +0 -0
  113. {experimaestro-1.8.0rc6 → experimaestro-1.8.0rc7}/src/experimaestro/tests/connectors/test_local.py +0 -0
  114. {experimaestro-1.8.0rc6 → experimaestro-1.8.0rc7}/src/experimaestro/tests/connectors/utils.py +0 -0
  115. {experimaestro-1.8.0rc6/src/experimaestro/tests/launchers → experimaestro-1.8.0rc7/src/experimaestro/tests/core}/__init__.py +0 -0
  116. {experimaestro-1.8.0rc6 → experimaestro-1.8.0rc7}/src/experimaestro/tests/definitions_types.py +0 -0
  117. {experimaestro-1.8.0rc6/src/experimaestro/tests/launchers/config_slurm → experimaestro-1.8.0rc7/src/experimaestro/tests/launchers}/__init__.py +0 -0
  118. {experimaestro-1.8.0rc6 → experimaestro-1.8.0rc7}/src/experimaestro/tests/launchers/bin/sacct +0 -0
  119. {experimaestro-1.8.0rc6 → experimaestro-1.8.0rc7}/src/experimaestro/tests/launchers/bin/sbatch +0 -0
  120. {experimaestro-1.8.0rc6 → experimaestro-1.8.0rc7}/src/experimaestro/tests/launchers/bin/srun +0 -0
  121. {experimaestro-1.8.0rc6 → experimaestro-1.8.0rc7}/src/experimaestro/tests/launchers/bin/test.py +0 -0
  122. {experimaestro-1.8.0rc6 → experimaestro-1.8.0rc7}/src/experimaestro/tests/launchers/common.py +0 -0
  123. {experimaestro-1.8.0rc6/src/experimaestro/tests/tasks → experimaestro-1.8.0rc7/src/experimaestro/tests/launchers/config_slurm}/__init__.py +0 -0
  124. {experimaestro-1.8.0rc6 → experimaestro-1.8.0rc7}/src/experimaestro/tests/launchers/config_slurm/launchers.py +0 -0
  125. {experimaestro-1.8.0rc6 → experimaestro-1.8.0rc7}/src/experimaestro/tests/launchers/test_local.py +0 -0
  126. {experimaestro-1.8.0rc6 → experimaestro-1.8.0rc7}/src/experimaestro/tests/launchers/test_slurm.py +0 -0
  127. {experimaestro-1.8.0rc6 → experimaestro-1.8.0rc7}/src/experimaestro/tests/restart_main.py +0 -0
  128. {experimaestro-1.8.0rc6 → experimaestro-1.8.0rc7}/src/experimaestro/tests/scripts/notifyandwait.py +0 -0
  129. {experimaestro-1.8.0rc6 → experimaestro-1.8.0rc7}/src/experimaestro/tests/scripts/waitforfile.py +0 -0
  130. {experimaestro-1.8.0rc6 → experimaestro-1.8.0rc7}/src/experimaestro/tests/task_tokens.py +0 -0
  131. {experimaestro-1.8.0rc6/src/experimaestro/tools → experimaestro-1.8.0rc7/src/experimaestro/tests/tasks}/__init__.py +0 -0
  132. {experimaestro-1.8.0rc6 → experimaestro-1.8.0rc7}/src/experimaestro/tests/tasks/all.py +0 -0
  133. {experimaestro-1.8.0rc6 → experimaestro-1.8.0rc7}/src/experimaestro/tests/tasks/foreign.py +0 -0
  134. {experimaestro-1.8.0rc6 → experimaestro-1.8.0rc7}/src/experimaestro/tests/test_checkers.py +0 -0
  135. {experimaestro-1.8.0rc6 → experimaestro-1.8.0rc7}/src/experimaestro/tests/test_dependencies.py +0 -0
  136. {experimaestro-1.8.0rc6 → experimaestro-1.8.0rc7}/src/experimaestro/tests/test_experiment.py +0 -0
  137. {experimaestro-1.8.0rc6 → experimaestro-1.8.0rc7}/src/experimaestro/tests/test_findlauncher.py +0 -0
  138. {experimaestro-1.8.0rc6 → experimaestro-1.8.0rc7}/src/experimaestro/tests/test_forward.py +0 -0
  139. {experimaestro-1.8.0rc6 → experimaestro-1.8.0rc7}/src/experimaestro/tests/test_identifier.py +0 -0
  140. {experimaestro-1.8.0rc6 → experimaestro-1.8.0rc7}/src/experimaestro/tests/test_outputs.py +0 -0
  141. {experimaestro-1.8.0rc6 → experimaestro-1.8.0rc7}/src/experimaestro/tests/test_param.py +0 -0
  142. {experimaestro-1.8.0rc6 → experimaestro-1.8.0rc7}/src/experimaestro/tests/test_progress.py +0 -0
  143. {experimaestro-1.8.0rc6 → experimaestro-1.8.0rc7}/src/experimaestro/tests/test_snippets.py +0 -0
  144. {experimaestro-1.8.0rc6 → experimaestro-1.8.0rc7}/src/experimaestro/tests/test_ssh.py +0 -0
  145. {experimaestro-1.8.0rc6 → experimaestro-1.8.0rc7}/src/experimaestro/tests/test_tags.py +0 -0
  146. {experimaestro-1.8.0rc6 → experimaestro-1.8.0rc7}/src/experimaestro/tests/test_tasks.py +0 -0
  147. {experimaestro-1.8.0rc6 → experimaestro-1.8.0rc7}/src/experimaestro/tests/test_tokens.py +0 -0
  148. {experimaestro-1.8.0rc6 → experimaestro-1.8.0rc7}/src/experimaestro/tests/test_validation.py +0 -0
  149. {experimaestro-1.8.0rc6 → experimaestro-1.8.0rc7}/src/experimaestro/tests/token_reschedule.py +0 -0
  150. {experimaestro-1.8.0rc6 → experimaestro-1.8.0rc7}/src/experimaestro/tests/utils.py +0 -0
  151. {experimaestro-1.8.0rc6 → experimaestro-1.8.0rc7}/src/experimaestro/tokens.py +0 -0
  152. {experimaestro-1.8.0rc6 → experimaestro-1.8.0rc7}/src/experimaestro/tools/diff.py +0 -0
  153. {experimaestro-1.8.0rc6 → experimaestro-1.8.0rc7}/src/experimaestro/tools/documentation.py +0 -0
  154. {experimaestro-1.8.0rc6 → experimaestro-1.8.0rc7}/src/experimaestro/tools/jobs.py +0 -0
  155. {experimaestro-1.8.0rc6 → experimaestro-1.8.0rc7}/src/experimaestro/typingutils.py +0 -0
  156. {experimaestro-1.8.0rc6 → experimaestro-1.8.0rc7}/src/experimaestro/utils/__init__.py +0 -0
  157. {experimaestro-1.8.0rc6 → experimaestro-1.8.0rc7}/src/experimaestro/utils/asyncio.py +0 -0
  158. {experimaestro-1.8.0rc6 → experimaestro-1.8.0rc7}/src/experimaestro/utils/jobs.py +0 -0
  159. {experimaestro-1.8.0rc6 → experimaestro-1.8.0rc7}/src/experimaestro/utils/jupyter.py +0 -0
  160. {experimaestro-1.8.0rc6 → experimaestro-1.8.0rc7}/src/experimaestro/utils/resources.py +0 -0
  161. {experimaestro-1.8.0rc6 → experimaestro-1.8.0rc7}/src/experimaestro/utils/settings.py +0 -0
  162. {experimaestro-1.8.0rc6 → experimaestro-1.8.0rc7}/src/experimaestro/xpmutils.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: experimaestro
3
- Version: 1.8.0rc6
3
+ Version: 1.8.0rc7
4
4
  Summary: "Experimaestro is a computer science experiment manager"
5
5
  License: GPL-3
6
6
  Keywords: experiment manager
@@ -28,7 +28,7 @@ Requires-Dist: docstring-parser (>=0.15,<0.16)
28
28
  Requires-Dist: fasteners (>=0.19,<0.20)
29
29
  Requires-Dist: flask (>=2.3,<3.0)
30
30
  Requires-Dist: flask-socketio (>=5.3,<6.0)
31
- Requires-Dist: gevent (>=23.9,<24.0)
31
+ Requires-Dist: gevent (>=24.11.1,<25.0.0)
32
32
  Requires-Dist: gevent-websocket (>=0.10,<0.11)
33
33
  Requires-Dist: huggingface-hub (>0.17)
34
34
  Requires-Dist: humanfriendly (>=10,<11)
@@ -144,11 +144,11 @@ def cli(port, workdir, sleeptime):
144
144
  # Sets the working directory and the name of the xp
145
145
  with experiment(workdir, "helloworld", port=port) as xp:
146
146
  # Submit the tasks
147
- hello = Say(word="hello", sleeptime=sleeptime).submit()
148
- world = Say(word="world", sleeptime=sleeptime).submit()
147
+ hello = Say.C(word="hello", sleeptime=sleeptime).submit()
148
+ world = Say.C(word="world", sleeptime=sleeptime).submit()
149
149
 
150
150
  # Concat will depend on the two first tasks
151
- Concat(strings=[hello, world], sleeptime=sleeptime).tag("y", 1).submit()
151
+ Concat.C(strings=[hello, world], sleeptime=sleeptime).tag("y", 1).submit()
152
152
 
153
153
 
154
154
  if __name__ == "__main__":
@@ -93,11 +93,11 @@ def cli(port, workdir, sleeptime):
93
93
  # Sets the working directory and the name of the xp
94
94
  with experiment(workdir, "helloworld", port=port) as xp:
95
95
  # Submit the tasks
96
- hello = Say(word="hello", sleeptime=sleeptime).submit()
97
- world = Say(word="world", sleeptime=sleeptime).submit()
96
+ hello = Say.C(word="hello", sleeptime=sleeptime).submit()
97
+ world = Say.C(word="world", sleeptime=sleeptime).submit()
98
98
 
99
99
  # Concat will depend on the two first tasks
100
- Concat(strings=[hello, world], sleeptime=sleeptime).tag("y", 1).submit()
100
+ Concat.C(strings=[hello, world], sleeptime=sleeptime).tag("y", 1).submit()
101
101
 
102
102
 
103
103
  if __name__ == "__main__":
@@ -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.8.0-rc6"
23
+ version = "1.8.0-rc7"
24
24
  repository = "https://github.com/experimaestro/experimaestro-python"
25
25
  documentation = "https://experimaestro-python.readthedocs.io/"
26
26
 
@@ -55,7 +55,7 @@ sortedcontainers = "^2.4"
55
55
  pyparsing = "^3.1"
56
56
  humanfriendly = "^10"
57
57
  huggingface-hub = ">0.17"
58
- gevent = "^23.9"
58
+ gevent = "^24.11.1"
59
59
  gevent-websocket = "^0.10"
60
60
  flask = "^2.3"
61
61
  flask-socketio = "^5.3"
@@ -122,7 +122,7 @@ warn_unused_ignores = true
122
122
 
123
123
  [tool.commitizen]
124
124
  name = "cz_conventional_commits"
125
- version = "1.8.0rc6"
125
+ version = "1.8.0rc7"
126
126
  changelog_start_rev = "v1.0.0"
127
127
  tag_format = "v$major.$minor.$patch$prerelease"
128
128
  # update_changelog_on_bump = true
@@ -0,0 +1,296 @@
1
+ from contextlib import contextmanager
2
+ from enum import Enum
3
+ from functools import cached_property
4
+ import hashlib
5
+ import logging
6
+ import os
7
+ import struct
8
+ from typing import Optional
9
+ from experimaestro.core.objects import Config
10
+
11
+
12
+ class ConfigPath:
13
+ """Used to keep track of cycles when computing a hash"""
14
+
15
+ def __init__(self):
16
+ self.loops: list[bool] = []
17
+ """Indicates whether a loop was detected up to this node"""
18
+
19
+ self.config2index = {}
20
+ """Associate an index in the list with a configuration"""
21
+
22
+ def detect_loop(self, config) -> Optional[int]:
23
+ """If there is a loop, return the relative index and update the path"""
24
+ index = self.config2index.get(id(config), None)
25
+ if index is not None:
26
+ for i in range(index, self.depth):
27
+ self.loops[i] = True
28
+ return self.depth - index
29
+ return None
30
+
31
+ def has_loop(self):
32
+ return self.loops[-1]
33
+
34
+ @property
35
+ def depth(self):
36
+ return len(self.loops)
37
+
38
+ @contextmanager
39
+ def push(self, config):
40
+ config_id = id(config)
41
+ assert config_id not in self.config2index
42
+
43
+ self.config2index[config_id] = self.depth
44
+ self.loops.append(False)
45
+
46
+ try:
47
+ yield
48
+ finally:
49
+ self.loops.pop()
50
+ del self.config2index[config_id]
51
+
52
+
53
+ hash_logger = logging.getLogger("xpm.hash")
54
+
55
+
56
+ def is_ignored(value):
57
+ """Returns True if the value should be ignored by itself"""
58
+ return value is not None and isinstance(value, Config) and (value.__xpm__.meta)
59
+
60
+
61
+ def remove_meta(value):
62
+ """Cleanup a dict/list by removing ignored values"""
63
+ if isinstance(value, list):
64
+ return [el for el in value if not is_ignored(el)]
65
+ if isinstance(value, dict):
66
+ return {key: value for key, value in value.items() if not is_ignored(value)}
67
+ return value
68
+
69
+
70
+ class Identifier:
71
+ def __init__(self, main: bytes):
72
+ self.main = main
73
+ self.has_loops = False
74
+
75
+ @cached_property
76
+ def all(self):
77
+ """Returns the overall identifier"""
78
+ return self.main
79
+
80
+ def __hash__(self) -> int:
81
+ return hash(self.main)
82
+
83
+ def state_dict(self):
84
+ return self.main.hex()
85
+
86
+ def __eq__(self, other: object):
87
+ if not isinstance(other, Identifier):
88
+ return False
89
+ return self.main == other.main
90
+
91
+ @staticmethod
92
+ def from_state_dict(data: dict[str, str] | str):
93
+ if isinstance(data, str):
94
+ return Identifier(bytes.fromhex(data))
95
+
96
+ return Identifier(bytes.fromhex(data["main"]))
97
+
98
+ def __repr__(self):
99
+ return self.main.hex()
100
+
101
+
102
+ class IdentifierComputer:
103
+ """This class is in charge of computing a config/task identifier"""
104
+
105
+ OBJECT_ID = b"\x00"
106
+ INT_ID = b"\x01"
107
+ FLOAT_ID = b"\x02"
108
+ STR_ID = b"\x03"
109
+ PATH_ID = b"\x04"
110
+ NAME_ID = b"\x05"
111
+ NONE_ID = b"\x06"
112
+ LIST_ID = b"\x07"
113
+ TASK_ID = b"\x08"
114
+ DICT_ID = b"\x09"
115
+ ENUM_ID = b"\x0a"
116
+ CYCLE_REFERENCE = b"\x0b"
117
+ INIT_TASKS = b"\x0c"
118
+
119
+ def __init__(self, config: "Config", config_path: ConfigPath, *, version=None):
120
+ # Hasher for parameters
121
+ self._hasher = hashlib.sha256()
122
+ self.config = config
123
+ self.config_path = config_path
124
+ self.version = version or int(os.environ.get("XPM_HASH_COMPUTER", 2))
125
+ if hash_logger.isEnabledFor(logging.DEBUG):
126
+ hash_logger.debug(
127
+ "starting hash (%s): %s", hash(str(self.config)), self.config
128
+ )
129
+
130
+ def identifier(self) -> Identifier:
131
+ main = self._hasher.digest()
132
+ if hash_logger.isEnabledFor(logging.DEBUG):
133
+ hash_logger.debug("hash (%s): %s", hash(str(self.config)), str(main))
134
+ return Identifier(main)
135
+
136
+ def _hashupdate(self, bytes: bytes):
137
+ """Update the hash computers with some bytes"""
138
+ if hash_logger.isEnabledFor(logging.DEBUG):
139
+ hash_logger.debug(
140
+ "updating hash (%s): %s", hash(str(self.config)), str(bytes)
141
+ )
142
+ self._hasher.update(bytes)
143
+
144
+ def update(self, value, *, myself=False): # noqa: C901
145
+ """Update the hash
146
+
147
+ :param value: The value to add to the hash
148
+ :param myself: True if the value is the configuration for which we wish
149
+ to compute the identifier, defaults to False
150
+ :raises NotImplementedError: If the value cannot be processed
151
+ """
152
+ if value is None:
153
+ self._hashupdate(IdentifierComputer.NONE_ID)
154
+ elif isinstance(value, float):
155
+ self._hashupdate(IdentifierComputer.FLOAT_ID)
156
+ self._hashupdate(struct.pack("!d", value))
157
+ elif isinstance(value, int):
158
+ self._hashupdate(IdentifierComputer.INT_ID)
159
+ self._hashupdate(struct.pack("!q", value))
160
+ elif isinstance(value, str):
161
+ self._hashupdate(IdentifierComputer.STR_ID)
162
+ self._hashupdate(value.encode("utf-8"))
163
+ elif isinstance(value, list):
164
+ values = [el for el in value if not is_ignored(el)]
165
+ self._hashupdate(IdentifierComputer.LIST_ID)
166
+ self._hashupdate(struct.pack("!d", len(values)))
167
+ for x in values:
168
+ self.update(x)
169
+ elif isinstance(value, Enum):
170
+ self._hashupdate(IdentifierComputer.ENUM_ID)
171
+ k = value.__class__
172
+ self._hashupdate(
173
+ f"{k.__module__}.{k.__qualname__ }:{value.name}".encode("utf-8"),
174
+ )
175
+ elif isinstance(value, dict):
176
+ self._hashupdate(IdentifierComputer.DICT_ID)
177
+ items = [
178
+ (key, value) for key, value in value.items() if not is_ignored(value)
179
+ ]
180
+ items.sort(key=lambda x: x[0])
181
+ for key, value in items:
182
+ self.update(key)
183
+ self.update(value)
184
+
185
+ # Handles configurations
186
+ elif isinstance(value, Config):
187
+ # Encodes the identifier
188
+ self._hashupdate(IdentifierComputer.OBJECT_ID)
189
+
190
+ # If we encode another config, then
191
+ if not myself:
192
+ if loop_ix := self.config_path.detect_loop(value):
193
+ # Loop detected: use cycle reference
194
+ self._hashupdate(IdentifierComputer.CYCLE_REFERENCE)
195
+ self._hashupdate(struct.pack("!q", loop_ix))
196
+
197
+ else:
198
+ # Just use the object identifier
199
+ value_id = IdentifierComputer.compute(
200
+ value, version=self.version, config_path=self.config_path
201
+ )
202
+ self._hashupdate(value_id.all)
203
+
204
+ # And that's it!
205
+ return
206
+
207
+ # Process tasks
208
+ if value.__xpm__.task is not None and (value.__xpm__.task is not value):
209
+ hash_logger.debug("Computing hash for task %s", value.__xpm__.task)
210
+ self._hashupdate(IdentifierComputer.TASK_ID)
211
+ self.update(value.__xpm__.task)
212
+
213
+ xpmtype = value.__xpmtype__
214
+ self._hashupdate(xpmtype.identifier.name.encode("utf-8"))
215
+
216
+ # Process arguments (sort by name to ensure uniqueness)
217
+ arguments = sorted(xpmtype.arguments.values(), key=lambda a: a.name)
218
+ for argument in arguments:
219
+ # Ignored argument
220
+ if argument.ignored:
221
+ argvalue = value.__xpm__.values.get(argument.name, None)
222
+
223
+ # ... unless meta is set to false
224
+ if (
225
+ argvalue is None
226
+ or not isinstance(argvalue, Config)
227
+ or (argvalue.__xpm__.meta is not False)
228
+ ):
229
+ continue
230
+
231
+ if argument.generator:
232
+ continue
233
+
234
+ # Argument value
235
+ # Skip if the argument is not a constant, and
236
+ # - optional argument: both value and default are None
237
+ # - the argument value is equal to the default value
238
+ argvalue = getattr(value, argument.name, None)
239
+ if not argument.constant and (
240
+ (
241
+ not argument.required
242
+ and argument.default is None
243
+ and argvalue is None
244
+ )
245
+ or (
246
+ argument.default is not None
247
+ and argument.default == remove_meta(argvalue)
248
+ )
249
+ ):
250
+ # No update if same value (and not constant)
251
+ continue
252
+
253
+ if (
254
+ argvalue is not None
255
+ and isinstance(argvalue, Config)
256
+ and argvalue.__xpm__.meta
257
+ ):
258
+ continue
259
+
260
+ # Hash name
261
+ self.update(argument.name)
262
+
263
+ # Hash value
264
+ self._hashupdate(IdentifierComputer.NAME_ID)
265
+ self.update(argvalue)
266
+
267
+ else:
268
+ raise NotImplementedError("Cannot compute hash of type %s" % type(value))
269
+
270
+ @staticmethod
271
+ def compute(
272
+ config: "Config", config_path: ConfigPath | None = None, version=None
273
+ ) -> Identifier:
274
+ """Compute the identifier for a configuration
275
+
276
+ :param config: the configuration for which we compute the identifier
277
+ :param config_path: used to track down cycles between configurations
278
+ :param version: version for the hash computation (None for the last one)
279
+ """
280
+
281
+ # Try to use the cached value first
282
+ # (if there are no loops)
283
+ if config.__xpm__._sealed:
284
+ identifier = config.__xpm__._raw_identifier
285
+ if identifier is not None and not identifier.has_loops:
286
+ return identifier
287
+
288
+ config_path = config_path or ConfigPath()
289
+
290
+ with config_path.push(config):
291
+ self = IdentifierComputer(config, config_path, version=version)
292
+ self.update(config, myself=True)
293
+ identifier = self.identifier()
294
+ identifier.has_loops = config_path.has_loop()
295
+
296
+ return identifier
@@ -0,0 +1,44 @@
1
+ from .config_walk import ConfigWalkContext, ConfigWalk
2
+ from .config import (
3
+ ConfigMixin,
4
+ Config,
5
+ ConfigInformation,
6
+ Task,
7
+ LightweightTask,
8
+ WatchedOutput,
9
+ DependentMarker,
10
+ copyconfig,
11
+ setmeta,
12
+ cache,
13
+ logger,
14
+ )
15
+
16
+ from .config_utils import (
17
+ getqualattr,
18
+ add_to_path,
19
+ ObjectStore,
20
+ SealedError,
21
+ TaggedValue,
22
+ )
23
+
24
+
25
+ __all__ = [
26
+ "ConfigMixin",
27
+ "Config",
28
+ "ConfigInformation",
29
+ "ConfigWalkContext",
30
+ "ConfigWalk",
31
+ "Task",
32
+ "LightweightTask",
33
+ "ObjectStore",
34
+ "WatchedOutput",
35
+ "SealedError",
36
+ "DependentMarker",
37
+ "TaggedValue",
38
+ "getqualattr",
39
+ "copyconfig",
40
+ "setmeta",
41
+ "cache",
42
+ "add_to_path",
43
+ "logger",
44
+ ]