simvue 2.6.1__tar.gz → 2.6.2__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.
Files changed (182) hide show
  1. {simvue-2.6.1 → simvue-2.6.2}/.github/workflows/common.yml +1 -0
  2. {simvue-2.6.1 → simvue-2.6.2}/.github/workflows/deploy.yaml +2 -0
  3. {simvue-2.6.1 → simvue-2.6.2}/CHANGELOG.md +4 -0
  4. {simvue-2.6.1 → simvue-2.6.2}/CITATION.cff +3 -3
  5. {simvue-2.6.1 → simvue-2.6.2}/PKG-INFO +2 -2
  6. {simvue-2.6.1 → simvue-2.6.2}/prek.toml +5 -5
  7. {simvue-2.6.1 → simvue-2.6.2}/pyproject.toml +2 -2
  8. {simvue-2.6.1 → simvue-2.6.2}/simvue/config/simvue_config_schema.json +91 -27
  9. {simvue-2.6.1 → simvue-2.6.2}/simvue/run.py +10 -7
  10. {simvue-2.6.1 → simvue-2.6.2}/tests/example_data/python_poetry/poetry.lock +5 -5
  11. {simvue-2.6.1 → simvue-2.6.2}/tests/example_data/python_uv/uv.lock +3 -3
  12. {simvue-2.6.1 → simvue-2.6.2}/tests/functional/test_run_class.py +90 -1
  13. {simvue-2.6.1 → simvue-2.6.2}/uv.lock +1039 -530
  14. {simvue-2.6.1 → simvue-2.6.2}/.github/ISSUE_TEMPLATE/bug_report.md +0 -0
  15. {simvue-2.6.1 → simvue-2.6.2}/.github/ISSUE_TEMPLATE/feature_request.md +0 -0
  16. {simvue-2.6.1 → simvue-2.6.2}/.github/PULL_REQUEST_TEMPLATE/bug_fix.md +0 -0
  17. {simvue-2.6.1 → simvue-2.6.2}/.github/PULL_REQUEST_TEMPLATE/feature.md +0 -0
  18. {simvue-2.6.1 → simvue-2.6.2}/.github/dependabot.yml +0 -0
  19. {simvue-2.6.1 → simvue-2.6.2}/.github/pull_request_template.md +0 -0
  20. {simvue-2.6.1 → simvue-2.6.2}/.github/workflows/citation.yml +0 -0
  21. {simvue-2.6.1 → simvue-2.6.2}/.github/workflows/codeql.yml +0 -0
  22. {simvue-2.6.1 → simvue-2.6.2}/.github/workflows/examples.yml +0 -0
  23. {simvue-2.6.1 → simvue-2.6.2}/.github/workflows/prek.yml +0 -0
  24. {simvue-2.6.1 → simvue-2.6.2}/.github/workflows/test_client_macos_nightlies.yml +0 -0
  25. {simvue-2.6.1 → simvue-2.6.2}/.github/workflows/test_client_ubuntu.yml +0 -0
  26. {simvue-2.6.1 → simvue-2.6.2}/.github/workflows/test_client_ubuntu_nightlies.yml +0 -0
  27. {simvue-2.6.1 → simvue-2.6.2}/.github/workflows/test_client_windows_nightlies.yml +0 -0
  28. {simvue-2.6.1 → simvue-2.6.2}/.github/workflows/test_group.yml +0 -0
  29. {simvue-2.6.1 → simvue-2.6.2}/.github/workflows/test_multiple_python.yml +0 -0
  30. {simvue-2.6.1 → simvue-2.6.2}/.gitignore +0 -0
  31. {simvue-2.6.1 → simvue-2.6.2}/.gitmodules +0 -0
  32. {simvue-2.6.1 → simvue-2.6.2}/CODE_OF_CONDUCT.md +0 -0
  33. {simvue-2.6.1 → simvue-2.6.2}/CONTRIBUTING.md +0 -0
  34. {simvue-2.6.1 → simvue-2.6.2}/LICENSE +0 -0
  35. {simvue-2.6.1 → simvue-2.6.2}/README.md +0 -0
  36. {simvue-2.6.1 → simvue-2.6.2}/examples/Bluemira/README.md +0 -0
  37. {simvue-2.6.1 → simvue-2.6.2}/examples/Bluemira/geometry_optimisation.py +0 -0
  38. {simvue-2.6.1 → simvue-2.6.2}/examples/Geant4/README.md +0 -0
  39. {simvue-2.6.1 → simvue-2.6.2}/examples/Geant4/geant4_simvue.py +0 -0
  40. {simvue-2.6.1 → simvue-2.6.2}/examples/Geant4/requirements.txt +0 -0
  41. {simvue-2.6.1 → simvue-2.6.2}/examples/Logging/README.md +0 -0
  42. {simvue-2.6.1 → simvue-2.6.2}/examples/Logging/logging-to-simvue.py +0 -0
  43. {simvue-2.6.1 → simvue-2.6.2}/examples/Optuna/README.md +0 -0
  44. {simvue-2.6.1 → simvue-2.6.2}/examples/Optuna/requirements.txt +0 -0
  45. {simvue-2.6.1 → simvue-2.6.2}/examples/Optuna/simvue_optuna_pytorch.py +0 -0
  46. {simvue-2.6.1 → simvue-2.6.2}/examples/PyTorch/README.md +0 -0
  47. {simvue-2.6.1 → simvue-2.6.2}/examples/PyTorch/main.py +0 -0
  48. {simvue-2.6.1 → simvue-2.6.2}/examples/PyTorch/requirements.txt +0 -0
  49. {simvue-2.6.1 → simvue-2.6.2}/examples/README.md +0 -0
  50. {simvue-2.6.1 → simvue-2.6.2}/examples/SU2/README.md +0 -0
  51. {simvue-2.6.1 → simvue-2.6.2}/examples/SU2/SU2.py +0 -0
  52. {simvue-2.6.1 → simvue-2.6.2}/examples/SU2/SU2.sh +0 -0
  53. {simvue-2.6.1 → simvue-2.6.2}/notebooks/README.md +0 -0
  54. {simvue-2.6.1 → simvue-2.6.2}/notebooks/basic_example.ipynb +0 -0
  55. {simvue-2.6.1 → simvue-2.6.2}/notebooks/detailed_example.ipynb +0 -0
  56. {simvue-2.6.1 → simvue-2.6.2}/notebooks/non_python_example.ipynb +0 -0
  57. {simvue-2.6.1 → simvue-2.6.2}/notebooks/temperatures.sh +0 -0
  58. {simvue-2.6.1 → simvue-2.6.2}/poetry.lock +0 -0
  59. {simvue-2.6.1 → simvue-2.6.2}/simvue/__init__.py +0 -0
  60. {simvue-2.6.1 → simvue-2.6.2}/simvue/api/__init__.py +0 -0
  61. {simvue-2.6.1 → simvue-2.6.2}/simvue/api/objects/__init__.py +0 -0
  62. {simvue-2.6.1 → simvue-2.6.2}/simvue/api/objects/administrator/__init__.py +0 -0
  63. {simvue-2.6.1 → simvue-2.6.2}/simvue/api/objects/administrator/tenant.py +0 -0
  64. {simvue-2.6.1 → simvue-2.6.2}/simvue/api/objects/administrator/user.py +0 -0
  65. {simvue-2.6.1 → simvue-2.6.2}/simvue/api/objects/alert/__init__.py +0 -0
  66. {simvue-2.6.1 → simvue-2.6.2}/simvue/api/objects/alert/base.py +0 -0
  67. {simvue-2.6.1 → simvue-2.6.2}/simvue/api/objects/alert/events.py +0 -0
  68. {simvue-2.6.1 → simvue-2.6.2}/simvue/api/objects/alert/fetch.py +0 -0
  69. {simvue-2.6.1 → simvue-2.6.2}/simvue/api/objects/alert/metrics.py +0 -0
  70. {simvue-2.6.1 → simvue-2.6.2}/simvue/api/objects/alert/user.py +0 -0
  71. {simvue-2.6.1 → simvue-2.6.2}/simvue/api/objects/artifact/__init__.py +0 -0
  72. {simvue-2.6.1 → simvue-2.6.2}/simvue/api/objects/artifact/base.py +0 -0
  73. {simvue-2.6.1 → simvue-2.6.2}/simvue/api/objects/artifact/fetch.py +0 -0
  74. {simvue-2.6.1 → simvue-2.6.2}/simvue/api/objects/artifact/file.py +0 -0
  75. {simvue-2.6.1 → simvue-2.6.2}/simvue/api/objects/artifact/object.py +0 -0
  76. {simvue-2.6.1 → simvue-2.6.2}/simvue/api/objects/base.py +0 -0
  77. {simvue-2.6.1 → simvue-2.6.2}/simvue/api/objects/events.py +0 -0
  78. {simvue-2.6.1 → simvue-2.6.2}/simvue/api/objects/filter/__init__.py +0 -0
  79. {simvue-2.6.1 → simvue-2.6.2}/simvue/api/objects/filter/base.py +0 -0
  80. {simvue-2.6.1 → simvue-2.6.2}/simvue/api/objects/filter/folder.py +0 -0
  81. {simvue-2.6.1 → simvue-2.6.2}/simvue/api/objects/filter/run.py +0 -0
  82. {simvue-2.6.1 → simvue-2.6.2}/simvue/api/objects/folder.py +0 -0
  83. {simvue-2.6.1 → simvue-2.6.2}/simvue/api/objects/grids.py +0 -0
  84. {simvue-2.6.1 → simvue-2.6.2}/simvue/api/objects/metrics.py +0 -0
  85. {simvue-2.6.1 → simvue-2.6.2}/simvue/api/objects/run.py +0 -0
  86. {simvue-2.6.1 → simvue-2.6.2}/simvue/api/objects/stats.py +0 -0
  87. {simvue-2.6.1 → simvue-2.6.2}/simvue/api/objects/storage/__init__.py +0 -0
  88. {simvue-2.6.1 → simvue-2.6.2}/simvue/api/objects/storage/base.py +0 -0
  89. {simvue-2.6.1 → simvue-2.6.2}/simvue/api/objects/storage/fetch.py +0 -0
  90. {simvue-2.6.1 → simvue-2.6.2}/simvue/api/objects/storage/file.py +0 -0
  91. {simvue-2.6.1 → simvue-2.6.2}/simvue/api/objects/storage/s3.py +0 -0
  92. {simvue-2.6.1 → simvue-2.6.2}/simvue/api/objects/tag.py +0 -0
  93. {simvue-2.6.1 → simvue-2.6.2}/simvue/api/request.py +0 -0
  94. {simvue-2.6.1 → simvue-2.6.2}/simvue/api/url.py +0 -0
  95. {simvue-2.6.1 → simvue-2.6.2}/simvue/bin/__init__.py +0 -0
  96. {simvue-2.6.1 → simvue-2.6.2}/simvue/bin/sender.py +0 -0
  97. {simvue-2.6.1 → simvue-2.6.2}/simvue/client.py +0 -0
  98. {simvue-2.6.1 → simvue-2.6.2}/simvue/config/__init__.py +0 -0
  99. {simvue-2.6.1 → simvue-2.6.2}/simvue/config/files.py +0 -0
  100. {simvue-2.6.1 → simvue-2.6.2}/simvue/config/parameters.py +0 -0
  101. {simvue-2.6.1 → simvue-2.6.2}/simvue/config/user.py +0 -0
  102. {simvue-2.6.1 → simvue-2.6.2}/simvue/converters.py +0 -0
  103. {simvue-2.6.1 → simvue-2.6.2}/simvue/dispatch/__init__.py +0 -0
  104. {simvue-2.6.1 → simvue-2.6.2}/simvue/dispatch/base.py +0 -0
  105. {simvue-2.6.1 → simvue-2.6.2}/simvue/dispatch/direct.py +0 -0
  106. {simvue-2.6.1 → simvue-2.6.2}/simvue/dispatch/dispatcher.py +0 -0
  107. {simvue-2.6.1 → simvue-2.6.2}/simvue/dispatch/queued.py +0 -0
  108. {simvue-2.6.1 → simvue-2.6.2}/simvue/eco/__init__.py +0 -0
  109. {simvue-2.6.1 → simvue-2.6.2}/simvue/eco/api_client.py +0 -0
  110. {simvue-2.6.1 → simvue-2.6.2}/simvue/eco/config.py +0 -0
  111. {simvue-2.6.1 → simvue-2.6.2}/simvue/eco/emissions_monitor.py +0 -0
  112. {simvue-2.6.1 → simvue-2.6.2}/simvue/exception.py +0 -0
  113. {simvue-2.6.1 → simvue-2.6.2}/simvue/executor.py +0 -0
  114. {simvue-2.6.1 → simvue-2.6.2}/simvue/handler.py +0 -0
  115. {simvue-2.6.1 → simvue-2.6.2}/simvue/metadata.py +0 -0
  116. {simvue-2.6.1 → simvue-2.6.2}/simvue/metrics.py +0 -0
  117. {simvue-2.6.1 → simvue-2.6.2}/simvue/models.py +0 -0
  118. {simvue-2.6.1 → simvue-2.6.2}/simvue/pynvml.py +0 -0
  119. {simvue-2.6.1 → simvue-2.6.2}/simvue/sender/__init__.py +0 -0
  120. {simvue-2.6.1 → simvue-2.6.2}/simvue/sender/actions.py +0 -0
  121. {simvue-2.6.1 → simvue-2.6.2}/simvue/sender/base.py +0 -0
  122. {simvue-2.6.1 → simvue-2.6.2}/simvue/serialization.py +0 -0
  123. {simvue-2.6.1 → simvue-2.6.2}/simvue/simvue_types.py +0 -0
  124. {simvue-2.6.1 → simvue-2.6.2}/simvue/system.py +0 -0
  125. {simvue-2.6.1 → simvue-2.6.2}/simvue/utilities.py +0 -0
  126. {simvue-2.6.1 → simvue-2.6.2}/simvue/version.py +0 -0
  127. {simvue-2.6.1 → simvue-2.6.2}/simvue-black.png +0 -0
  128. {simvue-2.6.1 → simvue-2.6.2}/tests/cli/test_sender_command.py +0 -0
  129. {simvue-2.6.1 → simvue-2.6.2}/tests/conftest.py +0 -0
  130. {simvue-2.6.1 → simvue-2.6.2}/tests/example_data/Cargo.lock +0 -0
  131. {simvue-2.6.1 → simvue-2.6.2}/tests/example_data/Cargo.toml +0 -0
  132. {simvue-2.6.1 → simvue-2.6.2}/tests/example_data/Project.toml +0 -0
  133. {simvue-2.6.1 → simvue-2.6.2}/tests/example_data/SU2_inv_ONERAM6.cfg +0 -0
  134. {simvue-2.6.1 → simvue-2.6.2}/tests/example_data/package-lock.json +0 -0
  135. {simvue-2.6.1 → simvue-2.6.2}/tests/example_data/pyproject.toml +0 -0
  136. {simvue-2.6.1 → simvue-2.6.2}/tests/example_data/python_conda/environment.yml +0 -0
  137. {simvue-2.6.1 → simvue-2.6.2}/tests/example_data/python_poetry/pyproject.toml +0 -0
  138. {simvue-2.6.1 → simvue-2.6.2}/tests/example_data/python_uv/pyproject.toml +0 -0
  139. {simvue-2.6.1 → simvue-2.6.2}/tests/functional/test_client.py +0 -0
  140. {simvue-2.6.1 → simvue-2.6.2}/tests/functional/test_config.py +0 -0
  141. {simvue-2.6.1 → simvue-2.6.2}/tests/functional/test_dispatch.py +0 -0
  142. {simvue-2.6.1 → simvue-2.6.2}/tests/functional/test_executor.py +0 -0
  143. {simvue-2.6.1 → simvue-2.6.2}/tests/functional/test_run_artifact_upload.py +0 -0
  144. {simvue-2.6.1 → simvue-2.6.2}/tests/functional/test_run_execute_process.py +0 -0
  145. {simvue-2.6.1 → simvue-2.6.2}/tests/functional/test_scenarios.py +0 -0
  146. {simvue-2.6.1 → simvue-2.6.2}/tests/functional/test_utilities.py +0 -0
  147. {simvue-2.6.1 → simvue-2.6.2}/tests/unit/test_conversion_to_dataframe.py +0 -0
  148. {simvue-2.6.1 → simvue-2.6.2}/tests/unit/test_ecoclient.py +0 -0
  149. {simvue-2.6.1 → simvue-2.6.2}/tests/unit/test_event_alert.py +0 -0
  150. {simvue-2.6.1 → simvue-2.6.2}/tests/unit/test_events.py +0 -0
  151. {simvue-2.6.1 → simvue-2.6.2}/tests/unit/test_fetch.py +0 -0
  152. {simvue-2.6.1 → simvue-2.6.2}/tests/unit/test_file_artifact.py +0 -0
  153. {simvue-2.6.1 → simvue-2.6.2}/tests/unit/test_file_storage.py +0 -0
  154. {simvue-2.6.1 → simvue-2.6.2}/tests/unit/test_filters.py +0 -0
  155. {simvue-2.6.1 → simvue-2.6.2}/tests/unit/test_folder.py +0 -0
  156. {simvue-2.6.1 → simvue-2.6.2}/tests/unit/test_grids.py +0 -0
  157. {simvue-2.6.1 → simvue-2.6.2}/tests/unit/test_matplotlib_figure_mime_type.py +0 -0
  158. {simvue-2.6.1 → simvue-2.6.2}/tests/unit/test_metadata.py +0 -0
  159. {simvue-2.6.1 → simvue-2.6.2}/tests/unit/test_metric_range_alert.py +0 -0
  160. {simvue-2.6.1 → simvue-2.6.2}/tests/unit/test_metric_threshold_alert.py +0 -0
  161. {simvue-2.6.1 → simvue-2.6.2}/tests/unit/test_metrics.py +0 -0
  162. {simvue-2.6.1 → simvue-2.6.2}/tests/unit/test_numpy_array_mime_type.py +0 -0
  163. {simvue-2.6.1 → simvue-2.6.2}/tests/unit/test_numpy_array_serialization.py +0 -0
  164. {simvue-2.6.1 → simvue-2.6.2}/tests/unit/test_object_artifact.py +0 -0
  165. {simvue-2.6.1 → simvue-2.6.2}/tests/unit/test_pandas_dataframe_mimetype.py +0 -0
  166. {simvue-2.6.1 → simvue-2.6.2}/tests/unit/test_pandas_dataframe_serialization.py +0 -0
  167. {simvue-2.6.1 → simvue-2.6.2}/tests/unit/test_pickle_serialization.py +0 -0
  168. {simvue-2.6.1 → simvue-2.6.2}/tests/unit/test_plotly_figure_mime_type.py +0 -0
  169. {simvue-2.6.1 → simvue-2.6.2}/tests/unit/test_pytorch_tensor_mime_type.py +0 -0
  170. {simvue-2.6.1 → simvue-2.6.2}/tests/unit/test_pytorch_tensor_serialization.py +0 -0
  171. {simvue-2.6.1 → simvue-2.6.2}/tests/unit/test_request.py +0 -0
  172. {simvue-2.6.1 → simvue-2.6.2}/tests/unit/test_run.py +0 -0
  173. {simvue-2.6.1 → simvue-2.6.2}/tests/unit/test_run_init_folder.py +0 -0
  174. {simvue-2.6.1 → simvue-2.6.2}/tests/unit/test_run_init_tags.py +0 -0
  175. {simvue-2.6.1 → simvue-2.6.2}/tests/unit/test_s3_storage.py +0 -0
  176. {simvue-2.6.1 → simvue-2.6.2}/tests/unit/test_sender.py +0 -0
  177. {simvue-2.6.1 → simvue-2.6.2}/tests/unit/test_stats.py +0 -0
  178. {simvue-2.6.1 → simvue-2.6.2}/tests/unit/test_suppress_errors.py +0 -0
  179. {simvue-2.6.1 → simvue-2.6.2}/tests/unit/test_tag.py +0 -0
  180. {simvue-2.6.1 → simvue-2.6.2}/tests/unit/test_tenant.py +0 -0
  181. {simvue-2.6.1 → simvue-2.6.2}/tests/unit/test_user.py +0 -0
  182. {simvue-2.6.1 → simvue-2.6.2}/tests/unit/test_user_alert.py +0 -0
