mlrun 1.10.0rc1__py3-none-any.whl → 1.10.0rc2__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.

Potentially problematic release.


This version of mlrun might be problematic. Click here for more details.

mlrun/__main__.py CHANGED
@@ -13,6 +13,8 @@
13
13
  # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
14
  # See the License for the specific language governing permissions and
15
15
  # limitations under the License.
16
+ import functools
17
+ import importlib.metadata
16
18
  import json
17
19
  import pathlib
18
20
  import socket
@@ -25,12 +27,14 @@ from pprint import pprint
25
27
  import click
26
28
  import dotenv
27
29
  import pandas as pd
30
+ import semver
28
31
  import yaml
29
32
  from tabulate import tabulate
30
33
 
31
34
  import mlrun
32
35
  import mlrun.common.constants as mlrun_constants
33
36
  import mlrun.common.schemas
37
+ import mlrun.platforms
34
38
  import mlrun.utils.helpers
35
39
  from mlrun.common.helpers import parse_versioned_object_uri
36
40
  from mlrun.runtimes.mounts import auto_mount as auto_mount_modifier
@@ -63,12 +67,19 @@ from .utils.version import Version
63
67
  pd.set_option("mode.chained_assignment", None)
64
68
 
65
69
 
66
- def validate_base_argument(ctx, param, value):
70
+ def validate_base_argument(ctx: click.Context, param: click.Parameter, value: str):
71
+ # click 8.2 expects the context to be passed to make_metavar
72
+ if semver.VersionInfo.parse(
73
+ importlib.metadata.version("click")
74
+ ) < semver.VersionInfo.parse("8.2.0"):
75
+ metavar_func = functools.partial(param.make_metavar)
76
+ else:
77
+ metavar_func = functools.partial(param.make_metavar, ctx)
67
78
  if value and value.startswith("-"):
68
79
  raise click.BadParameter(
69
80
  f"{param.human_readable_name} ({value}) cannot start with '-', ensure the command options are typed "
70
81
  f"correctly. Preferably use '--' to separate options and arguments "
71
- f"e.g. 'mlrun run --option1 --option2 -- {param.make_metavar()} [--arg1|arg1] [--arg2|arg2]'",
82
+ f"e.g. 'mlrun run --option1 --option2 -- {metavar_func()} [--arg1|arg1] [--arg2|arg2]'",
72
83
  ctx=ctx,
73
84
  param=param,
74
85
  )
mlrun/common/constants.py CHANGED
@@ -90,6 +90,13 @@ class MLRunInternalLabels:
90
90
  if not key.startswith("__") and isinstance(value, str)
91
91
  ]
92
92
 
93
+ @staticmethod
94
+ def default_run_labels_to_enrich():
95
+ return [
96
+ MLRunInternalLabels.owner,
97
+ MLRunInternalLabels.v3io_user,
98
+ ]
99
+
93
100
 
94
101
  class DeployStatusTextKind(mlrun.common.types.StrEnum):
95
102
  logs = "logs"
@@ -15,6 +15,8 @@
15
15
  import enum
16
16
  import typing
17
17
 
18
+ from deprecated import deprecated
19
+
18
20
  import mlrun.common.constants as mlrun_constants
19
21
  import mlrun_pipelines.common.models
20
22
 
@@ -237,7 +239,12 @@ class RunStates:
237
239
  }[pipeline_run_status]
238
240
 
239
241
 
240
- # TODO: remove this class in 1.9.0 - use only MlrunInternalLabels
242
+ # TODO: remove this class in 1.11.0 - use only MLRunInternalLabels
243
+ @deprecated(
244
+ version="1.9.0",
245
+ reason="This class is deprecated and will be removed in 1.11.0. Use MLRunInternalLabels instead.",
246
+ category=FutureWarning,
247
+ )
241
248
  class RunLabels(enum.Enum):
242
249
  owner = mlrun_constants.MLRunInternalLabels.owner
243
250
  v3io_user = mlrun_constants.MLRunInternalLabels.v3io_user
@@ -214,6 +214,7 @@ from .secret import (
214
214
  SecretsData,
215
215
  UserSecretCreationRequest,
216
216
  )
