zenml-nightly 0.73.0.dev20250123__py3-none-any.whl → 0.73.0.dev20250125__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 (79) hide show
  1. zenml/VERSION +1 -1
  2. zenml/analytics/context.py +2 -6
  3. zenml/cli/annotator.py +1 -1
  4. zenml/cli/login.py +17 -6
  5. zenml/cli/server.py +1 -0
  6. zenml/cli/service_connectors.py +5 -5
  7. zenml/cli/stack.py +2 -2
  8. zenml/cli/utils.py +2 -54
  9. zenml/config/pipeline_configurations.py +3 -2
  10. zenml/config/schedule.py +0 -24
  11. zenml/enums.py +1 -0
  12. zenml/event_hub/base_event_hub.py +3 -4
  13. zenml/integrations/airflow/orchestrators/airflow_orchestrator.py +3 -4
  14. zenml/integrations/aws/__init__.py +2 -1
  15. zenml/integrations/aws/flavors/sagemaker_orchestrator_flavor.py +15 -0
  16. zenml/integrations/aws/orchestrators/sagemaker_orchestrator.py +310 -70
  17. zenml/integrations/aws/service_connectors/aws_service_connector.py +8 -13
  18. zenml/integrations/azure/service_connectors/azure_service_connector.py +4 -10
  19. zenml/integrations/gcp/service_connectors/gcp_service_connector.py +3 -3
  20. zenml/integrations/huggingface/__init__.py +1 -6
  21. zenml/integrations/kubernetes/orchestrators/kube_utils.py +3 -3
  22. zenml/integrations/kubernetes/service_connectors/kubernetes_service_connector.py +6 -2
  23. zenml/integrations/whylogs/data_validators/whylogs_data_validator.py +2 -3
  24. zenml/logging/step_logging.py +7 -7
  25. zenml/login/credentials.py +6 -5
  26. zenml/login/credentials_store.py +4 -3
  27. zenml/models/v2/core/api_key.py +5 -2
  28. zenml/models/v2/core/schedule.py +19 -3
  29. zenml/orchestrators/publish_utils.py +4 -4
  30. zenml/orchestrators/step_launcher.py +3 -3
  31. zenml/orchestrators/step_run_utils.py +2 -2
  32. zenml/pipelines/run_utils.py +2 -2
  33. zenml/service_connectors/service_connector.py +7 -4
  34. zenml/stack/stack.py +5 -4
  35. zenml/stack/stack_component.py +10 -2
  36. zenml/stack_deployments/stack_deployment.py +2 -3
  37. zenml/utils/string_utils.py +2 -2
  38. zenml/utils/time_utils.py +138 -0
  39. zenml/zen_server/auth.py +8 -9
  40. zenml/zen_server/cloud_utils.py +4 -6
  41. zenml/zen_server/routers/devices_endpoints.py +2 -4
  42. zenml/zen_server/routers/workspaces_endpoints.py +2 -0
  43. zenml/zen_server/zen_server_api.py +9 -8
  44. zenml/zen_stores/migrations/versions/25155145c545_separate_actions_and_triggers.py +3 -2
  45. zenml/zen_stores/migrations/versions/3dcc5d20e82f_add_last_user_activity.py +3 -3
  46. zenml/zen_stores/migrations/versions/46506f72f0ed_add_server_settings.py +3 -2
  47. zenml/zen_stores/migrations/versions/5994f9ad0489_introduce_role_permissions.py +10 -7
  48. zenml/zen_stores/migrations/versions/7500f434b71c_remove_shared_columns.py +3 -2
  49. zenml/zen_stores/migrations/versions/a91762e6be36_artifact_version_table.py +5 -3
  50. zenml/zen_stores/schemas/action_schemas.py +2 -2
  51. zenml/zen_stores/schemas/api_key_schemas.py +5 -4
  52. zenml/zen_stores/schemas/artifact_schemas.py +3 -3
  53. zenml/zen_stores/schemas/base_schemas.py +5 -7
  54. zenml/zen_stores/schemas/code_repository_schemas.py +2 -2
  55. zenml/zen_stores/schemas/component_schemas.py +2 -2
  56. zenml/zen_stores/schemas/device_schemas.py +5 -4
  57. zenml/zen_stores/schemas/event_source_schemas.py +2 -2
  58. zenml/zen_stores/schemas/flavor_schemas.py +2 -2
  59. zenml/zen_stores/schemas/model_schemas.py +3 -3
  60. zenml/zen_stores/schemas/pipeline_run_schemas.py +11 -3
  61. zenml/zen_stores/schemas/pipeline_schemas.py +2 -2
  62. zenml/zen_stores/schemas/run_template_schemas.py +2 -2
  63. zenml/zen_stores/schemas/schedule_schema.py +20 -4
  64. zenml/zen_stores/schemas/secret_schemas.py +2 -2
  65. zenml/zen_stores/schemas/server_settings_schemas.py +6 -9
  66. zenml/zen_stores/schemas/service_connector_schemas.py +3 -2
  67. zenml/zen_stores/schemas/service_schemas.py +2 -2
  68. zenml/zen_stores/schemas/stack_schemas.py +2 -2
  69. zenml/zen_stores/schemas/step_run_schemas.py +3 -2
  70. zenml/zen_stores/schemas/tag_schemas.py +2 -2
  71. zenml/zen_stores/schemas/trigger_schemas.py +2 -2
  72. zenml/zen_stores/schemas/user_schemas.py +3 -3
  73. zenml/zen_stores/schemas/workspace_schemas.py +2 -2
  74. zenml/zen_stores/sql_zen_store.py +6 -14
  75. {zenml_nightly-0.73.0.dev20250123.dist-info → zenml_nightly-0.73.0.dev20250125.dist-info}/METADATA +2 -2
  76. {zenml_nightly-0.73.0.dev20250123.dist-info → zenml_nightly-0.73.0.dev20250125.dist-info}/RECORD +79 -78
  77. {zenml_nightly-0.73.0.dev20250123.dist-info → zenml_nightly-0.73.0.dev20250125.dist-info}/LICENSE +0 -0
  78. {zenml_nightly-0.73.0.dev20250123.dist-info → zenml_nightly-0.73.0.dev20250125.dist-info}/WHEEL +0 -0
  79. {zenml_nightly-0.73.0.dev20250123.dist-info → zenml_nightly-0.73.0.dev20250125.dist-info}/entry_points.txt +0 -0
