mlrun 1.6.0rc35__py3-none-any.whl → 1.7.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.

Files changed (199) hide show
  1. mlrun/__main__.py +3 -3
  2. mlrun/api/schemas/__init__.py +1 -1
  3. mlrun/artifacts/base.py +11 -6
  4. mlrun/artifacts/dataset.py +2 -2
  5. mlrun/artifacts/model.py +30 -24
  6. mlrun/artifacts/plots.py +2 -2
  7. mlrun/common/db/sql_session.py +5 -3
  8. mlrun/common/helpers.py +1 -2
  9. mlrun/common/schemas/artifact.py +3 -3
  10. mlrun/common/schemas/auth.py +3 -3
  11. mlrun/common/schemas/background_task.py +1 -1
  12. mlrun/common/schemas/client_spec.py +1 -1
  13. mlrun/common/schemas/feature_store.py +16 -16
  14. mlrun/common/schemas/frontend_spec.py +7 -7
  15. mlrun/common/schemas/function.py +1 -1
  16. mlrun/common/schemas/hub.py +4 -9
  17. mlrun/common/schemas/memory_reports.py +2 -2
  18. mlrun/common/schemas/model_monitoring/grafana.py +4 -4
  19. mlrun/common/schemas/model_monitoring/model_endpoints.py +14 -15
  20. mlrun/common/schemas/notification.py +4 -4
  21. mlrun/common/schemas/object.py +2 -2
  22. mlrun/common/schemas/pipeline.py +1 -1
  23. mlrun/common/schemas/project.py +3 -3
  24. mlrun/common/schemas/runtime_resource.py +8 -12
  25. mlrun/common/schemas/schedule.py +3 -3
  26. mlrun/common/schemas/tag.py +1 -2
  27. mlrun/common/schemas/workflow.py +2 -2
  28. mlrun/config.py +8 -4
  29. mlrun/data_types/to_pandas.py +1 -3
  30. mlrun/datastore/base.py +0 -28
  31. mlrun/datastore/datastore_profile.py +9 -9
  32. mlrun/datastore/filestore.py +0 -1
  33. mlrun/datastore/google_cloud_storage.py +1 -1
  34. mlrun/datastore/sources.py +7 -11
  35. mlrun/datastore/spark_utils.py +1 -2
  36. mlrun/datastore/targets.py +31 -31
  37. mlrun/datastore/utils.py +4 -6
  38. mlrun/datastore/v3io.py +70 -46
  39. mlrun/db/base.py +22 -23
  40. mlrun/db/httpdb.py +34 -34
  41. mlrun/db/nopdb.py +19 -19
  42. mlrun/errors.py +1 -1
  43. mlrun/execution.py +4 -4
  44. mlrun/feature_store/api.py +20 -21
  45. mlrun/feature_store/common.py +1 -1
  46. mlrun/feature_store/feature_set.py +28 -32
  47. mlrun/feature_store/feature_vector.py +24 -27
  48. mlrun/feature_store/retrieval/base.py +7 -7
  49. mlrun/feature_store/retrieval/conversion.py +2 -4
  50. mlrun/feature_store/steps.py +7 -15
  51. mlrun/features.py +5 -7
  52. mlrun/frameworks/_common/artifacts_library.py +9 -9
  53. mlrun/frameworks/_common/mlrun_interface.py +5 -5
  54. mlrun/frameworks/_common/model_handler.py +48 -48
  55. mlrun/frameworks/_common/plan.py +2 -3
  56. mlrun/frameworks/_common/producer.py +3 -4
  57. mlrun/frameworks/_common/utils.py +5 -5
  58. mlrun/frameworks/_dl_common/loggers/logger.py +6 -7
  59. mlrun/frameworks/_dl_common/loggers/mlrun_logger.py +9 -9
  60. mlrun/frameworks/_dl_common/loggers/tensorboard_logger.py +16 -35
  61. mlrun/frameworks/_ml_common/artifacts_library.py +1 -2
  62. mlrun/frameworks/_ml_common/loggers/logger.py +3 -4
  63. mlrun/frameworks/_ml_common/loggers/mlrun_logger.py +4 -5
  64. mlrun/frameworks/_ml_common/model_handler.py +24 -24
  65. mlrun/frameworks/_ml_common/pkl_model_server.py +2 -2
  66. mlrun/frameworks/_ml_common/plan.py +1 -1
  67. mlrun/frameworks/_ml_common/plans/calibration_curve_plan.py +2 -3
  68. mlrun/frameworks/_ml_common/plans/confusion_matrix_plan.py +2 -3
  69. mlrun/frameworks/_ml_common/plans/dataset_plan.py +3 -3
  70. mlrun/frameworks/_ml_common/plans/feature_importance_plan.py +3 -3
  71. mlrun/frameworks/_ml_common/plans/roc_curve_plan.py +4 -4
  72. mlrun/frameworks/_ml_common/utils.py +4 -4
  73. mlrun/frameworks/auto_mlrun/auto_mlrun.py +7 -7
  74. mlrun/frameworks/huggingface/model_server.py +4 -4
  75. mlrun/frameworks/lgbm/__init__.py +32 -32
  76. mlrun/frameworks/lgbm/callbacks/logging_callback.py +4 -5
  77. mlrun/frameworks/lgbm/callbacks/mlrun_logging_callback.py +4 -5
  78. mlrun/frameworks/lgbm/mlrun_interfaces/booster_mlrun_interface.py +1 -3
  79. mlrun/frameworks/lgbm/mlrun_interfaces/mlrun_interface.py +6 -6
  80. mlrun/frameworks/lgbm/model_handler.py +9 -9
  81. mlrun/frameworks/lgbm/model_server.py +6 -6
  82. mlrun/frameworks/lgbm/utils.py +5 -5
  83. mlrun/frameworks/onnx/dataset.py +8 -8
  84. mlrun/frameworks/onnx/mlrun_interface.py +3 -3
  85. mlrun/frameworks/onnx/model_handler.py +6 -6
  86. mlrun/frameworks/onnx/model_server.py +7 -7
  87. mlrun/frameworks/parallel_coordinates.py +2 -2
  88. mlrun/frameworks/pytorch/__init__.py +16 -16
  89. mlrun/frameworks/pytorch/callbacks/callback.py +4 -5
  90. mlrun/frameworks/pytorch/callbacks/logging_callback.py +17 -17
  91. mlrun/frameworks/pytorch/callbacks/mlrun_logging_callback.py +11 -11
  92. mlrun/frameworks/pytorch/callbacks/tensorboard_logging_callback.py +23 -29
  93. mlrun/frameworks/pytorch/callbacks_handler.py +38 -38
  94. mlrun/frameworks/pytorch/mlrun_interface.py +20 -20
  95. mlrun/frameworks/pytorch/model_handler.py +17 -17
  96. mlrun/frameworks/pytorch/model_server.py +7 -7
  97. mlrun/frameworks/sklearn/__init__.py +12 -12
  98. mlrun/frameworks/sklearn/estimator.py +4 -4
  99. mlrun/frameworks/sklearn/metrics_library.py +14 -14
  100. mlrun/frameworks/sklearn/mlrun_interface.py +3 -6
  101. mlrun/frameworks/sklearn/model_handler.py +2 -2
  102. mlrun/frameworks/tf_keras/__init__.py +5 -5
  103. mlrun/frameworks/tf_keras/callbacks/logging_callback.py +14 -14
  104. mlrun/frameworks/tf_keras/callbacks/mlrun_logging_callback.py +11 -11
  105. mlrun/frameworks/tf_keras/callbacks/tensorboard_logging_callback.py +19 -23
  106. mlrun/frameworks/tf_keras/mlrun_interface.py +7 -9
  107. mlrun/frameworks/tf_keras/model_handler.py +14 -14
  108. mlrun/frameworks/tf_keras/model_server.py +6 -6
  109. mlrun/frameworks/xgboost/__init__.py +12 -12
  110. mlrun/frameworks/xgboost/model_handler.py +6 -6
  111. mlrun/k8s_utils.py +4 -5
  112. mlrun/kfpops.py +2 -2
  113. mlrun/launcher/base.py +10 -10
  114. mlrun/launcher/local.py +8 -8
  115. mlrun/launcher/remote.py +7 -7
  116. mlrun/lists.py +3 -4
  117. mlrun/model.py +205 -55
  118. mlrun/model_monitoring/api.py +21 -24
  119. mlrun/model_monitoring/application.py +4 -4
  120. mlrun/model_monitoring/batch.py +17 -17
  121. mlrun/model_monitoring/controller.py +2 -1
  122. mlrun/model_monitoring/features_drift_table.py +44 -31
  123. mlrun/model_monitoring/prometheus.py +1 -4
  124. mlrun/model_monitoring/stores/kv_model_endpoint_store.py +11 -13
  125. mlrun/model_monitoring/stores/model_endpoint_store.py +9 -11
  126. mlrun/model_monitoring/stores/models/__init__.py +2 -2
  127. mlrun/model_monitoring/stores/sql_model_endpoint_store.py +11 -13
  128. mlrun/model_monitoring/stream_processing.py +16 -34
  129. mlrun/model_monitoring/tracking_policy.py +2 -1
  130. mlrun/package/__init__.py +6 -6
  131. mlrun/package/context_handler.py +5 -5
  132. mlrun/package/packager.py +7 -7
  133. mlrun/package/packagers/default_packager.py +6 -6
  134. mlrun/package/packagers/numpy_packagers.py +15 -15
  135. mlrun/package/packagers/pandas_packagers.py +5 -5
  136. mlrun/package/packagers/python_standard_library_packagers.py +10 -10
  137. mlrun/package/packagers_manager.py +18 -23
  138. mlrun/package/utils/_formatter.py +4 -4
  139. mlrun/package/utils/_pickler.py +2 -2
  140. mlrun/package/utils/_supported_format.py +4 -4
  141. mlrun/package/utils/log_hint_utils.py +2 -2
  142. mlrun/package/utils/type_hint_utils.py +4 -9
  143. mlrun/platforms/other.py +1 -2
  144. mlrun/projects/operations.py +5 -5
  145. mlrun/projects/pipelines.py +9 -9
  146. mlrun/projects/project.py +58 -46
  147. mlrun/render.py +1 -1
  148. mlrun/run.py +9 -9
  149. mlrun/runtimes/__init__.py +7 -4
  150. mlrun/runtimes/base.py +20 -23
  151. mlrun/runtimes/constants.py +5 -5
  152. mlrun/runtimes/daskjob.py +8 -8
  153. mlrun/runtimes/databricks_job/databricks_cancel_task.py +1 -1
  154. mlrun/runtimes/databricks_job/databricks_runtime.py +7 -7
  155. mlrun/runtimes/function_reference.py +1 -1
  156. mlrun/runtimes/local.py +1 -1
  157. mlrun/runtimes/mpijob/abstract.py +1 -2
  158. mlrun/runtimes/nuclio/__init__.py +20 -0
  159. mlrun/runtimes/{function.py → nuclio/function.py} +15 -16
  160. mlrun/runtimes/{nuclio.py → nuclio/nuclio.py} +6 -6
  161. mlrun/runtimes/{serving.py → nuclio/serving.py} +13 -12
  162. mlrun/runtimes/pod.py +95 -48
  163. mlrun/runtimes/remotesparkjob.py +1 -1
  164. mlrun/runtimes/sparkjob/spark3job.py +50 -33
  165. mlrun/runtimes/utils.py +1 -2
  166. mlrun/secrets.py +3 -3
  167. mlrun/serving/remote.py +0 -4
  168. mlrun/serving/routers.py +6 -6
  169. mlrun/serving/server.py +4 -4
  170. mlrun/serving/states.py +29 -0
  171. mlrun/serving/utils.py +3 -3
  172. mlrun/serving/v1_serving.py +6 -7
  173. mlrun/serving/v2_serving.py +50 -8
  174. mlrun/track/tracker_manager.py +3 -3
  175. mlrun/track/trackers/mlflow_tracker.py +1 -2
  176. mlrun/utils/async_http.py +5 -7
  177. mlrun/utils/azure_vault.py +1 -1
  178. mlrun/utils/clones.py +1 -2
  179. mlrun/utils/condition_evaluator.py +3 -3
  180. mlrun/utils/db.py +3 -3
  181. mlrun/utils/helpers.py +37 -119
  182. mlrun/utils/http.py +1 -4
  183. mlrun/utils/logger.py +49 -14
  184. mlrun/utils/notifications/notification/__init__.py +3 -3
  185. mlrun/utils/notifications/notification/base.py +2 -2
  186. mlrun/utils/notifications/notification/ipython.py +1 -1
  187. mlrun/utils/notifications/notification_pusher.py +8 -14
  188. mlrun/utils/retryer.py +207 -0
  189. mlrun/utils/singleton.py +1 -1
  190. mlrun/utils/v3io_clients.py +2 -3
  191. mlrun/utils/version/version.json +2 -2
  192. mlrun/utils/version/version.py +2 -6
  193. {mlrun-1.6.0rc35.dist-info → mlrun-1.7.0rc2.dist-info}/METADATA +9 -9
  194. mlrun-1.7.0rc2.dist-info/RECORD +315 -0
  195. mlrun-1.6.0rc35.dist-info/RECORD +0 -313
  196. {mlrun-1.6.0rc35.dist-info → mlrun-1.7.0rc2.dist-info}/LICENSE +0 -0
  197. {mlrun-1.6.0rc35.dist-info → mlrun-1.7.0rc2.dist-info}/WHEEL +0 -0
  198. {mlrun-1.6.0rc35.dist-info → mlrun-1.7.0rc2.dist-info}/entry_points.txt +0 -0
  199. {mlrun-1.6.0rc35.dist-info → mlrun-1.7.0rc2.dist-info}/top_level.txt +0 -0
