zenml-nightly 0.75.0.dev20250312__py3-none-any.whl → 0.75.0.dev20250313__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 (160) hide show
  1. zenml/VERSION +1 -1
  2. zenml/__init__.py +2 -0
  3. zenml/analytics/context.py +7 -0
  4. zenml/artifacts/utils.py +0 -2
  5. zenml/cli/login.py +6 -0
  6. zenml/cli/model.py +7 -15
  7. zenml/cli/secret.py +47 -44
  8. zenml/cli/service_connectors.py +0 -1
  9. zenml/cli/stack.py +0 -1
  10. zenml/cli/tag.py +3 -5
  11. zenml/cli/utils.py +25 -23
  12. zenml/cli/workspace.py +79 -5
  13. zenml/client.py +615 -348
  14. zenml/config/global_config.py +16 -3
  15. zenml/config/pipeline_configurations.py +3 -2
  16. zenml/config/pipeline_run_configuration.py +2 -1
  17. zenml/config/secret_reference_mixin.py +1 -1
  18. zenml/constants.py +1 -3
  19. zenml/enums.py +0 -7
  20. zenml/event_hub/event_hub.py +3 -1
  21. zenml/exceptions.py +0 -24
  22. zenml/integrations/aws/orchestrators/sagemaker_orchestrator.py +5 -3
  23. zenml/integrations/bitbucket/plugins/event_sources/bitbucket_webhook_event_source.py +1 -4
  24. zenml/integrations/github/plugins/event_sources/github_webhook_event_source.py +1 -4
  25. zenml/integrations/mlflow/steps/mlflow_registry.py +1 -1
  26. zenml/integrations/seldon/model_deployers/seldon_model_deployer.py +1 -1
  27. zenml/integrations/wandb/flavors/wandb_experiment_tracker_flavor.py +3 -3
  28. zenml/model/model.py +8 -8
  29. zenml/models/__init__.py +18 -1
  30. zenml/models/v2/base/base.py +0 -5
  31. zenml/models/v2/base/filter.py +1 -1
  32. zenml/models/v2/base/scoped.py +104 -121
  33. zenml/models/v2/core/api_key.py +1 -1
  34. zenml/models/v2/core/artifact.py +31 -18
  35. zenml/models/v2/core/artifact_version.py +42 -25
  36. zenml/models/v2/core/component.py +22 -33
  37. zenml/models/v2/core/device.py +3 -2
  38. zenml/models/v2/core/event_source.py +2 -2
  39. zenml/models/v2/core/flavor.py +19 -47
  40. zenml/models/v2/core/logs.py +1 -2
  41. zenml/models/v2/core/model.py +7 -4
  42. zenml/models/v2/core/model_version.py +36 -27
  43. zenml/models/v2/core/pipeline.py +1 -1
  44. zenml/models/v2/core/pipeline_run.py +5 -13
  45. zenml/models/v2/core/run_template.py +1 -2
  46. zenml/models/v2/core/schedule.py +0 -9
  47. zenml/models/v2/core/secret.py +93 -127
  48. zenml/models/v2/core/server_settings.py +2 -2
  49. zenml/models/v2/core/service.py +43 -12
  50. zenml/models/v2/core/service_connector.py +14 -16
  51. zenml/models/v2/core/stack.py +24 -26
  52. zenml/models/v2/core/step_run.py +3 -15
  53. zenml/models/v2/core/tag.py +41 -15
  54. zenml/models/v2/core/user.py +19 -2
  55. zenml/models/v2/misc/statistics.py +45 -0
  56. zenml/models/v2/misc/tag.py +27 -0
  57. zenml/orchestrators/cache_utils.py +1 -1
  58. zenml/orchestrators/input_utils.py +1 -0
  59. zenml/orchestrators/step_launcher.py +0 -1
  60. zenml/orchestrators/step_run_utils.py +0 -2
  61. zenml/orchestrators/step_runner.py +10 -1
  62. zenml/pipelines/build_utils.py +0 -2
  63. zenml/pipelines/pipeline_decorator.py +3 -2
  64. zenml/pipelines/pipeline_definition.py +4 -5
  65. zenml/pipelines/run_utils.py +3 -3
  66. zenml/service_connectors/service_connector.py +0 -7
  67. zenml/service_connectors/service_connector_utils.py +0 -1
  68. zenml/stack/authentication_mixin.py +1 -1
  69. zenml/stack/flavor.py +3 -14
  70. zenml/stack/stack_component.py +1 -5
  71. zenml/steps/step_context.py +19 -0
  72. zenml/utils/string_utils.py +1 -1
  73. zenml/utils/tag_utils.py +642 -0
  74. zenml/zen_server/cloud_utils.py +21 -0
  75. zenml/zen_server/exceptions.py +0 -6
  76. zenml/zen_server/rbac/endpoint_utils.py +134 -46
  77. zenml/zen_server/rbac/models.py +65 -3
  78. zenml/zen_server/rbac/rbac_interface.py +9 -0
  79. zenml/zen_server/rbac/rbac_sql_zen_store.py +15 -7
  80. zenml/zen_server/rbac/utils.py +156 -29
  81. zenml/zen_server/rbac/zenml_cloud_rbac.py +43 -11
  82. zenml/zen_server/routers/actions_endpoints.py +3 -5
  83. zenml/zen_server/routers/artifact_endpoint.py +0 -5
  84. zenml/zen_server/routers/artifact_version_endpoints.py +15 -9
  85. zenml/zen_server/routers/auth_endpoints.py +22 -7
  86. zenml/zen_server/routers/code_repositories_endpoints.py +56 -3
  87. zenml/zen_server/routers/devices_endpoints.py +0 -4
  88. zenml/zen_server/routers/event_source_endpoints.py +0 -5
  89. zenml/zen_server/routers/flavors_endpoints.py +0 -5
  90. zenml/zen_server/routers/logs_endpoints.py +0 -1
  91. zenml/zen_server/routers/model_versions_endpoints.py +102 -23
  92. zenml/zen_server/routers/models_endpoints.py +51 -68
  93. zenml/zen_server/routers/pipeline_builds_endpoints.py +58 -4
  94. zenml/zen_server/routers/pipeline_deployments_endpoints.py +58 -4
  95. zenml/zen_server/routers/pipelines_endpoints.py +73 -4
  96. zenml/zen_server/routers/plugin_endpoints.py +0 -1
  97. zenml/zen_server/routers/run_metadata_endpoints.py +99 -0
  98. zenml/zen_server/routers/run_templates_endpoints.py +66 -3
  99. zenml/zen_server/routers/runs_endpoints.py +60 -8
  100. zenml/zen_server/routers/schedule_endpoints.py +69 -6
  101. zenml/zen_server/routers/secrets_endpoints.py +40 -4
  102. zenml/zen_server/routers/server_endpoints.py +53 -1
  103. zenml/zen_server/routers/service_accounts_endpoints.py +14 -15
  104. zenml/zen_server/routers/service_connectors_endpoints.py +96 -14
  105. zenml/zen_server/routers/service_endpoints.py +20 -7
  106. zenml/zen_server/routers/stack_components_endpoints.py +68 -7
  107. zenml/zen_server/routers/stacks_endpoints.py +98 -7
  108. zenml/zen_server/routers/steps_endpoints.py +17 -11
  109. zenml/zen_server/routers/tag_resource_endpoints.py +115 -0
  110. zenml/zen_server/routers/tags_endpoints.py +6 -17
  111. zenml/zen_server/routers/triggers_endpoints.py +5 -8
  112. zenml/zen_server/routers/users_endpoints.py +47 -12
  113. zenml/zen_server/routers/workspaces_endpoints.py +56 -1285
  114. zenml/zen_server/template_execution/utils.py +5 -4
  115. zenml/zen_server/utils.py +21 -0
  116. zenml/zen_server/zen_server_api.py +4 -0
  117. zenml/zen_stores/base_zen_store.py +29 -44
  118. zenml/zen_stores/migrations/versions/1cb6477f72d6_move_artifact_save_type.py +20 -10
  119. zenml/zen_stores/migrations/versions/1f9d1cd00b90_add_unique_name_constraints.py +231 -0
  120. zenml/zen_stores/migrations/versions/288f4fb6e112_make_tags_user_scoped.py +74 -0
  121. zenml/zen_stores/migrations/versions/2e695a26fe7a_add_user_default_workspace.py +45 -0
  122. zenml/zen_stores/migrations/versions/3b1776345020_remove_workspace_from_globals.py +81 -0
  123. zenml/zen_stores/migrations/versions/41b28cae31ce_make_artifacts_workspace_scoped.py +136 -0
  124. zenml/zen_stores/migrations/versions/9e7bf0970266_adding_exclusive_attribute_to_tags.py +47 -0
  125. zenml/zen_stores/migrations/versions/b557b2871693_update_step_run_input_types.py +8 -4
  126. zenml/zen_stores/migrations/versions/cc269488e5a9_separate_run_metadata.py +12 -6
  127. zenml/zen_stores/migrations/versions/f1d723fd723b_add_secret_private_attr.py +61 -0
  128. zenml/zen_stores/migrations/versions/f76a368a25a5_add_stack_description.py +35 -0
  129. zenml/zen_stores/rest_zen_store.py +172 -171
  130. zenml/zen_stores/schemas/action_schemas.py +8 -1
  131. zenml/zen_stores/schemas/api_key_schemas.py +8 -1
  132. zenml/zen_stores/schemas/artifact_schemas.py +28 -1
  133. zenml/zen_stores/schemas/code_repository_schemas.py +8 -1
  134. zenml/zen_stores/schemas/component_schemas.py +9 -14
  135. zenml/zen_stores/schemas/event_source_schemas.py +8 -1
  136. zenml/zen_stores/schemas/flavor_schemas.py +14 -20
  137. zenml/zen_stores/schemas/model_schemas.py +3 -0
  138. zenml/zen_stores/schemas/pipeline_deployment_schemas.py +3 -1
  139. zenml/zen_stores/schemas/pipeline_run_schemas.py +0 -3
  140. zenml/zen_stores/schemas/run_template_schemas.py +8 -4
  141. zenml/zen_stores/schemas/schedule_schema.py +9 -14
  142. zenml/zen_stores/schemas/secret_schemas.py +15 -25
  143. zenml/zen_stores/schemas/service_connector_schemas.py +8 -17
  144. zenml/zen_stores/schemas/service_schemas.py +0 -1
  145. zenml/zen_stores/schemas/stack_schemas.py +12 -15
  146. zenml/zen_stores/schemas/step_run_schemas.py +7 -8
  147. zenml/zen_stores/schemas/tag_schemas.py +30 -2
  148. zenml/zen_stores/schemas/trigger_schemas.py +8 -1
  149. zenml/zen_stores/schemas/user_schemas.py +24 -2
  150. zenml/zen_stores/schemas/utils.py +16 -0
  151. zenml/zen_stores/schemas/workspace_schemas.py +7 -25
  152. zenml/zen_stores/secrets_stores/service_connector_secrets_store.py +0 -3
  153. zenml/zen_stores/sql_zen_store.py +2905 -2280
  154. zenml/zen_stores/template_utils.py +1 -1
  155. zenml/zen_stores/zen_store_interface.py +82 -58
  156. {zenml_nightly-0.75.0.dev20250312.dist-info → zenml_nightly-0.75.0.dev20250313.dist-info}/METADATA +1 -1
  157. {zenml_nightly-0.75.0.dev20250312.dist-info → zenml_nightly-0.75.0.dev20250313.dist-info}/RECORD +160 -147
  158. {zenml_nightly-0.75.0.dev20250312.dist-info → zenml_nightly-0.75.0.dev20250313.dist-info}/LICENSE +0 -0
  159. {zenml_nightly-0.75.0.dev20250312.dist-info → zenml_nightly-0.75.0.dev20250313.dist-info}/WHEEL +0 -0
  160. {zenml_nightly-0.75.0.dev20250312.dist-info → zenml_nightly-0.75.0.dev20250313.dist-info}/entry_points.txt +0 -0
