pybiolib 1.2.1187__tar.gz → 1.2.1232__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 (164) hide show
  1. {pybiolib-1.2.1187 → pybiolib-1.2.1232}/PKG-INFO +4 -2
  2. {pybiolib-1.2.1187 → pybiolib-1.2.1232}/biolib/_internal/push_application.py +8 -6
  3. pybiolib-1.2.1232/biolib/_internal/utils/__init__.py +43 -0
  4. {pybiolib-1.2.1187 → pybiolib-1.2.1232}/biolib/experiments/experiment.py +76 -0
  5. {pybiolib-1.2.1187 → pybiolib-1.2.1232}/biolib/jobs/job.py +43 -3
  6. {pybiolib-1.2.1187 → pybiolib-1.2.1232}/biolib/jobs/job_result.py +6 -27
  7. {pybiolib-1.2.1187 → pybiolib-1.2.1232}/pyproject.toml +1 -1
  8. pybiolib-1.2.1187/biolib/_internal/utils/__init__.py +0 -18
  9. {pybiolib-1.2.1187 → pybiolib-1.2.1232}/LICENSE +0 -0
  10. {pybiolib-1.2.1187 → pybiolib-1.2.1232}/PYPI_README.md +0 -0
  11. {pybiolib-1.2.1187 → pybiolib-1.2.1232}/biolib/__init__.py +0 -0
  12. {pybiolib-1.2.1187 → pybiolib-1.2.1232}/biolib/_data_record/data_record.py +0 -0
  13. {pybiolib-1.2.1187 → pybiolib-1.2.1232}/biolib/_internal/__init__.py +0 -0
  14. {pybiolib-1.2.1187 → pybiolib-1.2.1232}/biolib/_internal/add_copilot_prompts.py +0 -0
  15. {pybiolib-1.2.1187 → pybiolib-1.2.1232}/biolib/_internal/add_gui_files.py +0 -0
  16. {pybiolib-1.2.1187 → pybiolib-1.2.1232}/biolib/_internal/data_record/__init__.py +0 -0
  17. {pybiolib-1.2.1187 → pybiolib-1.2.1232}/biolib/_internal/data_record/data_record.py +0 -0
  18. {pybiolib-1.2.1187 → pybiolib-1.2.1232}/biolib/_internal/data_record/push_data.py +0 -0
  19. {pybiolib-1.2.1187 → pybiolib-1.2.1232}/biolib/_internal/data_record/remote_storage_endpoint.py +0 -0
  20. {pybiolib-1.2.1187 → pybiolib-1.2.1232}/biolib/_internal/errors.py +0 -0
  21. {pybiolib-1.2.1187 → pybiolib-1.2.1232}/biolib/_internal/file_utils.py +0 -0
  22. {pybiolib-1.2.1187 → pybiolib-1.2.1232}/biolib/_internal/fuse_mount/__init__.py +0 -0
  23. {pybiolib-1.2.1187 → pybiolib-1.2.1232}/biolib/_internal/fuse_mount/experiment_fuse_mount.py +0 -0
  24. {pybiolib-1.2.1187 → pybiolib-1.2.1232}/biolib/_internal/http_client.py +0 -0
  25. {pybiolib-1.2.1187 → pybiolib-1.2.1232}/biolib/_internal/lfs/__init__.py +0 -0
  26. {pybiolib-1.2.1187 → pybiolib-1.2.1232}/biolib/_internal/lfs/cache.py +0 -0
  27. {pybiolib-1.2.1187 → pybiolib-1.2.1232}/biolib/_internal/libs/__init__.py +0 -0
  28. {pybiolib-1.2.1187 → pybiolib-1.2.1232}/biolib/_internal/libs/fusepy/__init__.py +0 -0
  29. {pybiolib-1.2.1187 → pybiolib-1.2.1232}/biolib/_internal/runtime.py +0 -0
  30. {pybiolib-1.2.1187 → pybiolib-1.2.1232}/biolib/_internal/string_utils.py +0 -0
  31. {pybiolib-1.2.1187 → pybiolib-1.2.1232}/biolib/_internal/templates/__init__.py +0 -0
  32. {pybiolib-1.2.1187 → pybiolib-1.2.1232}/biolib/_internal/templates/copilot_template/.github/instructions/general-app-knowledge.instructions.md +0 -0
  33. {pybiolib-1.2.1187 → pybiolib-1.2.1232}/biolib/_internal/templates/copilot_template/.github/instructions/style-general.instructions.md +0 -0
  34. {pybiolib-1.2.1187 → pybiolib-1.2.1232}/biolib/_internal/templates/copilot_template/.github/instructions/style-python.instructions.md +0 -0
  35. {pybiolib-1.2.1187 → pybiolib-1.2.1232}/biolib/_internal/templates/copilot_template/.github/instructions/style-react-ts.instructions.md +0 -0
  36. {pybiolib-1.2.1187 → pybiolib-1.2.1232}/biolib/_internal/templates/copilot_template/.github/prompts/biolib_app_inputs.prompt.md +0 -0
  37. {pybiolib-1.2.1187 → pybiolib-1.2.1232}/biolib/_internal/templates/copilot_template/.github/prompts/biolib_onboard_repo.prompt.md +0 -0
  38. {pybiolib-1.2.1187 → pybiolib-1.2.1232}/biolib/_internal/templates/copilot_template/.github/prompts/biolib_run_apps.prompt.md +0 -0
  39. {pybiolib-1.2.1187 → pybiolib-1.2.1232}/biolib/_internal/templates/gui_template/.yarnrc.yml +0 -0
  40. {pybiolib-1.2.1187 → pybiolib-1.2.1232}/biolib/_internal/templates/gui_template/App.tsx +0 -0
  41. {pybiolib-1.2.1187 → pybiolib-1.2.1232}/biolib/_internal/templates/gui_template/Dockerfile +0 -0
  42. {pybiolib-1.2.1187 → pybiolib-1.2.1232}/biolib/_internal/templates/gui_template/index.css +0 -0
  43. {pybiolib-1.2.1187 → pybiolib-1.2.1232}/biolib/_internal/templates/gui_template/index.html +0 -0
  44. {pybiolib-1.2.1187 → pybiolib-1.2.1232}/biolib/_internal/templates/gui_template/index.tsx +0 -0
  45. {pybiolib-1.2.1187 → pybiolib-1.2.1232}/biolib/_internal/templates/gui_template/package.json +0 -0
  46. {pybiolib-1.2.1187 → pybiolib-1.2.1232}/biolib/_internal/templates/gui_template/tsconfig.json +0 -0
  47. {pybiolib-1.2.1187 → pybiolib-1.2.1232}/biolib/_internal/templates/gui_template/vite.config.mts +0 -0
  48. {pybiolib-1.2.1187 → pybiolib-1.2.1232}/biolib/_internal/templates/init_template/.biolib/config.yml +0 -0
  49. {pybiolib-1.2.1187 → pybiolib-1.2.1232}/biolib/_internal/templates/init_template/.github/workflows/biolib.yml +0 -0
  50. {pybiolib-1.2.1187 → pybiolib-1.2.1232}/biolib/_internal/templates/init_template/.gitignore +0 -0
  51. {pybiolib-1.2.1187 → pybiolib-1.2.1232}/biolib/_internal/templates/init_template/Dockerfile +0 -0
  52. {pybiolib-1.2.1187 → pybiolib-1.2.1232}/biolib/_internal/templates/init_template/requirements.txt +0 -0
  53. {pybiolib-1.2.1187 → pybiolib-1.2.1232}/biolib/_internal/templates/init_template/run.py +0 -0
  54. {pybiolib-1.2.1187 → pybiolib-1.2.1232}/biolib/_internal/templates/init_template/run.sh +0 -0
  55. {pybiolib-1.2.1187 → pybiolib-1.2.1232}/biolib/_internal/templates/templates.py +0 -0
  56. {pybiolib-1.2.1187 → pybiolib-1.2.1232}/biolib/_internal/tree_utils.py +0 -0
  57. {pybiolib-1.2.1187 → pybiolib-1.2.1232}/biolib/_internal/types/__init__.py +0 -0
  58. {pybiolib-1.2.1187 → pybiolib-1.2.1232}/biolib/_internal/types/account.py +0 -0
  59. {pybiolib-1.2.1187 → pybiolib-1.2.1232}/biolib/_internal/types/account_member.py +0 -0
  60. {pybiolib-1.2.1187 → pybiolib-1.2.1232}/biolib/_internal/types/app.py +0 -0
  61. {pybiolib-1.2.1187 → pybiolib-1.2.1232}/biolib/_internal/types/data_record.py +0 -0
  62. {pybiolib-1.2.1187 → pybiolib-1.2.1232}/biolib/_internal/types/experiment.py +0 -0
  63. {pybiolib-1.2.1187 → pybiolib-1.2.1232}/biolib/_internal/types/file_node.py +0 -0
  64. {pybiolib-1.2.1187 → pybiolib-1.2.1232}/biolib/_internal/types/push.py +0 -0
  65. {pybiolib-1.2.1187 → pybiolib-1.2.1232}/biolib/_internal/types/resource.py +0 -0
  66. {pybiolib-1.2.1187 → pybiolib-1.2.1232}/biolib/_internal/types/resource_permission.py +0 -0
  67. {pybiolib-1.2.1187 → pybiolib-1.2.1232}/biolib/_internal/types/resource_version.py +0 -0
  68. {pybiolib-1.2.1187 → pybiolib-1.2.1232}/biolib/_internal/types/result.py +0 -0
  69. {pybiolib-1.2.1187 → pybiolib-1.2.1232}/biolib/_internal/types/typing.py +0 -0
  70. {pybiolib-1.2.1187 → pybiolib-1.2.1232}/biolib/_internal/types/user.py +0 -0
  71. {pybiolib-1.2.1187 → pybiolib-1.2.1232}/biolib/_internal/utils/multinode.py +0 -0
  72. {pybiolib-1.2.1187 → pybiolib-1.2.1232}/biolib/_runtime/runtime.py +0 -0
  73. {pybiolib-1.2.1187 → pybiolib-1.2.1232}/biolib/_session/session.py +0 -0
  74. {pybiolib-1.2.1187 → pybiolib-1.2.1232}/biolib/api/__init__.py +0 -0
  75. {pybiolib-1.2.1187 → pybiolib-1.2.1232}/biolib/api/client.py +0 -0
  76. {pybiolib-1.2.1187 → pybiolib-1.2.1232}/biolib/app/__init__.py +0 -0
  77. {pybiolib-1.2.1187 → pybiolib-1.2.1232}/biolib/app/app.py +0 -0
  78. {pybiolib-1.2.1187 → pybiolib-1.2.1232}/biolib/app/search_apps.py +0 -0
  79. {pybiolib-1.2.1187 → pybiolib-1.2.1232}/biolib/biolib_api_client/__init__.py +0 -0
  80. {pybiolib-1.2.1187 → pybiolib-1.2.1232}/biolib/biolib_api_client/api_client.py +0 -0
  81. {pybiolib-1.2.1187 → pybiolib-1.2.1232}/biolib/biolib_api_client/app_types.py +0 -0
  82. {pybiolib-1.2.1187 → pybiolib-1.2.1232}/biolib/biolib_api_client/auth.py +0 -0
  83. {pybiolib-1.2.1187 → pybiolib-1.2.1232}/biolib/biolib_api_client/biolib_app_api.py +0 -0
  84. {pybiolib-1.2.1187 → pybiolib-1.2.1232}/biolib/biolib_api_client/biolib_job_api.py +0 -0
  85. {pybiolib-1.2.1187 → pybiolib-1.2.1232}/biolib/biolib_api_client/common_types.py +0 -0
  86. {pybiolib-1.2.1187 → pybiolib-1.2.1232}/biolib/biolib_api_client/job_types.py +0 -0
  87. {pybiolib-1.2.1187 → pybiolib-1.2.1232}/biolib/biolib_api_client/lfs_types.py +0 -0
  88. {pybiolib-1.2.1187 → pybiolib-1.2.1232}/biolib/biolib_api_client/user_state.py +0 -0
  89. {pybiolib-1.2.1187 → pybiolib-1.2.1232}/biolib/biolib_binary_format/__init__.py +0 -0
  90. {pybiolib-1.2.1187 → pybiolib-1.2.1232}/biolib/biolib_binary_format/base_bbf_package.py +0 -0
  91. {pybiolib-1.2.1187 → pybiolib-1.2.1232}/biolib/biolib_binary_format/file_in_container.py +0 -0
  92. {pybiolib-1.2.1187 → pybiolib-1.2.1232}/biolib/biolib_binary_format/module_input.py +0 -0
  93. {pybiolib-1.2.1187 → pybiolib-1.2.1232}/biolib/biolib_binary_format/module_output_v2.py +0 -0
  94. {pybiolib-1.2.1187 → pybiolib-1.2.1232}/biolib/biolib_binary_format/remote_endpoints.py +0 -0
  95. {pybiolib-1.2.1187 → pybiolib-1.2.1232}/biolib/biolib_binary_format/remote_stream_seeker.py +0 -0
  96. {pybiolib-1.2.1187 → pybiolib-1.2.1232}/biolib/biolib_binary_format/saved_job.py +0 -0
  97. {pybiolib-1.2.1187 → pybiolib-1.2.1232}/biolib/biolib_binary_format/stdout_and_stderr.py +0 -0
  98. {pybiolib-1.2.1187 → pybiolib-1.2.1232}/biolib/biolib_binary_format/system_exception.py +0 -0
  99. {pybiolib-1.2.1187 → pybiolib-1.2.1232}/biolib/biolib_binary_format/system_status_update.py +0 -0
  100. {pybiolib-1.2.1187 → pybiolib-1.2.1232}/biolib/biolib_binary_format/utils.py +0 -0
  101. {pybiolib-1.2.1187 → pybiolib-1.2.1232}/biolib/biolib_docker_client/__init__.py +0 -0
  102. {pybiolib-1.2.1187 → pybiolib-1.2.1232}/biolib/biolib_download_container.py +0 -0
  103. {pybiolib-1.2.1187 → pybiolib-1.2.1232}/biolib/biolib_errors.py +0 -0
  104. {pybiolib-1.2.1187 → pybiolib-1.2.1232}/biolib/biolib_logging.py +0 -0
  105. {pybiolib-1.2.1187 → pybiolib-1.2.1232}/biolib/cli/__init__.py +0 -0
  106. {pybiolib-1.2.1187 → pybiolib-1.2.1232}/biolib/cli/auth.py +0 -0
  107. {pybiolib-1.2.1187 → pybiolib-1.2.1232}/biolib/cli/data_record.py +0 -0
  108. {pybiolib-1.2.1187 → pybiolib-1.2.1232}/biolib/cli/download_container.py +0 -0
  109. {pybiolib-1.2.1187 → pybiolib-1.2.1232}/biolib/cli/init.py +0 -0
  110. {pybiolib-1.2.1187 → pybiolib-1.2.1232}/biolib/cli/lfs.py +0 -0
  111. {pybiolib-1.2.1187 → pybiolib-1.2.1232}/biolib/cli/push.py +0 -0
  112. {pybiolib-1.2.1187 → pybiolib-1.2.1232}/biolib/cli/run.py +0 -0
  113. {pybiolib-1.2.1187 → pybiolib-1.2.1232}/biolib/cli/runtime.py +0 -0
  114. {pybiolib-1.2.1187 → pybiolib-1.2.1232}/biolib/cli/sdk.py +0 -0
  115. {pybiolib-1.2.1187 → pybiolib-1.2.1232}/biolib/cli/start.py +0 -0
  116. {pybiolib-1.2.1187 → pybiolib-1.2.1232}/biolib/compute_node/.gitignore +0 -0
  117. {pybiolib-1.2.1187 → pybiolib-1.2.1232}/biolib/compute_node/__init__.py +0 -0
  118. {pybiolib-1.2.1187 → pybiolib-1.2.1232}/biolib/compute_node/cloud_utils/__init__.py +0 -0
  119. {pybiolib-1.2.1187 → pybiolib-1.2.1232}/biolib/compute_node/cloud_utils/cloud_utils.py +0 -0
  120. {pybiolib-1.2.1187 → pybiolib-1.2.1232}/biolib/compute_node/job_worker/__init__.py +0 -0
  121. {pybiolib-1.2.1187 → pybiolib-1.2.1232}/biolib/compute_node/job_worker/cache_state.py +0 -0
  122. {pybiolib-1.2.1187 → pybiolib-1.2.1232}/biolib/compute_node/job_worker/cache_types.py +0 -0
  123. {pybiolib-1.2.1187 → pybiolib-1.2.1232}/biolib/compute_node/job_worker/docker_image_cache.py +0 -0
  124. {pybiolib-1.2.1187 → pybiolib-1.2.1232}/biolib/compute_node/job_worker/executors/__init__.py +0 -0
  125. {pybiolib-1.2.1187 → pybiolib-1.2.1232}/biolib/compute_node/job_worker/executors/docker_executor.py +0 -0
  126. {pybiolib-1.2.1187 → pybiolib-1.2.1232}/biolib/compute_node/job_worker/executors/docker_types.py +0 -0
  127. {pybiolib-1.2.1187 → pybiolib-1.2.1232}/biolib/compute_node/job_worker/executors/tars/__init__.py +0 -0
  128. {pybiolib-1.2.1187 → pybiolib-1.2.1232}/biolib/compute_node/job_worker/executors/types.py +0 -0
  129. {pybiolib-1.2.1187 → pybiolib-1.2.1232}/biolib/compute_node/job_worker/job_legacy_input_wait_timeout_thread.py +0 -0
  130. {pybiolib-1.2.1187 → pybiolib-1.2.1232}/biolib/compute_node/job_worker/job_max_runtime_timer_thread.py +0 -0
  131. {pybiolib-1.2.1187 → pybiolib-1.2.1232}/biolib/compute_node/job_worker/job_storage.py +0 -0
  132. {pybiolib-1.2.1187 → pybiolib-1.2.1232}/biolib/compute_node/job_worker/job_worker.py +0 -0
  133. {pybiolib-1.2.1187 → pybiolib-1.2.1232}/biolib/compute_node/job_worker/large_file_system.py +0 -0
  134. {pybiolib-1.2.1187 → pybiolib-1.2.1232}/biolib/compute_node/job_worker/mappings.py +0 -0
  135. {pybiolib-1.2.1187 → pybiolib-1.2.1232}/biolib/compute_node/job_worker/utilization_reporter_thread.py +0 -0
  136. {pybiolib-1.2.1187 → pybiolib-1.2.1232}/biolib/compute_node/job_worker/utils.py +0 -0
  137. {pybiolib-1.2.1187 → pybiolib-1.2.1232}/biolib/compute_node/remote_host_proxy.py +0 -0
  138. {pybiolib-1.2.1187 → pybiolib-1.2.1232}/biolib/compute_node/socker_listener_thread.py +0 -0
  139. {pybiolib-1.2.1187 → pybiolib-1.2.1232}/biolib/compute_node/socket_sender_thread.py +0 -0
  140. {pybiolib-1.2.1187 → pybiolib-1.2.1232}/biolib/compute_node/utils.py +0 -0
  141. {pybiolib-1.2.1187 → pybiolib-1.2.1232}/biolib/compute_node/webserver/__init__.py +0 -0
  142. {pybiolib-1.2.1187 → pybiolib-1.2.1232}/biolib/compute_node/webserver/compute_node_results_proxy.py +0 -0
  143. {pybiolib-1.2.1187 → pybiolib-1.2.1232}/biolib/compute_node/webserver/gunicorn_flask_application.py +0 -0
  144. {pybiolib-1.2.1187 → pybiolib-1.2.1232}/biolib/compute_node/webserver/proxy_utils.py +0 -0
  145. {pybiolib-1.2.1187 → pybiolib-1.2.1232}/biolib/compute_node/webserver/webserver.py +0 -0
  146. {pybiolib-1.2.1187 → pybiolib-1.2.1232}/biolib/compute_node/webserver/webserver_types.py +0 -0
  147. {pybiolib-1.2.1187 → pybiolib-1.2.1232}/biolib/compute_node/webserver/webserver_utils.py +0 -0
  148. {pybiolib-1.2.1187 → pybiolib-1.2.1232}/biolib/compute_node/webserver/worker_thread.py +0 -0
  149. {pybiolib-1.2.1187 → pybiolib-1.2.1232}/biolib/experiments/__init__.py +0 -0
  150. {pybiolib-1.2.1187 → pybiolib-1.2.1232}/biolib/jobs/__init__.py +0 -0
  151. {pybiolib-1.2.1187 → pybiolib-1.2.1232}/biolib/jobs/types.py +0 -0
  152. {pybiolib-1.2.1187 → pybiolib-1.2.1232}/biolib/py.typed +0 -0
  153. {pybiolib-1.2.1187 → pybiolib-1.2.1232}/biolib/runtime/__init__.py +0 -0
  154. {pybiolib-1.2.1187 → pybiolib-1.2.1232}/biolib/sdk/__init__.py +0 -0
  155. {pybiolib-1.2.1187 → pybiolib-1.2.1232}/biolib/tables.py +0 -0
  156. {pybiolib-1.2.1187 → pybiolib-1.2.1232}/biolib/typing_utils.py +0 -0
  157. {pybiolib-1.2.1187 → pybiolib-1.2.1232}/biolib/user/__init__.py +0 -0
  158. {pybiolib-1.2.1187 → pybiolib-1.2.1232}/biolib/user/sign_in.py +0 -0
  159. {pybiolib-1.2.1187 → pybiolib-1.2.1232}/biolib/utils/__init__.py +0 -0
  160. {pybiolib-1.2.1187 → pybiolib-1.2.1232}/biolib/utils/app_uri.py +0 -0
  161. {pybiolib-1.2.1187 → pybiolib-1.2.1232}/biolib/utils/cache_state.py +0 -0
  162. {pybiolib-1.2.1187 → pybiolib-1.2.1232}/biolib/utils/multipart_uploader.py +0 -0
  163. {pybiolib-1.2.1187 → pybiolib-1.2.1232}/biolib/utils/seq_util.py +0 -0
  164. {pybiolib-1.2.1187 → pybiolib-1.2.1232}/biolib/utils/zip/remote_zip.py +0 -0