mlrun/launcher/local.py CHANGED
@@ -13,7 +13,7 @@
13
13
  # limitations under the License.
14
14
  import os
15
15
  import pathlib
16
- from typing import Callable, Dict, List, Optional, Union
16
+ from typing import Callable, Optional, Union
17
17
 
18
18
  import mlrun.common.schemas.schedule
19
19
  import mlrun.errors
@@ -50,7 +50,7 @@ class ClientLocalLauncher(launcher.ClientBaseLauncher):
50
50
  name: Optional[str] = "",
51
51
  project: Optional[str] = "",
52
52
  params: Optional[dict] = None,
53
- inputs: Optional[Dict[str, str]] = None,
53
+ inputs: Optional[dict[str, str]] = None,
54
54
  out_path: Optional[str] = "",
55
55
  workdir: Optional[str] = "",
56
56
  artifact_path: Optional[str] = "",
@@ -58,16 +58,16 @@ class ClientLocalLauncher(launcher.ClientBaseLauncher):
58
58
  schedule: Optional[
59
59
  Union[str, mlrun.common.schemas.schedule.ScheduleCronTrigger]
60
60
  ] = None,
61
- hyperparams: Dict[str, list] = None,
61
+ hyperparams: dict[str, list] = None,
62
62
  hyper_param_options: Optional[mlrun.model.HyperParamOptions] = None,