@@ -109,6 +109,7 @@ jobs:
109
109
  test-command: ${{ inputs.test-command }}
110
110
  test-group: executor ${{ startsWith(inputs.platform, 'windows') && 'and not unix' || '' }}
111
111
  group-label: Executor
112
+ parallel-tasks: 0
112
113
  secrets:
113
114
  SIMVUE_URL: ${{ secrets.SIMVUE_URL }}
114
115
  SIMVUE_TOKEN: ${{ secrets.SIMVUE_TOKEN }}
@@ -74,6 +74,8 @@ jobs:
74
74
  contents: write
75
75
  id-token: write
76
76
  steps:
77
+ - name: Checkout repository
78
+ uses: actions/checkout@v7
77
79
  - name: Download all the dists
78
80
  uses: actions/download-artifact@v8
79
81
  with:
@@ -1,5 +1,9 @@
1
1
  # Change Log
2
2
 
3
+ ## [v2.6.2](https://github.com/simvue-io/python-api/releases/tag/v2.6.2) - 2026-10-21
4
+
5
+ - Added support for Python 3.15.
6
+
3
7
  ## [v2.6.1](https://github.com/simvue-io/python-api/releases/tag/v2.6.1) - 2026-09-29
4
8
 
5
9
  - Revert `datetime.UTC` to `datetime.timezone.utc` for legacy support.
