mlrun 1.6.3__py3-none-any.whl → 1.6.3rc2__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/config.py CHANGED
@@ -672,10 +672,6 @@ default_config = {
672
672
  "access_key": "",
673
673
  },
674
674
  "grafana_url": "",
675
- "auth_with_client_id": {
676
- "enabled": False,
677
- "request_timeout": 5,
678
- },
679
675
  }
680
676
 
681
677
  _is_running_as_api = None
@@ -1066,7 +1062,7 @@ class Config:
1066
1062
  target: str = "online",
1067
1063
  artifact_path: str = None,
1068
1064
  application_name: str = None,
1069
- ) -> typing.Union[str, list[str]]:
1065
+ ) -> str:
1070
1066
  """Get the full path from the configuration based on the provided project and kind.
1071
1067
 
1072
1068
  :param project: Project name.
@@ -1082,8 +1078,7 @@ class Config:
1082
1078
  relative artifact path will be taken from the global MLRun artifact path.
1083
1079
  :param application_name: Application name, None for model_monitoring_stream.
1084
1080
 
1085
- :return: Full configured path for the provided kind. Can be either a single path
1086
- or a list of paths in the case of the online model monitoring stream path.
1081
+ :return: Full configured path for the provided kind.
1087
1082
  """
1088
1083
 
1089
1084
  if target != "offline":
@@ -1104,22 +1099,12 @@ class Config:
1104
1099
  if application_name is None
1105
1100
  else f"{kind}-{application_name.lower()}",
1106
1101
  )
1107
- elif kind == "stream": # return list for mlrun<1.6.3 BC
1108
- return [
1109
- mlrun.mlconf.model_endpoint_monitoring.store_prefixes.default.format(
1110
- project=project,
1111
- kind=kind,
1112
- ), # old stream uri (pipelines) for BC ML-6043
1113
- mlrun.mlconf.model_endpoint_monitoring.store_prefixes.user_space.format(
1114
- project=project,
1115
- kind=kind,
1116
- ), # new stream uri (projects)
1117
- ]
1118
- else:
1119
- return mlrun.mlconf.model_endpoint_monitoring.store_prefixes.default.format(
1120
- project=project,
1121
- kind=kind,
1122
- )
1102
+ return mlrun.mlconf.model_endpoint_monitoring.store_prefixes.default.format(
1103
+ project=project,
1104
+ kind=kind
1105
+ if application_name is None
1106
+ else f"{kind}-{application_name.lower()}",
1107
+ )
1123
1108
 
1124
1109
  # Get the current offline path from the configuration