63
63
  verbose: Optional[bool] = None,
64
64
  scrape_metrics: Optional[bool] = None,
65
65
  local_code_path: Optional[str] = None,
66
66
  auto_build: Optional[bool] = None,
67
- param_file_secrets: Optional[Dict[str, str]] = None,
68
- notifications: Optional[List[mlrun.model.Notification]] = None,
69
- returns: Optional[List[Union[str, Dict[str, str]]]] = None,
70
- state_thresholds: Optional[Dict[str, int]] = None,
67
+ param_file_secrets: Optional[dict[str, str]] = None,
68
+ notifications: Optional[list[mlrun.model.Notification]] = None,
69
+ returns: Optional[list[Union[str, dict[str, str]]]] = None,
70
+ state_thresholds: Optional[dict[str, int]] = None,
71
71
  ) -> "mlrun.run.RunObject":
72
72
  # do not allow local function to be scheduled
73
73
  if self._is_run_local and schedule is not None:
@@ -247,7 +247,7 @@ class ClientLocalLauncher(launcher.ClientBaseLauncher):
247
247
  return fn
248
248
 
249
249
  @staticmethod
250
- def _resolve_local_code_path(local_code_path: str) -> (str, List[str]):
250
+ def _resolve_local_code_path(local_code_path: str) -> (str, list[str]):
251
251
  command = None