217
+ from .serving import ModelRunnerStepData, MonitoringData
217
218
  from .tag import Tag, TagObjects
218
219
  from .workflow import (
219
220
  GetWorkflowResponse,
@@ -14,9 +14,26 @@
14
14
 
15
15
  from pydantic.v1 import BaseModel
16
16
 
17
+ from mlrun.common.types import StrEnum
18
+
17
19
  from .background_task import BackgroundTaskList
18
20
 
19
21
 
20
22
  class DeployResponse(BaseModel):
21
23
  data: dict
22
24
  background_tasks: BackgroundTaskList
25
+
26
+
27
+ class ModelRunnerStepData(StrEnum):
28
+ MODELS = "models"
29
+ MONITORING_DATA = "monitoring_data"
30
+
31
+
32
+ class MonitoringData(StrEnum):
33
+ INPUTS = "inputs"
34
+ OUTPUTS = "outputs"
35
+ INPUT_PATH = "input_path"
36
+ CREATION_STRATEGY = "creation_strategy"
37
+ LABELS = "labels"
38
+ MODEL_PATH = "model_path"
39
+ MODEL_ENDPOINT_UID = "model_endpoint_uid"
@@ -16,7 +16,6 @@ import ast
16
16
  import base64
17
17
  import json
18
18
  import typing
19
- import warnings
20
19
  from urllib.parse import ParseResult, urlparse
21
20
 
22
21
  import pydantic.v1
@@ -142,7 +141,6 @@ class ConfigProfile(DatastoreProfile):
142
141
  class DatastoreProfileKafkaTarget(DatastoreProfile):
143
142
  type: str = pydantic.v1.Field("kafka_target")
144
143
  _private_attributes = "kwargs_private"
145
- bootstrap_servers: typing.Optional[str] = None
146
144
  brokers: typing.Optional[str] = None
147
145
  topic: str
148
146
  kwargs_public: typing.Optional[dict]
@@ -151,31 +149,16 @@ class DatastoreProfileKafkaTarget(DatastoreProfile):
151
149
  def __init__(self, **kwargs):
152
150
  super().__init__(**kwargs)
153
151
 
154
- if not self.brokers and not self.bootstrap_servers:
152
+ if not self.brokers:
155
153
  raise mlrun.errors.MLRunInvalidArgumentError(
156
154
  "DatastoreProfileKafkaTarget requires the 'brokers' field to be set"
157
155
  )
158
156
 
159
- if self.bootstrap_servers:
160
- if self.brokers:
161
- raise mlrun.errors.MLRunInvalidArgumentError(
162
- "DatastoreProfileKafkaTarget cannot be created with both 'brokers' and 'bootstrap_servers'"
163
- )
164
- else:
165
- self.brokers = self.bootstrap_servers
166
- self.bootstrap_servers = None
167
- warnings.warn(
168
- "'bootstrap_servers' parameter is deprecated in 1.7.0 and will be removed in 1.9.0, "
169
- "use 'brokers' instead.",
170
- # TODO: Remove this in 1.9.0
171
- FutureWarning,
172
- )
173
-
174
157
  def get_topic(self) -> typing.Optional[str]:
175
158
  return self.topic
176
159
 
177
160
  def attributes(self):
178
- attributes = {"brokers": self.brokers or self.bootstrap_servers}
161
+ attributes = {"brokers": self.brokers}
179
162
  if self.kwargs_public:
180
163
  attributes = merge(attributes, self.kwargs_public)
181
164
  if self.kwargs_private:
@@ -248,18 +231,7 @@ class DatastoreProfileS3(DatastoreProfile):
248
231
  assume_role_arn: typing.Optional[str] = None
249
232
  access_key_id: typing.Optional[str] = None
250
233
  secret_key: typing.Optional[str] = None
251
- bucket: typing.Optional[str] = None
252
-
253
- @pydantic.v1.validator("bucket")
254
- @classmethod
255
- def check_bucket(cls, v):
256
- if not v:
257
- warnings.warn(
258
- "The 'bucket' attribute will be mandatory starting from version 1.9",
259
- FutureWarning,
260
- stacklevel=2,
261
- )
262
- return v
234
+ bucket: str
263
235
 
264
236
  def secrets(self) -> dict:
265
237
  res = {}
@@ -353,18 +325,7 @@ class DatastoreProfileGCS(DatastoreProfile):
353
325
  _private_attributes = ("gcp_credentials",)
354
326
  credentials_path: typing.Optional[str] = None # path to file.
355
327
  gcp_credentials: typing.Optional[typing.Union[str, dict]] = None
356
- bucket: typing.Optional[str] = None
357
-
358
- @pydantic.v1.validator("bucket")
359
- @classmethod
360
- def check_bucket(cls, v):
361
- if not v:
362
- warnings.warn(
363
- "The 'bucket' attribute will be mandatory starting from version 1.9",
364
- FutureWarning,
365
- stacklevel=2,
366
- )
367
- return v
328
+ bucket: str
368
329
 
369
330
  @pydantic.v1.validator("gcp_credentials", pre=True, always=True)
370
331
  @classmethod
@@ -410,18 +371,7 @@ class DatastoreProfileAzureBlob(DatastoreProfile):
410
371
  client_secret: typing.Optional[str] = None
411
372
  sas_token: typing.Optional[str] = None
412
373
  credential: typing.Optional[str] = None
413
- container: typing.Optional[str] = None
414
-
415
- @pydantic.v1.validator("container")
416
- @classmethod
417
- def check_container(cls, v):
418
- if not v:
419
- warnings.warn(
420
- "The 'container' attribute will be mandatory starting from version 1.9",
421
- FutureWarning,
422
- stacklevel=2,
423
- )
424
- return v
374
+ container: str
425
375
 
426
376
  def url(self, subpath) -> str:
427
377
  if subpath.startswith("/"):
@@ -11,6 +11,7 @@
11
11
  # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
12
  # See the License for the specific language governing permissions and
13
13
  # limitations under the License.
14
+
14
15
  import json
15
16
  import operator
16
17
  import os
@@ -18,7 +19,7 @@ import warnings
18
19
  from base64 import b64encode
19
20
  from copy import copy
20
21
  from datetime import datetime
21
- from typing import Any, Optional, Union
22
+ from typing import Any, Literal, Optional, Union
22
23
 
23
24
  import pandas as pd
24
25
  import semver
@@ -1063,16 +1064,17 @@ class KafkaSource(OnlineSource):
1063
1064
 
1064
1065
  def __init__(
1065
1066
  self,
1066
- brokers=None,
1067
- topics=None,
1068
- group="serving",
1069
- initial_offset="earliest",
1070
- partitions=None,
1071
- sasl_user=None,
1072
- sasl_pass=None,
1073
- attributes=None,
1067
+ brokers: Optional[list[str]] = None,
1068
+ topics: Optional[list[str]] = None,
1069
+ group: str = "serving",
1070
+ initial_offset: Literal["earliest", "latest"] = "earliest",
1071
+ partitions: Optional[list[int]] = None,
1072
+ sasl_user: Optional[str] = None,
1073
+ sasl_pass: Optional[str] = None,
1074
+ tls_enable: Optional[bool] = None,
1075
+ attributes: Optional[dict] = None,
1074
1076
  **kwargs,
1075
- ):
1077
+ ) -> None:
1076
1078
  """Sets kafka source for the flow
1077
1079
 
1078
1080
  :param brokers: list of broker IP addresses
@@ -1082,6 +1084,7 @@ class KafkaSource(OnlineSource):
1082
1084
  :param partitions: Optional, A list of partitions numbers for which the function receives events.
1083
1085
  :param sasl_user: Optional, user name to use for sasl authentications
1084
1086
  :param sasl_pass: Optional, password to use for sasl authentications
1087
+ :param tls_enable: Optional, if set - whether to enable TLS or not.
1085
1088
  :param attributes: Optional, extra attributes to be passed to kafka trigger
1086
1089
  """