@@ -43,6 +43,6 @@ keywords:
43
43
  - alerting
44
44
  - simulation
45
45
  license: Apache-2.0
46
- commit: 87e2b9839f33d715363fa511e7db2e81fd073c0c
47
- version: 2.6.1
48
- date-released: "2026-09-29"
46
+ commit: 2081ea4ca78da2e05c12a29d10a6c2a6d3308e8d
47
+ version: 2.6.2
48
+ date-released: "2026-10-21"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: simvue
3
- Version: 2.6.1
3
+ Version: 2.6.2
4
4
  Summary: Simulation tracking and monitoring
5
5
  Project-URL: homepage, https://simvue.io
6
6
  Project-URL: repository, https://github.com/simvue-io/python-api
@@ -25,7 +25,7 @@ Classifier: Topic :: Scientific/Engineering
25
25
  Classifier: Topic :: System :: Monitoring
26
26
  Classifier: Topic :: Utilities
27
27
  Classifier: Typing :: Typed
28
- Requires-Python: <3.15,>=3.10
28
+ Requires-Python: <3.16,>=3.10
29
29
  Requires-Dist: click>=8.5.0
30
30
  Requires-Dist: deepmerge>=3.0.1
31
31
  Requires-Dist: email-validator>=2.3.0
@@ -34,7 +34,7 @@ id = "check-added-large-files"
34
34
 