252
252
  args = []
253
253
  if local_code_path:
mlrun/launcher/remote.py CHANGED
@@ -12,7 +12,7 @@
12
12
  # See the License for the specific language governing permissions and
13
13
  # limitations under the License.
14
14
  import os
15
- from typing import Dict, List, Optional, Union
15
+ from typing import Optional, Union
16
16
 
17
17
  import pandas as pd
18
18
  import requests
@@ -40,7 +40,7 @@ class ClientRemoteLauncher(launcher.ClientBaseLauncher):
40
40
  name: Optional[str] = "",
41
41
  project: Optional[str] = "",
42
42
  params: Optional[dict] = None,
43
- inputs: Optional[Dict[str, str]] = None,
43
+ inputs: Optional[dict[str, str]] = None,
44
44
  out_path: Optional[str] = "",
45
45
  workdir: Optional[str] = "",
46
46
  artifact_path: Optional[str] = "",
@@ -48,16 +48,16 @@ class ClientRemoteLauncher(launcher.ClientBaseLauncher):
48
48
  schedule: Optional[
49
49
  Union[str, mlrun.common.schemas.schedule.ScheduleCronTrigger]
50
50
  ] = None,
51
- hyperparams: Dict[str, list] = None,
51
+ hyperparams: dict[str, list] = None,
52
52
  hyper_param_options: Optional[mlrun.model.HyperParamOptions] = None,
53
53
  verbose: Optional[bool] = None,
54
54
  scrape_metrics: Optional[bool] = None,
55
55
  local_code_path: Optional[str] = None,
56
56
  auto_build: Optional[bool] = None,
57
- param_file_secrets: Optional[Dict[str, str]] = None,
58
- notifications: Optional[List[mlrun.model.Notification]] = None,
59
- returns: Optional[List[Union[str, Dict[str, str]]]] = None,
60
- state_thresholds: Optional[Dict[str, int]] = None,
57
+ param_file_secrets: Optional[dict[str, str]] = None,
58
+ notifications: Optional[list[mlrun.model.Notification]] = None,
59
+ returns: Optional[list[Union[str, dict[str, str]]]] = None,
60
+ state_thresholds: Optional[dict[str, int]] = None,
61
61
  ) -> "mlrun.run.RunObject":
62
62
  self.enrich_runtime(runtime, project)
63
63
  run = self._create_run_object(task)
mlrun/lists.py CHANGED
@@ -12,7 +12,6 @@
12
12
  # See the License for the specific language governing permissions and
13
13
  # limitations under the License.
14
14
  from copy import copy
15
- from typing import List
16
15
 
17
16
  import pandas as pd
18
17
 
@@ -119,7 +118,7 @@ class RunList(list):
119
118
  if not display:
120
119
  return html
121
120
 
122
- def to_objects(self) -> List["mlrun.RunObject"]:
121
+ def to_objects(self) -> list["mlrun.RunObject"]:
123
122
  """Return a list of Run Objects"""
124
123
  return [mlrun.RunObject.from_dict(run) for run in self]
125
124
 
@@ -215,11 +214,11 @@ class ArtifactList(list):
215
214
  if not display:
216
215
  return html
217
216
 
218
- def to_objects(self) -> List[Artifact]:
217
+ def to_objects(self) -> list[Artifact]:
219
218
  """return as a list of artifact objects"""
220
219
  return [dict_to_artifact(artifact) for artifact in self]
221
220
 
222
- def dataitems(self) -> List["mlrun.DataItem"]:
221
+ def dataitems(self) -> list["mlrun.DataItem"]:
223
222
  """return as a list of DataItem objects"""
224
223
  dataitems = []
225
224
  for item in self:
mlrun/model.py CHANGED
@@ -22,7 +22,7 @@ from collections import OrderedDict
22
22
  from copy import deepcopy
23
23
  from datetime import datetime
24
24
  from os import environ
25
- from typing import Any, Dict, List, Optional, Tuple, Union
25
+ from typing import Any, Optional, Union
26
26
 
27
27
  import pydantic.error_wrappers
28
28
 