@@ -1,8 +1,9 @@
1
- Metadata-Version: 2.3
1
+ Metadata-Version: 2.4
2
2
  Name: pybiolib
3
- Version: 1.2.1187
3
+ Version: 1.2.1232
4
4
  Summary: BioLib Python Client
5
5
  License: MIT
6
+ License-File: LICENSE
6
7
  Keywords: biolib
7
8
  Author: biolib
8
9
  Author-email: hello@biolib.com
@@ -17,6 +18,7 @@ Classifier: Programming Language :: Python :: 3.10
17
18
  Classifier: Programming Language :: Python :: 3.11
18
19
  Classifier: Programming Language :: Python :: 3.12
19
20
  Classifier: Programming Language :: Python :: 3.13
21
+ Classifier: Programming Language :: Python :: 3.14
20
22
  Provides-Extra: compute-node
21
23
  Requires-Dist: appdirs (>=1.4.3)
22
24
  Requires-Dist: click (>=8.0.0)
@@ -248,13 +248,15 @@ def push_application(
248
248
  f'(str, int, float, bool, list, dict, null) are used. Original error: {e}'
249
249
  ) from e
250
250
 
251
- app_data = config.get('app_data') or config.get('assets')
252
- if app_data:
253
- if config.get('app_data') and config.get('assets'):
254
- raise BioLibError(
255
- 'In .biolib/config.yml you cannot specify both "app_data" and "assets" fields. Please use only one.'
256
- )
251
+ if 'assets' in config and 'app_data' not in config:
252
+ config['app_data'] = config.pop('assets')
253
+ elif 'assets' in config and 'app_data' in config:
254
+ raise BioLibError(
255
+ 'In .biolib/config.yml you cannot specify both "app_data" and "assets" fields. Please use only one.'
256
+ )
257
257
 