35
35
  [[repos]]
36
36
  repo = "https://github.com/astral-sh/ruff-pre-commit"
37
- rev = "v0.16.8"
37
+ rev = "v0.16.9"
38
38
 
39
39
  [[repos.hooks]]
40
40
  id = "ruff-check"
@@ -62,7 +62,7 @@ args = ["-lll", "--recursive", "clumper"]
62
62
 
63
63
  [[repos]]
64
64
  repo = "https://github.com/python-poetry/poetry"
65
- rev = "2.4.1"
65
+ rev = "2.5.1"
66
66
  [[repos.hooks]]
67
67
  id = "poetry-check"
68
68
 
@@ -75,14 +75,14 @@ args = ["--all-extras", "--all-groups"]
75
75
 
76
76
  [[repos]]
77
77
  repo = "https://github.com/GabDug/sync-pre-commit-lock"
78
- rev = "v0.9.0"
78
+ rev = "v0.9.1"
79
79
 
80
80
  [[repos.hooks]]
81
81
  id = "sync-pre-commit-uv"
82
82
 
83
83
  [[repos]]
84
84
  repo = "https://github.com/astral-sh/uv-pre-commit"
85
- rev = "0.11.32"
85
+ rev = "0.12.21"
86
86
 
87
87
  [[repos.hooks]]