@@ -44,6 +44,15 @@ RUN_ID_PLACE_HOLDER = "{run_id}" # IMPORTANT: shouldn't be changed.
44
44
 
45
45
  class ModelObj:
46
46
  _dict_fields = []
47
+ # Bellow attributes are used in to_dict method
48
+ # Fields to strip from the object by default if strip=True
49
+ _default_fields_to_strip = []
50
+ # Fields that will be serialized by the object's _serialize_field method
51
+ _fields_to_serialize = []
52
+ # Fields that will be enriched by the object's _enrich_field method
53
+ _fields_to_enrich = []
54
+ # Fields that will be ignored by the object's _is_valid_field_value_for_serialization method
55
+ _fields_to_skip_validation = []
47
56
 
48
57
  @staticmethod
49
58
  def _verify_list(param, name):
@@ -62,26 +71,145 @@ class ModelObj:
62
71
  return new_type.from_dict(param)
63
72
  return param
64
73
 
65
- def to_dict(self, fields=None, exclude=None):
66
- """convert the object to a python dictionary
74
+ def to_dict(
75
+ self, fields: list = None, exclude: list = None, strip: bool = False
76
+ ) -> dict:
77
+ """
78
+ Convert the object to a dict
79
+
80
+ :param fields: A list of fields to include in the dictionary. If not provided, the default value is taken
81
+ from `self._dict_fields` or from the object __init__ params.
82
+ :param exclude: A list of fields to exclude from the dictionary.
83
+ :param strip: If True, the object's `_default_fields_to_strip` attribute is appended to the exclude list.
84
+ Strip purpose is to remove fields that are context / environment specific and not required for actually
85
+ define the object.
67
86
 
68
- :param fields: list of fields to include in the dict
69
- :param exclude: list of fields to exclude from the dict
87
+ :return: A dictionary representation of the object.
70
88
  """
71
89
  struct = {}
72
- fields = fields or self._dict_fields
73
- if not fields:
74
- fields = list(inspect.signature(self.__init__).parameters.keys())
75
- for t in fields:
76
- if not exclude or t not in exclude:
77
- val = getattr(self, t, None)
78
- if val is not None and not (isinstance(val, dict) and not val):
79
- if hasattr(val, "to_dict"):
80
- val = val.to_dict()
81
- if val:
82
- struct[t] = val
83
- else:
84
- struct[t] = val
90
+
91
+ fields = self._resolve_initial_to_dict_fields(fields)
92
+ fields_to_exclude = exclude or []
93
+ if strip:
94
+ fields_to_exclude += self._default_fields_to_strip
95
+
96
+ # fields_to_save is built from the fields list minus the fields to exclude minus the fields that requires
97
+ # serialization and enrichment (because they will be added later to the struct)
98
+ fields_to_save = (
99
+ set(fields)
100
+ - set(fields_to_exclude)
101
+ - set(self._fields_to_serialize)
102
+ - set(self._fields_to_enrich)
103
+ )
104
+
105
+ # Iterating over the fields to save and adding them to the struct
106
+ for field_name in fields_to_save:
107
+ field_value = getattr(self, field_name, None)
108
+ if self._is_valid_field_value_for_serialization(
109
+ field_name, field_value, strip
110
+ ):
111
+ # If the field value has attribute to_dict, we call it.
112
+ # If one of the attributes is a third party object that has to_dict method (such as k8s objects), then
113
+ # add it to the object's _fields_to_serialize attribute and handle it in the _serialize_field method.
114
+ if hasattr(field_value, "to_dict"):
115
+ field_value = field_value.to_dict(strip=strip)
116
+ if self._is_valid_field_value_for_serialization(
117
+ field_name, field_value, strip
118
+ ):
119
+ struct[field_name] = field_value
120
+ else:
121
+ struct[field_name] = field_value
122
+
123
+ # Subtracting the fields_to_exclude from the fields_to_serialize because if we want to exclude a field there
124
+ # is no need to serialize it.
125
+ fields_to_serialize = list(
126
+ set(self._fields_to_serialize) - set(fields_to_exclude)
127
+ )
128
+ self._resolve_field_value_by_method(
129
+ struct, self._serialize_field, fields_to_serialize, strip
130
+ )
131
+
132
+ # Subtracting the fields_to_exclude from the fields_to_enrich because if we want to exclude a field there
133
+ # is no need to enrich it.
134
+ fields_to_enrich = list(set(self._fields_to_enrich) - set(fields_to_exclude))
135
+ self._resolve_field_value_by_method(
136
+ struct, self._enrich_field, fields_to_enrich, strip
137
+ )
138
+
139
+ self._apply_enrichment_before_to_dict_completion(struct, strip=strip)
140
+ return struct
141
+
142
+ def _resolve_initial_to_dict_fields(self, fields: list = None) -> list:
143
+ """
144
+ Resolve fields to be used in to_dict method.
145
+ If fields is None, use `_dict_fields` attribute of the object.
146
+ If fields is None and `_dict_fields` is empty, use the object's __init__ parameters.
147
+ :param fields: List of fields to iterate over.
148
+
149
+ :return: List of fields to iterate over.
150
+ """
151
+ return (
152
+ fields
153
+ or self._dict_fields
154
+ or list(inspect.signature(self.__init__).parameters.keys())
155
+ )
156
+
157
+ def _is_valid_field_value_for_serialization(
158
+ self, field_name: str, field_value: str, strip: bool = False
159
+ ) -> bool:
160
+ """
161
+ Check if the field value is valid for serialization.
162
+ If field name is in `_fields_to_skip_validation` attribute, skip validation and return True.
163
+ If strip is False skip validation and return True.
164
+ If field value is None or empty dict/list, then no need to store it.
165
+ :param field_name: Field name.
166
+ :param field_value: Field value.
167
+
168
+ :return: True if the field value is valid for serialization, False otherwise.
169
+ """
170
+ if field_name in self._fields_to_skip_validation:
171
+ return True
172
+ # TODO: remove when Runtime initialization will be refactored and enrichment will be moved to BE
173
+ # if not strip:
174
+ # return True
175
+
176
+ return field_value is not None and not (
177
+ (isinstance(field_value, dict) or isinstance(field_value, list))
178
+ and not field_value
179
+ )
180
+
181
+ def _resolve_field_value_by_method(
182
+ self,
183
+ struct: dict,
184
+ method: typing.Callable,
185
+ fields: typing.Union[list, set] = None,
186
+ strip: bool = False,
187
+ ) -> dict:
188
+ for field_name in fields:
189
+ field_value = method(struct=struct, field_name=field_name, strip=strip)
190
+ if self._is_valid_field_value_for_serialization(
191
+ field_name, field_value, strip
192
+ ):
193
+ struct[field_name] = field_value
194
+ return struct
195
+
196
+ def _serialize_field(
197
+ self, struct: dict, field_name: str = None, strip: bool = False
198
+ ) -> typing.Any:
199
+ # We pull the field from self and not from struct because it was excluded from the struct when looping over
200
+ # the fields to save.
201
+ return getattr(self, field_name, None)
202
+
203
+ def _enrich_field(
204
+ self, struct: dict, field_name: str = None, strip: bool = False
205
+ ) -> typing.Any:
206
+ # We first try to pull from struct because the field might have been already serialized and if not,
207
+ # we pull from self
208
+ return struct.get(field_name, None) or getattr(self, field_name, None)
209
+
210
+ def _apply_enrichment_before_to_dict_completion(
211
+ self, struct: dict, strip: bool = False
212
+ ) -> dict:
85
213
  return struct
86
214
 
87
215
  @classmethod
@@ -110,19 +238,21 @@ class ModelObj:
110
238
 
111
239
  return new_obj
112
240
 
113
- def to_yaml(self, exclude=None) -> str:
241
+ def to_yaml(self, exclude=None, strip: bool = False) -> str:
114
242
  """convert the object to yaml
