zenml-nightly 0.58.2.dev20240622__py3-none-any.whl → 0.58.2.dev20240626__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 (54) hide show
  1. zenml/VERSION +1 -1
  2. zenml/actions/base_action.py +177 -174
  3. zenml/actions/pipeline_run/pipeline_run_action.py +28 -23
  4. zenml/artifact_stores/base_artifact_store.py +7 -1
  5. zenml/artifacts/utils.py +13 -10
  6. zenml/cli/service_connectors.py +1 -0
  7. zenml/client.py +234 -58
  8. zenml/config/compiler.py +10 -9
  9. zenml/config/docker_settings.py +25 -9
  10. zenml/constants.py +1 -1
  11. zenml/event_hub/base_event_hub.py +5 -5
  12. zenml/event_hub/event_hub.py +15 -6
  13. zenml/event_sources/base_event.py +0 -11
  14. zenml/event_sources/base_event_source.py +7 -0
  15. zenml/event_sources/webhooks/base_webhook_event_source.py +1 -4
  16. zenml/exceptions.py +4 -0
  17. zenml/hooks/hook_validators.py +2 -3
  18. zenml/integrations/bitbucket/plugins/event_sources/bitbucket_webhook_event_source.py +3 -3
  19. zenml/integrations/mlflow/__init__.py +1 -1
  20. zenml/integrations/s3/artifact_stores/s3_artifact_store.py +76 -3
  21. zenml/logging/step_logging.py +54 -51
  22. zenml/models/__init__.py +17 -0
  23. zenml/models/v2/core/action.py +276 -0
  24. zenml/models/v2/core/trigger.py +182 -141
  25. zenml/new/pipelines/pipeline.py +13 -3
  26. zenml/new/pipelines/pipeline_decorator.py +1 -2
  27. zenml/new/pipelines/run_utils.py +1 -12
  28. zenml/new/steps/step_decorator.py +2 -3
  29. zenml/pipelines/base_pipeline.py +0 -2
  30. zenml/pipelines/pipeline_decorator.py +1 -2
  31. zenml/stack/stack_component.py +4 -0
  32. zenml/steps/base_step.py +1 -2
  33. zenml/steps/step_decorator.py +1 -2
  34. zenml/types.py +10 -1
  35. zenml/utils/pipeline_docker_image_builder.py +20 -5
  36. zenml/zen_server/rbac/models.py +1 -0
  37. zenml/zen_server/rbac/utils.py +22 -1
  38. zenml/zen_server/routers/actions_endpoints.py +324 -0
  39. zenml/zen_server/routers/triggers_endpoints.py +30 -158
  40. zenml/zen_server/zen_server_api.py +2 -0
  41. zenml/zen_stores/migrations/versions/25155145c545_separate_actions_and_triggers.py +228 -0
  42. zenml/zen_stores/rest_zen_store.py +103 -4
  43. zenml/zen_stores/schemas/__init__.py +2 -0
  44. zenml/zen_stores/schemas/action_schemas.py +192 -0
  45. zenml/zen_stores/schemas/trigger_schemas.py +43 -50
  46. zenml/zen_stores/schemas/user_schemas.py +10 -2
  47. zenml/zen_stores/schemas/workspace_schemas.py +5 -0
  48. zenml/zen_stores/sql_zen_store.py +240 -30
  49. zenml/zen_stores/zen_store_interface.py +85 -0
  50. {zenml_nightly-0.58.2.dev20240622.dist-info → zenml_nightly-0.58.2.dev20240626.dist-info}/METADATA +2 -2
  51. {zenml_nightly-0.58.2.dev20240622.dist-info → zenml_nightly-0.58.2.dev20240626.dist-info}/RECORD +54 -50
  52. {zenml_nightly-0.58.2.dev20240622.dist-info → zenml_nightly-0.58.2.dev20240626.dist-info}/LICENSE +0 -0
  53. {zenml_nightly-0.58.2.dev20240622.dist-info → zenml_nightly-0.58.2.dev20240626.dist-info}/WHEEL +0 -0
  54. {zenml_nightly-0.58.2.dev20240622.dist-info → zenml_nightly-0.58.2.dev20240626.dist-info}/entry_points.txt +0 -0
