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.
- README.md +2 -2
- RELEASE_NOTES.md +120 -0
- zenml/VERSION +1 -1
- zenml/__init__.py +0 -4
- zenml/actions/pipeline_run/pipeline_run_action.py +19 -17
- zenml/analytics/enums.py +4 -6
- zenml/cli/__init__.py +28 -76
- zenml/cli/base.py +2 -2
- zenml/cli/pipeline.py +54 -61
- zenml/cli/stack.py +6 -8
- zenml/cli/web_login.py +8 -0
- zenml/client.py +232 -103
- zenml/config/build_configuration.py +43 -17
- zenml/config/compiler.py +14 -22
- zenml/config/docker_settings.py +80 -57
- zenml/config/pipeline_run_configuration.py +3 -0
- zenml/config/server_config.py +3 -0
- zenml/config/source.py +60 -1
- zenml/constants.py +11 -2
- zenml/entrypoints/base_entrypoint_configuration.py +53 -8
- zenml/enums.py +4 -1
- zenml/environment.py +25 -9
- zenml/image_builders/base_image_builder.py +1 -1
- zenml/image_builders/build_context.py +25 -72
- zenml/integrations/aws/orchestrators/sagemaker_orchestrator.py +13 -4
- zenml/integrations/azure/__init__.py +4 -0
- zenml/integrations/azure/flavors/__init__.py +11 -0
- zenml/integrations/azure/flavors/azureml_orchestrator_flavor.py +263 -0
- zenml/{_hub → integrations/azure/orchestrators}/__init__.py +7 -2
- zenml/integrations/azure/orchestrators/azureml_orchestrator.py +544 -0
- zenml/integrations/azure/orchestrators/azureml_orchestrator_entrypoint_config.py +86 -0
- zenml/integrations/azure/step_operators/azureml_step_operator.py +3 -0
- zenml/integrations/databricks/flavors/databricks_orchestrator_flavor.py +20 -2
- zenml/integrations/databricks/orchestrators/databricks_orchestrator.py +19 -13
- zenml/integrations/gcp/orchestrators/vertex_orchestrator.py +7 -2
- zenml/integrations/gcp/service_connectors/gcp_service_connector.py +123 -6
- zenml/integrations/kaniko/image_builders/kaniko_image_builder.py +1 -1
- zenml/integrations/mlflow/__init__.py +1 -1
- zenml/integrations/mlflow/experiment_trackers/mlflow_experiment_tracker.py +3 -1
- zenml/integrations/mlflow/flavors/mlflow_experiment_tracker_flavor.py +3 -0
- zenml/logger.py +13 -0
- zenml/models/__init__.py +26 -22
- zenml/models/v2/base/filter.py +32 -0
- zenml/models/v2/core/pipeline.py +73 -89
- zenml/models/v2/core/pipeline_build.py +15 -11
- zenml/models/v2/core/pipeline_deployment.py +72 -24
- zenml/models/v2/core/pipeline_run.py +65 -1
- zenml/models/v2/core/run_template.py +393 -0
- zenml/models/v2/core/server_settings.py +12 -0
- zenml/models/v2/core/user.py +0 -21
- zenml/models/v2/misc/server_models.py +7 -1
- zenml/models/v2/misc/stack_deployment.py +5 -0
- zenml/models/v2/misc/user_auth.py +0 -7
- zenml/new/pipelines/build_utils.py +220 -89
- zenml/new/pipelines/code_archive.py +157 -0
- zenml/new/pipelines/pipeline.py +46 -78
- zenml/new/pipelines/run_utils.py +79 -1
- zenml/post_execution/pipeline.py +1 -4
- zenml/service_connectors/service_connector_utils.py +18 -2
- zenml/stack_deployments/aws_stack_deployment.py +32 -8
- zenml/stack_deployments/azure_stack_deployment.py +122 -10
- zenml/stack_deployments/gcp_stack_deployment.py +36 -7
- zenml/stack_deployments/stack_deployment.py +23 -7
- zenml/steps/base_step.py +3 -0
- zenml/steps/utils.py +0 -4
- zenml/utils/archivable.py +149 -0
- zenml/utils/code_utils.py +244 -0
- zenml/utils/notebook_utils.py +122 -0
- zenml/utils/package_utils.py +39 -0
- zenml/utils/pipeline_docker_image_builder.py +3 -96
- zenml/utils/source_utils.py +109 -1
- zenml/zen_server/dashboard/assets/{404-B_YdvmwS.js → 404-CRAA_Lew.js} +1 -1
- zenml/zen_server/dashboard/assets/@radix-BXWm7HOa.js +85 -0
- zenml/zen_server/dashboard/assets/{@react-router-CO-OsFwI.js → @react-router-l3lMcXA2.js} +1 -1
- zenml/zen_server/dashboard/assets/{@reactflow-l_1hUr1S.js → @reactflow-CeVxyqYT.js} +2 -2
- zenml/zen_server/dashboard/assets/{@tanstack-DYiOyJUL.js → @tanstack-FmcYZMuX.js} +4 -4
- zenml/zen_server/dashboard/assets/AlertDialogDropdownItem-ErO9aOgK.js +1 -0
- zenml/zen_server/dashboard/assets/{AwarenessChannel-CFg5iX4Z.js → AwarenessChannel-CLXo5rKM.js} +1 -1
- zenml/zen_server/dashboard/assets/{CodeSnippet-Dvkx_82E.js → CodeSnippet-D0VLxT2A.js} +2 -2
- zenml/zen_server/dashboard/assets/CollapsibleCard-BaUPiVg0.js +1 -0
- zenml/zen_server/dashboard/assets/{Commands-DoN1xrEq.js → Commands-JrcZK-3j.js} +1 -1
- zenml/zen_server/dashboard/assets/CopyButton-Dbo52T1K.js +2 -0
- zenml/zen_server/dashboard/assets/{CsvVizualization-Ck-nZ43m.js → CsvVizualization-D3kAypDj.js} +3 -3
- zenml/zen_server/dashboard/assets/DisplayDate-DizbSeT-.js +1 -0
- zenml/zen_server/dashboard/assets/EditSecretDialog-Bd7mFLS4.js +1 -0
- zenml/zen_server/dashboard/assets/{EmptyState-BMLnFVlB.js → EmptyState-BHblM39I.js} +1 -1
- zenml/zen_server/dashboard/assets/{Error-kLtljEOM.js → Error-C6LeJSER.js} +1 -1
- zenml/zen_server/dashboard/assets/{ExecutionStatus-DguLLgTK.js → ExecutionStatus-jH4OrWBq.js} +1 -1
- zenml/zen_server/dashboard/assets/{Helpbox-BXUMP21n.js → Helpbox-aAB2XP-z.js} +1 -1
- zenml/zen_server/dashboard/assets/{Infobox-DSt0O-dm.js → Infobox-BQ0aty32.js} +1 -1
- zenml/zen_server/dashboard/assets/{InlineAvatar-xsrsIGE-.js → InlineAvatar-DpTLgM3Q.js} +1 -1
- zenml/zen_server/dashboard/assets/Lock-CNyJvf2r.js +1 -0
- zenml/zen_server/dashboard/assets/{MarkdownVisualization-xp3hhULl.js → MarkdownVisualization-Bajxn0HY.js} +1 -1
- zenml/zen_server/dashboard/assets/NumberBox-BmKE0qnO.js +1 -0
- zenml/zen_server/dashboard/assets/{PasswordChecker-DUveqlva.js → PasswordChecker-yGGoJSB-.js} +1 -1
- zenml/zen_server/dashboard/assets/ProviderRadio-BBqkIuTd.js +1 -0
- zenml/zen_server/dashboard/assets/RadioItem-xLhXoiFV.js +1 -0
- zenml/zen_server/dashboard/assets/SearchField-C9R0mdaX.js +1 -0
- zenml/zen_server/dashboard/assets/{SetPassword-BXGTWiwj.js → SetPassword-52sNxNiO.js} +1 -1
- zenml/zen_server/dashboard/assets/{SuccessStep-DZC60t0x.js → SuccessStep-DlkItqYG.js} +1 -1
- zenml/zen_server/dashboard/assets/Tick-uxv80Q6a.js +1 -0
- zenml/zen_server/dashboard/assets/{UpdatePasswordSchemas-DGvwFWO1.js → UpdatePasswordSchemas-oN4G3sKz.js} +1 -1
- zenml/zen_server/dashboard/assets/{aws-BgKTfTfx.js → aws-0_3UsPif.js} +1 -1
- zenml/zen_server/dashboard/assets/{check-circle-i56092KI.js → check-circle-1_I207rW.js} +1 -1
- zenml/zen_server/dashboard/assets/chevron-down-BpaF8JqM.js +1 -0
- zenml/zen_server/dashboard/assets/{chevron-right-double-CZBOf6JM.js → chevron-right-double-Dk8e2L99.js} +1 -1
- zenml/zen_server/dashboard/assets/{cloud-only-C_yFCAkP.js → cloud-only-BkUuI0lZ.js} +1 -1
- zenml/zen_server/dashboard/assets/components-Br2ezRib.js +1 -0
- zenml/zen_server/dashboard/assets/{copy-BXNk6BjL.js → copy-f3XGPPxt.js} +1 -1
- zenml/zen_server/dashboard/assets/{database-1xWSgZfO.js → database-cXYNX9tt.js} +1 -1
- zenml/zen_server/dashboard/assets/{docker-CQMVm_4d.js → docker-8uj__HHK.js} +1 -1
- zenml/zen_server/dashboard/assets/dots-horizontal-sKQlWEni.js +1 -0
- zenml/zen_server/dashboard/assets/edit-C0MVvPD2.js +1 -0
- zenml/zen_server/dashboard/assets/{file-text-CqD_iu6l.js → file-text-B9JibxTs.js} +1 -1
- zenml/zen_server/dashboard/assets/{help-bu_DgLKI.js → help-FuHlZwn0.js} +1 -1
- zenml/zen_server/dashboard/assets/{index-rK_Wuy2W.js → index-Bd1xgUQG.js} +1 -1
- zenml/zen_server/dashboard/assets/index-DaGknux4.css +1 -0
- zenml/zen_server/dashboard/assets/{index-BczVOqUf.js → index-DhIZtpxB.js} +5 -5
- zenml/zen_server/dashboard/assets/index.esm-DT4uyn2i.js +1 -0
- zenml/zen_server/dashboard/assets/layout-D6oiSbfd.js +1 -0
- zenml/zen_server/dashboard/assets/{login-mutation-CrHrndTI.js → login-mutation-13A_JSVA.js} +1 -1
- zenml/zen_server/dashboard/assets/{logs-D8k8BVFf.js → logs-CgeE2vZP.js} +1 -1
- zenml/zen_server/dashboard/assets/{not-found-DYa4pC-C.js → not-found-B0Mmb90p.js} +1 -1
- zenml/zen_server/dashboard/assets/package-DdkziX79.js +1 -0
- zenml/zen_server/dashboard/assets/page-7-v2OBm-.js +1 -0
- zenml/zen_server/dashboard/assets/{page-MFQyIJd3.js → page-B3ozwdD1.js} +1 -1
- zenml/zen_server/dashboard/assets/{page-BkuQDIf-.js → page-BGwA9B1M.js} +1 -1
- zenml/zen_server/dashboard/assets/{page-1iL8aMqs.js → page-BkjAUyTA.js} +1 -1
- zenml/zen_server/dashboard/assets/page-BnacgBiy.js +1 -0
- zenml/zen_server/dashboard/assets/page-BxF_KMQ3.js +2 -0
- zenml/zen_server/dashboard/assets/page-C4POHC0K.js +1 -0
- zenml/zen_server/dashboard/assets/page-C9kudd44.js +9 -0
- zenml/zen_server/dashboard/assets/page-CA1j3GpJ.js +1 -0
- zenml/zen_server/dashboard/assets/page-CCY6yfmu.js +1 -0
- zenml/zen_server/dashboard/assets/page-CgTe7Bme.js +1 -0
- zenml/zen_server/dashboard/assets/{page-8a4UMKXZ.js → page-Cgn-6v2Y.js} +1 -1
- zenml/zen_server/dashboard/assets/page-CxQmQqDw.js +1 -0
- zenml/zen_server/dashboard/assets/page-D2Goey3H.js +1 -0
- zenml/zen_server/dashboard/assets/page-DLpOnf7u.js +1 -0
- zenml/zen_server/dashboard/assets/{page-BhgCDInH.js → page-DSTQnBk-.js} +1 -1
- zenml/zen_server/dashboard/assets/{page-1h_sD1jz.js → page-DTysUGOy.js} +1 -1
- zenml/zen_server/dashboard/assets/{page-2grKx_MY.js → page-D_EXUFJb.js} +1 -1
- zenml/zen_server/dashboard/assets/page-Db15QzsM.js +1 -0
- zenml/zen_server/dashboard/assets/{page-BDns21Iz.js → page-DugsjcQ_.js} +1 -1
- zenml/zen_server/dashboard/assets/{page-C6-UGEbH.js → page-OFKSPyN7.js} +1 -1
- zenml/zen_server/dashboard/assets/{page-BkeAAYwp.js → page-RnG-qhv9.js} +1 -1
- zenml/zen_server/dashboard/assets/{page-CCNRIt_f.js → page-T2BtjwPl.js} +1 -1
- zenml/zen_server/dashboard/assets/page-TXe1Eo3Z.js +1 -0
- zenml/zen_server/dashboard/assets/{page-BnaevhnB.js → page-YiF_fNbe.js} +1 -1
- zenml/zen_server/dashboard/assets/{page-uA5prJGY.js → page-hQaiQXfg.js} +1 -1
- zenml/zen_server/dashboard/assets/persist-3-5nOJ6m.js +1 -0
- zenml/zen_server/dashboard/assets/{play-circle-CNtZKDnW.js → play-circle-XSkLR12B.js} +1 -1
- zenml/zen_server/dashboard/assets/plus-FB9-lEq_.js +1 -0
- zenml/zen_server/dashboard/assets/refresh-COb6KYDi.js +1 -0
- zenml/zen_server/dashboard/assets/sharedSchema-BoYx_B_L.js +14 -0
- zenml/zen_server/dashboard/assets/{stack-detail-query-Cficsl6d.js → stack-detail-query-B-US_-wa.js} +1 -1
- zenml/zen_server/dashboard/assets/{terminal-By9cErXc.js → terminal-grtjrIEJ.js} +1 -1
- zenml/zen_server/dashboard/assets/trash-Cd5CSFqA.js +1 -0
- zenml/zen_server/dashboard/assets/{update-server-settings-mutation-7d8xi1tS.js → update-server-settings-mutation-B8GB_ubU.js} +1 -1
- zenml/zen_server/dashboard/assets/{url-D7mAQGUM.js → url-hcMJkz8p.js} +1 -1
- zenml/zen_server/dashboard/assets/{zod-BhoGpZ63.js → zod-CnykDKJj.js} +1 -1
- zenml/zen_server/dashboard/index.html +7 -7
- zenml/zen_server/dashboard_legacy/asset-manifest.json +4 -4
- zenml/zen_server/dashboard_legacy/index.html +1 -1
- zenml/zen_server/dashboard_legacy/{precache-manifest.12246c7548e71e2c4438e496360de80c.js → precache-manifest.9c473c96a43298343a7ce1256183123b.js} +4 -4
- zenml/zen_server/dashboard_legacy/service-worker.js +1 -1
- zenml/zen_server/dashboard_legacy/static/js/{main.3b27024b.chunk.js → main.463c90b9.chunk.js} +2 -2
- zenml/zen_server/dashboard_legacy/static/js/{main.3b27024b.chunk.js.map → main.463c90b9.chunk.js.map} +1 -1
- zenml/zen_server/deploy/helm/Chart.yaml +1 -1
- zenml/zen_server/deploy/helm/README.md +2 -2
- zenml/zen_server/rbac/models.py +1 -0
- zenml/zen_server/rbac/utils.py +4 -0
- zenml/zen_server/routers/pipeline_builds_endpoints.py +2 -66
- zenml/zen_server/routers/pipeline_deployments_endpoints.py +2 -53
- zenml/zen_server/routers/pipelines_endpoints.py +1 -74
- zenml/zen_server/routers/run_templates_endpoints.py +212 -0
- zenml/zen_server/routers/stack_deployment_endpoints.py +6 -0
- zenml/zen_server/routers/users_endpoints.py +0 -7
- zenml/zen_server/routers/workspaces_endpoints.py +79 -0
- zenml/zen_server/{pipeline_deployment → template_execution}/runner_entrypoint_configuration.py +1 -8
- zenml/zen_server/{pipeline_deployment → template_execution}/utils.py +214 -92
- zenml/zen_server/utils.py +77 -2
- zenml/zen_server/zen_server_api.py +54 -2
- zenml/zen_stores/base_zen_store.py +7 -1
- zenml/zen_stores/migrations/versions/0.63.0_release.py +23 -0
- zenml/zen_stores/migrations/versions/0.64.0_release.py +23 -0
- zenml/zen_stores/migrations/versions/026d4577b6a0_add_code_path.py +39 -0
- zenml/zen_stores/migrations/versions/3dcc5d20e82f_add_last_user_activity.py +51 -0
- zenml/zen_stores/migrations/versions/7d1919bb1ef0_add_run_templates.py +100 -0
- zenml/zen_stores/migrations/versions/909550c7c4da_remove_user_hub_token.py +36 -0
- zenml/zen_stores/migrations/versions/b59aa68fdb1f_simplify_pipelines.py +139 -0
- zenml/zen_stores/rest_zen_store.py +112 -39
- zenml/zen_stores/schemas/__init__.py +2 -0
- zenml/zen_stores/schemas/pipeline_build_schemas.py +3 -3
- zenml/zen_stores/schemas/pipeline_deployment_schemas.py +32 -2
- zenml/zen_stores/schemas/pipeline_run_schemas.py +29 -3
- zenml/zen_stores/schemas/pipeline_schemas.py +29 -30
- zenml/zen_stores/schemas/run_template_schemas.py +264 -0
- zenml/zen_stores/schemas/server_settings_schemas.py +2 -0
- zenml/zen_stores/schemas/step_run_schemas.py +11 -4
- zenml/zen_stores/schemas/user_schemas.py +0 -2
- zenml/zen_stores/sql_zen_store.py +389 -151
- zenml/zen_stores/template_utils.py +261 -0
- zenml/zen_stores/zen_store_interface.py +93 -20
- {zenml_nightly-0.62.0.dev20240729.dist-info → zenml_nightly-0.64.0.dev20240809.dist-info}/METADATA +3 -3
- {zenml_nightly-0.62.0.dev20240729.dist-info → zenml_nightly-0.64.0.dev20240809.dist-info}/RECORD +211 -184
- zenml/_hub/client.py +0 -289
- zenml/_hub/constants.py +0 -21
- zenml/_hub/utils.py +0 -79
- zenml/cli/hub.py +0 -1116
- zenml/models/v2/core/pipeline_namespace.py +0 -113
- zenml/models/v2/misc/hub_plugin_models.py +0 -79
- zenml/new/pipelines/deserialization_utils.py +0 -292
- zenml/zen_server/dashboard/assets/@radix-CFOkMR_E.js +0 -85
- zenml/zen_server/dashboard/assets/CollapsibleCard-opiuBHHc.js +0 -1
- zenml/zen_server/dashboard/assets/CopyButton-Cr7xYEPb.js +0 -2
- zenml/zen_server/dashboard/assets/DisplayDate-DYgIjlDF.js +0 -1
- zenml/zen_server/dashboard/assets/Pagination-C6X-mifw.js +0 -1
- zenml/zen_server/dashboard/assets/index-EpMIKgrI.css +0 -1
- zenml/zen_server/dashboard/assets/index.esm-Corw4lXQ.js +0 -1
- zenml/zen_server/dashboard/assets/package-B3fWP-Dh.js +0 -1
- zenml/zen_server/dashboard/assets/page-5NCOHOsy.js +0 -1
- zenml/zen_server/dashboard/assets/page-B6h3iaHJ.js +0 -1
- zenml/zen_server/dashboard/assets/page-Bi-wtWiO.js +0 -5
- zenml/zen_server/dashboard/assets/page-Bq0YxkLV.js +0 -1
- zenml/zen_server/dashboard/assets/page-Bs2F4eoD.js +0 -2
- zenml/zen_server/dashboard/assets/page-CHNxpz3n.js +0 -1
- zenml/zen_server/dashboard/assets/page-DgorQFqi.js +0 -1
- zenml/zen_server/dashboard/assets/page-K8ebxVIs.js +0 -1
- zenml/zen_server/dashboard/assets/page-TgCF0P_U.js +0 -1
- zenml/zen_server/dashboard/assets/page-ZnCEe-eK.js +0 -9
- zenml/zen_server/dashboard/assets/persist-D7HJNBWx.js +0 -1
- zenml/zen_server/dashboard/assets/plus-C8WOyCzt.js +0 -1
- zenml/zen_server/dashboard/assets/secrets-video-OBJ6irhH.svg +0 -21
- zenml/zen_server/dashboard/assets/stacks-video-7gfxpAq4.svg +0 -21
- /zenml/zen_server/{pipeline_deployment → template_execution}/__init__.py +0 -0
- /zenml/zen_server/{pipeline_deployment → template_execution}/workload_manager_interface.py +0 -0
- {zenml_nightly-0.62.0.dev20240729.dist-info → zenml_nightly-0.64.0.dev20240809.dist-info}/LICENSE +0 -0
- {zenml_nightly-0.62.0.dev20240729.dist-info → zenml_nightly-0.64.0.dev20240809.dist-info}/WHEEL +0 -0
- {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
|
+
)
|
zenml/utils/package_utils.py
CHANGED
@@ -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
|
-
|
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,
|