115
243
 
116
244
  :param exclude: list of fields to exclude from the yaml
245
+ :param strip: if True, strip fields that are not required for actually define the object
117
246
  """
118
- return dict_to_yaml(self.to_dict(exclude=exclude))
247
+ return dict_to_yaml(self.to_dict(exclude=exclude, strip=strip))
119
248
 
120
- def to_json(self, exclude=None):
249
+ def to_json(self, exclude=None, strip: bool = False):
121
250
  """convert the object to json
122
251
 
123
252
  :param exclude: list of fields to exclude from the json
253
+ :param strip: if True, strip fields that are not required for actually define the object
124
254
  """
125
- return dict_to_json(self.to_dict(exclude=exclude))
255
+ return dict_to_json(self.to_dict(exclude=exclude, strip=strip))
126
256
 
127
257
  def to_str(self):
128
258
  """convert the object to string (with dict layout)"""
@@ -174,8 +304,8 @@ class ObjectDict:
174
304
  self._children[key] = child
175
305
  return child
176
306
 
177
- def to_dict(self):
178
- return {k: v.to_dict() for k, v in self._children.items()}
307
+ def to_dict(self, strip: bool = False):
308
+ return {k: v.to_dict(strip=strip) for k, v in self._children.items()}
179
309
 
180
310
  @classmethod
181
311
  def from_dict(cls, classes_map: dict, children=None, default_kind=""):
@@ -257,9 +387,9 @@ class ObjectList:
257
387
  def __delitem__(self, key):
258
388
  del self._children[key]
259
389
 
260
- def to_dict(self):
390
+ def to_dict(self, strip: bool = False):
261
391
  # method used by ModelObj class to serialize the object to nested dict
262
- return [t.to_dict() for t in self._children.values()]
392
+ return [t.to_dict(strip=strip) for t in self._children.values()]
263
393
 
264
394
  @classmethod
265
395
  def from_list(cls, child_class, children=None):
@@ -304,6 +434,18 @@ class Credentials(ModelObj):
304
434
 
305
435
 
306
436
  class BaseMetadata(ModelObj):
437
+ _default_fields_to_strip = ModelObj._default_fields_to_strip + [
438
+ "hash",
439
+ # Below are environment specific fields, no need to keep when stripping
440
+ "namespace",
441
+ "project",
442
+ "labels",
443
+ "annotations",
444
+ "credentials",
445
+ # Below are state fields, no need to keep when stripping
446
+ "updated",
447
+ ]
448
+
307
449
  def __init__(
308
450
  self,
309
451
  name=None,
@@ -443,7 +585,7 @@ class ImageBuilder(ModelObj):
443
585
 
444
586
  def with_commands(
445
587
  self,
446
- commands: List[str],
588
+ commands: list[str],
447
589
  overwrite: bool = False,
448
590
  ):
449
591
  """add commands to build spec.