@@ -0,0 +1,138 @@
1
+ # Copyright (c) ZenML GmbH 2025. 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
+ """Time utils."""
15
+
16
+ from datetime import datetime, timedelta, timezone
17
+ from typing import Optional, Union
18
+
19
+
20
+ def utc_now(tz_aware: Union[bool, datetime] = False) -> datetime:
21
+ """Get the current time in the UTC timezone.
22
+
23
+ Args:
24
+ tz_aware: Use this flag to control whether the returned datetime is
25
+ timezone-aware or timezone-naive. If a datetime is provided, the
26
+ returned datetime will be timezone-aware if and only if the input
27
+ datetime is also timezone-aware.
28
+
29
+ Returns:
30
+ The current UTC time. If tz_aware is a datetime, the returned datetime
31
+ will be timezone-aware only if the input datetime is also timezone-aware.
32
+ If tz_aware is a boolean, the returned datetime will be timezone-aware
33
+ if True, and timezone-naive if False.
34
+ """
35
+ now = datetime.now(timezone.utc)
36
+ if (
37
+ isinstance(tz_aware, bool)
38
+ and tz_aware is False
39
+ or isinstance(tz_aware, datetime)
40
+ and tz_aware.tzinfo is None
41
+ ):
42
+ return now.replace(tzinfo=None)
43
+
44
+ return now
45
+
46
+
47
+ def utc_now_tz_aware() -> datetime:
48
+ """Get the current timezone-aware UTC time.
49
+
50
+ Returns:
51
+ The current UTC time.
52
+ """
53
+ return utc_now(tz_aware=True)
54
+
55
+
56
+ def to_local_tz(dt: datetime) -> datetime:
57
+ """Convert a datetime to the local timezone.
58
+
59
+ If the input datetime is timezone-naive, it will be assumed to be in the UTC
60
+ timezone.
61
+
62
+ Args:
63
+ dt: datetime to convert.
64
+
65
+ Returns:
66
+ Datetime in the local timezone.
67
+ """
68
+ if dt.tzinfo is None:
69
+ dt = dt.replace(tzinfo=timezone.utc)
70
+ return dt.astimezone()
71
+
72
+
73
+ def to_utc_timezone(dt: datetime) -> datetime:
74
+ """Convert a datetime to the UTC timezone.
75
+
76
+ If the input datetime is timezone-naive, it will be assumed to be in the UTC
77
+ timezone.
78
+
79
+ Args:
80
+ dt: datetime to convert.
81
+
82
+ Returns:
83
+ Datetime in the UTC timezone.
84
+ """
85
+ if dt.tzinfo is None:
86
+ dt = dt.replace(tzinfo=timezone.utc)
87
+ return dt.astimezone(timezone.utc)
88
+
89
+
90
+ def seconds_to_human_readable(time_seconds: int) -> str:
91
+ """Converts seconds to human-readable format.
92
+
93
+ Args:
94
+ time_seconds: Seconds to convert.
95
+
96
+ Returns:
97
+ Human readable string.
98
+ """
99
+ seconds = time_seconds % 60
100
+ minutes = (time_seconds // 60) % 60
101
+ hours = (time_seconds // 3600) % 24
102
+ days = time_seconds // 86400
103
+ tokens = []
104
+ if days:
105
+ tokens.append(f"{days}d")
106
+ if hours:
107
+ tokens.append(f"{hours}h")
108
+ if minutes:
109
+ tokens.append(f"{minutes}m")
110
+ if seconds:
111
+ tokens.append(f"{seconds}s")
112
+
113
+ return "".join(tokens)
114
+
115
+
116
+ def expires_in(
117
+ expires_at: datetime,
118
+ expired_str: str,
119
+ skew_tolerance: Optional[int] = None,
120
+ ) -> str:
121
+ """Returns a human-readable string of the time until an expiration.
122
+
123
+ Args:
124
+ expires_at: Expiration time.
125
+ expired_str: String to return if the expiration is in the past.
126
+ skew_tolerance: Seconds of skew tolerance to subtract from the
127
+ expiration time. If the expiration is within the skew tolerance,
128
+ the function will return the expired string.
129
+
130
+ Returns:
131
+ Human readable string.
132
+ """
133
+ now = utc_now(tz_aware=expires_at)
134
+ if skew_tolerance:
135
+ expires_at -= timedelta(seconds=skew_tolerance)
136
+ if expires_at < now:
137
+ return expired_str
138
+ return seconds_to_human_readable(int((expires_at - now).total_seconds()))
zenml/zen_server/auth.py CHANGED
@@ -14,7 +14,7 @@
14
14
  """Authentication module for ZenML server."""
15
15
 
16
16
  from contextvars import ContextVar
17
- from datetime import datetime, timedelta, timezone
17
+ from datetime import datetime, timedelta
18
18
  from typing import Callable, Optional, Union
19
19
  from urllib.parse import urlencode, urlparse
20
20
  from uuid import UUID, uuid4
@@ -62,6 +62,7 @@ from zenml.models import (
62
62
  UserResponse,
63
63
  UserUpdate,
64
64
  )
65
+ from zenml.utils.time_utils import utc_now
65
66
  from zenml.zen_server.cache import cache_result
66
67
  from zenml.zen_server.csrf import CSRFToken
67
68
  from zenml.zen_server.exceptions import http_exception_from_error
@@ -350,7 +351,8 @@ def authenticate_credentials(
350
351
 
351
352
  if (
352
353
  device_model.expires
353
- and datetime.now(timezone.utc) >= device_model.expires
354
+ and utc_now(tz_aware=device_model.expires)
355
+ >= device_model.expires
354
356
  ):
355
357
  error = (
356
358
  f"Authentication error: device {decoded_token.device_id} "
@@ -589,7 +591,7 @@ def authenticate_device(client_id: UUID, device_code: str) -> AuthContext:
589
591
 
590
592
  if (
591
593
  device_model.expires
592
- and datetime.now(timezone.utc) >= device_model.expires
594
+ and utc_now(tz_aware=device_model.expires) >= device_model.expires
593
595
  ):
594
596
  error = (
595
597
  f"Authentication error: device for client ID {client_id} has "
@@ -892,21 +894,18 @@ def generate_access_token(
892
894
  if expires_in == 0:
893
895
  expires_in = None
894
896
  elif expires_in is not None:
895
- expires = datetime.now(timezone.utc) + timedelta(seconds=expires_in)
897
+ expires = utc_now() + timedelta(seconds=expires_in)
896
898
  elif device:
897
899
  # If a device was used for authentication, the token will expire
898
900
  # at the same time as the device.
899
901
  expires = device.expires
900
902
  if expires:
901
903
  expires_in = max(
902
- int(
903
- expires.timestamp()
904
- - datetime.now(timezone.utc).timestamp()
905
- ),
904
+ int(expires.timestamp() - utc_now().timestamp()),
906
905
  0,
907
906
  )
908
907
  elif config.jwt_token_expire_minutes:
909
- expires = datetime.now(timezone.utc) + timedelta(
908
+ expires = utc_now() + timedelta(
910
909
  minutes=config.jwt_token_expire_minutes
911
910
  )
912
911
  expires_in = config.jwt_token_expire_minutes * 60
@@ -1,6 +1,6 @@
1
1
  """Utils concerning anything concerning the cloud control plane backend."""
2
2
 
3
- from datetime import datetime, timedelta, timezone
3
+ from datetime import datetime, timedelta
4
4
  from typing import Any, Dict, Optional
5
5
 
6
6
  import requests
@@ -8,6 +8,7 @@ from requests.adapters import HTTPAdapter, Retry
8
8
 
9
9
  from zenml.config.server_config import ServerProConfiguration
10
10
  from zenml.exceptions import SubscriptionUpgradeRequiredError
11
+ from zenml.utils.time_utils import utc_now
11
12
  from zenml.zen_server.utils import get_zenml_headers, server_config
12
13
 
13
14
  _cloud_connection: Optional["ZenMLCloudConnection"] = None
@@ -185,8 +186,7 @@ class ZenMLCloudConnection:
185
186
  if (
186
187
  self._token is not None
187
188
  and self._token_expires_at is not None
188
- and datetime.now(timezone.utc) + timedelta(minutes=5)
189
- < self._token_expires_at
189
+ and utc_now() + timedelta(minutes=5) < self._token_expires_at
190
190
  ):
191
191
  return self._token
192
192
 
@@ -227,9 +227,7 @@ class ZenMLCloudConnection:
227
227
  )
228
228
 
229
229
  self._token = access_token
230
- self._token_expires_at = datetime.now(timezone.utc) + timedelta(
231
- seconds=expires_in
232
- )
230
+ self._token_expires_at = utc_now() + timedelta(seconds=expires_in)
233
231
 
234
232
  assert self._token is not None
235
233
  return self._token
@@ -13,7 +13,6 @@
13
13
  # permissions and limitations under the License.
14
14
  """Endpoint definitions for code repositories."""
15
15
 
16
- from datetime import datetime, timezone
17
16
  from typing import Optional
18
17
  from uuid import UUID
19
18
 
@@ -36,6 +35,7 @@ from zenml.models import (
36
35
  OAuthDeviceVerificationRequest,
37
36
  Page,
38
37
  )
38
+ from zenml.utils.time_utils import utc_now
39
39
  from zenml.zen_server.auth import AuthContext, authorize
40
40
  from zenml.zen_server.exceptions import error_response
41
41
  from zenml.zen_server.utils import (
@@ -219,9 +219,7 @@ def verify_authorized_device(
219
219
  )
220
220
 
221
221
  # Check if the device verification has expired.
222
- if device_model.expires and device_model.expires < datetime.now(
223
- timezone.utc
224
- ):
222
+ if device_model.expires and device_model.expires < utc_now():
225
223
  raise ValueError(
226
224
  "Invalid request: device verification expired.",
227
225
  )
@@ -1009,6 +1009,8 @@ def create_run_metadata(
1009
1009
  verify_models.append(zen_store().get_artifact_version(resource.id))
1010
1010
  elif resource.type == MetadataResourceTypes.MODEL_VERSION:
1011
1011
  verify_models.append(zen_store().get_model_version(resource.id))
1012
+ elif resource.type == MetadataResourceTypes.SCHEDULE:
1013
+ verify_models.append(zen_store().get_schedule(resource.id))
1012
1014
  else:
1013
1015
  raise RuntimeError(f"Unknown resource type: {resource.type}")
1014
1016
 
@@ -22,7 +22,7 @@ To run this file locally, execute:
22
22
 
23
23
  import os
24
24
  from asyncio.log import logger
25
- from datetime import datetime, timedelta, timezone
25
+ from datetime import datetime, timedelta
26
26
  from genericpath import isfile
27
27
  from typing import Any, List, Set
28
28
 
@@ -54,6 +54,7 @@ from zenml.constants import (
54
54
  )
55
55
  from zenml.enums import AuthScheme, SourceContextTypes
56
56
  from zenml.models import ServerDeploymentType
57
+ from zenml.utils.time_utils import utc_now
57
58
  from zenml.zen_server.cloud_utils import send_pro_tenant_status_update
58
59
  from zenml.zen_server.exceptions import error_detail
59
60
  from zenml.zen_server.routers import (
@@ -129,8 +130,8 @@ app = FastAPI(
129
130
  )
130
131
 
131
132
  # Initialize last_user_activity
132
- last_user_activity: datetime = datetime.now(timezone.utc)
133
- last_user_activity_reported: datetime = datetime.now(timezone.utc) + timedelta(
133
+ last_user_activity: datetime = utc_now()
134
+ last_user_activity_reported: datetime = last_user_activity + timedelta(
134
135
  seconds=-DEFAULT_ZENML_SERVER_REPORT_USER_ACTIVITY_TO_DB_SECONDS
135
136
  )
136
137
 
@@ -306,20 +307,20 @@ async def track_last_user_activity(request: Request, call_next: Any) -> Any:
306
307
  global last_user_activity
307
308
  global last_user_activity_reported
308
309
 
310
+ now = utc_now()
311
+
309
312
  try:
310
313
  if is_user_request(request):
311
- last_user_activity = datetime.now(timezone.utc)
314
+ last_user_activity = now
312
315
  except Exception as e:
313
316
  logger.debug(
314
317
  f"An unexpected error occurred while checking user activity: {e}"
315
318
  )
316
319
  if (
317
- (
318
- datetime.now(timezone.utc) - last_user_activity_reported
319
- ).total_seconds()
320
+ (now - last_user_activity_reported).total_seconds()
320
321
  > DEFAULT_ZENML_SERVER_REPORT_USER_ACTIVITY_TO_DB_SECONDS
321
322
  ):
322
- last_user_activity_reported = datetime.now(timezone.utc)
323
+ last_user_activity_reported = now
323
324
  zen_store()._update_last_user_activity_timestamp(
324
325
  last_user_activity=last_user_activity
325
326
  )
@@ -6,13 +6,14 @@ Create Date: 2024-05-16 11:29:53.341275
6
6
 
7
7
  """
8
8
 
9
- from datetime import datetime, timezone
10
9
  from uuid import uuid4
11
10
 
12
11
  import sqlalchemy as sa
13
12
  import sqlmodel
14
13
  from alembic import op
15
14
 
15
+ from zenml.utils.time_utils import utc_now
16
+
16
17
  # revision identifiers, used by Alembic.
17
18
  revision = "25155145c545"
18
19
  down_revision = "0.58.2"
@@ -42,7 +43,7 @@ def migrate_actions() -> None:
42
43
  )
43
44
  ).fetchall()
44
45
 
45
- now = datetime.now(timezone.utc)
46
+ now = utc_now()
46
47
 
47
48
  actions_to_insert = []
48
49
  trigger_updates = {}
@@ -6,12 +6,12 @@ Create Date: 2024-08-07 14:49:07.623500
6
6
 
7
7
  """
8
8
 
9
- from datetime import datetime, timezone
10
-
11
9
  import sqlalchemy as sa
12
10
  import sqlmodel
13
11
  from alembic import op
14
12
 
13
+ from zenml.utils.time_utils import utc_now
14
+
15
15
  # revision identifiers, used by Alembic.
16
16
  revision = "3dcc5d20e82f"
17
17
  down_revision = "026d4577b6a0"
@@ -36,7 +36,7 @@ def upgrade() -> None:
36
36
  SET last_user_activity = :last_user_activity
37
37
  """
38
38
  ),
39
- params=(dict(last_user_activity=datetime.now(timezone.utc))),
39
+ params=(dict(last_user_activity=utc_now())),
40
40
  )
41
41
 
42
42
  with op.batch_alter_table("server_settings", schema=None) as batch_op:
@@ -6,13 +6,14 @@ Create Date: 2024-04-17 14:17:08.142652
6
6
 
7
7
  """
8
8
 
9
- from datetime import datetime, timezone
10
9
  from uuid import UUID
11
10
 
12
11
  import sqlalchemy as sa
13
12
  import sqlmodel
14
13
  from alembic import op
15
14
 
15
+ from zenml.utils.time_utils import utc_now
16
+
16
17
  # revision identifiers, used by Alembic.
17
18
  revision = "46506f72f0ed"
18
19
  down_revision = "cc9894cb58aa"
@@ -76,7 +77,7 @@ def upgrade() -> None:
76
77
  "display_announcements": True,
77
78
  "display_updates": True,
78
79
  # Set the updated timestamp to the current time
79
- "updated": datetime.now(timezone.utc),
80
+ "updated": utc_now(),
80
81
  },
81
82
  ],
82
83
  )
@@ -6,7 +6,6 @@ Create Date: 2022-10-25 23:52:25.935344
6
6
 
7
7
  """
8
8
 
9
- import datetime
10
9
  import uuid
11
10
 
12
11
  import sqlalchemy as sa
@@ -14,6 +13,8 @@ import sqlmodel
14
13
  from alembic import op
15
14
  from sqlalchemy import or_, select
16
15
 
16
+ from zenml.utils.time_utils import utc_now
17
+
17
18
  # revision identifiers, used by Alembic.
18
19
  revision = "5994f9ad0489"
19
20
  down_revision = "0.21.1"
@@ -100,6 +101,7 @@ def upgrade() -> None:
100
101
  guest_id = str(uuid.uuid4()).replace("-", "")
101
102
 
102
103
  # Prefill the roles table with the admin and guest role
104
+ now = utc_now()
103
105
  op.bulk_insert(
104
106
  sa.Table(
105
107
  "roleschema",
@@ -109,14 +111,14 @@ def upgrade() -> None:
109
111
  {
110
112
  "id": admin_id,
111
113
  "name": "admin",
112
- "created": datetime.datetime.now(datetime.timezone.utc),
113
- "updated": datetime.datetime.now(datetime.timezone.utc),
114
+ "created": now,
115
+ "updated": now,
114
116
  },
115
117
  {
116
118
  "id": guest_id,
117
119
  "name": "guest",
118
- "created": datetime.datetime.now(datetime.timezone.utc),
119
- "updated": datetime.datetime.now(datetime.timezone.utc),
120
+ "created": now,
121
+ "updated": now,
120
122
  },
121
123
  ],
122
124
  )
@@ -148,6 +150,7 @@ def upgrade() -> None:
148
150
  res = conn.execute(select(userschema.c.id)).fetchall()
149
151
  user_ids = [i[0] for i in res]
150
152
 
153
+ now = utc_now()
151
154
  for user_id in user_ids:
152
155
  op.bulk_insert(
153
156
  sa.Table(
@@ -159,8 +162,8 @@ def upgrade() -> None:
159
162
  "id": str(uuid.uuid4()).replace("-", ""),
160
163
  "role_id": admin_id,
161
164
  "user_id": user_id,
162
- "created": datetime.datetime.now(datetime.timezone.utc),
163
- "updated": datetime.datetime.now(datetime.timezone.utc),
165
+ "created": now,
166
+ "updated": now,
164
167
  }
165
168
  ],
166
169
  )
@@ -8,13 +8,14 @@ Create Date: 2023-10-16 15:15:34.865337
8
8
 
9
9
  import base64
10
10
  from collections import defaultdict
11
- from datetime import datetime, timezone
12
11
  from typing import Dict, Optional, Set
13
12
  from uuid import uuid4
14
13
 
15
14
  import sqlalchemy as sa
16
15
  from alembic import op
17
16
 
17
+ from zenml.utils.time_utils import utc_now
18
+
18
19
  # revision identifiers, used by Alembic.
19
20
  revision = "7500f434b71c"
20
21
  down_revision = "14d687c8fa1c"
@@ -123,7 +124,7 @@ def resolve_duplicate_names() -> None:
123
124
  _rename_duplicate_entities(service_connector_table)
124
125
 
125
126
  workspace_query = sa.select(workspace_table.c.id)
126
- utcnow = datetime.now(timezone.utc)
127
+ utcnow = utc_now()
127
128
 
128
129
  stack_components = []
129
130
  stacks = []
@@ -6,13 +6,14 @@ Create Date: 2023-11-25 11:01:09.217299
6
6
 
7
7
  """
8
8
 
9
- from datetime import datetime, timezone
10
9
  from uuid import uuid4
11
10
 
12
11
  import sqlalchemy as sa
13
12
  import sqlmodel
14
13
  from alembic import op
15
14
 
15
+ from zenml.utils.time_utils import utc_now
16
+
16
17
  # revision identifiers, used by Alembic.
17
18
  revision = "a91762e6be36"
18
19
  down_revision = "0.50.0"
@@ -58,12 +59,13 @@ def upgrade() -> None:
58
59
  unique_artifact_names = {
59
60
  name: has_custom_name for name, has_custom_name in artifact_names
60
61
  }
62
+ now = utc_now()
61
63
  for name, has_custom_name in unique_artifact_names.items():
62
64
  conn.execute(
63
65
  artifacts.insert().values(
64
66
  id=uuid4().hex,
65
- created=datetime.now(timezone.utc),
66
- updated=datetime.now(timezone.utc),
67
+ created=now,
68
+ updated=now,
67
69
  name=name,
68
70
  has_custom_name=has_custom_name,
69
71
  )
@@ -15,7 +15,6 @@
15
15
 
16
16
  import base64
17
17
  import json
18
- from datetime import datetime, timezone
19
18
  from typing import TYPE_CHECKING, Any, List, Optional
20
19
  from uuid import UUID
21
20
 
@@ -31,6 +30,7 @@ from zenml.models import (
31
30
  ActionResponseResources,
32
31
  ActionUpdate,
33
32
  )
33
+ from zenml.utils.time_utils import utc_now
34
34
  from zenml.zen_stores.schemas.base_schemas import NamedSchema
35
35
  from zenml.zen_stores.schemas.schema_utils import build_foreign_key_field
36
36
  from zenml.zen_stores.schemas.user_schemas import UserSchema
@@ -140,7 +140,7 @@ class ActionSchema(NamedSchema, table=True):
140
140
  else:
141
141
  setattr(self, field, value)
142
142
 
143
- self.updated = datetime.now(timezone.utc)
143
+ self.updated = utc_now()
144
144
  return self
145
145
 
146
146
  def to_model(
@@ -13,7 +13,7 @@
13
13
  # permissions and limitations under the License.
14
14
  """SQLModel implementation of user tables."""
15
15
 
16
- from datetime import datetime, timezone
16
+ from datetime import datetime
17
17
  from secrets import token_hex
18
18
  from typing import Any, Optional, Tuple
19
19
  from uuid import UUID
@@ -32,6 +32,7 @@ from zenml.models import (
32
32
  APIKeyRotateRequest,
33
33
  APIKeyUpdate,
34
34
  )
35
+ from zenml.utils.time_utils import utc_now
35
36
  from zenml.zen_stores.schemas.base_schemas import NamedSchema
36
37
  from zenml.zen_stores.schemas.schema_utils import build_foreign_key_field
37
38
  from zenml.zen_stores.schemas.user_schemas import UserSchema
@@ -100,7 +101,7 @@ class APIKeySchema(NamedSchema, table=True):
100
101
  """
101
102
  key = cls._generate_jwt_secret_key()
102
103
  hashed_key = cls._get_hashed_key(key)
103
- now = datetime.now(timezone.utc)
104
+ now = utc_now()
104
105
  return (
105
106
  cls(
106
107
  name=request.name,
@@ -197,7 +198,7 @@ class APIKeySchema(NamedSchema, table=True):
197
198
  if hasattr(self, field):
198
199
  setattr(self, field, value)
199
200
 
200
- self.updated = datetime.now(timezone.utc)
201
+ self.updated = utc_now()
201
202
  return self
202
203
 
203
204
  def internal_update(self, update: APIKeyInternalUpdate) -> "APIKeySchema":
@@ -230,7 +231,7 @@ class APIKeySchema(NamedSchema, table=True):
230
231
  Returns:
231
232
  The updated `APIKeySchema` and the new un-hashed key.
232
233
  """
233
- self.updated = datetime.now(timezone.utc)
234
+ self.updated = utc_now()
234
235
  self.previous_key = self.key
235
236
  self.retain_period = rotate_request.retain_period_minutes
236
237
  new_key = self._generate_jwt_secret_key()
@@ -13,7 +13,6 @@
13
13
  # permissions and limitations under the License.
14
14
  """SQLModel implementation of artifact table."""
15
15
 
16
- from datetime import datetime, timezone
17
16
  from typing import TYPE_CHECKING, Any, List, Optional
18
17
  from uuid import UUID
19
18
 
@@ -41,6 +40,7 @@ from zenml.models import (
41
40
  ArtifactVersionUpdate,
42
41
  )
43
42
  from zenml.models.v2.core.artifact import ArtifactRequest
43
+ from zenml.utils.time_utils import utc_now
44
44
  from zenml.zen_stores.schemas.base_schemas import BaseSchema, NamedSchema
45
45
  from zenml.zen_stores.schemas.component_schemas import StackComponentSchema
46
46
  from zenml.zen_stores.schemas.schema_utils import build_foreign_key_field
@@ -163,7 +163,7 @@ class ArtifactSchema(NamedSchema, table=True):
163
163
  Returns:
164
164
  The updated `ArtifactSchema`.
165
165
  """
166
- self.updated = datetime.now(timezone.utc)
166
+ self.updated = utc_now()
167
167
  if artifact_update.name:
168
168
  self.name = artifact_update.name
169
169
  self.has_custom_name = True
@@ -401,5 +401,5 @@ class ArtifactVersionSchema(BaseSchema, RunMetadataInterface, table=True):
401
401
  Returns:
402
402
  The updated `ArtifactVersionSchema`.
403
403
  """
404
- self.updated = datetime.now(timezone.utc)
404
+ self.updated = utc_now()
405
405
  return self
@@ -13,12 +13,14 @@
13
13
  # permissions and limitations under the License.
14
14
  """Base classes for SQLModel schemas."""
15
15
 
16
- from datetime import datetime, timezone
16
+ from datetime import datetime
17
17
  from typing import TYPE_CHECKING, Any, TypeVar
18
18
  from uuid import UUID, uuid4
19
19
 
20
20
  from sqlmodel import Field, SQLModel
21
21
 
22
+ from zenml.utils.time_utils import utc_now
23
+
22
24
  if TYPE_CHECKING:
23
25
  from zenml.models.v2.base.base import BaseResponse
24
26
 
@@ -29,12 +31,8 @@ class BaseSchema(SQLModel):
29
31
  """Base SQL Model for ZenML entities."""
30
32
 
31
33
  id: UUID = Field(default_factory=uuid4, primary_key=True)
32
- created: datetime = Field(
33
- default_factory=lambda: datetime.now(timezone.utc)
34
- )
35
- updated: datetime = Field(
36
- default_factory=lambda: datetime.now(timezone.utc)
37
- )
34
+ created: datetime = Field(default_factory=utc_now)
35
+ updated: datetime = Field(default_factory=utc_now)
38
36
 
39
37
  def to_model(
40
38
  self,
@@ -14,7 +14,6 @@
14
14
  """SQL Model Implementations for code repositories."""
15
15
 
16
16
  import json
17
- from datetime import datetime, timezone
18
17
  from typing import Any, Optional
19
18
  from uuid import UUID
20
19
 
@@ -32,6 +31,7 @@ from zenml.models import (
32
31
  CodeRepositoryResponseMetadata,
33
32
  CodeRepositoryUpdate,
34
33
  )
34
+ from zenml.utils.time_utils import utc_now
35
35
  from zenml.zen_stores.schemas.base_schemas import BaseSchema, NamedSchema
36
36
  from zenml.zen_stores.schemas.schema_utils import build_foreign_key_field
37
37
  from zenml.zen_stores.schemas.user_schemas import UserSchema
@@ -151,7 +151,7 @@ class CodeRepositorySchema(NamedSchema, table=True):
151
151
  if update.logo_url:
152
152
  self.logo_url = update.logo_url
153
153
 
154
- self.updated = datetime.now(timezone.utc)
154
+ self.updated = utc_now()
155
155
  return self
156
156
 
157
157