1125
1110
  file_path = mlrun.mlconf.model_endpoint_monitoring.offline_storage_path.format(
@@ -1376,14 +1361,10 @@ def read_env(env=None, prefix=env_prefix):
1376
1361
  if log_formatter_name := config.get("log_formatter"):
1377
1362
  import mlrun.utils.logger
1378
1363
 
1379
- log_formatter = mlrun.utils.resolve_formatter_by_kind(
1364
+ log_formatter = mlrun.utils.create_formatter_instance(
1380
1365
  mlrun.utils.FormatterKinds(log_formatter_name)
1381
1366
  )
1382
- current_handler = mlrun.utils.logger.get_handler("default")
1383
- current_formatter_name = current_handler.formatter.__class__.__name__
1384
- desired_formatter_name = log_formatter.__name__
1385
- if current_formatter_name != desired_formatter_name:
1386
- current_handler.setFormatter(log_formatter())
1367
+ mlrun.utils.logger.get_handler("default").setFormatter(log_formatter)
1387
1368
 
1388
1369
  # The default function pod resource values are of type str; however, when reading from environment variable numbers,
1389
1370
  # it converts them to type int if contains only number, so we want to convert them to str.
@@ -0,0 +1,18 @@
1
+ # Copyright 2023 Iguazio
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
+ # http://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 or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+ #
15
+
16
+
17
+ ONE_GB = 1024 * 1024 * 1024
18
+ ONE_MB = 1024 * 1024
mlrun/datastore/v3io.py CHANGED
@@ -12,6 +12,8 @@
12
12
  # See the License for the specific language governing permissions and
13
13
  # limitations under the License.
14
14
 
15
+ import mmap
16
+ import os
15
17
  import time
16
18
  from datetime import datetime
17
19
 
@@ -20,6 +22,7 @@ import v3io
20
22
  from v3io.dataplane.response import HttpResponseError
21
23
 
22
24
  import mlrun
25
+ from mlrun.datastore.helpers import ONE_GB, ONE_MB
23
26
 
24
27
  from ..platforms.iguazio import parse_path, split_path
25
28
  from .base import (
@@ -29,7 +32,6 @@ from .base import (
29
32
  )
30
33
 
31
34
  V3IO_LOCAL_ROOT = "v3io"
32
- V3IO_DEFAULT_UPLOAD_CHUNK_SIZE = 1024 * 1024 * 100
33
35
 
34
36
 
35
37
  class V3ioStore(DataStore):
@@ -92,28 +94,46 @@ class V3ioStore(DataStore):
92
94
  )
93
95
  return self._sanitize_storage_options(res)
94
96
 
95
- def _upload(
96
- self,
97
- key: str,
98
- src_path: str,
99
- max_chunk_size: int = V3IO_DEFAULT_UPLOAD_CHUNK_SIZE,
100
- ):
97
+ def _upload(self, key: str, src_path: str, max_chunk_size: int = ONE_GB):
101
98
  """helper function for upload method, allows for controlling max_chunk_size in testing"""
102
99
  container, path = split_path(self._join(key))
100
+ file_size = os.path.getsize(src_path) # in bytes
101
+ if file_size <= ONE_MB:
102
+ with open(src_path, "rb") as source_file:
103
+ data = source_file.read()
104
+ self._do_object_request(
105
+ self.object.put,
106
+ container=container,
107
+ path=path,
108
+ body=data,
109
+ append=False,
110
+ )
111
+ return
112
+ # chunk must be a multiple of the ALLOCATIONGRANULARITY
113
+ # https://docs.python.org/3/library/mmap.html
114
+ if residue := max_chunk_size % mmap.ALLOCATIONGRANULARITY:
115
+ # round down to the nearest multiple of ALLOCATIONGRANULARITY
116
+ max_chunk_size -= residue
117
+
103
118
  with open(src_path, "rb") as file_obj:
104
- append = False
105
- while True:
106
- data = memoryview(file_obj.read(max_chunk_size))
107
- if not data:
108
- break
109
- self._do_object_request(
110
- self.object.put,
111
- container=container,
112
- path=path,
113
- body=data,
114
- append=append,
115
- )
116
- append = True
119
+ file_offset = 0
120
+ while file_offset < file_size:
121
+ chunk_size = min(file_size - file_offset, max_chunk_size)
122
+ with mmap.mmap(
123
+ file_obj.fileno(),
124
+ length=chunk_size,
125
+ access=mmap.ACCESS_READ,
126
+ offset=file_offset,
127
+ ) as mmap_obj:
128
+ append = file_offset != 0
129
+ self._do_object_request(
130
+ self.object.put,
131
+ container=container,
132
+ path=path,
133
+ body=mmap_obj,
134
+ append=append,
135
+ )
136
+ file_offset += chunk_size
117
137
 
118
138
  def upload(self, key, src_path):
119
139
  return self._upload(key, src_path)
@@ -128,16 +148,19 @@ class V3ioStore(DataStore):
128
148
  num_bytes=size,
129
149
  ).body
130
150
 
131
- def _put(
132
- self,
133
- key,
134
- data,
135
- append=False,
136
- max_chunk_size: int = V3IO_DEFAULT_UPLOAD_CHUNK_SIZE,
137
- ):
151
+ def _put(self, key, data, append=False, max_chunk_size: int = ONE_GB):
138
152
  """helper function for put method, allows for controlling max_chunk_size in testing"""
139
153
  container, path = split_path(self._join(key))
140
154
  buffer_size = len(data) # in bytes
155
+ if buffer_size <= ONE_MB:
156
+ self._do_object_request(
157
+ self.object.put,
158
+ container=container,
159
+ path=path,
160
+ body=data,
161
+ append=append,
162
+ )
163
+ return
141
164
  buffer_offset = 0
142
165
  try:
143
166
  data = memoryview(data)
mlrun/db/httpdb.py CHANGED
@@ -33,7 +33,6 @@ import mlrun.common.schemas
33
33
  import mlrun.model_monitoring.model_endpoint
34
34
  import mlrun.platforms
35
35
  import mlrun.projects
36
- from mlrun.db.auth_utils import OAuthClientIDTokenProvider, StaticTokenProvider
37
36
  from mlrun.errors import MLRunInvalidArgumentError, err_to_str
38
37
 
39
38
  from ..artifacts import Artifact
@@ -134,28 +133,17 @@ class HTTPRunDB(RunDBInterface):
134
133
  endpoint += f":{parsed_url.port}"
135
134
  base_url = f"{parsed_url.scheme}://{endpoint}{parsed_url.path}"
136
135
 
137
- self.base_url = base_url
138
136
  username = parsed_url.username or config.httpdb.user
139
137
  password = parsed_url.password or config.httpdb.password
140
- self.token_provider = None
141
-
142
- if config.auth_with_client_id.enabled:
143
- self.token_provider = OAuthClientIDTokenProvider(
144
- token_endpoint=mlrun.get_secret_or_env("MLRUN_AUTH_TOKEN_ENDPOINT"),
145
- client_id=mlrun.get_secret_or_env("MLRUN_AUTH_CLIENT_ID"),
146
- client_secret=mlrun.get_secret_or_env("MLRUN_AUTH_CLIENT_SECRET"),
147
- timeout=config.auth_with_client_id.request_timeout,
148
- )
149
- else:
150
- username, password, token = mlrun.platforms.add_or_refresh_credentials(
151
- parsed_url.hostname, username, password, config.httpdb.token
152
- )
153
138
 
154
- if token:
155
- self.token_provider = StaticTokenProvider(token)
139
+ username, password, token = mlrun.platforms.add_or_refresh_credentials(
140
+ parsed_url.hostname, username, password, config.httpdb.token
141
+ )
156
142
 
143
+ self.base_url = base_url
157
144
  self.user = username
158
145
  self.password = password
146
+ self.token = token
159
147
 
160
148
  def __repr__(self):
161
149
  cls = self.__class__.__name__
@@ -225,19 +213,17 @@ class HTTPRunDB(RunDBInterface):
225
213
 
226
214
  if self.user:
227
215
  kw["auth"] = (self.user, self.password)
228
- elif self.token_provider:
229
- token = self.token_provider.get_token()
230
- if token:
231
- # Iguazio auth doesn't support passing token through bearer, so use cookie instead
232
- if self.token_provider.is_iguazio_session():
233
- session_cookie = f'j:{{"sid": "{token}"}}'
234
- cookies = {
235
- "session": session_cookie,
236
- }
237
- kw["cookies"] = cookies
238
- else:
239
- if "Authorization" not in kw.setdefault("headers", {}):
240
- kw["headers"].update({"Authorization": "Bearer " + token})
216
+ elif self.token:
217
+ # Iguazio auth doesn't support passing token through bearer, so use cookie instead
218
+ if mlrun.platforms.iguazio.is_iguazio_session(self.token):
219
+ session_cookie = f'j:{{"sid": "{self.token}"}}'
220
+ cookies = {
221
+ "session": session_cookie,
222
+ }
223
+ kw["cookies"] = cookies
224
+ else:
225
+ if "Authorization" not in kw.setdefault("headers", {}):
226
+ kw["headers"].update({"Authorization": "Bearer " + self.token})
241
227
 
242
228
  if mlrun.common.schemas.HeaderNames.client_version not in kw.setdefault(
243
229
  "headers", {}
@@ -944,7 +930,6 @@ class HTTPRunDB(RunDBInterface):
944
930
  kind: str = None,
945
931
  category: Union[str, mlrun.common.schemas.ArtifactCategories] = None,
946
932
  tree: str = None,
947
- producer_uri: str = None,
948
933
  ) -> ArtifactList:
949
934
  """List artifacts filtered by various parameters.
950
935
 
@@ -971,12 +956,9 @@ class HTTPRunDB(RunDBInterface):
971
956
  :param best_iteration: Returns the artifact which belongs to the best iteration of a given run, in the case of
972
957
  artifacts generated from a hyper-param run. If only a single iteration exists, will return the artifact
973
958
  from that iteration. If using ``best_iter``, the ``iter`` parameter must not be used.
974
- :param kind: Return artifacts of the requested kind.
975
- :param category: Return artifacts of the requested category.
976
- :param tree: Return artifacts of the requested tree.
977
- :param producer_uri: Return artifacts produced by the requested producer URI. Producer URI usually
978
- points to a run and is used to filter artifacts by the run that produced them when the artifact producer id
979
- is a workflow id (artifact was created as part of a workflow).
959
+ :param kind: Return artifacts of the requested kind.
960
+ :param category: Return artifacts of the requested category.
961
+ :param tree: Return artifacts of the requested tree.
980
962
  """
981
963
 
982
964
  project = project or config.default_project
@@ -995,7 +977,6 @@ class HTTPRunDB(RunDBInterface):
995
977
  "category": category,
996
978
  "tree": tree,
997
979
  "format": mlrun.common.schemas.ArtifactsFormat.full.value,
998
- "producer_uri": producer_uri,
999
980
  }
1000
981
  error = "list artifacts"
1001
982
  endpoint_path = f"projects/{project}/artifacts"
mlrun/lists.py CHANGED
@@ -36,7 +36,6 @@ list_header = [
36
36
  "parameters",
37
37
  "results",
38
38
  "artifacts",
39
- "artifact_uris",
40
39
  "error",
41
40
  ]
42
41
 
@@ -64,7 +63,6 @@ class RunList(list):
64
63
  get_in(run, "spec.parameters", ""),
65
64
  get_in(run, "status.results", ""),
66
65
  get_in(run, "status.artifacts", []),
67
- get_in(run, "status.artifact_uris", {}),
68
66
  get_in(run, "status.error", ""),
69
67
  ]
70
68
  if extend_iterations and iterations:
mlrun/model.py CHANGED
@@ -624,11 +624,6 @@ class RunMetadata(ModelObj):
624
624
  def iteration(self, iteration):