@@ -470,7 +612,7 @@ class ImageBuilder(ModelObj):
470
612
 
471
613
  def with_requirements(
472
614
  self,
473
- requirements: Optional[List[str]] = None,
615
+ requirements: Optional[list[str]] = None,
474
616
  requirements_file: str = "",
475
617
  overwrite: bool = False,
476
618
  ):
@@ -503,7 +645,7 @@ class ImageBuilder(ModelObj):
503
645
 
504
646
  # handle the requirements_file argument
505
647
  if requirements_file:
506
- with open(requirements_file, "r") as fp:
648
+ with open(requirements_file) as fp:
507
649
  requirements_to_resolve.extend(fp.read().splitlines())
508
650
 
509
651
  # handle the requirements argument
@@ -582,7 +724,7 @@ class Notification(ModelObj):
582
724
  )
583
725
 
584
726
  @staticmethod
585
- def validate_notification_uniqueness(notifications: List["Notification"]):
727
+ def validate_notification_uniqueness(notifications: list["Notification"]):
586
728
  """Validate that all notifications in the list are unique by name"""
587
729
  names = [notification.name for notification in notifications]
588
730
  if len(names) != len(set(names)):
@@ -686,6 +828,10 @@ class HyperParamOptions(ModelObj):
686
828
  class RunSpec(ModelObj):
687
829
  """Run specification"""
688
830
 