1087
1090
  if isinstance(topics, str):
@@ -1095,10 +1098,15 @@ class KafkaSource(OnlineSource):
1095
1098
  attributes["initial_offset"] = initial_offset
1096
1099
  if partitions is not None:
1097
1100
  attributes["partitions"] = partitions
1098
- if sasl := mlrun.datastore.utils.KafkaParameters(attributes).sasl(
1099
- usr=sasl_user, pwd=sasl_pass
1100
- ):
1101
+
1102
+ kafka_params = mlrun.datastore.utils.KafkaParameters(attributes)
1103
+
1104
+ if sasl := kafka_params.sasl(usr=sasl_user, pwd=sasl_pass):
1101
1105
  attributes["sasl"] = sasl
1106
+
1107
+ if tls := kafka_params.tls(tls_enable=tls_enable):
1108
+ attributes["tls"] = tls
1109
+
1102
1110
  super().__init__(attributes=attributes, **kwargs)
1103
1111
 
1104
1112
  def to_dataframe(
mlrun/datastore/utils.py CHANGED
@@ -246,6 +246,9 @@ class KafkaParameters:
246
246
  "partitions": "",
247
247
  "sasl": "",
248
248
  "worker_allocation_mode": "",
249
+ "tls_enable": "", # for Nuclio with Confluent Kafka (Sarama client)
250
+ "tls": "",
251
+ "new_topic": "",
249
252
  }