625
625
  self._iteration = iteration
626
626
 
627
- def is_workflow_runner(self):
628
- if not self.labels:
629
- return False
630
- return self.labels.get("job-type", "") == "workflow-runner"
631
-
632
627
 
633
628
  class HyperParamStrategies:
634
629
  grid = "grid"
@@ -1058,7 +1053,6 @@ class RunStatus(ModelObj):
1058
1053
  ui_url=None,
1059
1054
  reason: str = None,
1060
1055
  notifications: Dict[str, Notification] = None,
1061
- artifact_uris: dict[str, str] = None,
1062
1056
  ):
1063
1057
  self.state = state or "created"
1064
1058
  self.status_text = status_text
@@ -1073,21 +1067,6 @@ class RunStatus(ModelObj):
1073
1067
  self.ui_url = ui_url
1074
1068
  self.reason = reason
1075
1069
  self.notifications = notifications or {}
1076
- # Artifact key -> URI mapping, since the full artifacts are not stored in the runs DB table
1077
- self.artifact_uris = artifact_uris or {}
1078
-
1079
- def is_failed(self) -> Optional[bool]:
1080
- """
1081
- This method returns whether a run has failed.
1082
- Returns none if state has yet to be defined. callee is responsible for handling None.
1083
- (e.g wait for state to be defined)
1084
- """
1085
- if not self.state:
1086
- return None
1087
- return self.state.casefold() in [
1088
- mlrun.run.RunStatuses.failed.casefold(),
1089
- mlrun.run.RunStatuses.error.casefold(),
1090
- ]
1091
1070
 
1092
1071
 
1093
1072
  class RunTemplate(ModelObj):
@@ -1387,10 +1366,8 @@ class RunObject(RunTemplate):
1387
1366
  iter=self.metadata.iteration,
1388
1367
  )
1389
1368
  if run:
1390
- run_status = run.get("status", {})
1391
- # Artifacts are not stored in the DB, so we need to preserve them here
1392
- run_status["artifacts"] = self.status.artifacts
1393
- self.status = RunStatus.from_dict(run_status)
1369
+ self.status = RunStatus.from_dict(run.get("status", {}))
1370
+ self.status.from_dict(run.get("status", {}))
1394
1371
  return self
1395
1372
 
1396
1373
  def show(self):
@@ -19,7 +19,6 @@ import plotly.graph_objects as go
19
19
  from plotly.subplots import make_subplots
20
20
 
21
21
  import mlrun.common.schemas.model_monitoring
22
- import mlrun.common.schemas.model_monitoring.constants as mm_constants
23
22
 
24
23
  # A type for representing a drift result, a tuple of the status and the drift mean:
25
24
  DriftResultType = Tuple[mlrun.common.schemas.model_monitoring.DriftStatus, float]
@@ -113,11 +112,6 @@ class FeaturesDriftTablePlot:
113
112
  :return: The full path to the html file of the plot.
114
113
  """
115
114
  # Plot the drift table:
116
- features = [
117
- feature
118
- for feature in features
119
- if feature not in mm_constants.FeatureSetFeatures.list()
120
- ]
121
115
  figure = self._plot(
122
116
  features=features,
123
117
  sample_set_statistics=sample_set_statistics,
@@ -41,7 +41,7 @@ class _MLRunNoRunsFoundError(Exception):
41
41
  pass
42
42
 
43
43
 
44
- def get_stream_path(project: str = None, application_name: str = None) -> str:
44
+ def get_stream_path(project: str = None, application_name: str = None):
45
45
  """
46
46
  Get stream path from the project secret. If wasn't set, take it from the system configurations
47
47
 
@@ -62,9 +62,6 @@ def get_stream_path(project: str = None, application_name: str = None) -> str:
62
62
  application_name=application_name,
63
63
  )
64
64
 
65
- if isinstance(stream_uri, list): # ML-6043 - user side gets only the new stream uri
66
- stream_uri = stream_uri[1]
67
-
68
65
  return mlrun.common.model_monitoring.helpers.parse_monitoring_stream_path(
69
66
  stream_uri=stream_uri, project=project, application_name=application_name
70
67
  )
@@ -350,6 +350,7 @@ class EventStreamProcessor:
350
350
  rate="10/m",
351
351
  time_col=EventFieldType.TIMESTAMP,
352
352
  container=self.tsdb_container,
353
+ access_key=self.v3io_access_key,
353
354
  v3io_frames=self.v3io_framesd,
354
355
  infer_columns_from_data=True,
355
356
  index_cols=[
@@ -13,7 +13,6 @@
13
13
  # limitations under the License.
14
14
  import abc
15
15
  import builtins
16
- import http
17
16
  import importlib.util as imputil
18
17
  import os
19
18
  import tempfile
@@ -878,33 +877,17 @@ class _RemoteRunner(_PipelineRunner):
878
877
  get_workflow_id_timeout=get_workflow_id_timeout,
879
878
  )
880
879
 
881
- def _get_workflow_id_or_bail():
882
- try:
883
- return run_db.get_workflow_id(
884
- project=project.name,
885
- name=workflow_response.name,
886
- run_id=workflow_response.run_id,
887
- engine=workflow_spec.engine,
888
- )
889
- except mlrun.errors.MLRunHTTPStatusError as get_wf_exc:
890
- # fail fast on specific errors
891
- if get_wf_exc.error_status_code in [
892
- http.HTTPStatus.PRECONDITION_FAILED
893
- ]:
894
- raise mlrun.errors.MLRunFatalFailureError(
895
- original_exception=get_wf_exc
896
- )
897
-
898
- # raise for a retry (on other errors)
899
- raise
900
-
901
880
  # Getting workflow id from run:
902
881
  response = retry_until_successful(
903
882
  1,
904
883
  get_workflow_id_timeout,
905
884
  logger,
906
885
  False,
907
- _get_workflow_id_or_bail,
886
+ run_db.get_workflow_id,
887
+ project=project.name,
888
+ name=workflow_response.name,
889
+ run_id=workflow_response.run_id,
890
+ engine=workflow_spec.engine,
908
891
  )
909
892
  workflow_id = response.workflow_id
910
893
  # After fetching the workflow_id the workflow executed successfully
mlrun/projects/project.py CHANGED
@@ -2650,14 +2650,12 @@ class MlrunProject(ModelObj):
2650
2650
  "Remote repo is not defined, use .create_remote() + push()"
2651
2651
  )
2652
2652
 
2653
- if engine not in ["remote"]:
2654
- # for remote runs we don't require the functions to be synced as they can be loaded dynamically during run
2655
- self.sync_functions(always=sync)
2656
- if not self.spec._function_objects:
2657
- raise ValueError(
2658
- "There are no functions in the project."
2659
- " Make sure you've set your functions with project.set_function()."
2660
- )
2653
+ self.sync_functions(always=sync)
2654
+ if not self.spec._function_objects:
2655
+ raise ValueError(
2656
+ "There are no functions in the project."
2657
+ " Make sure you've set your functions with project.set_function()."
2658
+ )
2661
2659
 
2662
2660
  if not name and not workflow_path and not workflow_handler:
2663
2661
  raise ValueError("Workflow name, path, or handler must be specified")