zenml/VERSION CHANGED
@@ -1 +1 @@
1
- 0.75.0.dev20250312
1
+ 0.75.0.dev20250313
zenml/__init__.py CHANGED
@@ -49,9 +49,11 @@ from zenml.pipelines import get_pipeline_context, pipeline
49
49
  from zenml.steps import step, get_step_context
50
50
  from zenml.steps.utils import log_step_metadata
51
51
  from zenml.utils.metadata_utils import log_metadata
52
+ from zenml.utils.tag_utils import Tag, add_tags, remove_tags
52
53
  from zenml.entrypoints import entrypoint
53
54
 
54
55
  __all__ = [
56
+ "add_tags",
55
57
  "ArtifactConfig",
56
58
  "ExternalArtifact",
57
59
  "get_pipeline_context",
@@ -118,7 +118,10 @@ class AnalyticsContext:
118
118
  self.external_user_id = auth_context.user.external_user_id
119
119
 
120
120
  self.external_server_id = server_config().external_server_id
121
+ self.workspace_id = None
121
122
  else:
123
+ from zenml.client import Client
124
+
122
125
  # If the code is running on the client, use the default user.
123
126
  active_user = gc.zen_store.get_user()
124
127
  self.user_id = active_user.id
@@ -126,6 +129,7 @@ class AnalyticsContext:
126
129
  active_user.is_service_account
127
130
  )