250
253
  self._reference_dicts = (
251
254
  self._custom_attributes,
@@ -270,7 +273,9 @@ class KafkaParameters:
270
273
  }
271
274
  if sasl := self._kwargs.get("sasl"):
272
275
  res |= {
273
- "security_protocol": "SASL_PLAINTEXT",
276
+ "security_protocol": self._kwargs.get(
277
+ "security_protocol", "SASL_PLAINTEXT"
278
+ ),
274
279
  "sasl_mechanism": sasl["mechanism"],
275
280
  "sasl_plain_username": sasl["user"],
276
281
  "sasl_plain_password": sasl["password"],
@@ -288,15 +293,25 @@ class KafkaParameters:
288
293
 
289
294
  def sasl(
290
295
  self, *, usr: typing.Optional[str] = None, pwd: typing.Optional[str] = None
291
- ) -> dict:
292
- usr = usr or self._kwargs.get("sasl_plain_username", None)
293
- pwd = pwd or self._kwargs.get("sasl_plain_password", None)
296
+ ) -> dict[str, typing.Union[str, bool]]:
294
297
  res = self._kwargs.get("sasl", {})
298
+ usr = usr or self._kwargs.get("sasl_plain_username")
299
+ pwd = pwd or self._kwargs.get("sasl_plain_password")
295
300
  if usr and pwd:
296
301
  res["enable"] = True
297
302
  res["user"] = usr
298
303
  res["password"] = pwd
299
304
  res["mechanism"] = self._kwargs.get("sasl_mechanism", "PLAIN")
305
+ res["handshake"] = self._kwargs.get("sasl_handshake", True)
306
+ return res
307
+
308
+ def tls(self, *, tls_enable: typing.Optional[bool] = None) -> dict[str, bool]:
309
+ res = self._kwargs.get("tls", {})
310
+ tls_enable = (
311
+ tls_enable if tls_enable is not None else self._kwargs.get("tls_enable")
312
+ )
313
+ if tls_enable:
314
+ res["enable"] = tls_enable
300
315
  return res
301
316
 
302
317
  def valid_entries_only(self, input_dict: dict) -> dict:
mlrun/db/base.py CHANGED
@@ -734,7 +734,7 @@ class RunDBInterface(ABC):
734
734
  labels: Optional[Union[str, dict[str, Optional[str]], list[str]]] = None,
735
735
  start: Optional[datetime.datetime] = None,
736
736
  end: Optional[datetime.datetime] = None,
737
- tsdb_metrics: bool = True,
737
+ tsdb_metrics: bool = False,
738
738
  metric_list: Optional[list[str]] = None,
739
739
  top_level: bool = False,
740
740
  uids: Optional[list[str]] = None,
mlrun/db/httpdb.py CHANGED
@@ -21,7 +21,7 @@ import typing
21
21
  import warnings