mlrun/render.py CHANGED
@@ -134,7 +134,7 @@ def artifacts_html(
134
134
 
135
135
  if not attribute_value:
136
136
  mlrun.utils.logger.warning(
137
- f"Artifact required attribute {attribute_name} is missing, omitting from output",
137
+ "Artifact is incomplete, omitting from output (most likely due to a failed artifact logging)",
138
138
  artifact_key=key,
139
139
  )
140
140
  continue
@@ -404,21 +404,12 @@ def runs_to_html(
404
404
  df.drop("labels", axis=1, inplace=True)
405
405
  df.drop("inputs", axis=1, inplace=True)
406
406
  df.drop("artifacts", axis=1, inplace=True)
407
- df.drop("artifact_uris", axis=1, inplace=True)
408
407
  else:
409
408
  df["labels"] = df["labels"].apply(dict_html)
410
409
  df["inputs"] = df["inputs"].apply(inputs_html)
411
- if df["artifacts"][0]:
412
- df["artifacts"] = df["artifacts"].apply(
413
- lambda artifacts: artifacts_html(artifacts, "target_path"),
414
- )
415
- df.drop("artifact_uris", axis=1, inplace=True)
416
- elif df["artifact_uris"][0]:
417
- df["artifact_uris"] = df["artifact_uris"].apply(dict_html)
418
- df.drop("artifacts", axis=1, inplace=True)
419
- else:
420
- df.drop("artifacts", axis=1, inplace=True)
421
- df.drop("artifact_uris", axis=1, inplace=True)
410
+ df["artifacts"] = df["artifacts"].apply(
411
+ lambda artifacts: artifacts_html(artifacts, "target_path"),
412
+ )
422
413
 
423
414
  def expand_error(x):
424
415
  if x["state"] == "error":
mlrun/runtimes/pod.py CHANGED
@@ -1012,12 +1012,12 @@ class KubeResource(BaseRuntime):
1012
1012
 
1013
1013
  def _set_env(self, name, value=None, value_from=None):
1014
1014
  new_var = k8s_client.V1EnvVar(name=name, value=value, value_from=value_from)
1015
-
1016
- # ensure we don't have duplicate env vars with the same name
1017
- for env_index, value_item in enumerate(self.spec.env):
1018
- if get_item_name(value_item) == name:
1019
- self.spec.env[env_index] = new_var
1015
+ i = 0
1016
+ for v in self.spec.env:
1017
+ if get_item_name(v) == name:
1018
+ self.spec.env[i] = new_var
1020
1019
  return self
1020
+ i += 1
1021
1021
  self.spec.env.append(new_var)
1022
1022
  return self
1023
1023
 
mlrun/utils/async_http.py CHANGED
@@ -24,7 +24,7 @@ from aiohttp_retry import ExponentialRetry, RequestParams, RetryClient, RetryOpt
24
24
  from aiohttp_retry.client import _RequestContext
25
25
 
26
26
  from mlrun.config import config
27
- from mlrun.errors import err_to_str, raise_for_status
27
+ from mlrun.errors import err_to_str
28
28
 
29
29
  from .helpers import logger as mlrun_logger
30
30
 
@@ -48,21 +48,12 @@ class AsyncClientWithRetry(RetryClient):
48
48
  *args,
49
49
  **kwargs,
50
50
  ):
51
- # do not retry on PUT / PATCH as they might have side effects (not truly idempotent)
52
- blacklisted_methods = (
53
- blacklisted_methods
54
- if blacklisted_methods is not None
55
- else [
56
- "POST",
57
- "PUT",
58
- "PATCH",
59
- ]
60
- )
61
51
  super().__init__(
62
52
  *args,
63
53
  retry_options=ExponentialRetryOverride(
64
54
  retry_on_exception=retry_on_exception,
65
- blacklisted_methods=blacklisted_methods,
55
+ # do not retry on PUT / PATCH as they might have side effects (not truly idempotent)
56
+ blacklisted_methods=blacklisted_methods or ["POST", "PUT", "PATCH"],
66
57
  attempts=max_retries,
67
58
  statuses=retry_on_status_codes,
68
59
  factor=retry_backoff_factor,
@@ -74,12 +65,6 @@ class AsyncClientWithRetry(RetryClient):
74
65
  **kwargs,
75
66
  )
76
67
 
77
- def methods_blacklist_update_required(self, new_blacklist: str):
78
- self._retry_options: ExponentialRetryOverride
79
- return set(self._retry_options.blacklisted_methods).difference(
80
- set(new_blacklist)
81
- )
82
-
83
68
  def _make_requests(
84
69
  self,
85
70
  params_list: List[RequestParams],
@@ -190,7 +175,7 @@ class _CustomRequestContext(_RequestContext):
190
175
  last_attempt = current_attempt == self._retry_options.attempts
191
176
  if self._is_status_code_ok(response.status) or last_attempt:
192
177
  if self._raise_for_status:
193
- raise_for_status(response)
178
+ response.raise_for_status()
194
179
 
195
180
  self._response = response
196
181
  return response
@@ -292,11 +277,6 @@ class _CustomRequestContext(_RequestContext):
292
277
  if isinstance(exc.os_error, exc_type):
293
278
  return
294
279
  if exc.__cause__:
295
- # If the cause exception is retriable, return, otherwise, raise the original exception
296
- try:
297
- self.verify_exception_type(exc.__cause__)
298
- except Exception:
299
- raise exc
300
- return
280
+ return self.verify_exception_type(exc.__cause__)
301
281
  else:
302
282
  raise exc
mlrun/utils/logger.py CHANGED
@@ -14,7 +14,6 @@
14
14
 
15
15
  import json
16
16
  import logging
17
- import typing
18
17
  from enum import Enum
19
18
  from sys import stdout
20
19
  from traceback import format_exception
@@ -187,15 +186,11 @@ class FormatterKinds(Enum):
187
186
  JSON = "json"
188
187
 
189
188
 
190
- def resolve_formatter_by_kind(
191
- formatter_kind: FormatterKinds,
192
- ) -> typing.Type[
193
- typing.Union[HumanReadableFormatter, HumanReadableExtendedFormatter, JSONFormatter]
194
- ]:
189
+ def create_formatter_instance(formatter_kind: FormatterKinds) -> logging.Formatter:
195
190
  return {
196
- FormatterKinds.HUMAN: HumanReadableFormatter,
197
- FormatterKinds.HUMAN_EXTENDED: HumanReadableExtendedFormatter,
198
- FormatterKinds.JSON: JSONFormatter,
191
+ FormatterKinds.HUMAN: HumanReadableFormatter(),
192
+ FormatterKinds.HUMAN_EXTENDED: HumanReadableExtendedFormatter(),
193
+ FormatterKinds.JSON: JSONFormatter(),
199
194
  }[formatter_kind]
200
195
 
201
196
 
@@ -213,11 +208,11 @@ def create_logger(
213
208
  logger_instance = Logger(level, name=name, propagate=False)
214
209
 
215
210
  # resolve formatter
216
- formatter_instance = resolve_formatter_by_kind(
211
+ formatter_instance = create_formatter_instance(
217
212
  FormatterKinds(formatter_kind.lower())
218
213
  )
219
214
 
220
215
  # set handler
221
- logger_instance.set_handler("default", stream or stdout, formatter_instance())
216
+ logger_instance.set_handler("default", stream or stdout, formatter_instance)
222
217
 
223
218
  return logger_instance
@@ -1,4 +1,4 @@
1
1
  {
2
- "git_commit": "43c50e9853e34113270e42ef4b4ccc1b0cdb28cb",
3
- "version": "1.6.3"
2
+ "git_commit": "e48bcf64d5c6c36239e481620674182e88911c68",
3
+ "version": "1.6.3-rc2"
4
4
  }
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: mlrun
3
- Version: 1.6.3
3
+ Version: 1.6.3rc2
4
4
  Summary: Tracking and config of machine learning runs
5
5
  Home-page: https://github.com/mlrun/mlrun
6
6
  Author: Yaron Haviv
@@ -26,7 +26,7 @@ Requires-Dist: GitPython >=3.1.41,~=3.1
26
26
  Requires-Dist: aiohttp ~=3.9
27
27
  Requires-Dist: aiohttp-retry ~=2.8
28
28
  Requires-Dist: click ~=8.1
29
- Requires-Dist: kfp >=1.8.14,~=1.8
29
+ Requires-Dist: kfp ~=1.8
30
30
  Requires-Dist: nest-asyncio ~=1.0
31
31
  Requires-Dist: ipython ~=8.10
32
32
  Requires-Dist: nuclio-jupyter ~=0.9.15
@@ -37,7 +37,7 @@ Requires-Dist: pyyaml ~=5.1
37
37
  Requires-Dist: requests ~=2.31
38
38
  Requires-Dist: tabulate ~=0.8.6
39
39
  Requires-Dist: v3io ~=0.6.4
40
- Requires-Dist: pydantic <1.10.15,>=1.10.8
40
+ Requires-Dist: pydantic >=1.10.8,~=1.10
41
41
  Requires-Dist: mergedeep ~=1.3
42
42
  Requires-Dist: v3io-frames ~=0.10.12
43
43
  Requires-Dist: semver ~=3.0
@@ -82,14 +82,13 @@ Requires-Dist: uvicorn ~=0.27.1 ; extra == 'api'
82
82
  Requires-Dist: dask-kubernetes ~=0.11.0 ; extra == 'api'
83
83
  Requires-Dist: apscheduler <4,>=3.10.3 ; extra == 'api'
84
84
  Requires-Dist: objgraph ~=3.6 ; extra == 'api'
85
- Requires-Dist: igz-mgmt ==0.1.0 ; extra == 'api'
85
+ Requires-Dist: igz-mgmt ~=0.1.0 ; extra == 'api'
86
86
  Requires-Dist: humanfriendly ~=10.0 ; extra == 'api'
87
87
  Requires-Dist: fastapi ~=0.110.0 ; extra == 'api'
88
88
  Requires-Dist: sqlalchemy ~=1.4 ; extra == 'api'
89
89
  Requires-Dist: pymysql ~=1.0 ; extra == 'api'
90
90
  Requires-Dist: alembic ~=1.9 ; extra == 'api'
91
91
  Requires-Dist: timelength ~=1.1 ; extra == 'api'
92
- Requires-Dist: memray ~=1.12 ; extra == 'api'
93
92
  Provides-Extra: azure-blob-storage
94
93
  Requires-Dist: msrest ~=0.6.21 ; extra == 'azure-blob-storage'
95
94
  Requires-Dist: azure-core ~=1.24 ; extra == 'azure-blob-storage'
@@ -142,9 +141,8 @@ Requires-Dist: gcsfs ==2023.9.2 ; extra == 'complete-api'
142
141
  Requires-Dist: google-cloud-bigquery[bqstorage,pandas] ==3.14.1 ; extra == 'complete-api'
143
142
  Requires-Dist: graphviz ~=0.20.0 ; extra == 'complete-api'
144
143
  Requires-Dist: humanfriendly ~=10.0 ; extra == 'complete-api'
145
- Requires-Dist: igz-mgmt ==0.1.0 ; extra == 'complete-api'
144
+ Requires-Dist: igz-mgmt ~=0.1.0 ; extra == 'complete-api'
146
145
  Requires-Dist: kafka-python ~=2.0 ; extra == 'complete-api'
147
- Requires-Dist: memray ~=1.12 ; extra == 'complete-api'
148
146
  Requires-Dist: mlflow ~=2.8 ; extra == 'complete-api'
149
147
  Requires-Dist: msrest ~=0.6.21 ; extra == 'complete-api'
150
148
  Requires-Dist: objgraph ~=3.6 ; extra == 'complete-api'
@@ -1,14 +1,14 @@
1
1
  mlrun/__init__.py,sha256=o9dHUfVFADfsi6GnOPLr2OkfkHdPvOnA7rkoECen0-I,7248
2
2
  mlrun/__main__.py,sha256=zd-o0SkFH69HhIWKhqXnNURsrtpIcOJYYq50JfAxW7k,49234
3
- mlrun/config.py,sha256=4scerZD_BdeDIOXJw9xrXCmpSBHxEvuc7R9QZpAb5EA,63789
3
+ mlrun/config.py,sha256=lhZTNnF3OQvoQuxN75hPPkRkWMCcEvu2hlHLaFBz7Zg,62795
4
4
  mlrun/errors.py,sha256=YdUtkN3qJ6yrseNygmKxmSWOfQ_RdKBhRxwwyMlTQCM,7106
5
5
  mlrun/execution.py,sha256=tgp6PcujZvGhDDVzPNs32YH_JNzaxfSd25yeuLwmjzg,40880
6
6
  mlrun/features.py,sha256=UQQ2uh5Xh9XsMGiYBqh3bKgDhOHANjv1gQgWyId9qQE,15624
7
7
  mlrun/k8s_utils.py,sha256=-6egUEZNPhzOxJ2gFytubvQvCYU9nPPg5Yn0zsTK-NQ,7065
8
8
  mlrun/kfpops.py,sha256=VgvS_4DappCPHzV7057SbraBTbF2mn7zZ7iPAaks3KU,30493
9
- mlrun/lists.py,sha256=sEEfl_mw_oVUD1dfkvQZIkJ8q7LJKJPDrb5B_Dg85nY,8388
10
- mlrun/model.py,sha256=EbSxpwtNFkqodTwM_StcmDG5MFSG50ZuDMhqzIyzLmM,64896
11
- mlrun/render.py,sha256=pfAky9fTHRbINs93_Km_hEdVxmra9RA8BwqMepPbiuE,13451
9
+ mlrun/lists.py,sha256=JMc4Ch4wQxD_B9zbrE3JZwXD8cCYWLqHb1FQXWoaGzM,8310
10
+ mlrun/model.py,sha256=Ggz9D8YCHCFluJQe1aPiddK1Mr34LYREirqJLa2i7IU,63956
11
+ mlrun/render.py,sha256=_Jrtqw54AkvUZDWK5ORGUQWnGewREh_lQnUQWuCkTV4,13016
12
12
  mlrun/run.py,sha256=gyxYJqVCBsZxp0_HWAG5_lwi2_KPqcxy6un5ZLw_-2Q,42456
13
13
  mlrun/secrets.py,sha256=m7jM8fdjGLR-j9Vx-08eNmtOmlxFx9mTUBqBWtMSVQo,7782
14
14
  mlrun/api/schemas/__init__.py,sha256=ggWbnqhp7By5HNYYfRsZ4D4EdVvjLuz4qfNfR3Kq6M4,14219
@@ -72,6 +72,7 @@ mlrun/datastore/datastore_profile.py,sha256=vBpkAcnGiYUO09ihg_jPgPdpo2GVBrOOSHYW
72
72
  mlrun/datastore/dbfs_store.py,sha256=5IkxnFQXkW0fdx-ca5jjQnUdTsTfNdJzMvV31ZpDNrM,6634
73
73
  mlrun/datastore/filestore.py,sha256=cI_YvQqY5J3kEvdyPelfWofxKfBitoNHJvABBkpCGRc,3788
74
74
  mlrun/datastore/google_cloud_storage.py,sha256=zQlrZnYOFfwQNBw5J2ufoyCDMlbZKA9c9no7NvnylZ4,6038
75
+ mlrun/datastore/helpers.py,sha256=-bKveE9rteLd0hJd6OSMuMbfz09W_OXyu1G5O2ihZjs,622
75
76
  mlrun/datastore/inmem.py,sha256=6PAltUk7uyYlDgnsaJPOkg_P98iku1ys2e2wpAmPRkc,2779
76
77
  mlrun/datastore/redis.py,sha256=DDA1FsixfnzNwjVUU9MgVCKFo3X3tYvPDcREKyy9zS4,5517
77
78
  mlrun/datastore/s3.py,sha256=BCyVDznEsmU1M1HtRROdLo4HkLOy4fjEmgpNrTpsoW0,8030
@@ -81,14 +82,13 @@ mlrun/datastore/spark_utils.py,sha256=54rF64aC19ojUFCcwzsoBLQ-5Nmzs_KTQl9iEkK2hY
81
82
  mlrun/datastore/store_resources.py,sha256=dfMdFy2urilECtlwLJr5CSG12MA645b-NPYDnbr5s1A,6839
82
83
  mlrun/datastore/targets.py,sha256=EaeNzwHQHkMo7WRezgMeWWjTL1NmYwMx8F0RG1swb48,70159
83
84
  mlrun/datastore/utils.py,sha256=x4pm0gvpcNWSjxo99MOmwcd9I5HCwuzCh6IA4uiXlZs,7077
84
- mlrun/datastore/v3io.py,sha256=3UI22DQ1A4yQEpbMWAbopIzjqlL2k5bhmhg0Cjs3tEk,8039
85
+ mlrun/datastore/v3io.py,sha256=OyldGthdycuk7thQYHuCY-2XrGIef_14c2ReqgHh_7I,9188
85
86
  mlrun/datastore/wasbfs/__init__.py,sha256=s5Ul-0kAhYqFjKDR2X0O2vDGDbLQQduElb32Ev56Te4,1343
86
87
  mlrun/datastore/wasbfs/fs.py,sha256=MnSj7Q4OKA2L55ihCmUnj2t3GA3B77oLMdAw-yxvN9w,6151
87
88
  mlrun/db/__init__.py,sha256=WqJ4x8lqJ7ZoKbhEyFqkYADd9P6E3citckx9e9ZLcIU,1163
88
- mlrun/db/auth_utils.py,sha256=hpg8D2r82oN0BWabuWN04BTNZ7jYMAF242YSUpK7LFM,5211
89
89
  mlrun/db/base.py,sha256=Rg2TrcwvzN28vmoyhq8sSxNjiBS1EA6BAHr24fhcmNU,18221
90
90
  mlrun/db/factory.py,sha256=wTEKHEmdDkylM6IkTYvmEYVF8gn2HdjLoLoWICCyatI,2403
91
- mlrun/db/httpdb.py,sha256=FHrAwU7c4AQPCoXDIdkEkFfr8ViUS7XLJYIGrXrv_rs,157292
91
+ mlrun/db/httpdb.py,sha256=gE01kWA7IwZqO9QTVCJ-YeGAnuwlEvxt-9q14pUfNfI,156147
92
92
  mlrun/db/nopdb.py,sha256=rpZy5cpW-8--4OvMzlVoKNYjbhWJ3cn_z-JFwfuPqnI,14520
93
93
  mlrun/feature_store/__init__.py,sha256=n1F5m1svFW2chbE2dJdWzZJJiYS4E-y8PQsG9Q-F0lU,1584
94
94
  mlrun/feature_store/api.py,sha256=ehEwKlmE07pq1FUwh-ehA8Jm9LTkQofl5MQpEiMwVqM,49520
@@ -202,11 +202,11 @@ mlrun/model_monitoring/batch.py,sha256=l0FTSTt9frLgqeSS9jY8IU_0OwlvSevuZBWBgnBGH
202
202
  mlrun/model_monitoring/controller.py,sha256=Zh7XnGevQyJCU8uMT8MC6kD7cP55vUJaIjqbBvSyuvA,27608
203
203
  mlrun/model_monitoring/controller_handler.py,sha256=kG3sTjhKdsd9QcHkUlYcvw08yPsVQs9FyFdcKP9iaCo,977
204
204
  mlrun/model_monitoring/evidently_application.py,sha256=o9PsDsjyRfcbuC1X1gb2ww5nzWCsR_KheabtpxKZ5yY,4824
205
- mlrun/model_monitoring/features_drift_table.py,sha256=OEDb_YZm3cyzszzC4SDqi7ufwOS8Kl5tDY1ZG4XIdZ4,24436
206
- mlrun/model_monitoring/helpers.py,sha256=l8RCxOGBoScZSL4M-x4fIfpsfH7wjSQpqA_t-HVk0BI,7087
205
+ mlrun/model_monitoring/features_drift_table.py,sha256=2r51W4xQ8gNq3PXt73IfsYu4l4mjwD-dLfRVAvKplTE,24209
206
+ mlrun/model_monitoring/helpers.py,sha256=25aTNY3jINyzE6KZfcLaWD7bHRyUDzRF1li3xD59i4Q,6955
207
207
  mlrun/model_monitoring/model_endpoint.py,sha256=BBtxdY5ciormI_al4zshmIp0GN7hGhOCn-hLgpCXek0,3938
208
208
  mlrun/model_monitoring/prometheus.py,sha256=Z0UWmhQ-dpGGH31gCiGdfmhfj-RFRf1Tu1bYVe-k4jk,7605
209
- mlrun/model_monitoring/stream_processing.py,sha256=mUmF12byO8h5CPszEqrI0xFbOWiyCYr493r58EElWyQ,49150
209
+ mlrun/model_monitoring/stream_processing.py,sha256=BxdyIn65sv_IBDTAwoNOiNq8a6uks_pJUaY5z9-F4-Q,49203
210
210
  mlrun/model_monitoring/tracking_policy.py,sha256=Q6_p4y1ZcRHqs24c_1_4m9E1gYnaOm6pLCNGT22dWKM,5221
211
211
  mlrun/model_monitoring/writer.py,sha256=IWPzPenoAkfIxlvn0IdcdB19Nxqmg4mjbo3-RnYWw9A,8669
212
212
  mlrun/model_monitoring/stores/__init__.py,sha256=adU_G07jkD3JUT8__d0jAxs9nNomL7igKmd6uVM9L50,4525
@@ -239,8 +239,8 @@ mlrun/platforms/iguazio.py,sha256=eOO8CbeSD0ooUKp-hbXbRfzWo5OTP7QaBo6zh0BXTKc,19
239
239
  mlrun/platforms/other.py,sha256=z4pWqxXkVVuMLk-MbNb0Y_ZR5pmIsUm0R8vHnqpEnew,11852
240
240
  mlrun/projects/__init__.py,sha256=Lv5rfxyXJrw6WGOWJKhBz66M6t3_zsNMCfUD6waPwx4,1153
241
241
  mlrun/projects/operations.py,sha256=CJRGKEFhqKXlg0VOKhcfjOUVAmWHA9WwAFNiXtUqBhg,18550
242
- mlrun/projects/pipelines.py,sha256=FcKNsFtRUP1sOuSEp5Hk0_Qv4ZIKT9gWpatg6bSUCsI,41165
243
- mlrun/projects/project.py,sha256=U98H5DF0q17qZcQB4VqqPoEETFgh_VvV51DTHXQhsbA,153280
242
+ mlrun/projects/pipelines.py,sha256=D4Ny3znuThM2IO6drxJBqlHY_Ipaiiqf2xwETXzp08E,40481
243
+ mlrun/projects/project.py,sha256=ShoPjF2l3d_jp6nAWEfgxOwFUZoolMlr5RNSXGrOe1U,153100
244
244
  mlrun/runtimes/__init__.py,sha256=f5cdEg4raKNXQawJE-AuWzK6AqIsLfDODREeMnI2Ies,7062
245
245
  mlrun/runtimes/base.py,sha256=GTVqCR3sBqbyAlEBnDClThOf0EZUoAMzlEIFqjfoyLQ,36604
246
246
  mlrun/runtimes/constants.py,sha256=tB7nIlHob3yF0K9Uf9BUZ8yxjZNSzlzrd3K32K_vV7w,9550
@@ -252,7 +252,7 @@ mlrun/runtimes/generators.py,sha256=v28HdNgxdHvj888G1dTnUeQZz-D9iTO0hoGeZbCdiuQ,
252
252
  mlrun/runtimes/kubejob.py,sha256=UfSm7hiPLAtM0TfIE5nbBdSvrbsKWCZfvKP-SZhGyAk,12500
253
253
  mlrun/runtimes/local.py,sha256=depdJkbyas7V7SMXB1T6Y_jPXxTLEB1TL5HYzDxlcXI,21791
254
254
  mlrun/runtimes/nuclio.py,sha256=hwk4dUaZefI-Qbb4s289vQpt1h0nAucxf6eINzVI-d8,2908
255
- mlrun/runtimes/pod.py,sha256=loo1ysAbGslrHRhcRaFrw-ATNhuOBayEb0MCPi2EduY,56865
255
+ mlrun/runtimes/pod.py,sha256=CBhKyNC20zCaRShS5UkgMR_61w_DpzEtEQ03T8J7u0k,56780
256
256
  mlrun/runtimes/remotesparkjob.py,sha256=W7WqlPbyqE6FjOZ2EFeOzlL1jLGWAWe61jOH0Umy3F4,7334
257
257
  mlrun/runtimes/serving.py,sha256=bV4Io-8K30IZs69pdZTSICR3HkUAAc1kSKuBJOx_jc0,30331
258
258
  mlrun/runtimes/utils.py,sha256=mNVu3ejmfEV3d7-fCAiSaF5K-Jyz2ladc5HzqhsY0Cs,16025
@@ -282,14 +282,14 @@ mlrun/track/tracker_manager.py,sha256=ZZzXtgQ-t4ah64XeAHNCbZ2VMOK_0E0RHv0pIowynj
282
282
  mlrun/track/trackers/__init__.py,sha256=9xft8YjJnblwqt8f05htmOt_eDzVBVQN07RfY_SYLCs,569
283
283
  mlrun/track/trackers/mlflow_tracker.py,sha256=zqqnECpxk_CHDKzEIdjwL9QUjXfueOw7xlZU7buguKY,23282
284
284
  mlrun/utils/__init__.py,sha256=g2pbT3loDw0GWELOC_rBq1NojSMCFnWrD-TYcDgAZiI,826
285
- mlrun/utils/async_http.py,sha256=J3z4dzU1zk96k1sLDiGUIciBu3pdgivqh-GExFv-Fn8,11773
285
+ mlrun/utils/async_http.py,sha256=N-zkACdr2CZNwYyMaocSR6ZH90F_WV_ZRBVe9ssi-Ns,11141
286
286
  mlrun/utils/azure_vault.py,sha256=IbPAZh-7mp0j4PcCy1L079LuEA6ENrkWhKZvkD4lcTY,3455
287
287
  mlrun/utils/clones.py,sha256=QG2ka65-ysfrOaoziudEjJqGgAxJvFKZOXkiD9WZGN4,7386
288
288
  mlrun/utils/condition_evaluator.py,sha256=KFZC-apM7RU5TIlRszAzMFc0NqPj3W1rgP0Zv17Ud-A,1918
289
289
  mlrun/utils/db.py,sha256=fp9p2_z7XW3DhsceJEObWKh-e5zKjPiCM55kSGNkZD8,1658
290
290
  mlrun/utils/helpers.py,sha256=MRfvRQlxh9IITpYX68Sc8Lnej-SB5sWzIAy_7NhB44o,53692
291
291
  mlrun/utils/http.py,sha256=BLkPfCmXwFrpPrPXzipmDr9IhY3q5css7hovncXYs9g,8735
292
- mlrun/utils/logger.py,sha256=KZFzojroEshCu1kvtCsavJpdIY8vNA-QZxiBjehP9f4,7260
292
+ mlrun/utils/logger.py,sha256=3SS-9ON0ScnUJp1S6P_jmaTRXuwF6MhBF5a5Ij8s3IU,7158
293
293
  mlrun/utils/regex.py,sha256=Nd7xnDHU9PEOsse6rFwLNVgU4AaYCyuwMmQ9qgx2-Vw,4581
294
294
  mlrun/utils/singleton.py,sha256=UUTQiBTXyzmjeYzsvTUsSxqyLJHOtesqif9GNfZO9rc,883
295
295
  mlrun/utils/v3io_clients.py,sha256=HL7Hma-pxaQBXMKcEnWqGLCpFgykwnszlabzeOHC9-E,1319
@@ -304,11 +304,11 @@ mlrun/utils/notifications/notification/ipython.py,sha256=qrBmtECiRG6sZpCIVMg7RZc
304
304
  mlrun/utils/notifications/notification/slack.py,sha256=5JysqIpUYUZKXPSeeZtbl7qb2L9dj7p2NvnEBcEsZkA,3898
305
305
  mlrun/utils/notifications/notification/webhook.py,sha256=QHezCuN5uXkLcroAGxGrhGHaxAdUvkDLIsp27_Yrfd4,2390
306
306
  mlrun/utils/version/__init__.py,sha256=7kkrB7hEZ3cLXoWj1kPoDwo4MaswsI2JVOBpbKgPAgc,614
307
- mlrun/utils/version/version.json,sha256=rk9MvAyByMv8Nmgcq1QMFOkTa8NP0K-g4bSlN47hPAo,84
307
+ mlrun/utils/version/version.json,sha256=Q79aTfuKYcBACUpj4fKVBaUC3pT95vGtpP_TT5NG1Gs,88
308
308
  mlrun/utils/version/version.py,sha256=HMwseV8xjTQ__6T6yUWojx_z6yUj7Io7O4NcCCH_sz8,1970
309
- mlrun-1.6.3.dist-info/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
310
- mlrun-1.6.3.dist-info/METADATA,sha256=ca26nwhJusTY2TzpDN36YP2Bb0OqX8t5B1Zj-RPQM2A,18400
311
- mlrun-1.6.3.dist-info/WHEEL,sha256=GJ7t_kWBFywbagK5eo9IoUwLW6oyOeTKmQ-9iHFVNxQ,92
312
- mlrun-1.6.3.dist-info/entry_points.txt,sha256=1Owd16eAclD5pfRCoJpYC2ZJSyGNTtUr0nCELMioMmU,46
313
- mlrun-1.6.3.dist-info/top_level.txt,sha256=NObLzw3maSF9wVrgSeYBv-fgnHkAJ1kEkh12DLdd5KM,6
314
- mlrun-1.6.3.dist-info/RECORD,,
309
+ mlrun-1.6.3rc2.dist-info/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
310
+ mlrun-1.6.3rc2.dist-info/METADATA,sha256=8GklrxH8xnHFI2ds8LGLvb-EZTWtxT_i52FtA-SlbCA,18291
311
+ mlrun-1.6.3rc2.dist-info/WHEEL,sha256=GJ7t_kWBFywbagK5eo9IoUwLW6oyOeTKmQ-9iHFVNxQ,92
312
+ mlrun-1.6.3rc2.dist-info/entry_points.txt,sha256=1Owd16eAclD5pfRCoJpYC2ZJSyGNTtUr0nCELMioMmU,46
313
+ mlrun-1.6.3rc2.dist-info/top_level.txt,sha256=NObLzw3maSF9wVrgSeYBv-fgnHkAJ1kEkh12DLdd5KM,6
314
+ mlrun-1.6.3rc2.dist-info/RECORD,,
mlrun/db/auth_utils.py DELETED
@@ -1,152 +0,0 @@
1
- # Copyright 2024 Iguazio
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
- # http://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 or implied.
12
- # See the License for the specific language governing permissions and
13
- # limitations under the License.
14
-
15
- from abc import ABC, abstractmethod
16
- from datetime import datetime, timedelta
17
-
18
- import requests
19
-
20
- import mlrun.errors
21
- from mlrun.utils import logger
22
-
23
-
24
- class TokenProvider(ABC):
25
- @abstractmethod
26
- def get_token(self):
27
- pass
28
-
29
- @abstractmethod
30
- def is_iguazio_session(self):
31
- pass
32
-
33
-
34
- class StaticTokenProvider(TokenProvider):
35
- def __init__(self, token: str):
36
- self.token = token
37
-
38
- def get_token(self):
39
- return self.token
40
-
41
- def is_iguazio_session(self):
42
- return mlrun.platforms.iguazio.is_iguazio_session(self.token)
43
-
44
-
45
- class OAuthClientIDTokenProvider(TokenProvider):
46
- def __init__(
47
- self, token_endpoint: str, client_id: str, client_secret: str, timeout=5
48
- ):
49
- if not token_endpoint or not client_id or not client_secret:
50
- raise mlrun.errors.MLRunValueError(
51
- "Invalid client_id configuration for authentication. Must provide token endpoint, client-id and secret"
52
- )
53
- self.token_endpoint = token_endpoint
54
- self.client_id = client_id
55
- self.client_secret = client_secret
56
- self.timeout = timeout
57
-
58
- # Since we're only issuing POST requests, which are actually a disguised GET, then it's ok to allow retries
59
- # on them.
60
- self._session = mlrun.utils.HTTPSessionWithRetry(
61
- retry_on_post=True,
62
- verbose=True,
63
- )
64
-
65
- self._cleanup()
66
- self._refresh_token_if_needed()
67
-
68
- def get_token(self):
69
- self._refresh_token_if_needed()
70
- return self.token
71
-
72
- def is_iguazio_session(self):
73
- return False
74
-
75
- def _cleanup(self):
76
- self.token = self.token_expiry_time = self.token_refresh_time = None
77
-
78
- def _refresh_token_if_needed(self):
79
- now = datetime.now()
80
- if self.token:
81
- if self.token_refresh_time and now <= self.token_refresh_time:
82
- return self.token
83
-
84
- # We only cleanup if token was really expired - even if we fail in refreshing the token, we can still
85
- # use the existing one given that it's not expired.
86
- if now >= self.token_expiry_time:
87
- self._cleanup()
88
-
89
- self._issue_token_request()
90
- return self.token
91
-
92
- def _issue_token_request(self, raise_on_error=False):
93
- try:
94
- headers = {"Content-Type": "application/x-www-form-urlencoded"}
95
- request_body = {
96
- "grant_type": "client_credentials",
97
- "client_id": self.client_id,
98
- "client_secret": self.client_secret,
99
- }
100
- response = self._session.request(
101
- "POST",
102
- self.token_endpoint,
103
- timeout=self.timeout,
104
- headers=headers,
105
- data=request_body,
106
- )
107
- except requests.RequestException as exc:
108
- error = f"Retrieving token failed: {mlrun.errors.err_to_str(exc)}"
109
- if raise_on_error:
110
- raise mlrun.errors.MLRunRuntimeError(error) from exc
111
- else:
112
- logger.warning(error)
113
- return
114
-
115
- if not response.ok:
116
- error = "No error available"
117
- if response.content:
118
- try:
119
- data = response.json()
120
- error = data.get("error")
121
- except Exception:
122
- pass
123
- logger.warning(
124
- "Retrieving token failed", status=response.status_code, error=error
125
- )
126
- if raise_on_error:
127
- mlrun.errors.raise_for_status(response)
128
- return
129
-
130
- self._parse_response(response.json())
131
-
132
- def _parse_response(self, data: dict):
133
- # Response is described in https://datatracker.ietf.org/doc/html/rfc6749#section-4.4.3
134
- # According to spec, there isn't a refresh token - just the access token and its expiry time (in seconds).
135
- self.token = data.get("access_token")
136
- expires_in = data.get("expires_in")
137
- if not self.token or not expires_in:
138
- token_str = "****" if self.token else "missing"
139
- logger.warning(
140
- "Failed to parse token response", token=token_str, expires_in=expires_in
141
- )
142
- return
143
-
144
- now = datetime.now()
145
- self.token_expiry_time = now + timedelta(seconds=expires_in)
146
- self.token_refresh_time = now + timedelta(seconds=expires_in / 2)
147
- logger.info(
148
- "Successfully retrieved client-id token",
149
- expires_in=expires_in,
150
- expiry=str(self.token_expiry_time),
151
- refresh=str(self.token_refresh_time),
152
- )