zenml/steps/base_step.py CHANGED
@@ -18,7 +18,6 @@ import hashlib
18
18
  import inspect
19
19
  from abc import abstractmethod
20
20
  from collections import defaultdict
21
- from types import FunctionType
22
21
  from typing import (
23
22
  TYPE_CHECKING,
24
23
  Any,
@@ -76,10 +75,10 @@ if TYPE_CHECKING:
76
75
  )
77
76
  from zenml.model.lazy_load import ModelVersionDataLazyLoader
78
77
  from zenml.model.model import Model
78
+ from zenml.types import HookSpecification
79
79
 
80
80
  ParametersOrDict = Union["BaseParameters", Dict[str, Any]]
81
81
  MaterializerClassOrSource = Union[str, Source, Type["BaseMaterializer"]]
82
- HookSpecification = Union[str, Source, FunctionType]
83
82
  OutputMaterializersSpecification = Union[
84
83
  "MaterializerClassOrSource",
85
84
  Sequence["MaterializerClassOrSource"],
@@ -13,7 +13,6 @@
13
13
  # permissions and limitations under the License.
14
14
  """Step decorator function."""
15
15
 
16
- from types import FunctionType
17
16
  from typing import (
18
17
  TYPE_CHECKING,
19
18
  Any,
@@ -37,9 +36,9 @@ if TYPE_CHECKING:
37
36
  from zenml.config.source import Source
38
37
  from zenml.materializers.base_materializer import BaseMaterializer
39
38
  from zenml.model.model import Model
39
+ from zenml.types import HookSpecification
40
40
 
41
41
  MaterializerClassOrSource = Union[str, "Source", Type["BaseMaterializer"]]
42
- HookSpecification = Union[str, "Source", FunctionType]
43
42
  OutputMaterializersSpecification = Union[
44
43
  "MaterializerClassOrSource",
45
44
  Sequence["MaterializerClassOrSource"],
zenml/types.py CHANGED
@@ -11,7 +11,16 @@
11
11
  # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
12
12
  # or implied. See the License for the specific language governing
13
13
  # permissions and limitations under the License.
14
- """Custom types that are used to indicate how to handle data."""
14
+ """Custom ZenML types."""
15
+
16
+ from typing import TYPE_CHECKING, Callable, Union
17
+
18
+ if TYPE_CHECKING:
19
+ from types import FunctionType
20
+
21
+ from zenml.config.source import Source
22
+
23
+ HookSpecification = Union[str, Source, FunctionType, Callable[..., None]]
15
24
 
16
25
 
17
26
  class HTMLString(str):
@@ -32,6 +32,7 @@ from typing import (
32
32
  import zenml
33
33
  from zenml.config import DockerSettings
34
34
  from zenml.config.docker_settings import (
35
+ DockerBuildConfig,
35
36
  PythonEnvironmentExportMethod,
36
37
  PythonPackageInstaller,
37
38
  )
@@ -212,8 +213,13 @@ class PipelineDockerImageBuilder:
212
213
  # used directly, so we tag it with the requested target name.
213
214
  user_image_name = target_image_name
214
215
 
216
+ build_config = (
217
+ docker_settings.parent_image_build_config
218
+ or DockerBuildConfig()
219
+ )
215
220
  build_context = build_context_class(
216
- root=docker_settings.build_context_root
221
+ root=docker_settings.build_context_root,
222
+ dockerignore_file=build_config.dockerignore,
217
223
  )
218
224
  build_context.add_file(
219
225
  source=docker_settings.dockerfile, destination="Dockerfile"
@@ -222,7 +228,8 @@ class PipelineDockerImageBuilder:
222
228
  image_name_or_digest = image_builder.build(
223
229
  image_name=user_image_name,
224
230
  build_context=build_context,
225
- docker_build_options=docker_settings.build_options,
231
+ docker_build_options=build_config.build_options
232
+ or docker_settings.build_options,
226
233
  container_registry=container_registry if push else None,
227
234
  )
228
235
 
@@ -247,13 +254,18 @@ class PipelineDockerImageBuilder:
247
254
 
248
255
  if requires_zenml_build:
249
256
  logger.info("Building Docker image `%s`.", target_image_name)
257
+ build_config = docker_settings.build_config or DockerBuildConfig()
258
+
250
259
  # Leave the build context empty if we don't want to include any files
251
260
  build_context_root = (
252
261
  source_utils.get_source_root() if include_files else None
253
262
  )
263
+ dockerignore = (
264
+ build_config.dockerignore or docker_settings.dockerignore
265
+ )
254
266
  build_context = build_context_class(
255
267
  root=build_context_root,
256
- dockerignore_file=docker_settings.dockerignore,
268
+ dockerignore_file=dockerignore,
257
269
  )
258
270
 
259
271
  requirements_files = self.gather_requirements_files(
@@ -304,8 +316,11 @@ class PipelineDockerImageBuilder:
304
316
  parent_image
305
317
  )
306
318
 
307
- build_options = {"pull": pull_parent_image, "rm": False}
308
-
319
+ build_options = {
320
+ "pull": pull_parent_image,
321
+ "rm": False,
322
+ **build_config.build_options,
323
+ }
309
324
  dockerfile = self._generate_zenml_pipeline_dockerfile(
310
325
  parent_image=parent_image,
311
326
  docker_settings=docker_settings,
@@ -46,6 +46,7 @@ class Action(StrEnum):
46
46
  class ResourceType(StrEnum):
47
47
  """Resource types of the server API."""
48
48
 
49
+ ACTION = "action"
49
50
  ARTIFACT = "artifact"
50
51
  ARTIFACT_VERSION = "artifact_version"
51
52
  CODE_REPOSITORY = "code_repository"
@@ -189,7 +189,12 @@ def get_permission_denied_model(model: AnyResponse) -> AnyResponse:
189
189
  The permission denied model.
190
190
  """
191
191
  return model.model_copy(
192
- update={"body": None, "metadata": None, "permission_denied": True}
192
+ update={
193
+ "body": None,
194
+ "metadata": None,
195
+ "resources": None,
196
+ "permission_denied": True,
197
+ }
193
198
  )
194
199
 
195
200
 
@@ -384,10 +389,12 @@ def get_resource_type_for_model(
384
389
  is not associated with any resource type.
385
390
  """
386
391
  from zenml.models import (
392
+ ActionResponse,
387
393
  ArtifactResponse,
388
394
  ArtifactVersionResponse,
389
395
  CodeRepositoryResponse,
390
396
  ComponentResponse,
397
+ EventSourceResponse,
391
398
  FlavorResponse,
392
399
  ModelResponse,
393
400
  ModelVersionResponse,
@@ -402,6 +409,8 @@ def get_resource_type_for_model(
402
409
  ServiceResponse,
403
410
  StackResponse,
404
411
  TagResponse,
412
+ TriggerExecutionResponse,
413
+ TriggerResponse,
405
414
  UserResponse,
406
415
  WorkspaceResponse,
407
416
  )
@@ -410,6 +419,8 @@ def get_resource_type_for_model(
410
419
  Any,
411
420
  ResourceType,
412
421
  ] = {
422
+ ActionResponse: ResourceType.ACTION,
423
+ EventSourceResponse: ResourceType.EVENT_SOURCE,
413
424
  FlavorResponse: ResourceType.FLAVOR,
414
425
  ServiceConnectorResponse: ResourceType.SERVICE_CONNECTOR,
415
426
  ComponentResponse: ResourceType.STACK_COMPONENT,
@@ -428,6 +439,8 @@ def get_resource_type_for_model(
428
439
  PipelineBuildResponse: ResourceType.PIPELINE_BUILD,
429
440
  PipelineRunResponse: ResourceType.PIPELINE_RUN,
430
441
  TagResponse: ResourceType.TAG,
442
+ TriggerResponse: ResourceType.TRIGGER,
443
+ TriggerExecutionResponse: ResourceType.TRIGGER_EXECUTION,
431
444
  ServiceAccountResponse: ResourceType.SERVICE_ACCOUNT,
432
445
  ServiceResponse: ResourceType.SERVICE,
433
446
  }
@@ -522,9 +535,11 @@ def get_schema_for_resource_type(
522
535
  The database schema.
523
536
  """
524
537
  from zenml.zen_stores.schemas import (
538
+ ActionSchema,
525
539
  ArtifactSchema,
526
540
  ArtifactVersionSchema,
527
541
  CodeRepositorySchema,
542
+ EventSourceSchema,
528
543
  FlavorSchema,
529
544
  ModelSchema,
530
545
  ModelVersionSchema,
@@ -539,6 +554,8 @@ def get_schema_for_resource_type(
539
554
  StackComponentSchema,
540
555
  StackSchema,
541
556
  TagSchema,
557
+ TriggerExecutionSchema,
558
+ TriggerSchema,
542
559
  UserSchema,
543
560
  WorkspaceSchema,
544
561
  )
@@ -564,6 +581,10 @@ def get_schema_for_resource_type(
564
581
  ResourceType.PIPELINE_BUILD: PipelineBuildSchema,
565
582
  ResourceType.RUN_METADATA: RunMetadataSchema,
566
583
  ResourceType.USER: UserSchema,
584
+ ResourceType.ACTION: ActionSchema,
585
+ ResourceType.EVENT_SOURCE: EventSourceSchema,
586
+ ResourceType.TRIGGER: TriggerSchema,
587
+ ResourceType.TRIGGER_EXECUTION: TriggerExecutionSchema,
567
588
  }
568
589
 
569
590
  return mapping[resource_type]
@@ -0,0 +1,324 @@
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
+ """Endpoint definitions for actions."""
15
+
16
+ from uuid import UUID
17
+
18
+ from fastapi import APIRouter, Depends, Security
19
+
20
+ from zenml import ActionRequest
21
+ from zenml.actions.base_action import BaseActionHandler
22
+ from zenml.constants import (
23
+ ACTIONS,
24
+ API,
25
+ VERSION_1,
26
+ )
27
+ from zenml.enums import PluginType
28
+ from zenml.models import (
29
+ ActionFilter,
30
+ ActionResponse,
31
+ ActionUpdate,
32
+ Page,
33
+ )
34
+ from zenml.zen_server.auth import AuthContext, authorize
35
+ from zenml.zen_server.exceptions import error_response
36
+ from zenml.zen_server.rbac.endpoint_utils import (
37
+ verify_permissions_and_create_entity,
38
+ verify_permissions_and_list_entities,
39
+ )
40
+ from zenml.zen_server.rbac.models import Action, ResourceType
41
+ from zenml.zen_server.rbac.utils import (
42
+ dehydrate_response_model,
43
+ verify_permission_for_model,
44
+ )
45
+ from zenml.zen_server.utils import (
46
+ handle_exceptions,
47
+ make_dependable,
48
+ plugin_flavor_registry,
49
+ zen_store,
50
+ )
51
+
52
+ router = APIRouter(
53
+ prefix=API + VERSION_1 + ACTIONS,
54
+ tags=["actions"],
55
+ responses={401: error_response, 403: error_response},
56
+ )
57
+
58
+
59
+ @router.get(
60
+ "",
61
+ response_model=Page[ActionResponse],
62
+ responses={401: error_response, 404: error_response, 422: error_response},
63
+ )
64
+ @handle_exceptions
65
+ def list_actions(
66
+ action_filter_model: ActionFilter = Depends(make_dependable(ActionFilter)),
67
+ hydrate: bool = False,
68
+ _: AuthContext = Security(authorize),
69
+ ) -> Page[ActionResponse]:
70
+ """List actions.
71
+
72
+ Args:
73
+ action_filter_model: Filter model used for pagination, sorting,
74
+ filtering.
75
+ hydrate: Flag deciding whether to hydrate the output model(s)
76
+ by including metadata fields in the response.
77
+
78
+ Returns:
79
+ Page of actions.
80
+ """
81
+
82
+ def list_actions_fn(
83
+ filter_model: ActionFilter,
84
+ ) -> Page[ActionResponse]:
85
+ """List actions through their associated plugins.
86
+
87
+ Args:
88
+ filter_model: Filter model used for pagination, sorting,
89
+ filtering.
90
+
91
+ Raises:
92
+ ValueError: If the action handler for flavor/type is not valid.
93
+
94
+ Returns:
95
+ All actions.
96
+ """
97
+ actions = zen_store().list_actions(
98
+ action_filter_model=filter_model, hydrate=hydrate
99
+ )
100
+
101
+ # Process the actions through their associated plugins
102
+ for idx, action in enumerate(actions.items):
103
+ action_handler = plugin_flavor_registry().get_plugin(
104
+ name=action.flavor,
105
+ _type=PluginType.ACTION,
106
+ subtype=action.plugin_subtype,
107
+ )
108
+
109
+ # Validate that the flavor and plugin_type correspond to an action
110
+ # handler implementation
111
+ if not isinstance(action_handler, BaseActionHandler):
112
+ raise ValueError(
113
+ f"Action handler plugin {action.plugin_subtype} "
114
+ f"for flavor {action.flavor} is not a valid action "
115
+ "handler plugin."
116
+ )
117
+
118
+ actions.items[idx] = action_handler.get_action(
119
+ action, hydrate=hydrate
120
+ )
121
+
122
+ return actions
123
+
124
+ return verify_permissions_and_list_entities(
125
+ filter_model=action_filter_model,
126
+ resource_type=ResourceType.ACTION,
127
+ list_method=list_actions_fn,
128
+ )
129
+
130
+
131
+ @router.get(
132
+ "/{action_id}",
133
+ response_model=ActionResponse,
134
+ responses={401: error_response, 404: error_response, 422: error_response},
135
+ )
136
+ @handle_exceptions
137
+ def get_action(
138
+ action_id: UUID,
139
+ hydrate: bool = True,
140
+ _: AuthContext = Security(authorize),
141
+ ) -> ActionResponse:
142
+ """Returns the requested action.
143
+
144
+ Args:
145
+ action_id: ID of the action.
146
+ hydrate: Flag deciding whether to hydrate the output model(s)
147
+ by including metadata fields in the response.
148
+
149
+ Raises:
150
+ ValueError: If the action handler for flavor/type is not valid.
151
+
152
+ Returns:
153
+ The requested action.
154
+ """
155
+ action = zen_store().get_action(action_id=action_id, hydrate=hydrate)
156
+
157
+ verify_permission_for_model(action, action=Action.READ)
158
+
159
+ action_handler = plugin_flavor_registry().get_plugin(
160
+ name=action.flavor,
161
+ _type=PluginType.ACTION,
162
+ subtype=action.plugin_subtype,
163
+ )
164
+
165
+ # Validate that the flavor and plugin_type correspond to an action
166
+ # handler implementation
167
+ if not isinstance(action_handler, BaseActionHandler):
168
+ raise ValueError(
169
+ f"Action handler plugin {action.plugin_subtype} "
170
+ f"for flavor {action.flavor} is not a valid action "
171
+ "handler plugin."
172
+ )
173
+
174
+ action = action_handler.get_action(action, hydrate=hydrate)
175
+
176
+ return dehydrate_response_model(action)
177
+
178
+
179
+ @router.post(
180
+ "",
181
+ response_model=ActionResponse,
182
+ responses={401: error_response, 409: error_response, 422: error_response},
183
+ )
184
+ @handle_exceptions
185
+ def create_action(
186
+ action: ActionRequest,
187
+ _: AuthContext = Security(authorize),
188
+ ) -> ActionResponse:
189
+ """Creates an action.
190
+
191
+ Args:
192
+ action: Action to create.
193
+
194
+ Raises:
195
+ ValueError: If the action handler for flavor/type is not valid.
196
+
197
+ Returns:
198
+ The created action.
199
+ """
200
+ service_account = zen_store().get_service_account(
201
+ service_account_name_or_id=action.service_account_id
202
+ )
203
+ verify_permission_for_model(service_account, action=Action.READ)
204
+
205
+ action_handler = plugin_flavor_registry().get_plugin(
206
+ name=action.flavor,
207
+ _type=PluginType.ACTION,
208
+ subtype=action.plugin_subtype,
209
+ )
210
+
211
+ # Validate that the flavor and plugin_type correspond to an action
212
+ # handler implementation
213
+ if not isinstance(action_handler, BaseActionHandler):
214
+ raise ValueError(
215
+ f"Action handler plugin {action.plugin_subtype} "
216
+ f"for flavor {action.flavor} is not a valid action "
217
+ "handler plugin."
218
+ )
219
+
220
+ return verify_permissions_and_create_entity(
221
+ request_model=action,
222
+ resource_type=ResourceType.ACTION,
223
+ create_method=action_handler.create_action,
224
+ )
225
+
226
+
227
+ @router.put(
228
+ "/{action_id}",
229
+ response_model=ActionResponse,
230
+ responses={401: error_response, 404: error_response, 422: error_response},
231
+ )
232
+ @handle_exceptions
233
+ def update_action(
234
+ action_id: UUID,
235
+ action_update: ActionUpdate,
236
+ _: AuthContext = Security(authorize),
237
+ ) -> ActionResponse:
238
+ """Update an action.
239
+
240
+ Args:
241
+ action_id: ID of the action to update.
242
+ action_update: The action update.
243
+
244
+ Raises:
245
+ ValueError: If the action handler for flavor/type is not valid.
246
+
247
+ Returns:
248
+ The updated action.
249
+ """
250
+ action = zen_store().get_action(action_id=action_id)
251
+
252
+ verify_permission_for_model(action, action=Action.UPDATE)
253
+
254
+ if action_update.service_account_id:
255
+ service_account = zen_store().get_service_account(
256
+ service_account_name_or_id=action_update.service_account_id
257
+ )
258
+ verify_permission_for_model(service_account, action=Action.READ)
259
+
260
+ action_handler = plugin_flavor_registry().get_plugin(
261
+ name=action.flavor,
262
+ _type=PluginType.ACTION,
263
+ subtype=action.plugin_subtype,
264
+ )
265
+
266
+ # Validate that the flavor and plugin_type correspond to an action
267
+ # handler implementation
268
+ if not isinstance(action_handler, BaseActionHandler):
269
+ raise ValueError(
270
+ f"Action handler plugin {action.plugin_subtype} "
271
+ f"for flavor {action.flavor} is not a valid action "
272
+ "handler plugin."
273
+ )
274
+
275
+ updated_action = action_handler.update_action(
276
+ action=action,
277
+ action_update=action_update,
278
+ )
279
+
280
+ return dehydrate_response_model(updated_action)
281
+
282
+
283
+ @router.delete(
284
+ "/{action_id}",
285
+ responses={401: error_response, 404: error_response, 422: error_response},
286
+ )
287
+ @handle_exceptions
288
+ def delete_action(
289
+ action_id: UUID,
290
+ force: bool = False,
291
+ _: AuthContext = Security(authorize),
292
+ ) -> None:
293
+ """Delete an action.
294
+
295
+ Args:
296
+ action_id: ID of the action.
297
+ force: Flag deciding whether to force delete the action.
298
+
299
+ Raises:
300
+ ValueError: If the action handler for flavor/type is not valid.
301
+ """
302
+ action = zen_store().get_action(action_id=action_id)
303
+
304
+ verify_permission_for_model(action, action=Action.DELETE)
305
+
306
+ action_handler = plugin_flavor_registry().get_plugin(
307
+ name=action.flavor,
308
+ _type=PluginType.ACTION,
309
+ subtype=action.plugin_subtype,
310
+ )
311
+
312
+ # Validate that the flavor and plugin_type correspond to an action
313
+ # handler implementation
314
+ if not isinstance(action_handler, BaseActionHandler):
315
+ raise ValueError(
316
+ f"Action handler plugin {action.plugin_subtype} "
317
+ f"for flavor {action.flavor} is not a valid action "
318
+ "handler plugin."
319
+ )
320
+
321
+ action_handler.delete_action(
322
+ action=action,
323
+ force=force,
324
+ )