zenml-nightly 0.62.0.dev20240729__py3-none-any.whl → 0.64.0.dev20240809__py3-none-any.whl

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 (240) hide show
  1. README.md +2 -2
  2. RELEASE_NOTES.md +120 -0
  3. zenml/VERSION +1 -1
  4. zenml/__init__.py +0 -4
  5. zenml/actions/pipeline_run/pipeline_run_action.py +19 -17
  6. zenml/analytics/enums.py +4 -6
  7. zenml/cli/__init__.py +28 -76
  8. zenml/cli/base.py +2 -2
  9. zenml/cli/pipeline.py +54 -61
  10. zenml/cli/stack.py +6 -8
  11. zenml/cli/web_login.py +8 -0
  12. zenml/client.py +232 -103
  13. zenml/config/build_configuration.py +43 -17
  14. zenml/config/compiler.py +14 -22
  15. zenml/config/docker_settings.py +80 -57
  16. zenml/config/pipeline_run_configuration.py +3 -0
  17. zenml/config/server_config.py +3 -0
  18. zenml/config/source.py +60 -1
  19. zenml/constants.py +11 -2
  20. zenml/entrypoints/base_entrypoint_configuration.py +53 -8
  21. zenml/enums.py +4 -1
  22. zenml/environment.py +25 -9
  23. zenml/image_builders/base_image_builder.py +1 -1
  24. zenml/image_builders/build_context.py +25 -72
  25. zenml/integrations/aws/orchestrators/sagemaker_orchestrator.py +13 -4
  26. zenml/integrations/azure/__init__.py +4 -0
  27. zenml/integrations/azure/flavors/__init__.py +11 -0
  28. zenml/integrations/azure/flavors/azureml_orchestrator_flavor.py +263 -0
  29. zenml/{_hub → integrations/azure/orchestrators}/__init__.py +7 -2
  30. zenml/integrations/azure/orchestrators/azureml_orchestrator.py +544 -0
  31. zenml/integrations/azure/orchestrators/azureml_orchestrator_entrypoint_config.py +86 -0
  32. zenml/integrations/azure/step_operators/azureml_step_operator.py +3 -0
  33. zenml/integrations/databricks/flavors/databricks_orchestrator_flavor.py +20 -2
  34. zenml/integrations/databricks/orchestrators/databricks_orchestrator.py +19 -13
  35. zenml/integrations/gcp/orchestrators/vertex_orchestrator.py +7 -2
  36. zenml/integrations/gcp/service_connectors/gcp_service_connector.py +123 -6
  37. zenml/integrations/kaniko/image_builders/kaniko_image_builder.py +1 -1
  38. zenml/integrations/mlflow/__init__.py +1 -1
  39. zenml/integrations/mlflow/experiment_trackers/mlflow_experiment_tracker.py +3 -1
  40. zenml/integrations/mlflow/flavors/mlflow_experiment_tracker_flavor.py +3 -0
  41. zenml/logger.py +13 -0
  42. zenml/models/__init__.py +26 -22
  43. zenml/models/v2/base/filter.py +32 -0
  44. zenml/models/v2/core/pipeline.py +73 -89
  45. zenml/models/v2/core/pipeline_build.py +15 -11
  46. zenml/models/v2/core/pipeline_deployment.py +72 -24
  47. zenml/models/v2/core/pipeline_run.py +65 -1
  48. zenml/models/v2/core/run_template.py +393 -0
  49. zenml/models/v2/core/server_settings.py +12 -0
  50. zenml/models/v2/core/user.py +0 -21
  51. zenml/models/v2/misc/server_models.py +7 -1
  52. zenml/models/v2/misc/stack_deployment.py +5 -0
  53. zenml/models/v2/misc/user_auth.py +0 -7
  54. zenml/new/pipelines/build_utils.py +220 -89
  55. zenml/new/pipelines/code_archive.py +157 -0
  56. zenml/new/pipelines/pipeline.py +46 -78
  57. zenml/new/pipelines/run_utils.py +79 -1
  58. zenml/post_execution/pipeline.py +1 -4
  59. zenml/service_connectors/service_connector_utils.py +18 -2
  60. zenml/stack_deployments/aws_stack_deployment.py +32 -8
  61. zenml/stack_deployments/azure_stack_deployment.py +122 -10
  62. zenml/stack_deployments/gcp_stack_deployment.py +36 -7
  63. zenml/stack_deployments/stack_deployment.py +23 -7
  64. zenml/steps/base_step.py +3 -0
  65. zenml/steps/utils.py +0 -4
  66. zenml/utils/archivable.py +149 -0
  67. zenml/utils/code_utils.py +244 -0
  68. zenml/utils/notebook_utils.py +122 -0
  69. zenml/utils/package_utils.py +39 -0
  70. zenml/utils/pipeline_docker_image_builder.py +3 -96
  71. zenml/utils/source_utils.py +109 -1
  72. zenml/zen_server/dashboard/assets/{404-B_YdvmwS.js → 404-CRAA_Lew.js} +1 -1
  73. zenml/zen_server/dashboard/assets/@radix-BXWm7HOa.js +85 -0
  74. zenml/zen_server/dashboard/assets/{@react-router-CO-OsFwI.js → @react-router-l3lMcXA2.js} +1 -1
  75. zenml/zen_server/dashboard/assets/{@reactflow-l_1hUr1S.js → @reactflow-CeVxyqYT.js} +2 -2
  76. zenml/zen_server/dashboard/assets/{@tanstack-DYiOyJUL.js → @tanstack-FmcYZMuX.js} +4 -4
  77. zenml/zen_server/dashboard/assets/AlertDialogDropdownItem-ErO9aOgK.js +1 -0
  78. zenml/zen_server/dashboard/assets/{AwarenessChannel-CFg5iX4Z.js → AwarenessChannel-CLXo5rKM.js} +1 -1
  79. zenml/zen_server/dashboard/assets/{CodeSnippet-Dvkx_82E.js → CodeSnippet-D0VLxT2A.js} +2 -2
  80. zenml/zen_server/dashboard/assets/CollapsibleCard-BaUPiVg0.js +1 -0
  81. zenml/zen_server/dashboard/assets/{Commands-DoN1xrEq.js → Commands-JrcZK-3j.js} +1 -1
  82. zenml/zen_server/dashboard/assets/CopyButton-Dbo52T1K.js +2 -0
  83. zenml/zen_server/dashboard/assets/{CsvVizualization-Ck-nZ43m.js → CsvVizualization-D3kAypDj.js} +3 -3
  84. zenml/zen_server/dashboard/assets/DisplayDate-DizbSeT-.js +1 -0
  85. zenml/zen_server/dashboard/assets/EditSecretDialog-Bd7mFLS4.js +1 -0
  86. zenml/zen_server/dashboard/assets/{EmptyState-BMLnFVlB.js → EmptyState-BHblM39I.js} +1 -1
  87. zenml/zen_server/dashboard/assets/{Error-kLtljEOM.js → Error-C6LeJSER.js} +1 -1
  88. zenml/zen_server/dashboard/assets/{ExecutionStatus-DguLLgTK.js → ExecutionStatus-jH4OrWBq.js} +1 -1
  89. zenml/zen_server/dashboard/assets/{Helpbox-BXUMP21n.js → Helpbox-aAB2XP-z.js} +1 -1
  90. zenml/zen_server/dashboard/assets/{Infobox-DSt0O-dm.js → Infobox-BQ0aty32.js} +1 -1
  91. zenml/zen_server/dashboard/assets/{InlineAvatar-xsrsIGE-.js → InlineAvatar-DpTLgM3Q.js} +1 -1
  92. zenml/zen_server/dashboard/assets/Lock-CNyJvf2r.js +1 -0
  93. zenml/zen_server/dashboard/assets/{MarkdownVisualization-xp3hhULl.js → MarkdownVisualization-Bajxn0HY.js} +1 -1
  94. zenml/zen_server/dashboard/assets/NumberBox-BmKE0qnO.js +1 -0
  95. zenml/zen_server/dashboard/assets/{PasswordChecker-DUveqlva.js → PasswordChecker-yGGoJSB-.js} +1 -1
  96. zenml/zen_server/dashboard/assets/ProviderRadio-BBqkIuTd.js +1 -0
  97. zenml/zen_server/dashboard/assets/RadioItem-xLhXoiFV.js +1 -0
  98. zenml/zen_server/dashboard/assets/SearchField-C9R0mdaX.js +1 -0
  99. zenml/zen_server/dashboard/assets/{SetPassword-BXGTWiwj.js → SetPassword-52sNxNiO.js} +1 -1
  100. zenml/zen_server/dashboard/assets/{SuccessStep-DZC60t0x.js → SuccessStep-DlkItqYG.js} +1 -1
  101. zenml/zen_server/dashboard/assets/Tick-uxv80Q6a.js +1 -0
  102. zenml/zen_server/dashboard/assets/{UpdatePasswordSchemas-DGvwFWO1.js → UpdatePasswordSchemas-oN4G3sKz.js} +1 -1
  103. zenml/zen_server/dashboard/assets/{aws-BgKTfTfx.js → aws-0_3UsPif.js} +1 -1
  104. zenml/zen_server/dashboard/assets/{check-circle-i56092KI.js → check-circle-1_I207rW.js} +1 -1
  105. zenml/zen_server/dashboard/assets/chevron-down-BpaF8JqM.js +1 -0
  106. zenml/zen_server/dashboard/assets/{chevron-right-double-CZBOf6JM.js → chevron-right-double-Dk8e2L99.js} +1 -1
  107. zenml/zen_server/dashboard/assets/{cloud-only-C_yFCAkP.js → cloud-only-BkUuI0lZ.js} +1 -1
  108. zenml/zen_server/dashboard/assets/components-Br2ezRib.js +1 -0
  109. zenml/zen_server/dashboard/assets/{copy-BXNk6BjL.js → copy-f3XGPPxt.js} +1 -1
  110. zenml/zen_server/dashboard/assets/{database-1xWSgZfO.js → database-cXYNX9tt.js} +1 -1
  111. zenml/zen_server/dashboard/assets/{docker-CQMVm_4d.js → docker-8uj__HHK.js} +1 -1
  112. zenml/zen_server/dashboard/assets/dots-horizontal-sKQlWEni.js +1 -0
  113. zenml/zen_server/dashboard/assets/edit-C0MVvPD2.js +1 -0
  114. zenml/zen_server/dashboard/assets/{file-text-CqD_iu6l.js → file-text-B9JibxTs.js} +1 -1
  115. zenml/zen_server/dashboard/assets/{help-bu_DgLKI.js → help-FuHlZwn0.js} +1 -1
  116. zenml/zen_server/dashboard/assets/{index-rK_Wuy2W.js → index-Bd1xgUQG.js} +1 -1
  117. zenml/zen_server/dashboard/assets/index-DaGknux4.css +1 -0
  118. zenml/zen_server/dashboard/assets/{index-BczVOqUf.js → index-DhIZtpxB.js} +5 -5
  119. zenml/zen_server/dashboard/assets/index.esm-DT4uyn2i.js +1 -0
  120. zenml/zen_server/dashboard/assets/layout-D6oiSbfd.js +1 -0
  121. zenml/zen_server/dashboard/assets/{login-mutation-CrHrndTI.js → login-mutation-13A_JSVA.js} +1 -1
  122. zenml/zen_server/dashboard/assets/{logs-D8k8BVFf.js → logs-CgeE2vZP.js} +1 -1
  123. zenml/zen_server/dashboard/assets/{not-found-DYa4pC-C.js → not-found-B0Mmb90p.js} +1 -1
  124. zenml/zen_server/dashboard/assets/package-DdkziX79.js +1 -0
  125. zenml/zen_server/dashboard/assets/page-7-v2OBm-.js +1 -0
  126. zenml/zen_server/dashboard/assets/{page-MFQyIJd3.js → page-B3ozwdD1.js} +1 -1
  127. zenml/zen_server/dashboard/assets/{page-BkuQDIf-.js → page-BGwA9B1M.js} +1 -1
  128. zenml/zen_server/dashboard/assets/{page-1iL8aMqs.js → page-BkjAUyTA.js} +1 -1
  129. zenml/zen_server/dashboard/assets/page-BnacgBiy.js +1 -0
  130. zenml/zen_server/dashboard/assets/page-BxF_KMQ3.js +2 -0
  131. zenml/zen_server/dashboard/assets/page-C4POHC0K.js +1 -0
  132. zenml/zen_server/dashboard/assets/page-C9kudd44.js +9 -0
  133. zenml/zen_server/dashboard/assets/page-CA1j3GpJ.js +1 -0
  134. zenml/zen_server/dashboard/assets/page-CCY6yfmu.js +1 -0
  135. zenml/zen_server/dashboard/assets/page-CgTe7Bme.js +1 -0
  136. zenml/zen_server/dashboard/assets/{page-8a4UMKXZ.js → page-Cgn-6v2Y.js} +1 -1
  137. zenml/zen_server/dashboard/assets/page-CxQmQqDw.js +1 -0
  138. zenml/zen_server/dashboard/assets/page-D2Goey3H.js +1 -0
  139. zenml/zen_server/dashboard/assets/page-DLpOnf7u.js +1 -0
  140. zenml/zen_server/dashboard/assets/{page-BhgCDInH.js → page-DSTQnBk-.js} +1 -1
  141. zenml/zen_server/dashboard/assets/{page-1h_sD1jz.js → page-DTysUGOy.js} +1 -1
  142. zenml/zen_server/dashboard/assets/{page-2grKx_MY.js → page-D_EXUFJb.js} +1 -1
  143. zenml/zen_server/dashboard/assets/page-Db15QzsM.js +1 -0
  144. zenml/zen_server/dashboard/assets/{page-BDns21Iz.js → page-DugsjcQ_.js} +1 -1
  145. zenml/zen_server/dashboard/assets/{page-C6-UGEbH.js → page-OFKSPyN7.js} +1 -1
  146. zenml/zen_server/dashboard/assets/{page-BkeAAYwp.js → page-RnG-qhv9.js} +1 -1
  147. zenml/zen_server/dashboard/assets/{page-CCNRIt_f.js → page-T2BtjwPl.js} +1 -1
  148. zenml/zen_server/dashboard/assets/page-TXe1Eo3Z.js +1 -0
  149. zenml/zen_server/dashboard/assets/{page-BnaevhnB.js → page-YiF_fNbe.js} +1 -1
  150. zenml/zen_server/dashboard/assets/{page-uA5prJGY.js → page-hQaiQXfg.js} +1 -1
  151. zenml/zen_server/dashboard/assets/persist-3-5nOJ6m.js +1 -0
  152. zenml/zen_server/dashboard/assets/{play-circle-CNtZKDnW.js → play-circle-XSkLR12B.js} +1 -1
  153. zenml/zen_server/dashboard/assets/plus-FB9-lEq_.js +1 -0
  154. zenml/zen_server/dashboard/assets/refresh-COb6KYDi.js +1 -0
  155. zenml/zen_server/dashboard/assets/sharedSchema-BoYx_B_L.js +14 -0
  156. zenml/zen_server/dashboard/assets/{stack-detail-query-Cficsl6d.js → stack-detail-query-B-US_-wa.js} +1 -1
  157. zenml/zen_server/dashboard/assets/{terminal-By9cErXc.js → terminal-grtjrIEJ.js} +1 -1
  158. zenml/zen_server/dashboard/assets/trash-Cd5CSFqA.js +1 -0
  159. zenml/zen_server/dashboard/assets/{update-server-settings-mutation-7d8xi1tS.js → update-server-settings-mutation-B8GB_ubU.js} +1 -1
  160. zenml/zen_server/dashboard/assets/{url-D7mAQGUM.js → url-hcMJkz8p.js} +1 -1
  161. zenml/zen_server/dashboard/assets/{zod-BhoGpZ63.js → zod-CnykDKJj.js} +1 -1
  162. zenml/zen_server/dashboard/index.html +7 -7
  163. zenml/zen_server/dashboard_legacy/asset-manifest.json +4 -4
  164. zenml/zen_server/dashboard_legacy/index.html +1 -1
  165. zenml/zen_server/dashboard_legacy/{precache-manifest.12246c7548e71e2c4438e496360de80c.js → precache-manifest.9c473c96a43298343a7ce1256183123b.js} +4 -4
  166. zenml/zen_server/dashboard_legacy/service-worker.js +1 -1
  167. zenml/zen_server/dashboard_legacy/static/js/{main.3b27024b.chunk.js → main.463c90b9.chunk.js} +2 -2
  168. zenml/zen_server/dashboard_legacy/static/js/{main.3b27024b.chunk.js.map → main.463c90b9.chunk.js.map} +1 -1
  169. zenml/zen_server/deploy/helm/Chart.yaml +1 -1
  170. zenml/zen_server/deploy/helm/README.md +2 -2
  171. zenml/zen_server/rbac/models.py +1 -0
  172. zenml/zen_server/rbac/utils.py +4 -0
  173. zenml/zen_server/routers/pipeline_builds_endpoints.py +2 -66
  174. zenml/zen_server/routers/pipeline_deployments_endpoints.py +2 -53
  175. zenml/zen_server/routers/pipelines_endpoints.py +1 -74
  176. zenml/zen_server/routers/run_templates_endpoints.py +212 -0
  177. zenml/zen_server/routers/stack_deployment_endpoints.py +6 -0
  178. zenml/zen_server/routers/users_endpoints.py +0 -7
  179. zenml/zen_server/routers/workspaces_endpoints.py +79 -0
  180. zenml/zen_server/{pipeline_deployment → template_execution}/runner_entrypoint_configuration.py +1 -8
  181. zenml/zen_server/{pipeline_deployment → template_execution}/utils.py +214 -92
  182. zenml/zen_server/utils.py +77 -2
  183. zenml/zen_server/zen_server_api.py +54 -2
  184. zenml/zen_stores/base_zen_store.py +7 -1
  185. zenml/zen_stores/migrations/versions/0.63.0_release.py +23 -0
  186. zenml/zen_stores/migrations/versions/0.64.0_release.py +23 -0
  187. zenml/zen_stores/migrations/versions/026d4577b6a0_add_code_path.py +39 -0
  188. zenml/zen_stores/migrations/versions/3dcc5d20e82f_add_last_user_activity.py +51 -0
  189. zenml/zen_stores/migrations/versions/7d1919bb1ef0_add_run_templates.py +100 -0
  190. zenml/zen_stores/migrations/versions/909550c7c4da_remove_user_hub_token.py +36 -0
  191. zenml/zen_stores/migrations/versions/b59aa68fdb1f_simplify_pipelines.py +139 -0
  192. zenml/zen_stores/rest_zen_store.py +112 -39
  193. zenml/zen_stores/schemas/__init__.py +2 -0
  194. zenml/zen_stores/schemas/pipeline_build_schemas.py +3 -3
  195. zenml/zen_stores/schemas/pipeline_deployment_schemas.py +32 -2
  196. zenml/zen_stores/schemas/pipeline_run_schemas.py +29 -3
  197. zenml/zen_stores/schemas/pipeline_schemas.py +29 -30
  198. zenml/zen_stores/schemas/run_template_schemas.py +264 -0
  199. zenml/zen_stores/schemas/server_settings_schemas.py +2 -0
  200. zenml/zen_stores/schemas/step_run_schemas.py +11 -4
  201. zenml/zen_stores/schemas/user_schemas.py +0 -2
  202. zenml/zen_stores/sql_zen_store.py +389 -151
  203. zenml/zen_stores/template_utils.py +261 -0
  204. zenml/zen_stores/zen_store_interface.py +93 -20
  205. {zenml_nightly-0.62.0.dev20240729.dist-info → zenml_nightly-0.64.0.dev20240809.dist-info}/METADATA +3 -3
  206. {zenml_nightly-0.62.0.dev20240729.dist-info → zenml_nightly-0.64.0.dev20240809.dist-info}/RECORD +211 -184
  207. zenml/_hub/client.py +0 -289
  208. zenml/_hub/constants.py +0 -21
  209. zenml/_hub/utils.py +0 -79
  210. zenml/cli/hub.py +0 -1116
  211. zenml/models/v2/core/pipeline_namespace.py +0 -113
  212. zenml/models/v2/misc/hub_plugin_models.py +0 -79
  213. zenml/new/pipelines/deserialization_utils.py +0 -292
  214. zenml/zen_server/dashboard/assets/@radix-CFOkMR_E.js +0 -85
  215. zenml/zen_server/dashboard/assets/CollapsibleCard-opiuBHHc.js +0 -1
  216. zenml/zen_server/dashboard/assets/CopyButton-Cr7xYEPb.js +0 -2
  217. zenml/zen_server/dashboard/assets/DisplayDate-DYgIjlDF.js +0 -1
  218. zenml/zen_server/dashboard/assets/Pagination-C6X-mifw.js +0 -1
  219. zenml/zen_server/dashboard/assets/index-EpMIKgrI.css +0 -1
  220. zenml/zen_server/dashboard/assets/index.esm-Corw4lXQ.js +0 -1
  221. zenml/zen_server/dashboard/assets/package-B3fWP-Dh.js +0 -1
  222. zenml/zen_server/dashboard/assets/page-5NCOHOsy.js +0 -1
  223. zenml/zen_server/dashboard/assets/page-B6h3iaHJ.js +0 -1
  224. zenml/zen_server/dashboard/assets/page-Bi-wtWiO.js +0 -5
  225. zenml/zen_server/dashboard/assets/page-Bq0YxkLV.js +0 -1
  226. zenml/zen_server/dashboard/assets/page-Bs2F4eoD.js +0 -2
  227. zenml/zen_server/dashboard/assets/page-CHNxpz3n.js +0 -1
  228. zenml/zen_server/dashboard/assets/page-DgorQFqi.js +0 -1
  229. zenml/zen_server/dashboard/assets/page-K8ebxVIs.js +0 -1
  230. zenml/zen_server/dashboard/assets/page-TgCF0P_U.js +0 -1
  231. zenml/zen_server/dashboard/assets/page-ZnCEe-eK.js +0 -9
  232. zenml/zen_server/dashboard/assets/persist-D7HJNBWx.js +0 -1
  233. zenml/zen_server/dashboard/assets/plus-C8WOyCzt.js +0 -1
  234. zenml/zen_server/dashboard/assets/secrets-video-OBJ6irhH.svg +0 -21
  235. zenml/zen_server/dashboard/assets/stacks-video-7gfxpAq4.svg +0 -21
  236. /zenml/zen_server/{pipeline_deployment → template_execution}/__init__.py +0 -0
  237. /zenml/zen_server/{pipeline_deployment → template_execution}/workload_manager_interface.py +0 -0
  238. {zenml_nightly-0.62.0.dev20240729.dist-info → zenml_nightly-0.64.0.dev20240809.dist-info}/LICENSE +0 -0
  239. {zenml_nightly-0.62.0.dev20240729.dist-info → zenml_nightly-0.64.0.dev20240809.dist-info}/WHEEL +0 -0
  240. {zenml_nightly-0.62.0.dev20240729.dist-info → zenml_nightly-0.64.0.dev20240809.dist-info}/entry_points.txt +0 -0