831
+ _fields_to_serialize = ModelObj._fields_to_serialize + [
832
+ "handler",
833
+ ]
834
+
689
835
  def __init__(
690
836
  self,
691
837
  parameters=None,
@@ -746,18 +892,22 @@ class RunSpec(ModelObj):
746
892
  self._notifications = notifications or []
747
893
  self.state_thresholds = state_thresholds or {}
748
894
 
749
- def to_dict(self, fields=None, exclude=None):
750
- struct = super().to_dict(fields, exclude=["handler"])
751
- if self.handler and isinstance(self.handler, str):
752
- struct["handler"] = self.handler
753
- return struct
895
+ def _serialize_field(
896
+ self, struct: dict, field_name: str = None, strip: bool = False
897
+ ) -> Optional[str]:
898
+ # We pull the field from self and not from struct because it was excluded from the struct
899
+ if field_name == "handler":
900
+ if self.handler and isinstance(self.handler, str):
901
+ return self.handler
902
+ return None
903
+ return super()._serialize_field(struct, field_name, strip)
754
904
 
755
905
  def is_hyper_job(self):
756
906
  param_file = self.param_file or self.hyper_param_options.param_file
757
907
  return param_file or self.hyperparams
758
908
 
759
909
  @property
760
- def inputs(self) -> Dict[str, str]:
910
+ def inputs(self) -> dict[str, str]:
761
911
  """
762
912
  Get the inputs dictionary. A dictionary of parameter names as keys and paths as values.
763
913
 
@@ -766,7 +916,7 @@ class RunSpec(ModelObj):
766
916
  return self._inputs
767
917
 
768
918
  @inputs.setter
769
- def inputs(self, inputs: Dict[str, str]):
919
+ def inputs(self, inputs: dict[str, str]):
770
920
  """
771
921
  Set the given inputs in the spec. Inputs can include a type hint string in their keys following a colon, meaning
772
922
  following this structure: "<input key : type hint>".
@@ -789,7 +939,7 @@ class RunSpec(ModelObj):
789
939
  self._inputs = self._verify_dict(inputs, "inputs")
790
940
 
791
941
  @property
792
- def inputs_type_hints(self) -> Dict[str, str]:
942
+ def inputs_type_hints(self) -> dict[str, str]:
793
943
  """
794
944
  Get the input type hints. A dictionary of parameter names as keys and their type hints as values.
795
945
 
@@ -798,7 +948,7 @@ class RunSpec(ModelObj):
798
948
  return self._inputs_type_hints
799
949
 
800
950
  @inputs_type_hints.setter
801
- def inputs_type_hints(self, inputs_type_hints: Dict[str, str]):
951
+ def inputs_type_hints(self, inputs_type_hints: dict[str, str]):
802
952
  """
803
953
  Set the inputs type hints to parse during a run.
804
954
 
@@ -819,7 +969,7 @@ class RunSpec(ModelObj):
819
969
  return self._returns
820
970
 
821
971
  @returns.setter
822
- def returns(self, returns: List[Union[str, Dict[str, str]]]):
972
+ def returns(self, returns: list[Union[str, dict[str, str]]]):
823
973
  """
824
974
  Set the returns list to log the returning values at the end of a run.
825
975
 
@@ -853,7 +1003,7 @@ class RunSpec(ModelObj):
853
1003
  )
854
1004
 
855
1005
  @property
856
- def outputs(self) -> List[str]:
1006
+ def outputs(self) -> list[str]:
857
1007
  """
858
1008
  Get the expected outputs. The list is constructed from keys of both the `outputs` and `returns` properties.
859
1009
 
@@ -918,7 +1068,7 @@ class RunSpec(ModelObj):
918
1068
  return self._state_thresholds
919
1069
 
920
1070
  @state_thresholds.setter
921
- def state_thresholds(self, state_thresholds: Dict[str, str]):
1071
+ def state_thresholds(self, state_thresholds: dict[str, str]):
922
1072
  """
923
1073
  Set the dictionary of k8s resource states to thresholds time strings.
924
1074
  The state will be matched against the pod's status. The threshold should be a time string that conforms
@@ -970,8 +1120,8 @@ class RunSpec(ModelObj):
970
1120
 
971
1121
  @staticmethod
972
1122
  def join_outputs_and_returns(
973
- outputs: List[str], returns: List[Union[str, Dict[str, str]]]
974
- ) -> List[str]:
1123
+ outputs: list[str], returns: list[Union[str, dict[str, str]]]
1124
+ ) -> list[str]:
975
1125
  """
976
1126
  Get the outputs set in the spec. The outputs are constructed from both the 'outputs' and 'returns' properties
977
1127
  that were set by the user.
@@ -1002,7 +1152,7 @@ class RunSpec(ModelObj):
1002
1152
  return outputs
1003
1153
 
1004
1154
  @staticmethod
1005
- def _separate_type_hint_from_input_key(input_key: str) -> Tuple[str, str]:
1155
+ def _separate_type_hint_from_input_key(input_key: str) -> tuple[str, str]:
1006
1156
  """
1007
1157
  An input key in the `inputs` dictionary parameter of a task (or `Runtime.run` method) or the docs setting of a
1008
1158
  `Runtime` handler can be provided with a colon to specify its type hint in the following structure:
@@ -1046,7 +1196,7 @@ class RunStatus(ModelObj):
1046
1196
  iterations=None,
1047
1197
  ui_url=None,
1048
1198
  reason: str = None,
1049
- notifications: Dict[str, Notification] = None,
1199
+ notifications: dict[str, Notification] = None,
1050
1200
  ):
1051
1201
  self.state = state or "created"
1052
1202
  self.status_text = status_text
@@ -1291,7 +1441,7 @@ class RunObject(RunTemplate):
1291
1441
  """UI URL (for relevant runtimes)"""
1292
1442
  self.refresh()
1293
1443
  if not self._status.ui_url:
1294
- print("UI currently not available (status={})".format(self._status.state))
1444
+ print(f"UI currently not available (status={self._status.state})")
1295
1445
  return self._status.ui_url
1296
1446
 
1297
1447
  @property
@@ -1454,7 +1604,7 @@ class RunObject(RunTemplate):
1454
1604
  return f"{project}@{uid}#{iteration}{tag}"
1455
1605
 
1456
1606
  @staticmethod
1457
- def parse_uri(uri: str) -> Tuple[str, str, str, str]:
1607
+ def parse_uri(uri: str) -> tuple[str, str, str, str]:
1458
1608
  uri_pattern = (
1459
1609
  r"^(?P<project>.*)@(?P<uid>.*)\#(?P<iteration>.*?)(:(?P<tag>.*))?$"
1460
1610
  )
@@ -1675,7 +1825,7 @@ class DataSource(ModelObj):
1675
1825
  self,
1676
1826
  name: str = None,
1677
1827
  path: str = None,
1678
- attributes: Dict[str, object] = None,
1828
+ attributes: dict[str, object] = None,
1679
1829
  key_field: str = None,
1680
1830
  time_field: str = None,
1681
1831
  schedule: str = None,
@@ -1741,16 +1891,16 @@ class DataTargetBase(ModelObj):
1741
1891
  kind: str = None,
1742
1892
  name: str = "",
1743
1893
  path=None,
1744
- attributes: Dict[str, str] = None,
1894
+ attributes: dict[str, str] = None,
1745
1895
  after_step=None,
1746
1896
  partitioned: bool = False,
1747
1897
  key_bucketing_number: Optional[int] = None,
1748
- partition_cols: Optional[List[str]] = None,
1898
+ partition_cols: Optional[list[str]] = None,
1749
1899
  time_partitioning_granularity: Optional[str] = None,
1750
1900
  max_events: Optional[int] = None,
1751
1901
  flush_after_seconds: Optional[int] = None,
1752
- storage_options: Dict[str, str] = None,
1753
- schema: Dict[str, Any] = None,
1902
+ storage_options: dict[str, str] = None,
1903
+ schema: dict[str, Any] = None,
1754
1904
  credentials_prefix=None,
1755
1905
  ):
1756
1906
  self.name = name
@@ -1837,8 +1987,8 @@ class VersionedObjMetadata(ModelObj):
1837
1987
  tag: str = None,
1838
1988
  uid: str = None,
1839
1989
  project: str = None,
1840
- labels: Dict[str, str] = None,
1841
- annotations: Dict[str, str] = None,
1990
+ labels: dict[str, str] = None,
1991
+ annotations: dict[str, str] = None,
1842
1992
  updated=None,
1843
1993
  ):
1844
1994
  self.name = name