128
131
  self.external_user_id = active_user.external_user_id
132
+ self.workspace_id = Client().active_workspace.id
129
133
 
130
134
  # Fetch the `client_id`
131
135
  if self.in_server:
@@ -304,6 +308,9 @@ class AnalyticsContext:
304
308
  if self.server_metadata:
305
309
  properties.update(self.server_metadata)
306
310
 
311
+ if self.workspace_id:
312
+ properties.setdefault("workspace_id", str(self.workspace_id))
313
+
307
314
  for k, v in properties.items():
308
315
  if isinstance(v, UUID):
309
316
  properties[k] = str(v)
zenml/artifacts/utils.py CHANGED
@@ -182,7 +182,6 @@ def _store_artifact_data_and_prepare_request(
182
182
  uri=materializer.uri,
183
183
  materializer=source_utils.resolve(materializer.__class__),
184
184
  data_type=source_utils.resolve(data_type),
185
- user=Client().active_user.id,
186
185
  workspace=Client().active_workspace.id,
187
186
  artifact_store_id=artifact_store.id,
188
187
  visualizations=visualizations,
@@ -350,7 +349,6 @@ def register_artifact(
350
349
  uri=folder_or_file_uri,
351
350
  materializer=source_utils.resolve(PreexistingDataMaterializer),
352
351
  data_type=source_utils.resolve(Path),
353
- user=Client().active_user.id,
354
352
  workspace=Client().active_workspace.id,
355
353
  artifact_store_id=artifact_store.id,
356
354
  has_custom_name=has_custom_name,
zenml/cli/login.py CHANGED
@@ -291,6 +291,11 @@ def connect_to_pro_server(
291
291
  login = True
292
292
 
293
293
  if login or refresh:
294
+ # If we reached this point, then we need to start a new login flow.
295
+ # We also need to remove all existing API tokens associated with the
296
+ # target ZenML Pro API, otherwise they will continue to be used after
297
+ # the re-login flow.
298
+ credentials_store.clear_all_pro_tokens(pro_api_url)
294
299
  try:
295
300
  token = web_login(
296
301
  pro_api_url=pro_api_url,
@@ -976,6 +981,7 @@ def logout(
976
981
  )
977
982
 
978
983
  pro_api_url = pro_api_url or ZENML_PRO_API_URL
984
+ pro_api_url = pro_api_url.rstrip("/")
979
985
  if credentials_store.has_valid_pro_authentication(pro_api_url):
980
986
  credentials_store.clear_pro_credentials(pro_api_url)
981
987
  cli_utils.declare("Logged out from ZenML Pro.")
zenml/cli/model.py CHANGED
@@ -91,9 +91,7 @@ def list_models(**kwargs: Any) -> None:
91
91
  Args:
92
92
  **kwargs: Keyword arguments to filter models.
93
93
  """
94
- models = Client().zen_store.list_models(
95
- model_filter_model=ModelFilter(**kwargs)
96
- )
94
+ models = Client().list_models(**kwargs)
97
95
 
98
96
  if not models:
99
97
  cli_utils.declare("No models found.")
@@ -399,24 +397,18 @@ def version() -> None:
399
397
 
400
398
 
401
399
  @cli_utils.list_options(ModelVersionFilter)
402
- @click.option(
403
- "--model-name",
404
- "-n",
405
- help="The name of the parent model.",
406
- type=str,
407
- required=False,
408
- )
409
400
  @version.command("list", help="List model versions with filter.")
410
- def list_model_versions(model_name: str, **kwargs: Any) -> None:
401
+ @click.argument("model_name_or_id")
402
+ def list_model_versions(model_name_or_id: str, **kwargs: Any) -> None:
411
403
  """List model versions with filter in the Model Control Plane.
412
404
 
413
405
  Args:
414
- model_name: The name of the parent model.
406
+ model_name_or_id: The name or ID of the parent model.
415
407
  **kwargs: Keyword arguments to filter models.
416
408
  """
417
- model_versions = Client().zen_store.list_model_versions(
418
- model_name_or_id=model_name,
419
- model_version_filter_model=ModelVersionFilter(**kwargs),
409
+ model_versions = Client().list_model_versions(
410
+ model_name_or_id=model_name_or_id,
411
+ **kwargs,
420
412
  )
421
413
 
422
414
  if not model_versions:
zenml/cli/secret.py CHANGED
@@ -38,7 +38,6 @@ from zenml.console import console
38
38
  from zenml.constants import SECRET_VALUES
39
39
  from zenml.enums import (
40
40
  CliCategories,
41
- SecretScope,
42
41
  )
43
42
  from zenml.exceptions import EntityExistsError, ZenKeyError
44
43
  from zenml.logger import get_logger
@@ -59,11 +58,12 @@ def secret() -> None:
59
58
  )
60
59
  @click.argument("name", type=click.STRING)
61
60
  @click.option(
62
- "--scope",
63
- "-s",
64
- "scope",
65
- type=click.Choice([scope.value for scope in list(SecretScope)]),
66
- default=SecretScope.WORKSPACE.value,
61
+ "--private",
62
+ "-p",
63
+ "private",
64
+ is_flag=True,
65
+ help="Whether the secret is private. A private secret is only accessible "
66
+ "to the user who creates it.",
67
67
  )
68
68
  @click.option(
69
69
  "--interactive",
@@ -84,13 +84,13 @@ def secret() -> None:
84
84
  )
85
85
  @click.argument("args", nargs=-1, type=click.UNPROCESSED)
86
86
  def create_secret(
87
- name: str, scope: str, interactive: bool, values: str, args: List[str]
87
+ name: str, private: bool, interactive: bool, values: str, args: List[str]
88
88
  ) -> None:
89
89
  """Create a secret.
90
90
 
91
91
  Args:
92
92
  name: The name of the secret to create.
93
- scope: The scope of the secret to create.
93
+ private: Whether the secret is private.
94
94
  interactive: Whether to use interactive mode to enter the secret values.
95
95
  values: Secret key-value pairs to be passed as JSON or YAML.
96
96
  args: The arguments to pass to the secret.
@@ -152,7 +152,7 @@ def create_secret(
152
152
  with console.status(f"Saving secret `{name}`..."):
153
153
  try:
154
154
  client.create_secret(
155
- name=name, values=parsed_args, scope=SecretScope(scope)
155
+ name=name, values=parsed_args, private=private
156
156
  )
157
157
  declare(f"Secret '{name}' successfully created.")
158
158
  except EntityExistsError as e:
@@ -186,7 +186,7 @@ def list_secrets(**kwargs: Any) -> None:
186
186
  dict(
187
187
  name=secret.name,
188
188
  id=str(secret.id),
189
- scope=secret.scope.value,
189
+ private=secret.private,
190
190
  )
191
191
  for secret in secrets.items
192
192
  ]
@@ -200,22 +200,26 @@ def list_secrets(**kwargs: Any) -> None:
200
200
  type=click.STRING,
201
201
  )
202
202
  @click.option(
203
- "--scope",
204
- "-s",
205
- type=click.Choice([scope.value for scope in list(SecretScope)]),
206
- default=None,
203
+ "--private",
204
+ "-p",
205
+ "private",
206
+ type=click.BOOL,
207
+ required=False,
208
+ help="Use this flag to explicitly fetch a private secret or a public secret.",
207
209
  )
208
- def get_secret(name_id_or_prefix: str, scope: Optional[str] = None) -> None:
210
+ def get_secret(name_id_or_prefix: str, private: Optional[bool] = None) -> None:
209
211
  """Get a secret and print it to the console.
210
212
 
211
213
  Args:
212
214
  name_id_or_prefix: The name of the secret to get.
213
- scope: The scope of the secret to get.
215
+ private: Private status of the secret to filter for.
214
216
  """
215
- secret = _get_secret(name_id_or_prefix, scope)
217
+ secret = _get_secret(name_id_or_prefix, private)
218
+ scope = ""
219
+ if private is not None:
220
+ scope = "private " if private else "public "
216
221
  declare(
217
- f"Fetched secret with name `{secret.name}` and ID `{secret.id}` in "
218
- f"scope `{secret.scope.value}`:"
222
+ f"Fetched {scope}secret with name `{secret.name}` and ID `{secret.id}`:"
219
223
  )
220
224
  if not secret.secret_values:
221
225
  warning(f"Secret with name `{name_id_or_prefix}` is empty.")
@@ -224,25 +228,22 @@ def get_secret(name_id_or_prefix: str, scope: Optional[str] = None) -> None:
224
228
 
225
229
 
226
230
  def _get_secret(
227
- name_id_or_prefix: str, scope: Optional[str] = None
231
+ name_id_or_prefix: str, private: Optional[bool] = None
228
232
  ) -> SecretResponse:
229
233
  """Get a secret with a given name, prefix or id.
230
234
 
231
235
  Args:
232
236
  name_id_or_prefix: The name of the secret to get.
233
- scope: The scope of the secret to get.
237
+ private: Private status of the secret to filter for.
234
238
 
235
239
  Returns:
236
240
  The secret response model.
237
241
  """
238
242
  client = Client()
239
243
  try:
240
- if scope:
241
- return client.get_secret(
242
- name_id_or_prefix=name_id_or_prefix, scope=SecretScope(scope)
243
- )
244
- else:
245
- return client.get_secret(name_id_or_prefix=name_id_or_prefix)
244
+ return client.get_secret(
245
+ name_id_or_prefix=name_id_or_prefix, private=private
246
+ )
246
247
  except ZenKeyError as e:
247
248
  error(
248
249
  f"Error fetching secret with name id or prefix "
@@ -267,9 +268,12 @@ def _get_secret(
267
268
  type=click.STRING,
268
269
  )
269
270
  @click.option(
270
- "--new-scope",
271
- "-s",
272
- type=click.Choice([scope.value for scope in list(SecretScope)]),
271
+ "--private",
272
+ "-p",
273
+ "private",
274
+ type=click.BOOL,
275
+ required=False,
276
+ help="Update the private status of the secret.",
273
277
  )
274
278
  @click.option(
275
279
  "--interactive",
@@ -293,7 +297,7 @@ def _get_secret(
293
297
  def update_secret(
294
298
  name_or_id: str,
295
299
  extra_args: List[str],
296
- new_scope: Optional[str] = None,
300
+ private: Optional[bool] = None,
297
301
  remove_keys: List[str] = [],
298
302
  interactive: bool = False,
299
303
  values: str = "",
@@ -302,7 +306,7 @@ def update_secret(
302
306
 
303
307
  Args:
304
308
  name_or_id: The name or id of the secret to update.
305
- new_scope: The new scope of the secret.
309
+ private: Private status of the secret to update.
306
310
  extra_args: The arguments to pass to the secret.
307
311
  interactive: Whether to use interactive mode to update the secret.
308
312
  remove_keys: The keys to remove from the secret.
@@ -331,10 +335,7 @@ def update_secret(
331
335
  except NotImplementedError as e:
332
336
  error(f"Centralized secrets management is disabled: {str(e)}")
333
337
 
334
- declare(
335
- f"Updating secret with name '{secret.name}' and ID '{secret.id}' in "
336
- f"scope '{secret.scope.value}:"
337
- )
338
+ declare(f"Updating secret with name '{secret.name}' and ID '{secret.id}'")
338
339
 
339
340
  if "name" in parsed_args:
340
341
  error("The word 'name' cannot be used as a key for a secret.")
@@ -388,7 +389,7 @@ def update_secret(
388
389
 
389
390
  client.update_secret(
390
391
  name_id_or_prefix=secret.id,
391
- new_scope=SecretScope(new_scope) if new_scope else None,
392
+ update_private=private,
392
393
  add_or_update_values=secret_args_add_update,
393
394
  remove_values=remove_keys,
394
395
  )
@@ -492,10 +493,12 @@ def delete_secret(name_or_id: str, yes: bool = False) -> None:
492
493
  type=click.STRING,
493
494
  )
494
495
  @click.option(
495
- "--scope",
496
- "-s",
497
- type=click.Choice([scope.value for scope in list(SecretScope)]),
498
- default=None,
496
+ "--private",
497
+ "-p",
498
+ "private",
499
+ type=click.BOOL,
500
+ required=False,
501
+ help="Use this flag to explicitly fetch a private secret or a public secret.",
499
502
  )
500
503
  @click.option(
501
504
  "--filename",
@@ -509,7 +512,7 @@ def delete_secret(name_or_id: str, yes: bool = False) -> None:
509
512
  )
510
513
  def export_secret(
511
514
  name_id_or_prefix: str,
512
- scope: Optional[str] = None,
515
+ private: Optional[bool] = None,
513
516
  filename: Optional[str] = None,
514
517
  ) -> None:
515
518
  """Export a secret as a YAML file.
@@ -519,12 +522,12 @@ def export_secret(
519
522
 
520
523
  Args:
521
524
  name_id_or_prefix: The name of the secret to export.
522
- scope: The scope of the secret to export.
525
+ private: Private status of the secret to export.
523
526
  filename: The name of the file to export the secret to.
524
527
  """
525
528
  from zenml.utils.yaml_utils import write_yaml
526
529
 
527
- secret = _get_secret(name_id_or_prefix=name_id_or_prefix, scope=scope)
530
+ secret = _get_secret(name_id_or_prefix=name_id_or_prefix, private=private)
528
531
  if not secret.secret_values:
529
532
  warning(f"Secret with name `{name_id_or_prefix}` is empty.")
530
533
  return
@@ -1131,7 +1131,6 @@ def describe_service_connector(
1131
1131
  )
1132
1132
 
1133
1133
  connector = connector_client.to_response_model(
1134
- workspace=client.active_workspace,
1135
1134
  user=client.active_user,
1136
1135
  )
1137
1136
  else:
zenml/cli/stack.py CHANGED
@@ -503,7 +503,6 @@ def register_stack(
503
503
  try:
504
504
  created_stack = client.zen_store.create_stack(
505
505
  stack=StackRequest(
506
- user=client.active_user.id,
507
506
  workspace=client.active_workspace.id,
508
507
  name=stack_name,
509
508
  components=components,
zenml/cli/tag.py CHANGED
@@ -26,8 +26,6 @@ from zenml.exceptions import EntityExistsError
26
26
  from zenml.logger import get_logger
27
27
  from zenml.models import (
28
28
  TagFilter,
29
- TagRequest,
30
- TagUpdate,
31
29
  )
32
30
  from zenml.utils.dict_utils import remove_none_values
33
31
 
@@ -47,7 +45,7 @@ def list_tags(**kwargs: Any) -> None:
47
45
  Args:
48
46
  **kwargs: Keyword arguments to filter models.
49
47
  """
50
- tags = Client().list_tags(TagFilter(**kwargs))
48
+ tags = Client().list_tags(**kwargs)
51
49
 
52
50
  if not tags:
53
51
  cli_utils.declare("No tags found.")
@@ -83,7 +81,7 @@ def register_tag(name: str, color: Optional[ColorVariants]) -> None:
83
81
  """
84
82
  request_dict = remove_none_values(dict(name=name, color=color))
85
83
  try:
86
- tag = Client().create_tag(TagRequest(**request_dict))
84
+ tag = Client().create_tag(**request_dict)
87
85
  except (EntityExistsError, ValueError) as e:
88
86
  cli_utils.error(str(e))
89
87
 
@@ -126,7 +124,7 @@ def update_tag(
126
124
 
127
125
  tag = Client().update_tag(
128
126
  tag_name_or_id=tag_name_or_id,
129
- tag_update_model=TagUpdate(**update_dict),
127
+ **update_dict,
130
128
  )
131
129
 
132
130
  cli_utils.print_pydantic_models(
zenml/cli/utils.py CHANGED
@@ -81,6 +81,7 @@ from zenml.stack.flavor import Flavor
81
81
  from zenml.stack.stack_component import StackComponentConfig
82
82
  from zenml.utils import secret_utils
83
83
  from zenml.utils.time_utils import expires_in
84
+ from zenml.utils.typing_utils import get_origin, is_union
84
85
 
85
86
  if TYPE_CHECKING:
86
87
  from uuid import UUID
@@ -1778,7 +1779,6 @@ def print_service_connector_configuration(
1778
1779
  "AUTH METHOD": connector.auth_method,
1779
1780
  "RESOURCE TYPES": ", ".join(connector.emojified_resource_types),
1780
1781
  "RESOURCE NAME": connector.resource_id or "<multiple>",
1781
- "SECRET ID": connector.secret_id or "",
1782
1782
  "SESSION DURATION": expiration,
1783
1783
  "EXPIRES IN": (
1784
1784
  expires_in(
@@ -1795,7 +1795,6 @@ def print_service_connector_configuration(
1795
1795
  else "N/A"
1796
1796
  ),
1797
1797
  "OWNER": user_name,
1798
- "WORKSPACE": connector.workspace.name,
1799
1798
  "CREATED_AT": connector.created,
1800
1799
  "UPDATED_AT": connector.updated,
1801
1800
  }
@@ -2161,9 +2160,6 @@ def print_debug_stack() -> None:
2161
2160
  console.print(f"ID: {str(stack.id)}")
2162
2161
  if stack.user and stack.user.name and stack.user.id: # mypy check
2163
2162
  console.print(f"User: {stack.user.name} / {str(stack.user.id)}")
2164
- console.print(
2165
- f"Workspace: {stack.workspace.name} / {str(stack.workspace.id)}"
2166
- )
2167
2163
 
2168
2164
  for component_type, components in stack.components.items():
2169
2165
  component = components[0]
@@ -2190,9 +2186,6 @@ def print_debug_stack() -> None:
2190
2186
  console.print(
2191
2187
  f"User: {component_response.user.name} / {str(component_response.user.id)}"
2192
2188
  )
2193
- console.print(
2194
- f"Workspace: {component_response.workspace.name} / {str(component_response.workspace.id)}"
2195
- )
2196
2189
 
2197
2190
 
2198
2191
  def _component_display_name(
@@ -2275,22 +2268,13 @@ def print_pipeline_runs_table(
2275
2268
  print_table(runs_dicts)
2276
2269
 
2277
2270
 
2278
- def warn_unsupported_non_default_workspace() -> None:
2279
- """Warning for unsupported non-default workspace."""
2280
- from zenml.constants import (
2281
- ENV_ZENML_DISABLE_WORKSPACE_WARNINGS,
2282
- handle_bool_env_var,
2283
- )
2284
-
2285
- disable_warnings = handle_bool_env_var(
2286
- ENV_ZENML_DISABLE_WORKSPACE_WARNINGS, False
2287
- )
2288
- if not disable_warnings:
2271
+ def check_zenml_pro_workspace_availability() -> None:
2272
+ """Check if the ZenML Pro workspace feature is available."""
2273
+ client = Client()
2274
+ if not client.zen_store.get_store_info().is_pro_server():
2289
2275
  warning(
2290
- "Currently the concept of `workspace` is not supported "
2291
- "within the Dashboard. The Project functionality will be "
2292
- "completed in the coming weeks. For the time being it "
2293
- "is recommended to stay within the `default` workspace."
2276
+ "The ZenML workspace feature is available only on ZenML Pro. "
2277
+ "Please visit https://zenml.io/pro to learn more."
2294
2278
  )
2295
2279
 
2296
2280
 
@@ -2402,6 +2386,23 @@ def create_data_type_help_text(
2402
2386
  return f"{field}"
2403
2387
 
2404
2388
 
2389
+ def _is_list_field(field_info: Any) -> bool:
2390
+ """Check if a field is a list field.
2391
+
2392
+ Args:
2393
+ field_info: The field info to check.
2394
+
2395
+ Returns:
2396
+ True if the field is a list field, False otherwise.
2397
+ """
2398
+ field_type = field_info.annotation
2399
+ origin = get_origin(field_type)
2400
+ return origin is list or (
2401
+ is_union(origin)
2402
+ and any(get_origin(arg) is list for arg in field_type.__args__)
2403
+ )
2404
+
2405
+
2405
2406
  def list_options(filter_model: Type[BaseFilter]) -> Callable[[F], F]:
2406
2407
  """Create a decorator to generate the correct list of filter parameters.
2407
2408
 
@@ -2430,6 +2431,7 @@ def list_options(filter_model: Type[BaseFilter]) -> Callable[[F], F]:
2430
2431
  type=str,
2431
2432
  default=v.default,
2432
2433
  required=False,
2434
+ multiple=_is_list_field(v),
2433
2435
  help=create_filter_help_text(filter_model, k),
2434
2436
  )
2435
2437
  )
zenml/cli/workspace.py CHANGED
@@ -20,9 +20,9 @@ import click
20
20
  from zenml.cli import utils as cli_utils
21
21
  from zenml.cli.cli import TagGroup, cli
22
22
  from zenml.cli.utils import (
23
+ check_zenml_pro_workspace_availability,
23
24
  is_sorted_or_filtered,
24
25
  list_options,
25
- warn_unsupported_non_default_workspace,
26
26
  )
27
27
  from zenml.client import Client
28
28
  from zenml.console import console
@@ -35,7 +35,7 @@ def workspace() -> None:
35
35
  """Commands for workspace management."""
36
36
 
37
37
 
38
- @workspace.command("list", hidden=True)
38
+ @workspace.command("list")
39
39
  @list_options(WorkspaceFilter)
40
40
  @click.pass_context
41
41
  def list_workspaces(ctx: click.Context, **kwargs: Any) -> None:
@@ -45,7 +45,7 @@ def list_workspaces(ctx: click.Context, **kwargs: Any) -> None:
45
45
  ctx: The click context object
46
46
  **kwargs: Keyword arguments to filter the list of workspaces.
47
47
  """
48
- warn_unsupported_non_default_workspace()
48
+ check_zenml_pro_workspace_availability()
49
49
  client = Client()
50
50
  with console.status("Listing workspaces...\n"):
51
51
  workspaces = client.list_workspaces(**kwargs)
@@ -60,7 +60,61 @@ def list_workspaces(ctx: click.Context, **kwargs: Any) -> None:
60
60
  cli_utils.declare("No workspaces found for the given filter.")
61
61
 
62
62
 
63
- @workspace.command("describe", hidden=True)
63
+ @workspace.command("register")
64
+ @click.option(
65
+ "--set",
66
+ "set_workspace",
67
+ is_flag=True,
68
+ help="Immediately set this workspace as active.",
69
+ type=click.BOOL,
70
+ )
71
+ @click.argument("workspace_name", type=str, required=True)
72
+ def register_workspace(
73
+ workspace_name: str, set_workspace: bool = False
74
+ ) -> None:
75
+ """Register a new workspace.
76
+
77
+ Args:
78
+ workspace_name: The name of the workspace to register.
79
+ set_workspace: Whether to set the workspace as active.
80
+ """
81
+ check_zenml_pro_workspace_availability()
82
+ client = Client()
83
+ with console.status("Creating workspace...\n"):
84
+ try:
85
+ client.create_workspace(workspace_name, description="")
86
+ cli_utils.declare("Workspace created successfully.")
87
+ except Exception as e:
88
+ cli_utils.error(str(e))
89
+
90
+ if set_workspace:
91
+ client.set_active_workspace(workspace_name)
92
+ cli_utils.declare(
93
+ f"The active workspace has been set to {workspace_name}"
94
+ )
95
+
96
+
97
+ @workspace.command("set")
98
+ @click.argument("workspace_name_or_id", type=str, required=True)
99
+ def set_workspace(workspace_name_or_id: str) -> None:
100
+ """Set the active workspace.
101
+
102
+ Args:
103
+ workspace_name_or_id: The name or ID of the workspace to set as active.
104
+ """
105
+ check_zenml_pro_workspace_availability()
106
+ client = Client()
107
+ with console.status("Setting workspace...\n"):
108
+ try:
109
+ client.set_active_workspace(workspace_name_or_id)
110
+ cli_utils.declare(
111
+ f"The active workspace has been set to {workspace_name_or_id}"
112
+ )
113
+ except Exception as e:
114
+ cli_utils.error(str(e))
115
+
116
+
117
+ @workspace.command("describe")
64
118
  @click.argument("workspace_name_or_id", type=str, required=False)
65
119
  def describe_workspace(workspace_name_or_id: Optional[str] = None) -> None:
66
120
  """Get the workspace.
@@ -68,7 +122,7 @@ def describe_workspace(workspace_name_or_id: Optional[str] = None) -> None:
68
122
  Args:
69
123
  workspace_name_or_id: The name or ID of the workspace to set as active.
70
124
  """
71
- warn_unsupported_non_default_workspace()
125
+ check_zenml_pro_workspace_availability()
72
126
  client = Client()
73
127
  if not workspace_name_or_id:
74
128
  active_workspace = client.active_workspace
@@ -84,3 +138,23 @@ def describe_workspace(workspace_name_or_id: Optional[str] = None) -> None:
84
138
  cli_utils.print_pydantic_models(
85
139
  [workspace_], exclude_columns=["created", "updated"]
86
140
  )
141
+
142
+
143
+ @workspace.command("delete")
144
+ @click.argument("workspace_name_or_id", type=str, required=True)
145
+ def delete_workspace(workspace_name_or_id: str) -> None:
146
+ """Delete a workspace.
147
+
148
+ Args:
149
+ workspace_name_or_id: The name or ID of the workspace to delete.
150
+ """
151
+ check_zenml_pro_workspace_availability()
152
+ client = Client()
153
+ with console.status("Deleting workspace...\n"):
154
+ try:
155
+ client.delete_workspace(workspace_name_or_id)
156
+ cli_utils.declare(
157
+ f"Workspace '{workspace_name_or_id}' deleted successfully."
158
+ )
159
+ except Exception as e:
160
+ cli_utils.error(str(e))