22
22
  from copy import deepcopy
23
23
  from datetime import datetime, timedelta
24
- from os import path, remove
24
+ from os import environ, path, remove
25
25
  from typing import Literal, Optional, Union
26
26
  from urllib.parse import urlparse
27
27
 
@@ -129,7 +129,9 @@ class HTTPRunDB(RunDBInterface):
129
129
  self._wait_for_background_task_terminal_state_retry_interval = 3
130
130
  self._wait_for_project_deletion_interval = 3
131
131
  self.client_version = version.Version().get()["version"]
132
- self.python_version = str(version.Version().get_python_version())
132
+ self.python_version = environ.get("MLRUN_PYTHON_VERSION") or str(
133
+ version.Version().get_python_version()
134
+ )
133
135
 
134
136
  self._enrich_and_validate(url)
135
137
 
@@ -1276,8 +1278,8 @@ class HTTPRunDB(RunDBInterface):
1276
1278
  :param producer_uri: Return artifacts produced by the requested producer URI. Producer URI usually
1277
1279
  points to a run and is used to filter artifacts by the run that produced them when the artifact producer id
1278
1280
  is a workflow id (artifact was created as part of a workflow).
1279
- :param format_: The format in which to return the artifacts. Default is 'full'.
1280
- :param limit: Maximum number of artifacts to return.
1281
+ :param format_: The format in which to return the artifacts. Default is 'full'.
1282
+ :param limit: Deprecated - Maximum number of artifacts to return (will be removed in 1.11.0).
1281
1283
  :param partition_by: Field to group results by. When `partition_by` is specified, the `partition_sort_by`
1282
1284
  parameter must be provided as well.
1283
1285
  :param rows_per_partition: How many top rows (per sorting defined by `partition_sort_by` and `partition_order`)
@@ -2221,18 +2223,20 @@ class HTTPRunDB(RunDBInterface):
2221
2223
  elif pipe_file.endswith(".zip"):
2222
2224
  headers = {"content-type": "application/zip"}
2223
2225
  else:
2224
- raise ValueError("pipeline file must be .yaml or .zip")
2226
+ raise ValueError("'pipeline' file must be .yaml or .zip")
2225
2227
  if arguments:
2226
2228
  if not isinstance(arguments, dict):
2227
- raise ValueError("arguments must be dict type")
2229
+ raise ValueError("'arguments' must be dict type")
2228
2230
  headers[mlrun.common.schemas.HeaderNames.pipeline_arguments] = str(
2229
2231
  arguments
2230
2232
  )
2231
2233
 
2232
2234
  if not path.isfile(pipe_file):
2233
- raise OSError(f"file {pipe_file} doesnt exist")
2235
+ raise OSError(f"File {pipe_file} doesnt exist")
2234
2236
  with open(pipe_file, "rb") as fp:
2235
2237
  data = fp.read()
2238
+ if not data:
2239
+ raise ValueError("The compiled pipe file is empty")
2236
2240
  if not isinstance(pipeline, str):
2237
2241
  remove(pipe_file)
2238
2242
 
@@ -3767,7 +3771,7 @@ class HTTPRunDB(RunDBInterface):
3767
3771
  labels: Optional[Union[str, dict[str, Optional[str]], list[str]]] = None,
3768
3772
  start: Optional[datetime] = None,
3769
3773
  end: Optional[datetime] = None,
3770
- tsdb_metrics: bool = True,
3774
+ tsdb_metrics: bool = False,
3771
3775
  metric_list: Optional[list[str]] = None,
3772
3776
  top_level: bool = False,
3773
3777
  uids: Optional[list[str]] = None,
@@ -3889,8 +3893,8 @@ class HTTPRunDB(RunDBInterface):
3889
3893
  attributes_keys = list(attributes.keys())
3890
3894
  attributes["name"] = name
3891
3895
  attributes["project"] = project
3892
- attributes["function-name"] = function_name or None
3893
- attributes["function-tag"] = function_tag or None
3896
+ attributes["function_name"] = function_name or None
3897
+ attributes["function_tag"] = function_tag or None
3894
3898
  attributes["uid"] = endpoint_id or None
