zenml-nightly 0.73.0.dev20250127__py3-none-any.whl → 0.73.0.dev20250129__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.
- zenml/VERSION +1 -1
- zenml/cli/code_repository.py +69 -1
- zenml/client.py +58 -19
- zenml/config/schedule.py +47 -10
- zenml/constants.py +2 -0
- zenml/entrypoints/base_entrypoint_configuration.py +46 -3
- zenml/exceptions.py +4 -0
- zenml/integrations/github/code_repositories/github_code_repository.py +20 -8
- zenml/integrations/gitlab/code_repositories/gitlab_code_repository.py +9 -3
- zenml/logger.py +15 -2
- zenml/models/v2/base/scoped.py +3 -2
- zenml/models/v2/core/pipeline_run.py +6 -6
- zenml/models/v2/core/schedule.py +42 -21
- zenml/pipelines/build_utils.py +43 -7
- zenml/pipelines/pipeline_definition.py +2 -1
- zenml/stack/flavor.py +27 -1
- zenml/stack/stack_component.py +3 -9
- zenml/utils/code_repository_utils.py +9 -2
- zenml/utils/code_utils.py +17 -13
- zenml/utils/dashboard_utils.py +24 -3
- zenml/utils/deprecation_utils.py +4 -2
- zenml/utils/pipeline_docker_image_builder.py +1 -4
- zenml/zen_stores/schemas/code_repository_schemas.py +3 -0
- {zenml_nightly-0.73.0.dev20250127.dist-info → zenml_nightly-0.73.0.dev20250129.dist-info}/METADATA +1 -1
- {zenml_nightly-0.73.0.dev20250127.dist-info → zenml_nightly-0.73.0.dev20250129.dist-info}/RECORD +28 -28
- {zenml_nightly-0.73.0.dev20250127.dist-info → zenml_nightly-0.73.0.dev20250129.dist-info}/LICENSE +0 -0
- {zenml_nightly-0.73.0.dev20250127.dist-info → zenml_nightly-0.73.0.dev20250129.dist-info}/WHEEL +0 -0
- {zenml_nightly-0.73.0.dev20250127.dist-info → zenml_nightly-0.73.0.dev20250129.dist-info}/entry_points.txt +0 -0
zenml/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.73.0.
|
1
|
+
0.73.0.dev20250129
|
zenml/cli/code_repository.py
CHANGED
@@ -42,7 +42,7 @@ def code_repository() -> None:
|
|
42
42
|
context_settings={"ignore_unknown_options": True},
|
43
43
|
help="Register a code repository.",
|
44
44
|
)
|
45
|
-
@click.argument("name", type=
|
45
|
+
@click.argument("name", type=str)
|
46
46
|
@click.option(
|
47
47
|
"--type",
|
48
48
|
"-t",
|
@@ -183,6 +183,74 @@ def list_code_repositories(**kwargs: Any) -> None:
|
|
183
183
|
)
|
184
184
|
|
185
185
|
|
186
|
+
@code_repository.command(
|
187
|
+
"update",
|
188
|
+
help="Update a code repository.",
|
189
|
+
context_settings={"ignore_unknown_options": True},
|
190
|
+
)
|
191
|
+
@click.argument("name_or_id", type=str, required=True)
|
192
|
+
@click.option(
|
193
|
+
"--name",
|
194
|
+
"-n",
|
195
|
+
type=str,
|
196
|
+
required=False,
|
197
|
+
help="The new code repository name.",
|
198
|
+
)
|
199
|
+
@click.option(
|
200
|
+
"--description",
|
201
|
+
"-d",
|
202
|
+
type=str,
|
203
|
+
required=False,
|
204
|
+
help="The new code repository description.",
|
205
|
+
)
|
206
|
+
@click.option(
|
207
|
+
"--logo-url",
|
208
|
+
"-l",
|
209
|
+
type=str,
|
210
|
+
required=False,
|
211
|
+
help="New URL of a logo (png, jpg or svg) for the code repository.",
|
212
|
+
)
|
213
|
+
@click.argument(
|
214
|
+
"args",
|
215
|
+
nargs=-1,
|
216
|
+
type=click.UNPROCESSED,
|
217
|
+
)
|
218
|
+
def update_code_repository(
|
219
|
+
name_or_id: str,
|
220
|
+
name: Optional[str],
|
221
|
+
description: Optional[str],
|
222
|
+
logo_url: Optional[str],
|
223
|
+
args: List[str],
|
224
|
+
) -> None:
|
225
|
+
"""Update a code repository.
|
226
|
+
|
227
|
+
Args:
|
228
|
+
name_or_id: Name or ID of the code repository to update.
|
229
|
+
name: New name of the code repository.
|
230
|
+
description: New description of the code repository.
|
231
|
+
logo_url: New logo URL of the code repository.
|
232
|
+
args: Code repository configurations.
|
233
|
+
"""
|
234
|
+
parsed_name_or_id, parsed_args = cli_utils.parse_name_and_extra_arguments(
|
235
|
+
list(args) + [name_or_id], expand_args=True, name_mandatory=True
|
236
|
+
)
|
237
|
+
assert parsed_name_or_id
|
238
|
+
|
239
|
+
with console.status(
|
240
|
+
f"Updating code repository '{parsed_name_or_id}'...\n"
|
241
|
+
):
|
242
|
+
Client().update_code_repository(
|
243
|
+
name_id_or_prefix=parsed_name_or_id,
|
244
|
+
name=name,
|
245
|
+
description=description,
|
246
|
+
logo_url=logo_url,
|
247
|
+
config=parsed_args,
|
248
|
+
)
|
249
|
+
cli_utils.declare(
|
250
|
+
f"Successfully updated code repository `{parsed_name_or_id}`."
|
251
|
+
)
|
252
|
+
|
253
|
+
|
186
254
|
@code_repository.command("delete")
|
187
255
|
@click.argument("name_or_id", type=str, required=True)
|
188
256
|
@click.option(
|
zenml/client.py
CHANGED
@@ -2453,7 +2453,9 @@ class Client(metaclass=ClientMetaClass):
|
|
2453
2453
|
def trigger_pipeline(
|
2454
2454
|
self,
|
2455
2455
|
pipeline_name_or_id: Union[str, UUID, None] = None,
|
2456
|
-
run_configuration:
|
2456
|
+
run_configuration: Union[
|
2457
|
+
PipelineRunConfiguration, Dict[str, Any], None
|
2458
|
+
] = None,
|
2457
2459
|
config_path: Optional[str] = None,
|
2458
2460
|
template_id: Optional[UUID] = None,
|
2459
2461
|
stack_name_or_id: Union[str, UUID, None] = None,
|
@@ -2524,6 +2526,11 @@ class Client(metaclass=ClientMetaClass):
|
|
2524
2526
|
if config_path:
|
2525
2527
|
run_configuration = PipelineRunConfiguration.from_yaml(config_path)
|
2526
2528
|
|
2529
|
+
if isinstance(run_configuration, Dict):
|
2530
|
+
run_configuration = PipelineRunConfiguration.model_validate(
|
2531
|
+
run_configuration
|
2532
|
+
)
|
2533
|
+
|
2527
2534
|
if run_configuration:
|
2528
2535
|
validate_run_config_is_runnable_from_server(run_configuration)
|
2529
2536
|
|
@@ -4952,25 +4959,15 @@ class Client(metaclass=ClientMetaClass):
|
|
4952
4959
|
|
4953
4960
|
# --------------------------- Code repositories ---------------------------
|
4954
4961
|
|
4955
|
-
|
4956
|
-
|
4957
|
-
|
4958
|
-
|
4959
|
-
|
4960
|
-
description: Optional[str] = None,
|
4961
|
-
logo_url: Optional[str] = None,
|
4962
|
-
) -> CodeRepositoryResponse:
|
4963
|
-
"""Create a new code repository.
|
4962
|
+
@staticmethod
|
4963
|
+
def _validate_code_repository_config(
|
4964
|
+
source: Source, config: Dict[str, Any]
|
4965
|
+
) -> None:
|
4966
|
+
"""Validate a code repository config.
|
4964
4967
|
|
4965
4968
|
Args:
|
4966
|
-
|
4967
|
-
config: The
|
4968
|
-
source: The code repository implementation source.
|
4969
|
-
description: The code repository description.
|
4970
|
-
logo_url: URL of a logo (png, jpg or svg) for the code repository.
|
4971
|
-
|
4972
|
-
Returns:
|
4973
|
-
The created code repository.
|
4969
|
+
source: The code repository source.
|
4970
|
+
config: The code repository config.
|
4974
4971
|
|
4975
4972
|
Raises:
|
4976
4973
|
RuntimeError: If the provided config is invalid.
|
@@ -4983,13 +4980,38 @@ class Client(metaclass=ClientMetaClass):
|
|
4983
4980
|
)
|
4984
4981
|
)
|
4985
4982
|
try:
|
4986
|
-
#
|
4983
|
+
# This does a login to verify the credentials
|
4987
4984
|
code_repo_class(id=uuid4(), config=config)
|
4985
|
+
|
4986
|
+
# Explicitly access the config for pydantic validation, in case
|
4987
|
+
# the login for some reason did not do that.
|
4988
|
+
_ = code_repo_class.config
|
4988
4989
|
except Exception as e:
|
4989
4990
|
raise RuntimeError(
|
4990
4991
|
"Failed to validate code repository config."
|
4991
4992
|
) from e
|
4992
4993
|
|
4994
|
+
def create_code_repository(
|
4995
|
+
self,
|
4996
|
+
name: str,
|
4997
|
+
config: Dict[str, Any],
|
4998
|
+
source: Source,
|
4999
|
+
description: Optional[str] = None,
|
5000
|
+
logo_url: Optional[str] = None,
|
5001
|
+
) -> CodeRepositoryResponse:
|
5002
|
+
"""Create a new code repository.
|
5003
|
+
|
5004
|
+
Args:
|
5005
|
+
name: Name of the code repository.
|
5006
|
+
config: The configuration for the code repository.
|
5007
|
+
source: The code repository implementation source.
|
5008
|
+
description: The code repository description.
|
5009
|
+
logo_url: URL of a logo (png, jpg or svg) for the code repository.
|
5010
|
+
|
5011
|
+
Returns:
|
5012
|
+
The created code repository.
|
5013
|
+
"""
|
5014
|
+
self._validate_code_repository_config(source=source, config=config)
|
4993
5015
|
repo_request = CodeRepositoryRequest(
|
4994
5016
|
user=self.active_user.id,
|
4995
5017
|
workspace=self.active_workspace.id,
|
@@ -5088,6 +5110,7 @@ class Client(metaclass=ClientMetaClass):
|
|
5088
5110
|
name: Optional[str] = None,
|
5089
5111
|
description: Optional[str] = None,
|
5090
5112
|
logo_url: Optional[str] = None,
|
5113
|
+
config: Optional[Dict[str, Any]] = None,
|
5091
5114
|
) -> CodeRepositoryResponse:
|
5092
5115
|
"""Update a code repository.
|
5093
5116
|
|
@@ -5097,6 +5120,10 @@ class Client(metaclass=ClientMetaClass):
|
|
5097
5120
|
name: New name of the code repository.
|
5098
5121
|
description: New description of the code repository.
|
5099
5122
|
logo_url: New logo URL of the code repository.
|
5123
|
+
config: New configuration options for the code repository. Will
|
5124
|
+
be used to update the existing configuration values. To remove
|
5125
|
+
values from the existing configuration, set the value for that
|
5126
|
+
key to `None`.
|
5100
5127
|
|
5101
5128
|
Returns:
|
5102
5129
|
The updated code repository.
|
@@ -5107,6 +5134,18 @@ class Client(metaclass=ClientMetaClass):
|
|
5107
5134
|
update = CodeRepositoryUpdate(
|
5108
5135
|
name=name, description=description, logo_url=logo_url
|
5109
5136
|
)
|
5137
|
+
if config is not None:
|
5138
|
+
combined_config = repo.config
|
5139
|
+
combined_config.update(config)
|
5140
|
+
combined_config = {
|
5141
|
+
k: v for k, v in combined_config.items() if v is not None
|
5142
|
+
}
|
5143
|
+
|
5144
|
+
self._validate_code_repository_config(
|
5145
|
+
source=repo.source, config=combined_config
|
5146
|
+
)
|
5147
|
+
update.config = combined_config
|
5148
|
+
|
5110
5149
|
return self.zen_store.update_code_repository(
|
5111
5150
|
code_repository_id=repo.id, update=update
|
5112
5151
|
)
|
zenml/config/schedule.py
CHANGED
@@ -13,10 +13,15 @@
|
|
13
13
|
# permissions and limitations under the License.
|
14
14
|
"""Class for defining a pipeline schedule."""
|
15
15
|
|
16
|
-
import datetime
|
16
|
+
from datetime import datetime, timedelta
|
17
17
|
from typing import Optional
|
18
18
|
|
19
|
-
from pydantic import
|
19
|
+
from pydantic import (
|
20
|
+
BaseModel,
|
21
|
+
ValidationInfo,
|
22
|
+
field_validator,
|
23
|
+
model_validator,
|
24
|
+
)
|
20
25
|
|
21
26
|
from zenml.logger import get_logger
|
22
27
|
|
@@ -32,8 +37,12 @@ class Schedule(BaseModel):
|
|
32
37
|
and time.
|
33
38
|
cron_expression: Cron expression for the pipeline schedule. If a value
|
34
39
|
for this is set it takes precedence over the start time + interval.
|
35
|
-
start_time:
|
36
|
-
|
40
|
+
start_time: When the schedule should start. If this is a datetime object
|
41
|
+
without any timezone, it is treated as a datetime in the local
|
42
|
+
timezone.
|
43
|
+
end_time: When the schedule should end. If this is a datetime object
|
44
|
+
without any timezone, it is treated as a datetime in the local
|
45
|
+
timezone.
|
37
46
|
interval_second: datetime timedelta indicating the seconds between two
|
38
47
|
recurring runs for a periodic schedule.
|
39
48
|
catchup: Whether the recurring run should catch up if behind schedule.
|
@@ -43,17 +52,45 @@ class Schedule(BaseModel):
|
|
43
52
|
schedules the latest interval if more than one interval is ready to
|
44
53
|
be scheduled. Usually, if your pipeline handles backfill
|
45
54
|
internally, you should turn catchup off to avoid duplicate backfill.
|
46
|
-
run_once_start_time:
|
47
|
-
|
55
|
+
run_once_start_time: When to run the pipeline once. If this is a
|
56
|
+
datetime object without any timezone, it is treated as a datetime
|
57
|
+
in the local timezone.
|
48
58
|
"""
|
49
59
|
|
50
60
|
name: Optional[str] = None
|
51
61
|
cron_expression: Optional[str] = None
|
52
|
-
start_time: Optional[datetime
|
53
|
-
end_time: Optional[datetime
|
54
|
-
interval_second: Optional[
|
62
|
+
start_time: Optional[datetime] = None
|
63
|
+
end_time: Optional[datetime] = None
|
64
|
+
interval_second: Optional[timedelta] = None
|
55
65
|
catchup: bool = False
|
56
|
-
run_once_start_time: Optional[datetime
|
66
|
+
run_once_start_time: Optional[datetime] = None
|
67
|
+
|
68
|
+
@field_validator(
|
69
|
+
"start_time", "end_time", "run_once_start_time", mode="after"
|
70
|
+
)
|
71
|
+
@classmethod
|
72
|
+
def _ensure_timezone(
|
73
|
+
cls, value: Optional[datetime], info: ValidationInfo
|
74
|
+
) -> Optional[datetime]:
|
75
|
+
"""Ensures that all datetimes are timezone aware.
|
76
|
+
|
77
|
+
Args:
|
78
|
+
value: The datetime.
|
79
|
+
info: The validation info.
|
80
|
+
|
81
|
+
Returns:
|
82
|
+
A timezone aware datetime or None.
|
83
|
+
"""
|
84
|
+
if value and value.tzinfo is None:
|
85
|
+
assert info.field_name
|
86
|
+
logger.warning(
|
87
|
+
"Your schedule `%s` is missing a timezone. It will be treated "
|
88
|
+
"as a datetime in your local timezone.",
|
89
|
+
info.field_name,
|
90
|
+
)
|
91
|
+
value = value.astimezone()
|
92
|
+
|
93
|
+
return value
|
57
94
|
|
58
95
|
@model_validator(mode="after")
|
59
96
|
def _ensure_cron_or_periodic_schedule_configured(self) -> "Schedule":
|
zenml/constants.py
CHANGED
@@ -138,6 +138,7 @@ ENV_ZENML_USER_ID = "ZENML_USER_ID"
|
|
138
138
|
ENV_ZENML_CONFIG_PATH = "ZENML_CONFIG_PATH"
|
139
139
|
ENV_ZENML_DEBUG = "ZENML_DEBUG"
|
140
140
|
ENV_ZENML_LOGGING_VERBOSITY = "ZENML_LOGGING_VERBOSITY"
|
141
|
+
ENV_ZENML_LOGGING_FORMAT = "ZENML_LOGGING_FORMAT"
|
141
142
|
ENV_ZENML_REPOSITORY_PATH = "ZENML_REPOSITORY_PATH"
|
142
143
|
ENV_ZENML_PREVENT_PIPELINE_EXECUTION = "ZENML_PREVENT_PIPELINE_EXECUTION"
|
143
144
|
ENV_ZENML_ENABLE_RICH_TRACEBACK = "ZENML_ENABLE_RICH_TRACEBACK"
|
@@ -154,6 +155,7 @@ ENV_ZENML_SECRETS_STORE_PREFIX = "ZENML_SECRETS_STORE_"
|
|
154
155
|
ENV_ZENML_BACKUP_SECRETS_STORE_PREFIX = "ZENML_BACKUP_SECRETS_STORE_"
|
155
156
|
ENV_ZENML_SKIP_PIPELINE_REGISTRATION = "ZENML_SKIP_PIPELINE_REGISTRATION"
|
156
157
|
ENV_AUTO_OPEN_DASHBOARD = "AUTO_OPEN_DASHBOARD"
|
158
|
+
ENV_ZENML_AUTO_OPEN_DASHBOARD = "ZENML_AUTO_OPEN_DASHBOARD"
|
157
159
|
ENV_ZENML_DISABLE_DATABASE_MIGRATION = "DISABLE_DATABASE_MIGRATION"
|
158
160
|
ENV_ZENML_LOCAL_STORES_PATH = "ZENML_LOCAL_STORES_PATH"
|
159
161
|
ENV_ZENML_CONTAINER = "ZENML_CONTAINER"
|
@@ -22,6 +22,8 @@ from uuid import UUID
|
|
22
22
|
|
23
23
|
from zenml.client import Client
|
24
24
|
from zenml.code_repositories import BaseCodeRepository
|
25
|
+
from zenml.enums import StackComponentType
|
26
|
+
from zenml.exceptions import CustomFlavorImportError
|
25
27
|
from zenml.logger import get_logger
|
26
28
|
from zenml.utils import (
|
27
29
|
code_repository_utils,
|
@@ -31,6 +33,7 @@ from zenml.utils import (
|
|
31
33
|
)
|
32
34
|
|
33
35
|
if TYPE_CHECKING:
|
36
|
+
from zenml.artifact_stores import BaseArtifactStore
|
34
37
|
from zenml.models import CodeReferenceResponse, PipelineDeploymentResponse
|
35
38
|
|
36
39
|
logger = get_logger(__name__)
|
@@ -204,6 +207,8 @@ class BaseEntrypointConfiguration(ABC):
|
|
204
207
|
decision instead.
|
205
208
|
|
206
209
|
Raises:
|
210
|
+
CustomFlavorImportError: If the artifact store flavor can't be
|
211
|
+
imported.
|
207
212
|
RuntimeError: If the current environment requires code download
|
208
213
|
but the deployment does not have a reference to any code.
|
209
214
|
"""
|
@@ -214,12 +219,34 @@ class BaseEntrypointConfiguration(ABC):
|
|
214
219
|
if not should_download_code:
|
215
220
|
return
|
216
221
|
|
217
|
-
if
|
222
|
+
if code_path := deployment.code_path:
|
223
|
+
# Load the artifact store not from the active stack but separately.
|
224
|
+
# This is required in case the stack has custom flavor components
|
225
|
+
# (other than the artifact store) for which the flavor
|
226
|
+
# implementations will only be available once the download finishes.
|
227
|
+
try:
|
228
|
+
artifact_store = self._load_active_artifact_store()
|
229
|
+
except CustomFlavorImportError as e:
|
230
|
+
raise CustomFlavorImportError(
|
231
|
+
"Failed to import custom artifact store flavor. The "
|
232
|
+
"artifact store flavor is needed to download your code, "
|
233
|
+
"but it looks like it might be part of the files "
|
234
|
+
"that we're trying to download. If this is the case, you "
|
235
|
+
"should disable downloading code from the artifact store "
|
236
|
+
"using `DockerSettings(allow_download_from_artifact_store=False)` "
|
237
|
+
"or make sure the artifact flavor files are included in "
|
238
|
+
"Docker image by using a custom parent image or installing "
|
239
|
+
"them as part of a pip dependency."
|
240
|
+
) from e
|
241
|
+
code_utils.download_code_from_artifact_store(
|
242
|
+
code_path=code_path, artifact_store=artifact_store
|
243
|
+
)
|
244
|
+
elif code_reference := deployment.code_reference:
|
245
|
+
# TODO: This might fail if the code repository had unpushed changes
|
246
|
+
# at the time the pipeline run was started.
|
218
247
|
self.download_code_from_code_repository(
|
219
248
|
code_reference=code_reference
|
220
249
|
)
|
221
|
-
elif code_path := deployment.code_path:
|
222
|
-
code_utils.download_code_from_artifact_store(code_path=code_path)
|
223
250
|
else:
|
224
251
|
raise RuntimeError(
|
225
252
|
"Code download required but no code reference or path provided."
|
@@ -298,6 +325,22 @@ class BaseEntrypointConfiguration(ABC):
|
|
298
325
|
|
299
326
|
return False
|
300
327
|
|
328
|
+
def _load_active_artifact_store(self) -> "BaseArtifactStore":
|
329
|
+
"""Load the active artifact store.
|
330
|
+
|
331
|
+
Returns:
|
332
|
+
The active artifact store.
|
333
|
+
"""
|
334
|
+
from zenml.artifact_stores import BaseArtifactStore
|
335
|
+
|
336
|
+
artifact_store_model = Client().active_stack_model.components[
|
337
|
+
StackComponentType.ARTIFACT_STORE
|
338
|
+
][0]
|
339
|
+
artifact_store = BaseArtifactStore.from_model(artifact_store_model)
|
340
|
+
assert isinstance(artifact_store, BaseArtifactStore)
|
341
|
+
|
342
|
+
return artifact_store
|
343
|
+
|
301
344
|
@abstractmethod
|
302
345
|
def run(self) -> None:
|
303
346
|
"""Runs the entrypoint configuration."""
|
zenml/exceptions.py
CHANGED
@@ -314,3 +314,7 @@ class SecretsStoreNotConfiguredError(NotImplementedError):
|
|
314
314
|
|
315
315
|
class BackupSecretsStoreNotConfiguredError(NotImplementedError):
|
316
316
|
"""Raised when a backup secrets store is not configured."""
|
317
|
+
|
318
|
+
|
319
|
+
class CustomFlavorImportError(ImportError):
|
320
|
+
"""Raised when failing to import a custom flavor."""
|
@@ -18,7 +18,7 @@ import re
|
|
18
18
|
from typing import List, Optional
|
19
19
|
|
20
20
|
import requests
|
21
|
-
from github import Github, GithubException
|
21
|
+
from github import Consts, Github, GithubException
|
22
22
|
from github.Repository import Repository
|
23
23
|
|
24
24
|
from zenml.code_repositories import (
|
@@ -30,6 +30,7 @@ from zenml.code_repositories.base_code_repository import (
|
|
30
30
|
)
|
31
31
|
from zenml.code_repositories.git import LocalGitRepositoryContext
|
32
32
|
from zenml.logger import get_logger
|
33
|
+
from zenml.utils import deprecation_utils
|
33
34
|
from zenml.utils.secret_utils import SecretField
|
34
35
|
|
35
36
|
logger = get_logger(__name__)
|
@@ -39,19 +40,24 @@ class GitHubCodeRepositoryConfig(BaseCodeRepositoryConfig):
|
|
39
40
|
"""Config for GitHub code repositories.
|
40
41
|
|
41
42
|
Args:
|
42
|
-
|
43
|
+
api_url: The GitHub API URL.
|
43
44
|
owner: The owner of the repository.
|
44
45
|
repository: The name of the repository.
|
45
46
|
host: The host of the repository.
|
46
47
|
token: The token to access the repository.
|
47
48
|
"""
|
48
49
|
|
49
|
-
|
50
|
+
api_url: Optional[str] = None
|
50
51
|
owner: str
|
51
52
|
repository: str
|
52
53
|
host: Optional[str] = "github.com"
|
53
54
|
token: Optional[str] = SecretField(default=None)
|
54
55
|
|
56
|
+
url: Optional[str] = None
|
57
|
+
_deprecation_validator = deprecation_utils.deprecate_pydantic_attributes(
|
58
|
+
("url", "api_url")
|
59
|
+
)
|
60
|
+
|
55
61
|
|
56
62
|
class GitHubCodeRepository(BaseCodeRepository):
|
57
63
|
"""GitHub code repository."""
|
@@ -95,7 +101,7 @@ class GitHubCodeRepository(BaseCodeRepository):
|
|
95
101
|
Raises:
|
96
102
|
RuntimeError: If the repository is not public.
|
97
103
|
"""
|
98
|
-
url = f"
|
104
|
+
url = f"{Consts.DEFAULT_BASE_URL}/repos/{owner}/{repo}"
|
99
105
|
response = requests.get(url, timeout=7)
|
100
106
|
|
101
107
|
try:
|
@@ -103,12 +109,15 @@ class GitHubCodeRepository(BaseCodeRepository):
|
|
103
109
|
pass
|
104
110
|
else:
|
105
111
|
raise RuntimeError(
|
106
|
-
"It is not possible to access this repository as it does
|
107
|
-
"
|
112
|
+
"It is not possible to access this repository as it does "
|
113
|
+
"not appear to be public. Access to private repositories "
|
114
|
+
"is only possible when a token is provided. Please provide "
|
115
|
+
"a token and try again"
|
108
116
|
)
|
109
117
|
except Exception as e:
|
110
118
|
raise RuntimeError(
|
111
|
-
|
119
|
+
"An error occurred while checking if repository is public: "
|
120
|
+
f"{str(e)}"
|
112
121
|
)
|
113
122
|
|
114
123
|
def login(
|
@@ -120,7 +129,10 @@ class GitHubCodeRepository(BaseCodeRepository):
|
|
120
129
|
RuntimeError: If the login fails.
|
121
130
|
"""
|
122
131
|
try:
|
123
|
-
self._github_session = Github(
|
132
|
+
self._github_session = Github(
|
133
|
+
login_or_token=self.config.token,
|
134
|
+
base_url=self.config.api_url or Consts.DEFAULT_BASE_URL,
|
135
|
+
)
|
124
136
|
if self.config.token:
|
125
137
|
user = self._github_session.get_user().login
|
126
138
|
logger.debug(f"Logged in as {user}")
|
@@ -31,6 +31,7 @@ from zenml.code_repositories.git.local_git_repository_context import (
|
|
31
31
|
LocalGitRepositoryContext,
|
32
32
|
)
|
33
33
|
from zenml.logger import get_logger
|
34
|
+
from zenml.utils import deprecation_utils
|
34
35
|
from zenml.utils.secret_utils import SecretField
|
35
36
|
|
36
37
|
logger = get_logger(__name__)
|
@@ -40,19 +41,24 @@ class GitLabCodeRepositoryConfig(BaseCodeRepositoryConfig):
|
|
40
41
|
"""Config for GitLab code repositories.
|
41
42
|
|
42
43
|
Args:
|
43
|
-
|
44
|
+
instance_url: The URL of the GitLab instance.
|
44
45
|
group: The group of the project.
|
45
46
|
project: The name of the GitLab project.
|
46
47
|
host: The host of GitLab in case it is self-hosted instance.
|
47
48
|
token: The token to access the repository.
|
48
49
|
"""
|
49
50
|
|
50
|
-
|
51
|
+
instance_url: Optional[str] = None
|
51
52
|
group: str
|
52
53
|
project: str
|
53
54
|
host: Optional[str] = "gitlab.com"
|
54
55
|
token: str = SecretField()
|
55
56
|
|
57
|
+
url: Optional[str] = None
|
58
|
+
_deprecation_validator = deprecation_utils.deprecate_pydantic_attributes(
|
59
|
+
("url", "instance_url")
|
60
|
+
)
|
61
|
+
|
56
62
|
|
57
63
|
class GitLabCodeRepository(BaseCodeRepository):
|
58
64
|
"""GitLab code repository."""
|
@@ -85,7 +91,7 @@ class GitLabCodeRepository(BaseCodeRepository):
|
|
85
91
|
"""
|
86
92
|
try:
|
87
93
|
self._gitlab_session = Gitlab(
|
88
|
-
self.config.
|
94
|
+
url=self.config.instance_url, private_token=self.config.token
|
89
95
|
)
|
90
96
|
self._gitlab_session.auth()
|
91
97
|
user = self._gitlab_session.user or None
|
zenml/logger.py
CHANGED
@@ -24,6 +24,7 @@ from rich.traceback import install as rich_tb_install
|
|
24
24
|
from zenml.constants import (
|
25
25
|
ENABLE_RICH_TRACEBACK,
|
26
26
|
ENV_ZENML_LOGGING_COLORS_DISABLED,
|
27
|
+
ENV_ZENML_LOGGING_FORMAT,
|
27
28
|
ENV_ZENML_SUPPRESS_LOGS,
|
28
29
|
ZENML_LOGGING_VERBOSITY,
|
29
30
|
handle_bool_env_var,
|
@@ -144,6 +145,18 @@ def set_root_verbosity() -> None:
|
|
144
145
|
get_logger(__name__).debug("Logging NOTSET")
|
145
146
|
|
146
147
|
|
148
|
+
def get_formatter() -> logging.Formatter:
|
149
|
+
"""Get a configured logging formatter.
|
150
|
+
|
151
|
+
Returns:
|
152
|
+
The formatter.
|
153
|
+
"""
|
154
|
+
if log_format := os.environ.get(ENV_ZENML_LOGGING_FORMAT, None):
|
155
|
+
return logging.Formatter(fmt=log_format)
|
156
|
+
else:
|
157
|
+
return CustomFormatter()
|
158
|
+
|
159
|
+
|
147
160
|
def get_console_handler() -> Any:
|
148
161
|
"""Get console handler for logging.
|
149
162
|
|
@@ -151,7 +164,7 @@ def get_console_handler() -> Any:
|
|
151
164
|
A console handler.
|
152
165
|
"""
|
153
166
|
console_handler = logging.StreamHandler(sys.stdout)
|
154
|
-
console_handler.setFormatter(
|
167
|
+
console_handler.setFormatter(get_formatter())
|
155
168
|
return console_handler
|
156
169
|
|
157
170
|
|
@@ -179,7 +192,7 @@ def init_logging() -> None:
|
|
179
192
|
set_root_verbosity()
|
180
193
|
|
181
194
|
console_handler = logging.StreamHandler(sys.stdout)
|
182
|
-
console_handler.setFormatter(
|
195
|
+
console_handler.setFormatter(get_formatter())
|
183
196
|
logging.root.addHandler(console_handler)
|
184
197
|
|
185
198
|
# Enable logs if environment variable SUPPRESS_ZENML_LOGS is not set to True
|
zenml/models/v2/base/scoped.py
CHANGED
@@ -241,8 +241,9 @@ class UserScopedFilter(BaseFilter):
|
|
241
241
|
if sort_by == "user":
|
242
242
|
column = UserSchema.name
|
243
243
|
|
244
|
-
query = query.
|
245
|
-
UserSchema,
|
244
|
+
query = query.outerjoin(
|
245
|
+
UserSchema,
|
246
|
+
getattr(table, "user_id") == UserSchema.id,
|
246
247
|
)
|
247
248
|
|
248
249
|
query = query.add_columns(UserSchema.name)
|
@@ -950,31 +950,31 @@ class PipelineRunFilter(WorkspaceScopedTaggableFilter):
|
|
950
950
|
sort_by, operand = self.sorting_params
|
951
951
|
|
952
952
|
if sort_by == "pipeline":
|
953
|
-
query = query.
|
953
|
+
query = query.outerjoin(
|
954
954
|
PipelineSchema,
|
955
955
|
PipelineRunSchema.pipeline_id == PipelineSchema.id,
|
956
956
|
)
|
957
957
|
column = PipelineSchema.name
|
958
958
|
elif sort_by == "stack":
|
959
|
-
query = query.
|
959
|
+
query = query.outerjoin(
|
960
960
|
PipelineDeploymentSchema,
|
961
961
|
PipelineRunSchema.deployment_id == PipelineDeploymentSchema.id,
|
962
|
-
).
|
962
|
+
).outerjoin(
|
963
963
|
StackSchema,
|
964
964
|
PipelineDeploymentSchema.stack_id == StackSchema.id,
|
965
965
|
)
|
966
966
|
column = StackSchema.name
|
967
967
|
elif sort_by == "model":
|
968
|
-
query = query.
|
968
|
+
query = query.outerjoin(
|
969
969
|
ModelVersionSchema,
|
970
970
|
PipelineRunSchema.model_version_id == ModelVersionSchema.id,
|
971
|
-
).
|
971
|
+
).outerjoin(
|
972
972
|
ModelSchema,
|
973
973
|
ModelVersionSchema.model_id == ModelSchema.id,
|
974
974
|
)
|
975
975
|
column = ModelSchema.name
|
976
976
|
elif sort_by == "model_version":
|
977
|
-
query = query.
|
977
|
+
query = query.outerjoin(
|
978
978
|
ModelVersionSchema,
|
979
979
|
PipelineRunSchema.model_version_id == ModelVersionSchema.id,
|
980
980
|
)
|