@@ -0,0 +1,244 @@
1
+ # Copyright (c) ZenML GmbH 2024. All Rights Reserved.
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at:
6
+ #
7
+ # https://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
12
+ # or implied. See the License for the specific language governing
13
+ # permissions and limitations under the License.
14
+ """Code utilities."""
15
+
16
+ import hashlib
17
+ import os
18
+ import shutil
19
+ import tempfile
20
+ from pathlib import Path
21
+ from typing import IO, TYPE_CHECKING, Dict, Optional
22
+
23
+ from zenml.client import Client
24
+ from zenml.io import fileio
25
+ from zenml.logger import get_logger
26
+ from zenml.utils import string_utils
27
+ from zenml.utils.archivable import Archivable
28
+
29
+ if TYPE_CHECKING:
30
+ from git.repo.base import Repo
31
+
32
+
33
+ logger = get_logger(__name__)
34
+
35
+
36
+ class CodeArchive(Archivable):
37
+ """Code archive class.
38
+
39
+ This class is used to archive user code before uploading it to the artifact
40
+ store. If the user code is stored in a Git repository, only files not
41
+ excluded by gitignores will be included in the archive.
42
+ """
43
+
44
+ def __init__(self, root: Optional[str] = None) -> None:
45
+ """Initialize the object.
46
+
47
+ Args:
48
+ root: Root directory of the archive.
49
+ """
50
+ super().__init__()
51
+ self._root = root
52
+
53
+ @property
54
+ def git_repo(self) -> Optional["Repo"]:
55
+ """Git repository active at the code archive root.
56
+
57
+ Returns:
58
+ The git repository if available.
59
+ """
60
+ try:
61
+ # These imports fail when git is not installed on the machine
62
+ from git.exc import InvalidGitRepositoryError
63
+ from git.repo.base import Repo
64
+ except ImportError:
65
+ return None
66
+
67
+ try:
68
+ git_repo = Repo(path=self._root, search_parent_directories=True)
69
+ except InvalidGitRepositoryError:
70
+ return None
71
+
72
+ return git_repo
73
+
74
+ def _get_all_files(self, archive_root: str) -> Dict[str, str]:
75
+ """Get all files inside the archive root.
76
+
77
+ Args:
78
+ archive_root: The root directory from which to get all files.
79
+
80
+ Returns:
81
+ All files inside the archive root.
82
+ """
83
+ all_files = {}
84
+ for root, _, files in os.walk(archive_root):
85
+ for file in files:
86
+ file_path = os.path.join(root, file)
87
+ path_in_archive = os.path.relpath(file_path, archive_root)
88
+ all_files[path_in_archive] = file_path
89
+
90
+ return all_files
91
+
92
+ def get_files(self) -> Dict[str, str]:
93
+ """Gets all regular files that should be included in the archive.
94
+
95
+ Raises:
96
+ RuntimeError: If the code archive would not include any files.
97
+
98
+ Returns:
99
+ A dict {path_in_archive: path_on_filesystem} for all regular files
100
+ in the archive.
101
+ """
102
+ if not self._root:
103
+ return {}
104
+
105
+ all_files = {}
106
+
107
+ if repo := self.git_repo:
108
+ try:
109
+ result = repo.git.ls_files(
110
+ "--cached",
111
+ "--others",
112
+ "--modified",
113
+ "--exclude-standard",
114
+ self._root,
115
+ )
116
+ except Exception as e:
117
+ logger.warning(
118
+ "Failed to get non-ignored files from git: %s", str(e)
119
+ )
120
+ all_files = self._get_all_files(archive_root=self._root)
121
+ else:
122
+ for file in result.split():
123
+ file_path = os.path.join(repo.working_dir, file)
124
+ path_in_archive = os.path.relpath(file_path, self._root)
125
+
126
+ if os.path.exists(file_path):
127
+ all_files[path_in_archive] = file_path
128
+ else:
129
+ all_files = self._get_all_files(archive_root=self._root)
130
+
131
+ if not all_files:
132
+ raise RuntimeError(
133
+ "The code archive to be uploaded does not contain any files. "
134
+ "This is probably because all files in your source root "
135
+ f"`{self._root}` are ignored by a .gitignore file."
136
+ )
137
+
138
+ # Explicitly remove .zen directories as we write an updated version
139
+ # to disk everytime ZenML is called. This updates the mtime of the
140
+ # file, which invalidates the code upload caching. The values in
141
+ # the .zen directory are not needed anyway as we set them as
142
+ # environment variables.
143
+ all_files = {
144
+ path_in_archive: file_path
145
+ for path_in_archive, file_path in sorted(all_files.items())
146
+ if ".zen" not in Path(path_in_archive).parts[:-1]
147
+ }
148
+
149
+ return all_files
150
+
151
+ def write_archive(
152
+ self, output_file: IO[bytes], use_gzip: bool = True
153
+ ) -> None:
154
+ """Writes an archive of the build context to the given file.
155
+
156
+ Args:
157
+ output_file: The file to write the archive to.
158
+ use_gzip: Whether to use `gzip` to compress the file.
159
+ """
160
+ super().write_archive(output_file=output_file, use_gzip=use_gzip)
161
+ archive_size = os.path.getsize(output_file.name)
162
+ if archive_size > 20 * 1024 * 1024:
163
+ logger.warning(
164
+ "Code archive size: `%s`. If you believe this is "
165
+ "unreasonably large, make sure to version your code in git and "
166
+ "ignore unnecessary files using a `.gitignore` file.",
167
+ string_utils.get_human_readable_filesize(archive_size),
168
+ )
169
+
170
+
171
+ def upload_code_if_necessary(code_archive: CodeArchive) -> str:
172
+ """Upload code to the artifact store if necessary.
173
+
174
+ This function computes a hash of the code to be uploaded, and if an archive
175
+ with the same hash already exists it will not re-upload but instead return
176
+ the path to the existing archive.
177
+
178
+ Args:
179
+ code_archive: The code archive to upload.
180
+
181
+ Returns:
182
+ The path where to archived code is uploaded.
183
+ """
184
+ artifact_store = Client().active_stack.artifact_store
185
+
186
+ with tempfile.NamedTemporaryFile(
187
+ mode="w+b", delete=False, suffix=".tar.gz"
188
+ ) as f:
189
+ code_archive.write_archive(f)
190
+
191
+ hash_ = hashlib.sha1() # nosec
192
+
193
+ while True:
194
+ data = f.read(64 * 1024)
195
+ if not data:
196
+ break
197
+ hash_.update(data)
198
+
199
+ filename = f"{hash_.hexdigest()}.tar.gz"
200
+ upload_dir = os.path.join(artifact_store.path, "code_uploads")
201
+ fileio.makedirs(upload_dir)
202
+ upload_path = os.path.join(upload_dir, filename)
203
+
204
+ if not fileio.exists(upload_path):
205
+ archive_size = string_utils.get_human_readable_filesize(
206
+ os.path.getsize(f.name)
207
+ )
208
+ logger.info(
209
+ "Uploading code to `%s` (Size: %s).", upload_path, archive_size
210
+ )
211
+ fileio.copy(f.name, upload_path)
212
+ logger.info("Code upload finished.")
213
+ else:
214
+ logger.info(
215
+ "Code already exists in artifact store, skipping upload."
216
+ )
217
+
218
+ if os.path.exists(f.name):
219
+ os.remove(f.name)
220
+
221
+ return upload_path
222
+
223
+
224
+ def download_and_extract_code(code_path: str, extract_dir: str) -> None:
225
+ """Download and extract code.
226
+
227
+ Args:
228
+ code_path: Path where the code is uploaded.
229
+ extract_dir: Directory where to code should be extracted to.
230
+
231
+ Raises:
232
+ RuntimeError: If the code is stored in an artifact store which is
233
+ not active.
234
+ """
235
+ artifact_store = Client().active_stack.artifact_store
236
+
237
+ if not code_path.startswith(artifact_store.path):
238
+ raise RuntimeError("Code stored in different artifact store.")
239
+
240
+ download_path = os.path.basename(code_path)
241
+ fileio.copy(code_path, download_path)
242
+
243
+ shutil.unpack_archive(filename=download_path, extract_dir=extract_dir)
244
+ os.remove(download_path)
@@ -0,0 +1,122 @@
1
+ # Copyright (c) ZenML GmbH 2024. All Rights Reserved.
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at:
6
+ #
7
+ # https://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
12
+ # or implied. See the License for the specific language governing
13
+ # permissions and limitations under the License.
14
+ """Notebook utilities."""
15
+
16
+ from typing import Any, Callable, Optional, TypeVar, Union
17
+
18
+ from zenml.environment import Environment
19
+ from zenml.logger import get_logger
20
+
21
+ ZENML_NOTEBOOK_CELL_CODE_ATTRIBUTE_NAME = "__zenml_notebook_cell_code__"
22
+
23
+ AnyObject = TypeVar("AnyObject", bound=Any)
24
+
25
+ logger = get_logger(__name__)
26
+
27
+
28
+ def is_defined_in_notebook_cell(obj: Any) -> bool:
29
+ """Check whether an object is defined in a notebook cell.
30
+
31
+ Args:
32
+ obj: The object to check.
33
+
34
+ Returns:
35
+ Whether the object is defined in a notebook cell.
36
+ """
37
+ if not Environment.in_notebook():
38
+ return False
39
+
40
+ module_name = getattr(obj, "__module__", None)
41
+ return module_name == "__main__"
42
+
43
+
44
+ def enable_notebook_code_extraction(
45
+ _obj: Optional["AnyObject"] = None,
46
+ ) -> Union["AnyObject", Callable[["AnyObject"], "AnyObject"]]:
47
+ """Decorator to enable code extraction from notebooks.
48
+
49
+ Args:
50
+ _obj: The class or function for which to enable code extraction.
51
+
52
+ Returns:
53
+ The decorated class or function.
54
+ """
55
+
56
+ def inner_decorator(obj: "AnyObject") -> "AnyObject":
57
+ try_to_save_notebook_cell_code(obj)
58
+ return obj
59
+
60
+ if _obj is None:
61
+ return inner_decorator
62
+ else:
63
+ return inner_decorator(_obj)
64
+
65
+
66
+ def get_active_notebook_cell_code() -> Optional[str]:
67
+ """Get the code of the currently active notebook cell.
68
+
69
+ Returns:
70
+ The code of the currently active notebook cell.
71
+ """
72
+ cell_code = None
73
+ try:
74
+ ipython = get_ipython() # type: ignore[name-defined]
75
+ cell_code = ipython.get_parent()["content"]["code"]
76
+ except (NameError, KeyError) as e:
77
+ logger.warning("Unable to extract cell code: %s.", str(e))
78
+
79
+ return cell_code
80
+
81
+
82
+ def try_to_save_notebook_cell_code(obj: Any) -> None:
83
+ """Try to save the notebook cell code for an object.
84
+
85
+ Args:
86
+ obj: The object for which to save the notebook cell code.
87
+ """
88
+ if is_defined_in_notebook_cell(obj):
89
+ if cell_code := get_active_notebook_cell_code():
90
+ setattr(
91
+ obj,
92
+ ZENML_NOTEBOOK_CELL_CODE_ATTRIBUTE_NAME,
93
+ cell_code,
94
+ )
95
+
96
+
97
+ def load_notebook_cell_code(obj: Any) -> Optional[str]:
98
+ """Load the notebook cell code for an object.
99
+
100
+ Args:
101
+ obj: The object for which to load the cell code.
102
+
103
+ Returns:
104
+ The notebook cell code if it was saved.
105
+ """
106
+ return getattr(obj, ZENML_NOTEBOOK_CELL_CODE_ATTRIBUTE_NAME, None)
107
+
108
+
109
+ def warn_about_notebook_cell_magic_commands(cell_code: str) -> None:
110
+ """Warn about magic commands in the cell code.
111
+
112
+ Args:
113
+ cell_code: The cell code.
114
+ """
115
+ if any(line.startswith(("%", "!")) for line in cell_code.splitlines()):
116
+ logger.warning(
117
+ "Some lines in your notebook cell start with a `!` or `%` "
118
+ "character. Running a ZenML step remotely from a notebook "
119
+ "only works if the cell only contains python code. If any "
120
+ "of these lines contain Jupyter notebook magic commands, "
121
+ "remove them and try again."
122
+ )
@@ -13,6 +13,8 @@
13
13
  # permissions and limitations under the License.