3895
3899
  model_endpoint = mlrun.common.schemas.ModelEndpoint.from_flat_dict(attributes)
3896
3900
  path = f"projects/{project}/model-endpoints"
@@ -3981,6 +3985,7 @@ class HTTPRunDB(RunDBInterface):
3981
3985
  "deploy_histogram_data_drift_app": deploy_histogram_data_drift_app,
3982
3986
  "fetch_credentials_from_sys_config": fetch_credentials_from_sys_config,
3983
3987
  },
3988
+ timeout=300, # 5 minutes
3984
3989
  )
3985
3990
 
3986
3991
  def disable_model_monitoring(
@@ -5099,6 +5104,13 @@ class HTTPRunDB(RunDBInterface):
5099
5104
  project = project or config.default_project
5100
5105
  labels = self._parse_labels(labels)
5101
5106
 
5107
+ if limit:
5108
+ # TODO: Remove this in 1.11.0
5109
+ warnings.warn(
5110
+ "'limit' is deprecated and will be removed in 1.11.0. Use 'page' and 'page_size' instead.",
5111
+ FutureWarning,
5112
+ )
5113
+
5102
5114
  params = {
5103
5115
  "name": name,
5104
5116
  "tag": tag,
mlrun/db/nopdb.py CHANGED
@@ -630,7 +630,7 @@ class NopDB(RunDBInterface):
630
630
  labels: Optional[Union[str, dict[str, Optional[str]], list[str]]] = None,
631
631
  start: Optional[datetime.datetime] = None,
632
632
  end: Optional[datetime.datetime] = None,
633
- tsdb_metrics: bool = True,
633
+ tsdb_metrics: bool = False,
634
634
  metric_list: Optional[list[str]] = None,
635
635
  top_level: bool = False,
636
636
  uids: Optional[list[str]] = None,
mlrun/errors.py CHANGED
@@ -230,6 +230,13 @@ class MLRunTSDBConnectionFailureError(MLRunHTTPStatusError, ValueError):
230
230
  error_status_code = HTTPStatus.BAD_REQUEST.value
231
231
 
232
232
 
233
+ class MLRunMissingProjectError(MLRunBadRequestError):
234
+ default_message = "Project must be provided"
235
+
236
+ def __init__(self, message=None):
237
+ super().__init__(message or self.default_message)
238
+
239
+
233
240
  class MLRunRetryExhaustedError(Exception):
234
241
  pass
235
242
 
mlrun/execution.py CHANGED
@@ -15,6 +15,7 @@
15
15
  import logging
16
16
  import os
17
17
  import uuid
18
+ import warnings
18
19
  from copy import deepcopy
19
20
  from typing import Optional, Union, cast
20
21
 
@@ -991,6 +992,14 @@ class MLClientCtx:
991
992
  self._update_run()
992
993
  return item
993
994
 
995
+ def get_cached_artifact(self, key):
996
+ """Return a logged artifact from cache (for potential updates)"""
997
+ warnings.warn(
998
+ "get_cached_artifact is deprecated in 1.8.0 and will be removed in 1.11.0. Use get_artifact instead.",
999
+ FutureWarning,
1000
+ )
1001
+ return self.get_artifact(key)
1002
+
994
1003
  def get_artifact(
995
1004
  self, key, tag=None, iter=None, tree=None, uid=None
996
1005
  ) -> Optional[Artifact]:
mlrun/launcher/client.py CHANGED
@@ -72,7 +72,7 @@ class ClientBaseLauncher(launcher.BaseLauncher, abc.ABC):
72
72
  ):
73
73
  run.metadata.labels[mlrun_constants.MLRunInternalLabels.kind] = runtime.kind
74
74
  mlrun.runtimes.utils.enrich_run_labels(
75
- run.metadata.labels, [mlrun.common.runtimes.constants.RunLabels.owner]
75
+ run.metadata.labels, [mlrun_constants.MLRunInternalLabels.owner]
76
76
  )
77
77
  if run.spec.output_path:
78
78
  run.spec.output_path = run.spec.output_path.replace(