258
+ app_data = config.get('app_data')
259
+ if app_data:
258
260
  field_name = 'app_data' if 'app_data' in config else 'assets'
259
261
  if not isinstance(app_data, str):
260
262
  raise BioLibError(
@@ -0,0 +1,43 @@
1
+ import time
2
+ import uuid
3
+ from fnmatch import fnmatch
4
+
5
+ from biolib.biolib_binary_format.utils import LazyLoadedFile
6
+ from biolib.typing_utils import Callable, List, Union, cast
7
+
8
+ PathFilter = Union[str, Callable[[str], bool]]
9
+
10
+
11
+ def filter_lazy_loaded_files(files: List[LazyLoadedFile], path_filter: PathFilter) -> List[LazyLoadedFile]:
12
+ if not (isinstance(path_filter, str) or callable(path_filter)):
13
+ raise Exception('Expected path_filter to be a string or a function')
14
+
15
+ if callable(path_filter):
16
+ return list(filter(lambda x: path_filter(x.path), files)) # type: ignore
17
+
18
+ glob_filter = cast(str, path_filter)
19
+
20
+ # since all file paths start with /, make sure filter does too
21
+ if not glob_filter.startswith('/'):
22
+ glob_filter = '/' + glob_filter
23
+
24
+ def _filter_function(file: LazyLoadedFile) -> bool:
25
+ return fnmatch(file.path, glob_filter)
26
+
27
+ return list(filter(_filter_function, files))
28
+
29
+
30
+ def open_browser_window_from_notebook(url_to_open: str) -> None:
31
+ try:
32
+ from IPython.display import ( # type:ignore # pylint: disable=import-error, import-outside-toplevel
33
+ Javascript,
34
+ display,
35
+ update_display,
36
+ )
37
+ except ImportError as error:
38
+ raise Exception('Unexpected environment. This function can only be called from a notebook.') from error
39
+
40
+ display_id = str(uuid.uuid4())
41
+ display(Javascript(f'window.open("{url_to_open}");'), display_id=display_id)
42
+ time.sleep(1)
43
+ update_display(Javascript(''), display_id=display_id)
@@ -1,5 +1,6 @@
1
1
  import time
2
2
  from collections import OrderedDict
3
+ from pathlib import Path
3
4
 
4
5
  from biolib import api
5
6
  from biolib._internal.types.experiment import DeprecatedExperimentDict, ExperimentDict
@@ -8,6 +9,7 @@ from biolib._internal.utils import open_browser_window_from_notebook
8
9
  from biolib.biolib_api_client import BiolibApiClient
9
10
  from biolib.biolib_errors import BioLibError
10
11
  from biolib.jobs.job import Job
12
+ from biolib.jobs.job_result import PathFilter
11
13
  from biolib.jobs.types import JobsPaginatedResponse
12
14
  from biolib.tables import BioLibTable
13
15
  from biolib.typing_utils import Dict, List, Optional, Union
@@ -223,6 +225,80 @@ class Experiment:
223
225
  """
224
226
  return self.get_jobs(status=status)
225
227
 
228
+ def save_completed_results(
229
+ self,
230
+ output_dir: Optional[str] = None,
231
+ path_filter: Optional[PathFilter] = None,
232
+ skip_file_if_exists: bool = False,
233
+ overwrite: bool = False,
234
+ ) -> None:
235
+ r"""Save all completed results in this experiment to local folders.
236
+
237
+ Creates a folder structure with the experiment name as the root directory,
238
+ containing a subfolder for each completed result. Only results with
239
+ 'completed' status will be saved.
240
+
241
+ Args:
242
+ output_dir (str, optional): Base directory where the experiment folder
243
+ will be created. If None, uses the current working directory.
244
+ path_filter (PathFilter, optional): Filter to select which files in the results to save.
245
+ Can be a glob pattern string or a callable function.
246
+ skip_file_if_exists (bool, optional): Whether to skip files that already exist
247
+ locally instead of raising an error. Defaults to False.
248
+ overwrite (bool, optional): Whether to overwrite existing files.
249
+ Defaults to False.
250
+
251
+ Example::
252
+
253
+ >>> # Save all completed results to current directory
254
+ >>> experiment.save_completed_results()
255
+ >>> # This creates: ./experiment_name/result_1/, ./experiment_name/result_2/, etc.
256
+
257
+ >>> # Save to specific directory
258
+ >>> experiment.save_completed_results(output_dir="/path/to/save")
259
+ >>> # This creates: /path/to/save/experiment_name/result_1/, etc.
260
+ """
261
+ base_dir = Path(output_dir) if output_dir else Path.cwd()
262
+
263
+ if base_dir == Path('/'):
264
+ raise BioLibError("Cannot save experiment results to root directory '/'")
265
+
266
+ experiment_folder = base_dir / self.name
267
+ experiment_folder.mkdir(parents=True, exist_ok=True)
268
+
269
+ completed_results: List[Job] = []
270
+ failed_results = False
271
+ print('Getting experiment status...')
272
+ for result in self.get_results():
273
+ if result.get_status() == 'completed':
274
+ completed_results.append(result)
275
+ elif result.get_status() != 'in_progress':
276
+ failed_results = True
277
+
278
+ if failed_results:
279
+ print(
280
+ 'WARNING: Found failed or cancelled results in the experiment. '
281
+ 'Please verify you have all your results, and consider removing the failed ones.'
282
+ )
283
+ if not completed_results:
284
+ print(f"No completed results found in experiment '{self.name}'")
285
+ return
286
+
287
+ print(f"Saving {len(completed_results)} completed results from experiment '{self.name}' to {experiment_folder}")
288
+
289
+ for result in completed_results:
290
+ result_name = result.get_name()
291
+ result_folder = experiment_folder / result_name
292
+
293
+ result_folder.mkdir(parents=True, exist_ok=True)
294
+
295
+ result.save_files(
296
+ output_dir=str(result_folder),
297
+ path_filter=path_filter,
298
+ skip_file_if_exists=skip_file_if_exists,
299
+ overwrite=overwrite,
300
+ )
301
+
226
302
  def rename(self, destination: str) -> None:
227
303
  api.client.patch(f'/resources/{self.uuid}/', data={'uri': destination})
228
304
  self._refetch()
@@ -10,7 +10,7 @@ import biolib.api.client
10
10
  from biolib import utils
11
11
  from biolib._internal.http_client import HttpClient
12
12
  from biolib._internal.tree_utils import build_tree_from_files, build_tree_str
13
- from biolib._internal.utils import open_browser_window_from_notebook
13
+ from biolib._internal.utils import PathFilter, filter_lazy_loaded_files, open_browser_window_from_notebook
14
14
  from biolib.api.client import ApiClient
15
15
  from biolib.biolib_api_client import BiolibApiClient, CreatedJobDict
16
16
  from biolib.biolib_api_client.biolib_app_api import BiolibAppApi
@@ -18,11 +18,12 @@ from biolib.biolib_api_client.biolib_job_api import BiolibJobApi
18
18
  from biolib.biolib_binary_format import LazyLoadedFile, ModuleInput, ModuleInputDict, ModuleOutputV2
19
19
  from biolib.biolib_binary_format.remote_endpoints import RemoteJobStorageEndpoint
20
20
  from biolib.biolib_binary_format.stdout_and_stderr import StdoutAndStderr
21
+ from biolib.biolib_binary_format.utils import InMemoryIndexableBuffer
21
22
  from biolib.biolib_errors import BioLibError, CloudJobFinishedError
22
23
  from biolib.biolib_logging import logger, logger_no_user_data
23
24
  from biolib.compute_node.job_worker.job_storage import JobStorage
24
25
  from biolib.compute_node.utils import SystemExceptionCodeMap, SystemExceptionCodes
25
- from biolib.jobs.job_result import JobResult, PathFilter
26
+ from biolib.jobs.job_result import JobResult
26
27
  from biolib.jobs.types import CloudJobDict, CloudJobStartedDict, JobDict
27
28
  from biolib.tables import BioLibTable
28
29
  from biolib.typing_utils import Dict, List, Optional, Union, cast
@@ -187,6 +188,45 @@ class Result:
187
188
  """
188
189
  return self.result.list_output_files(path_filter=path_filter)
189
190
 
191
+ def list_input_files(
192
+ self,
193
+ path_filter: Optional[PathFilter] = None,
194
+ ) -> List[LazyLoadedFile]:
195
+ """List input files from the result.
196
+
197
+ Args:
198
+ path_filter (PathFilter, optional): Filter to apply to the input files.
199
+ Can be a string glob pattern or a callable that takes a path string and returns a boolean.
200
+
201
+ Returns:
202
+ List[LazyLoadedFile]: List of input files.
203
+
204
+ Example::
205
+ >>> result = biolib.get_result("result_id")
206
+ >>> input_files = result.list_input_files()
207
+ >>> # Filter files with a glob pattern
208
+ >>> input_files = result.list_input_files("*.txt")
209
+ """
210
+ presigned_download_url = BiolibJobApi.get_job_storage_download_url(
211
+ job_uuid=self.id,
212
+ job_auth_token=self._auth_token,
213
+ storage_type='input',
214
+ )
215
+ response = HttpClient.request(url=presigned_download_url)
216
+ module_input_serialized: bytes = response.content
217
+ module_input = ModuleInput(module_input_serialized).deserialize()
218
+
219
+ files = []
220
+ for path, data in module_input['files'].items():
221
+ buffer = InMemoryIndexableBuffer(data)
222
+ lazy_file = LazyLoadedFile(path=path, buffer=buffer, start=0, length=len(data))
223
+ files.append(lazy_file)
224
+
225
+ if not path_filter:
226
+ return files
227
+
228
+ return filter_lazy_loaded_files(files, path_filter)
229
+
190
230
  def get_output_file(self, filename: str) -> LazyLoadedFile:
191
231
  return self.result.get_output_file(filename=filename)
192
232
 
@@ -256,7 +296,7 @@ class Result:
256
296
  self,
257
297
  output_dir: str,
258
298
  path_filter: Optional[PathFilter] = None,
259
- skip_file_if_exists: Optional[bool] = None,
299
+ skip_file_if_exists: bool = False,
260
300
  overwrite: bool = False,
261
301
  ) -> None:
262
302
  self.result.save_files(
@@ -1,16 +1,14 @@
1
1
  import time
2
- from fnmatch import fnmatch
3
2
  from pathlib import Path
4
3
 
4
+ from biolib._internal.utils import PathFilter, filter_lazy_loaded_files
5
5
  from biolib.biolib_binary_format import ModuleOutputV2
6
6
  from biolib.biolib_binary_format.remote_endpoints import RemoteJobStorageEndpoint
7
7
  from biolib.biolib_binary_format.remote_stream_seeker import StreamSeeker
8
8
  from biolib.biolib_binary_format.utils import LazyLoadedFile, RemoteIndexableBuffer
9
9
  from biolib.biolib_errors import BioLibError
10
10
  from biolib.biolib_logging import logger
11
- from biolib.typing_utils import Callable, List, Optional, Union, cast
12
-
13
- PathFilter = Union[str, Callable[[str], bool]]
11
+ from biolib.typing_utils import List, Optional
14
12
 
15
13
 
16
14
  class JobResult:
@@ -38,12 +36,12 @@ class JobResult:
38
36
  self,
39
37
  output_dir: str,
40
38
  path_filter: Optional[PathFilter] = None,
41
- skip_file_if_exists: Optional[bool] = None,
39
+ skip_file_if_exists: bool = False,
42
40
  overwrite: bool = False,
43
41
  ) -> None:
44
42
  module_output = self._get_module_output()
45
43
  output_files = module_output.get_files()
46
- filtered_output_files = self._get_filtered_files(output_files, path_filter) if path_filter else output_files
44
+ filtered_output_files = filter_lazy_loaded_files(output_files, path_filter) if path_filter else output_files
47
45
 
48
46
  if len(filtered_output_files) == 0:
49
47
  logger.debug('No output files to save')
@@ -100,7 +98,7 @@ class JobResult:
100
98
 
101
99
  def get_output_file(self, filename) -> LazyLoadedFile:
102
100
  files = self._get_module_output().get_files()
103
- filtered_files = self._get_filtered_files(files, path_filter=filename)
101
+ filtered_files = filter_lazy_loaded_files(files, path_filter=filename)
104
102
  if not filtered_files:
105
103
  raise BioLibError(f'File {filename} not found in results.')
106
104
 
@@ -114,26 +112,7 @@ class JobResult:
114
112
  if not path_filter:
115
113
  return files
116
114
 
117
- return self._get_filtered_files(files, path_filter)
118
-
119
- @staticmethod
120
- def _get_filtered_files(files: List[LazyLoadedFile], path_filter: PathFilter) -> List[LazyLoadedFile]:
121
- if not (isinstance(path_filter, str) or callable(path_filter)):
122
- raise Exception('Expected path_filter to be a string or a function')
123
-
124
- if callable(path_filter):
125
- return list(filter(lambda x: path_filter(x.path), files)) # type: ignore
126
-
127
- glob_filter = cast(str, path_filter)
128
-
129
- # since all file paths start with /, make sure filter does too
130
- if not glob_filter.startswith('/'):
131
- glob_filter = '/' + glob_filter
132
-
133
- def _filter_function(file: LazyLoadedFile) -> bool:
134
- return fnmatch(file.path, glob_filter)
135
-
136
- return list(filter(_filter_function, files))
115
+ return filter_lazy_loaded_files(files, path_filter)
137
116
 
138
117
  def _get_module_output(self) -> ModuleOutputV2:
139
118
  if self._module_output is None:
@@ -1,6 +1,6 @@
1
1
  [tool.poetry]
2
2
  name = "pybiolib"
3
- version = "1.2.1187"
3
+ version = "1.2.1232"
4
4
  description = "BioLib Python Client"
5
5
  readme = "PYPI_README.md"
6
6
  license = "MIT"
@@ -1,18 +0,0 @@
1
- import time
2
- import uuid
3
-
4
-
5
- def open_browser_window_from_notebook(url_to_open: str) -> None:
6
- try:
7
- from IPython.display import ( # type:ignore # pylint: disable=import-error, import-outside-toplevel
8
- Javascript,
9
- display,
10
- update_display,
11
- )
12
- except ImportError as error:
13
- raise Exception('Unexpected environment. This function can only be called from a notebook.') from error
14
-
15
- display_id = str(uuid.uuid4())
16
- display(Javascript(f'window.open("{url_to_open}");'), display_id=display_id)
17
- time.sleep(1)
18
- update_display(Javascript(''), display_id=display_id)
File without changes
File without changes
File without changes