14
14
  """Utility functions for the package."""
15
15
 
16
+ from typing import List
17
+
16
18
  import requests
17
19
  from packaging import version
18
20
 
@@ -48,3 +50,40 @@ def is_latest_zenml_version() -> bool:
48
50
  return False
49
51
  else:
50
52
  return True
53
+
54
+
55
+ def clean_requirements(requirements: List[str]) -> List[str]:
56
+ """Clean requirements list from redundant requirements.
57
+
58
+ Args:
59
+ requirements: List of requirements.
60
+
61
+ Returns:
62
+ Cleaned list of requirements
63
+
64
+ Raises:
65
+ TypeError: If input is not a list
66
+ ValueError: If any element in the list is not a string
67
+ """
68
+ if not isinstance(requirements, list):
69
+ raise TypeError("Input must be a list")
70
+
71
+ if not all(isinstance(req, str) for req in requirements):
72
+ raise ValueError("All elements in the list must be strings")
73
+
74
+ cleaned = {}
75
+ for req in requirements:
76
+ package = (
77
+ req.split(">=")[0]
78
+ .split("==")[0]
79
+ .split("<")[0]
80
+ .split("~=")[0]
81
+ .split("^=")[0]
82
+ .split("[")[0]
83
+ .strip()
84
+ )
85
+ if package not in cleaned or any(
86
+ op in req for op in ["=", ">", "<", "~", "^"]
87
+ ):
88
+ cleaned[package] = req
89
+ return sorted(cleaned.values())
@@ -17,11 +17,9 @@ import itertools
17
17
  import os