88
88
  id = "uv-lock"
@@ -96,7 +96,7 @@ id = "validate-cff"
96
96
 
97
97
  [[repos]]
98
98
  repo = "https://github.com/DetachHead/basedpyright-prek-mirror"
99
- rev = "1.39.9"
99
+ rev = "1.40.1"
100
100
 
101
101
  [[repos.hooks]]
102
102
  id = "basedpyright"
@@ -1,10 +1,10 @@
1
1
  [project]
2
2
  name = "simvue"
3
- version = "2.6.1"
3
+ version = "2.6.2"
4
4
  description = "Simulation tracking and monitoring"
5
5
  authors = [{ name = "Simvue Development Team", email = "info@simvue.io" }]
6
6
  license = "Apache-2.0"
7
- requires-python = ">=3.10,<3.15"
7
+ requires-python = ">=3.10,<3.16"
8
8
  readme = "README.md"
9
9
  classifiers = [
10
10
  "Development Status :: 5 - Production/Stable",
@@ -1,6 +1,7 @@
1
1
  {
2
2
  "$defs": {
3
3
  "CertificateSpecifications": {
4
+ "additionalProperties": false,
4
5
  "properties": {
5
6
  "client_cert": {
6
7
  "anyOf": [
@@ -60,6 +61,8 @@
60
61
  "type": "object"
61
62
  },
62
63
  "ClientGeneralOptions": {
64
+ "additionalProperties": false,
65
+ "description": "Specify client options.",
63
66
  "properties": {
64
67
  "debug": {
65
68
  "default": false,
@@ -71,6 +74,8 @@
71
74
  "type": "object"
72
75
  },
73
76
  "DefaultRunSpecifications": {
77
+ "additionalProperties": false,
78
+ "description": "Specify run default configurations.",
74
79
  "properties": {
75
80
  "description": {
76
81
  "anyOf": [
@@ -91,32 +96,14 @@
91
96
  "type": "string"
92
97
  },
93
98
  "metadata": {
94
- "anyOf": [
95
- {
96
- "additionalProperties": {
97
- "anyOf": [
98
- {
99
- "type": "string"
100
- },
101
- {
102
- "type": "integer"
103
- },
104
- {
105
- "type": "number"
106
- },
107
- {
108
- "type": "boolean"
109
- }
110
- ]
111
- },
112
- "type": "object"
113
- },
114
- {
115
- "type": "null"
116
- }
117
- ],
118
- "default": null,
119
- "title": "Metadata"
99
+ "$ref": "#/$defs/MetadataSpecifications",
100
+ "default": {
101
+ "custom": null,
102
+ "environment": true,
103
+ "git": true,
104
+ "record_shell_vars": null,
105
+ "system": true
106
+ }
120
107
  },
121
108
  "mode": {
122
109
  "default": "online",
@@ -267,7 +254,74 @@
267
254
  "title": "EcoConfig",
268
255
  "type": "object"
269
256
  },
257
+ "MetadataSpecifications": {
258
+ "additionalProperties": false,
259
+ "properties": {
260
+ "custom": {
261
+ "anyOf": [
262
+ {
263
+ "additionalProperties": {
264
+ "anyOf": [
265
+ {
266
+ "type": "string"
267
+ },
268
+ {
269
+ "type": "integer"
270
+ },
271
+ {
272
+ "type": "number"
273
+ },
274
+ {
275
+ "type": "boolean"
276
+ }
277
+ ]
278
+ },
279
+ "type": "object"
280
+ },
281
+ {
282
+ "type": "null"
283
+ }
284
+ ],
285
+ "default": null,
286
+ "title": "Custom"
287
+ },
288
+ "environment": {
289
+ "default": true,
290
+ "title": "Environment",
291
+ "type": "boolean"
292
+ },
293
+ "git": {
294
+ "default": true,
295
+ "title": "Git",
296
+ "type": "boolean"
297
+ },
298
+ "record_shell_vars": {
299
+ "anyOf": [
300
+ {
301
+ "items": {
302
+ "type": "string"
303
+ },
304
+ "type": "array"
305
+ },
306
+ {
307
+ "type": "null"
308
+ }
309
+ ],
310
+ "default": null,
311
+ "title": "Record Shell Vars"
312
+ },
313
+ "system": {
314
+ "default": true,
315
+ "title": "System",
316
+ "type": "boolean"
317
+ }
318
+ },
319
+ "title": "MetadataSpecifications",
320
+ "type": "object"
321
+ },
270
322
  "MetricsSpecifications": {
323
+ "additionalProperties": false,
324
+ "description": "Specify metric configurations.",
271
325
  "properties": {
272
326
  "enable_emission_metrics": {
273
327
  "default": false,
@@ -292,6 +346,8 @@
292
346
  "type": "object"
293
347
  },
294
348
  "OfflineSpecifications": {
349
+ "additionalProperties": false,
350
+ "description": "Specify offline mode configurations.",
295
351
  "properties": {
296
352
  "cache": {
297
353
  "anyOf": [
@@ -312,6 +368,7 @@
312
368
  },
313
369
  "ServerSpecifications": {
314
370
  "additionalProperties": false,
371
+ "description": "Specify server configurations.",
315
372
  "properties": {
316
373
  "certificates": {
317
374
  "$ref": "#/$defs/CertificateSpecifications"
@@ -367,6 +424,7 @@
367
424
  }
368
425
  },
369
426
  "additionalProperties": false,
427
+ "description": "Configure session.",
370
428
  "properties": {
371
429
  "client": {
372
430
  "$ref": "#/$defs/ClientGeneralOptions",
@@ -422,7 +480,13 @@
422
480
  "default": {
423
481
  "description": null,
424
482
  "folder": "/",
425
- "metadata": null,
483
+ "metadata": {
484
+ "custom": null,
485
+ "environment": true,
486
+ "git": true,
487
+ "record_shell_vars": null,
488
+ "system": true
489
+ },
426
490
  "mode": "online",
427
491
  "name": null,
428
492
  "record_shell_vars": null,
@@ -189,6 +189,7 @@ class Run:
189
189
  self._dispatcher: DispatcherBaseClass | None = None
190
190
 
191
191
  self._meta_cache: dict[str, typing.Any] = {}
192
+ self._meta_cache_lock: threading.Lock = threading.Lock()
192
193
 
193
194
  self._folder: Folder | None = None
194
195
  self._term_color: bool = True
@@ -1779,10 +1780,11 @@ class Run:
1779
1780
  ```
1780
1781
 
1781
1782
  """
1782
- # If there are any metric units to be uploaded do so now
1783
- if _units := self._meta_cache.get("metrics"):
1783
+ # Ensure this cannot get into a race with set_metric_units
1784
+ with self._meta_cache_lock:
1785
+ _units = self._meta_cache.pop("metrics", None)
1786
+ if _units:
1784
1787
  self.update_metadata({"simvue": {"metrics": _units}})
1785
- del self._meta_cache["metrics"]
1786
1788
 
1787
1789
  # TODO: When metrics and grids are combined into a single entity
1788
1790
  # this can be removed. For now need to separate tensor based metrics
@@ -2753,18 +2755,19 @@ class Run:
2753
2755
  ```
2754
2756
 
2755
2757
  """
2756
- self._meta_cache.setdefault("metrics", {})
2757
-
2758
2758
  try:
2759
2759
  _unit_obj: unyt_quantity = unyt_quantity.from_string(units)
2760
- self._meta_cache["metrics"][metric_name] = {
2760
+ _payload = {
2761
2761
  "units": units,
2762
2762
  "mks_conversion": mks_conversion or float(_unit_obj.in_mks().value),
2763
2763
  "mks_units": mks_unit or f"{_unit_obj.in_mks().units}",
2764
2764
  }
2765
2765
  except (UnitParseError, ValueError):
2766
- self._meta_cache["metrics"][metric_name] = {
2766
+ _payload = {
2767
2767
  "units": units,
2768
2768
  "mks_conversion": mks_conversion,
2769
2769
  "mks_units": mks_unit,
2770
2770
  }
2771
+ # Ensure this cannot get into a race with log_metrics
2772
+ with self._meta_cache_lock:
2773
+ self._meta_cache.setdefault("metrics", {})[metric_name] = _payload
@@ -1,4 +1,4 @@
1
- # This file is automatically @generated by Poetry 2.4.1 and should not be changed by hand.
1
+ # This file is automatically @generated by Poetry 2.5.1 and should not be changed by hand.
2
2
 
3
3
  [[package]]
4
4
  name = "certifi"
@@ -366,14 +366,14 @@ files = [
366
366
 
367
367
  [[package]]
368
368
  name = "urllib3"
369
- version = "2.6.3"
369
+ version = "2.8.0"
370
370
  description = "HTTP library with thread-safe connection pooling, file post, and more."
371
371
  optional = false
372
- python-versions = ">=3.9"
372
+ python-versions = ">=3.10"
373
373
  groups = ["main"]
374
374
  files = [
375
- {file = "urllib3-2.6.3-py3-none-any.whl", hash = "sha256:bf272323e553dfb2e87d9bfd225ca7b0f467b919d7bbd355436d3fd37cb0acd4"},
376
- {file = "urllib3-2.6.3.tar.gz", hash = "sha256:1b62b6884944a57dbe321509ab94fd4d3b307075e0c2eae991ac71ee15ad38ed"},
375
+ {file = "urllib3-2.8.0-py3-none-any.whl", hash = "sha256:0cf3cae568d36aa9576b28dfb35f11328f1cb974ca7647d9475ebb86c75ac6e3"},
376
+ {file = "urllib3-2.8.0.tar.gz", hash = "sha256:63bf2ead4c879426ebf22ef2a781eeb4aa3b4ae798a0435506f8687fd5bb9b63"},
377
377
  ]
378
378
 
379
379
  [package.extras]
@@ -172,9 +172,9 @@ wheels = [
172
172
 
173
173
  [[package]]
174
174
  name = "urllib3"
175
- version = "2.6.3"
175
+ version = "2.8.0"
176
176
  source = { registry = "https://pypi.org/simple" }
177
- sdist = { url = "https://files.pythonhosted.org/packages/c7/24/5f1b3bdffd70275f6661c76461e25f024d5a38a46f04aaca912426a2b1d3/urllib3-2.6.3.tar.gz", hash = "sha256:1b62b6884944a57dbe321509ab94fd4d3b307075e0c2eae991ac71ee15ad38ed", size = 435556, upload-time = "2026-01-07T16:24:43.925Z" }
177
+ sdist = { url = "https://files.pythonhosted.org/packages/e3/05/b17359e1cefb4f909b5e40b1b90a496d987258916dbbf88e842c729f510e/urllib3-2.8.0.tar.gz", hash = "sha256:63bf2ead4c879426ebf22ef2a781eeb4aa3b4ae798a0435506f8687fd5bb9b63", size = 458972, upload-time = "2026-09-15T19:29:36.253Z" }
178
178
  wheels = [
179
- { url = "https://files.pythonhosted.org/packages/39/08/aaaad47bc4e9dc8c725e68f9d04865dbcb2052843ff09c97b08904852d84/urllib3-2.6.3-py3-none-any.whl", hash = "sha256:bf272323e553dfb2e87d9bfd225ca7b0f467b919d7bbd355436d3fd37cb0acd4", size = 131584, upload-time = "2026-01-07T16:24:42.685Z" },
179
+ { url = "https://files.pythonhosted.org/packages/92/9d/c4e665119135114480843e7ab388fa94d8480650450e6f8e26b70d323a4c/urllib3-2.8.0-py3-none-any.whl", hash = "sha256:0cf3cae568d36aa9576b28dfb35f11328f1cb974ca7647d9475ebb86c75ac6e3", size = 135717, upload-time = "2026-09-15T19:29:34.577Z" },
180
180
  ]
@@ -12,12 +12,14 @@ import contextlib
12
12
  import inspect
13
13
  import tempfile
14
14
  import threading
15
+ import traceback
15
16
  import uuid
16
17
  import psutil
17
18
  import pathlib
18
19
  import concurrent.futures
19
20
  import random
20
21
  import datetime
22
+ from unyt import unyt_quantity
21
23
  import simvue
22
24
  from simvue.api.objects import Alert, EventsAlert, Metrics, MetricsRangeAlert, MetricsThresholdAlert, UserAlert
23
25
  from simvue.api.objects.grids import GridMetrics
@@ -1402,7 +1404,12 @@ def test_abort_on_alert_process(mocker: pytest_mock.MockerFixture) -> None:
1402
1404
  assert run._system_metrics_interval == 1
1403
1405
  for child in child_processes:
1404
1406
  assert not child.is_running()
1405
- if run._status != "terminated":
1407
+ counter = 0
1408
+ while run._status != "terminated" and counter < 15:
1409
+ time.sleep(1)
1410
+ assert run._sv_obj.abort_trigger, "Abort trigger was not set"
1411
+ counter += 1
1412
+ if counter >= 15:
1406
1413
  run.kill_all_processes()
1407
1414
  raise AssertionError("Run was not terminated")
1408
1415
  assert trigger.is_set()
@@ -1779,3 +1786,85 @@ def test_set_metric_units(create_plain_run: tuple[sv_run.Run, dict]) -> None:
1779
1786
  assert _metric_data["x"] == {"units": "ft", "mks_units": "m", "mks_conversion": 0.3048}
1780
1787
  assert _metric_data["y"] == {"units": "m", "mks_units": "m", "mks_conversion": 1}
1781
1788
  assert _metric_data["z"] == {"units": "Foobars", "mks_units": "m", "mks_conversion": 3.542}
1789
+
1790
+
1791
+ @pytest.mark.run
1792
+ @pytest.mark.offline
1793
+ def test_set_metric_units_thread_safety(
1794
+ create_plain_run_offline: tuple[sv_run.Run, dict],
1795
+ monkeypatch: pytest.MonkeyPatch,
1796
+ ) -> None:
1797
+ """Concurrent set_metric_units / log_metrics must not corrupt the units cache.
1798
+ """
1799
+ run, _ = create_plain_run_offline
1800
+
1801
+ # Seed a pending units entry so log_metrics takes the fetch path
1802
+ run.set_metric_units("seed", units="m")
1803
+ assert "metrics" in run._meta_cache
1804
+
1805
+ log_frozen = threading.Event()
1806
+ log_released = threading.Event()
1807
+ units_frozen = threading.Event()
1808
+ units_released = threading.Event()
1809
+
1810
+ _orig_update_metadata = run.update_metadata
1811
+
1812
+ def _blocking_update_metadata(metadata: dict[str, typing.Any]) -> bool:
1813
+ """Freeze log_metrics after the units fetch, before the cache delete."""
1814
+ log_frozen.set()
1815
+ log_released.wait(timeout=10)
1816
+ return _orig_update_metadata(metadata)
1817
+
1818
+ _orig_from_string = unyt_quantity.from_string
1819
+
1820
+ def _blocking_from_string(
1821
+ cls: type, s: str, *args: typing.Any, **kwargs: typing.Any
1822
+ ):
1823
+ """Freeze set_metric_units after the setdefault, before the item assignment."""
1824
+ units_frozen.set()
1825
+ units_released.wait(timeout=10)
1826
+ return _orig_from_string(s, *args, **kwargs)
1827
+
1828
+ monkeypatch.setattr(run, "update_metadata", _blocking_update_metadata)
1829
+ monkeypatch.setattr(
1830
+ unyt_quantity, "from_string", classmethod(_blocking_from_string)
1831
+ )
1832
+
1833
+ errors: list[str] = []
1834
+
1835
+ def log_metrics_task() -> None:
1836
+ try:
1837
+ run.log_metrics({"a": 1.0}, step=0)
1838
+ except Exception:
1839
+ errors.append(f"log_metrics:\n{traceback.format_exc()}")
1840
+
1841
+ def set_metric_units_task() -> None:
1842
+ try:
1843
+ run.set_metric_units("b", units="ft")
1844
+ except Exception:
1845
+ errors.append(f"set_metric_units:\n{traceback.format_exc()}")
1846
+
1847
+ log_thread = threading.Thread(target=log_metrics_task, name="log-metrics")
1848
+ log_thread.start()
1849
+ assert log_frozen.wait(timeout=10), "log_metrics did not reach update_metadata"
1850
+
1851
+ units_thread = threading.Thread(
1852
+ target=set_metric_units_task, name="set-metric-units"
1853
+ )
1854
+ units_thread.start()
1855
+ assert units_frozen.wait(timeout=10), "set_metric_units did not reach unit parsing"
1856
+
1857
+ # Release in the fatal order: fetch / setdefault / delete / item-assignment
1858
+ log_released.set()
1859
+ log_thread.join(timeout=10)
1860
+ units_released.set()
1861
+ units_thread.join(timeout=10)
1862
+
1863
+ assert not log_thread.is_alive() and not units_thread.is_alive()
1864
+ assert not errors, "concurrent metric logging raised:\n" + "\n".join(errors)
1865
+
1866
+ # log_metrics must have uploaded the seeded units, and the units set by
1867
+ # set_metric_units must still be pending for the next log_metrics call
1868
+ assert run._meta_cache.get("metrics") == {
1869
+ "b": {"units": "ft", "mks_units": "m", "mks_conversion": 0.3048}
1870
+ }