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 CHANGED
@@ -1 +1 @@
1
- 0.73.0.dev20250127
1
+ 0.73.0.dev20250129
@@ -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=click.STRING)
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: Optional[PipelineRunConfiguration] = None,
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
- def create_code_repository(
4956
- self,
4957
- name: str,
4958
- config: Dict[str, Any],
4959
- source: Source,
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
- name: Name of the code repository.
4967
- config: The configuration for the code repository.
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
- # Validate the repo config
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 BaseModel, model_validator
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: datetime object to indicate when to start the schedule.
36
- end_time: datetime object to indicate when to end the schedule.
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: datetime object to indicate when to run the
47
- pipeline once. This is useful for one-off runs.
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.datetime] = None
53
- end_time: Optional[datetime.datetime] = None
54
- interval_second: Optional[datetime.timedelta] = None
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.datetime] = None
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 code_reference := deployment.code_reference:
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
- url: The URL of the GitHub instance.
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
- url: Optional[str]
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"https://api.github.com/repos/{owner}/{repo}"
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 not appear to be public."
107
- "Access to private repositories is only possible when a token is provided. Please provide a token and try again"
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
- f"An error occurred while checking if repository is public: {str(e)}"
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(self.config.token)
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
- url: The full URL of the GitLab project.
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
- url: Optional[str]
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.url, private_token=self.config.token
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(CustomFormatter())
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(CustomFormatter())
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
@@ -241,8 +241,9 @@ class UserScopedFilter(BaseFilter):
241
241
  if sort_by == "user":
242
242
  column = UserSchema.name
243
243
 
244
- query = query.join(
245
- UserSchema, getattr(table, "user_id") == UserSchema.id
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.join(
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.join(
959
+ query = query.outerjoin(
960
960
  PipelineDeploymentSchema,
961
961
  PipelineRunSchema.deployment_id == PipelineDeploymentSchema.id,
962
- ).join(
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.join(
968
+ query = query.outerjoin(
969
969
  ModelVersionSchema,
970
970
  PipelineRunSchema.model_version_id == ModelVersionSchema.id,
971
- ).join(
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.join(
977
+ query = query.outerjoin(
978
978
  ModelVersionSchema,
979
979
  PipelineRunSchema.model_version_id == ModelVersionSchema.id,
980
980
  )