18
18
  import subprocess
19
19
  import sys
20
- from collections import defaultdict
21
20
  from typing import (
22
21
  TYPE_CHECKING,
23
22
  Any,
24
- DefaultDict,
25
23
  Dict,
26
24
  List,
27
25
  Optional,
@@ -277,9 +275,7 @@ class PipelineDockerImageBuilder:
277
275
  requirements_files = self.gather_requirements_files(
278
276
  docker_settings=docker_settings,
279
277
  stack=stack,
280
- # Only pass code repo to include its dependencies if we actually
281
- # need to download code
282
- code_repository=code_repository if download_files else None,
278
+ code_repository=code_repository,
283
279
  )
284
280
 
285
281
  self._add_requirements_files(
@@ -444,8 +440,9 @@ class PipelineDockerImageBuilder:
444
440
  requirements files.
445
441
  The files will be in the following order:
446
442
  - Packages installed in the local Python environment
443
+ - Requirements defined by stack integrations
444
+ - Requirements defined by user integrations
447
445
  - User-defined requirements
448
- - Requirements defined by user-defined and/or stack integrations
449
446
  """
450
447
  requirements_files: List[Tuple[str, str, List[str]]] = []
451
448
 
@@ -481,43 +478,6 @@ class PipelineDockerImageBuilder:
481
478
  "- Including python packages from local environment"
482
479
  )
483
480
 
484
- # Generate requirements files for all ZenML Hub plugins
485
- if docker_settings.required_hub_plugins:
486
- (
487
- hub_internal_requirements,
488
- hub_pypi_requirements,
489
- ) = PipelineDockerImageBuilder._get_hub_requirements(
490
- docker_settings.required_hub_plugins
491
- )
492
-
493
- # Plugin packages themselves
494
- for i, (index, packages) in enumerate(
495
- hub_internal_requirements.items()
496
- ):
497
- file_name = f".zenml_hub_internal_requirements_{i}"
498
- file_lines = [f"-i {index}", *packages]
499
- file_contents = "\n".join(file_lines)
500
- requirements_files.append(
501
- (file_name, file_contents, ["--no-deps"])
502
- )
503
- if log:
504
- logger.info(
505
- "- Including internal hub packages from index `%s`: %s",
506
- index,
507
- ", ".join(f"`{r}`" for r in packages),
508
- )
509
-
510
- # PyPI requirements of plugin packages
511
- if hub_pypi_requirements:
512
- file_name = ".zenml_hub_pypi_requirements"
513
- file_contents = "\n".join(hub_pypi_requirements)
514
- requirements_files.append((file_name, file_contents, []))
515
- if log:
516
- logger.info(
517
- "- Including hub requirements from PyPI: %s",
518
- ", ".join(f"`{r}`" for r in hub_pypi_requirements),
519
- )
520
-
521
481
  if docker_settings.install_stack_requirements:
522
482
  stack_requirements = stack.requirements()
523
483
  if code_repository:
@@ -599,59 +559,6 @@ class PipelineDockerImageBuilder:
599
559
 
600
560
  return requirements_files
601
561
 
602
- @staticmethod
603
- def _get_hub_requirements(
604
- required_hub_plugins: List[str],
605
- ) -> Tuple[Dict[str, List[str]], List[str]]:
606
- """Get package requirements for ZenML Hub plugins.
607
-
608
- Args:
609
- required_hub_plugins: List of hub plugin names in the format
610
- `(<author_username>/)<plugin_name>(==<version>)`.
611
-
612
- Returns:
613
- - A dict of the hub plugin packages themselves (which need to be
614
- installed from a custom index, mapping index URLs to lists of
615
- package names.
616
- - A list of all unique dependencies of the required hub plugins
617
- (which can be installed from PyPI).
618
- """
619
- from zenml._hub.client import HubClient
620
- from zenml._hub.utils import parse_plugin_name, plugin_display_name
621
-
622
- client = HubClient()
623
-
624
- internal_requirements: DefaultDict[str, List[str]] = defaultdict(list)
625
- pypi_requirements: List[str] = []
626
-
627
- for plugin_str in required_hub_plugins:
628
- author, name, version = parse_plugin_name(
629
- plugin_str, version_separator="=="
630
- )
631
-
632
- plugin = client.get_plugin(
633
- name=name,
634
- version=version,
635
- author=author,
636
- )
637
-
638
- if plugin and plugin.index_url and plugin.package_name:
639
- internal_requirements[plugin.index_url].append(
640
- plugin.package_name
641
- )
642
- if plugin.requirements:
643
- pypi_requirements.extend(plugin.requirements)
644
- else:
645
- display_name = plugin_display_name(name, version, author)
646
- logger.warning(
647
- "Hub plugin `%s` does not exist or cannot be installed."
648
- "Skipping installation of this plugin.",
649
- display_name,
650
- )
651
-
652
- pypi_requirements = sorted(set(pypi_requirements))
653
- return dict(internal_requirements), pypi_requirements
654
-
655
562
  @staticmethod
656
563
  def _generate_zenml_pipeline_dockerfile(
657
564
  parent_image: str,