mlrun 1.3.1rc5__py3-none-any.whl → 1.4.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 (67) hide show
  1. mlrun/__main__.py +57 -4
  2. mlrun/api/api/endpoints/marketplace.py +57 -4
  3. mlrun/api/api/endpoints/runs.py +2 -0
  4. mlrun/api/api/utils.py +102 -0
  5. mlrun/api/crud/__init__.py +1 -0
  6. mlrun/api/crud/marketplace.py +133 -44
  7. mlrun/api/crud/notifications.py +80 -0
  8. mlrun/api/crud/runs.py +2 -0
  9. mlrun/api/crud/secrets.py +1 -0
  10. mlrun/api/db/base.py +32 -0
  11. mlrun/api/db/session.py +3 -11
  12. mlrun/api/db/sqldb/db.py +162 -1
  13. mlrun/api/db/sqldb/models/models_mysql.py +41 -0
  14. mlrun/api/db/sqldb/models/models_sqlite.py +35 -0
  15. mlrun/api/main.py +54 -1
  16. mlrun/api/migrations_mysql/versions/c905d15bd91d_notifications.py +70 -0
  17. mlrun/api/migrations_sqlite/versions/959ae00528ad_notifications.py +61 -0
  18. mlrun/api/schemas/__init__.py +1 -0
  19. mlrun/api/schemas/marketplace.py +18 -8
  20. mlrun/api/{db/filedb/__init__.py → schemas/notification.py} +17 -1
  21. mlrun/api/utils/singletons/db.py +8 -14
  22. mlrun/builder.py +37 -26
  23. mlrun/config.py +12 -2
  24. mlrun/data_types/spark.py +9 -2
  25. mlrun/datastore/base.py +10 -1
  26. mlrun/datastore/sources.py +1 -1
  27. mlrun/db/__init__.py +6 -4
  28. mlrun/db/base.py +1 -2
  29. mlrun/db/httpdb.py +32 -6
  30. mlrun/db/nopdb.py +463 -0
  31. mlrun/db/sqldb.py +47 -7
  32. mlrun/execution.py +3 -0
  33. mlrun/feature_store/api.py +26 -12
  34. mlrun/feature_store/common.py +1 -1
  35. mlrun/feature_store/steps.py +110 -13
  36. mlrun/k8s_utils.py +10 -0
  37. mlrun/model.py +43 -0
  38. mlrun/projects/operations.py +5 -2
  39. mlrun/projects/pipelines.py +4 -3
  40. mlrun/projects/project.py +50 -10
  41. mlrun/run.py +5 -4
  42. mlrun/runtimes/__init__.py +2 -6
  43. mlrun/runtimes/base.py +82 -31
  44. mlrun/runtimes/function.py +22 -0
  45. mlrun/runtimes/kubejob.py +10 -8
  46. mlrun/runtimes/serving.py +1 -1
  47. mlrun/runtimes/sparkjob/__init__.py +0 -1
  48. mlrun/runtimes/sparkjob/abstract.py +0 -2
  49. mlrun/serving/states.py +2 -2
  50. mlrun/utils/helpers.py +1 -1
  51. mlrun/utils/notifications/notification/__init__.py +1 -1
  52. mlrun/utils/notifications/notification/base.py +14 -13
  53. mlrun/utils/notifications/notification/console.py +6 -3
  54. mlrun/utils/notifications/notification/git.py +19 -12
  55. mlrun/utils/notifications/notification/ipython.py +6 -3
  56. mlrun/utils/notifications/notification/slack.py +13 -12
  57. mlrun/utils/notifications/notification_pusher.py +185 -37
  58. mlrun/utils/version/version.json +2 -2
  59. {mlrun-1.3.1rc5.dist-info → mlrun-1.4.0rc2.dist-info}/METADATA +6 -2
  60. {mlrun-1.3.1rc5.dist-info → mlrun-1.4.0rc2.dist-info}/RECORD +64 -63
  61. mlrun/api/db/filedb/db.py +0 -518
  62. mlrun/db/filedb.py +0 -899
  63. mlrun/runtimes/sparkjob/spark2job.py +0 -59
  64. {mlrun-1.3.1rc5.dist-info → mlrun-1.4.0rc2.dist-info}/LICENSE +0 -0
  65. {mlrun-1.3.1rc5.dist-info → mlrun-1.4.0rc2.dist-info}/WHEEL +0 -0
  66. {mlrun-1.3.1rc5.dist-info → mlrun-1.4.0rc2.dist-info}/entry_points.txt +0 -0
  67. {mlrun-1.3.1rc5.dist-info → mlrun-1.4.0rc2.dist-info}/top_level.txt +0 -0
@@ -12,6 +12,7 @@
12
12
  # See the License for the specific language governing permissions and
13
13
  # limitations under the License.
14
14
  #
15
+ import math
15
16
  import re
16
17
  import uuid
17
18
  import warnings
@@ -55,7 +56,7 @@ class MLRunStep(MapClass):
55
56
  engine = get_engine(event)
56
57
  self.do = self._engine_to_do_method.get(engine, None)
57
58
  if self.do is None:
58
- raise mlrun.errors.InvalidArgummentError(
59
+ raise mlrun.errors.MLRunInvalidArgumentError(
59
60
  f"Unrecognized engine: {engine}. Available engines are: pandas, spark and storey"
60
61
  )
61
62
 
@@ -136,7 +137,7 @@ class MapValues(StepToDict, MLRunStep):
136
137
 
137
138
  def __init__(
138
139
  self,
139
- mapping: Dict[str, Dict[str, Any]],
140
+ mapping: Dict[str, Dict[Union[str, int, bool], Any]],
140
141
  with_original_features: bool = False,
141
142
  suffix: str = "mapped",
142
143
  **kwargs,
@@ -226,34 +227,130 @@ class MapValues(StepToDict, MLRunStep):
226
227
  def _do_spark(self, event):
227
228
  from itertools import chain
228
229
 
229
- from pyspark.sql.functions import col, create_map, lit, when
230
+ from pyspark.sql.functions import col, create_map, isnan, isnull, lit, when
231
+ from pyspark.sql.types import DecimalType, DoubleType, FloatType
232
+ from pyspark.sql.utils import AnalysisException
230
233
 
234
+ df = event
235
+ source_column_names = df.columns
231
236
  for column, column_map in self.mapping.items():
232
237
  new_column_name = self._get_feature_name(column)
233
- if "ranges" not in column_map:
238
+ if not self.get_ranges_key() in column_map:
239
+ if column not in source_column_names:
240
+ continue
234
241
  mapping_expr = create_map([lit(x) for x in chain(*column_map.items())])
235
- event = event.withColumn(
236
- new_column_name, mapping_expr.getItem(col(column))
237
- )
242
+ try:
243
+ df = df.withColumn(
244
+ new_column_name,
245
+ when(
246
+ col(column).isin(list(column_map.keys())),
247
+ mapping_expr.getItem(col(column)),
248
+ ).otherwise(col(column)),
249
+ )
250
+ # if failed to use otherwise it is probably because the new column has different type
251
+ # then the original column.
252
+ # we will try to replace the values without using 'otherwise'.
253
+ except AnalysisException:
254
+ df = df.withColumn(
255
+ new_column_name, mapping_expr.getItem(col(column))
256
+ )
257
+ col_type = df.schema[column].dataType
258
+ new_col_type = df.schema[new_column_name].dataType
259
+ # in order to avoid exception at isna on non-decimal/float columns -
260
+ # we need to check their types before filtering.
261
+ if isinstance(col_type, (FloatType, DoubleType, DecimalType)):
262
+ column_filter = (~isnull(col(column))) & (~isnan(col(column)))
263
+ else:
264
+ column_filter = ~isnull(col(column))
265
+ if isinstance(new_col_type, (FloatType, DoubleType, DecimalType)):
266
+ new_column_filter = isnull(col(new_column_name)) | isnan(
267
+ col(new_column_name)
268
+ )
269
+ else:
270
+ # we need to check that every value replaced if we changed column type - except None or NaN.
271
+ new_column_filter = isnull(col(new_column_name))
272
+ mapping_to_null = [
273
+ k
274
+ for k, v in column_map.items()
275
+ if v is None
276
+ or (
277
+ isinstance(v, (float, np.float64, np.float32, np.float16))
278
+ and math.isnan(v)
279
+ )
280
+ ]
281
+ turned_to_none_values = df.filter(
282
+ column_filter & new_column_filter
283
+ ).filter(~col(column).isin(mapping_to_null))
284
+
285
+ if len(turned_to_none_values.head(1)) > 0:
286
+ raise mlrun.errors.MLRunInvalidArgumentError(
287
+ f"MapValues - mapping that changes column type must change all values accordingly,"
288
+ f" which is not the case for column '{column}'"
289
+ )
238
290
  else:
239
291
  for val, val_range in column_map["ranges"].items():
240
292
  min_val = val_range[0] if val_range[0] != "-inf" else -np.inf
241
293
  max_val = val_range[1] if val_range[1] != "inf" else np.inf
242
294
  otherwise = ""
243
- if new_column_name in event.columns:
244
- otherwise = event[new_column_name]
245
- event = event.withColumn(
295
+ if new_column_name in df.columns:
296
+ otherwise = df[new_column_name]
297
+ df = df.withColumn(
246
298
  new_column_name,
247
299
  when(
248
- (event[column] < max_val) & (event[column] >= min_val),
300
+ (df[column] < max_val) & (df[column] >= min_val),
249
301
  lit(val),
250
302
  ).otherwise(otherwise),
251
303
  )
252
304
 
253
305
  if not self.with_original_features:
254
- event = event.select(*self.mapping.keys())
306
+ df = df.select(*self.mapping.keys())
255
307
 
256
- return event
308
+ return df
309
+
310
+ @classmethod
311
+ def validate_args(cls, feature_set, **kwargs):
312
+ mapping = kwargs.get("mapping", [])
313
+ for column, column_map in mapping.items():
314
+ if not cls.get_ranges_key() in column_map:
315
+ types = set(
316
+ type(val)
317
+ for val in column_map.values()
318
+ if type(val) is not None
319
+ and not (
320
+ isinstance(val, (float, np.float64, np.float32, np.float16))
321
+ and math.isnan(val)
322
+ )
323
+ )
324
+ else:
325
+ if len(column_map) > 1:
326
+ raise mlrun.errors.MLRunInvalidArgumentError(
327
+ f"MapValues - mapping values of the same column can not combine ranges and "
328
+ f"single replacement, which is the case for column '{column}'"
329
+ )
330
+ ranges_dict = column_map[cls.get_ranges_key()]
331
+ types = set()
332
+ for ranges_mapping_values in ranges_dict.values():
333
+ range_types = set(
334
+ type(val)
335
+ for val in ranges_mapping_values
336
+ if type(val) is not None
337
+ and val != "-inf"
338
+ and val != "inf"
339
+ and not (
340
+ isinstance(val, (float, np.float64, np.float32, np.float16))
341
+ and math.isnan(val)
342
+ )
343
+ )
344
+ types = types.union(range_types)
345
+ if len(types) > 1:
346
+ raise mlrun.errors.MLRunInvalidArgumentError(
347
+ f"MapValues - mapping values of the same column must be in the"
348
+ f" same type, which was not the case for Column '{column}'"
349
+ )
350
+
351
+ @staticmethod
352
+ def get_ranges_key():
353
+ return "ranges"
257
354
 
258
355
 
259
356
  class Imputer(StepToDict, MLRunStep):
mlrun/k8s_utils.py CHANGED
@@ -508,6 +508,16 @@ class K8sHelper:
508
508
  k8s_secret.data = secret_data
509
509
  self.v1api.replace_namespaced_secret(secret_name, namespace, k8s_secret)
510
510
 
511
+ def load_secret(self, secret_name, namespace=""):
512
+ namespace = namespace or self.resolve_namespace(namespace)
513
+
514
+ try:
515
+ k8s_secret = self.v1api.read_namespaced_secret(secret_name, namespace)
516
+ except ApiException:
517
+ return None
518
+
519
+ return k8s_secret.data
520
+
511
521
  def delete_project_secrets(self, project, secrets, namespace=""):
512
522
  secret_name = self.get_project_secret_name(project)
513
523
  self.delete_secrets(secret_name, secrets, namespace)
mlrun/model.py CHANGED
@@ -338,6 +338,7 @@ class ImageBuilder(ModelObj):
338
338
  origin_filename=None,
339
339
  with_mlrun=None,
340
340
  auto_build=None,
341
+ requirements: list = None,
341
342
  ):
342
343
  self.functionSourceCode = functionSourceCode #: functionSourceCode
343
344
  self.codeEntryType = "" #: codeEntryType
@@ -355,6 +356,7 @@ class ImageBuilder(ModelObj):
355
356
  self.with_mlrun = with_mlrun #: with_mlrun
356
357
  self.auto_build = auto_build #: auto_build
357
358
  self.build_pod = None
359
+ self.requirements = requirements or [] #: pip requirements
358
360
 
359
361
  @property
360
362
  def source(self):
@@ -376,6 +378,32 @@ class ImageBuilder(ModelObj):
376
378
  self._source = source
377
379
 
378
380
 
381
+ class Notification(ModelObj):
382
+ """Notification specification"""
383
+
384
+ def __init__(
385
+ self,
386
+ kind=None,
387
+ name=None,
388
+ message=None,
389
+ severity=None,
390
+ when=None,
391
+ condition=None,
392
+ params=None,
393
+ status=None,
394
+ sent_time=None,
395
+ ):
396
+ self.kind = kind
397
+ self.name = name
398
+ self.message = message
399
+ self.severity = severity
400
+ self.when = when
401
+ self.condition = condition
402
+ self.params = params or {}
403
+ self.status = status
404
+ self.sent_time = sent_time
405
+
406
+
379
407
  class RunMetadata(ModelObj):
380
408
  """Run metadata"""
381
409
 
@@ -492,6 +520,7 @@ class RunSpec(ModelObj):
492
520
  allow_empty_resources=None,
493
521
  inputs_type_hints=None,
494
522
  returns=None,
523
+ notifications=None,
495
524
  ):
496
525
  # A dictionary of parsing configurations that will be read from the inputs the user set. The keys are the inputs
497
526
  # keys (parameter names) and the values are the type hint given in the input keys after the colon.
@@ -526,6 +555,7 @@ class RunSpec(ModelObj):
526
555
  self.verbose = verbose
527
556
  self.scrape_metrics = scrape_metrics
528
557
  self.allow_empty_resources = allow_empty_resources
558
+ self._notifications = notifications or []
529
559
 
530
560
  def to_dict(self, fields=None, exclude=None):
531
561
  struct = super().to_dict(fields, exclude=["handler"])
@@ -678,6 +708,19 @@ class RunSpec(ModelObj):
678
708
  return str(self.handler)
679
709
  return ""
680
710
 
711
+ @property
712
+ def notifications(self):
713
+ return self._notifications
714
+
715
+ @notifications.setter
716
+ def notifications(self, notifications):
717
+ if isinstance(notifications, list):
718
+ self._notifications = ObjectList.from_list(Notification, notifications)
719
+ elif isinstance(notifications, ObjectList):
720
+ self._notifications = notifications
721
+ else:
722
+ raise ValueError("Notifications must be a list")
723
+
681
724
  def extract_type_hints_from_inputs(self):
682
725
  """
683
726
  This method extracts the type hints from the inputs keys in the input dictionary.
@@ -72,6 +72,7 @@ def run_function(
72
72
  auto_build: bool = None,
73
73
  schedule: Union[str, mlrun.api.schemas.ScheduleCronTrigger] = None,
74
74
  artifact_path: str = None,
75
+ notifications: List[mlrun.model.Notification] = None,
75
76
  returns: Optional[List[Union[str, Dict[str, str]]]] = None,
76
77
  ) -> Union[mlrun.model.RunObject, kfp.dsl.ContainerOp]:
77
78
  """Run a local or remote task as part of a local/kubeflow pipeline
@@ -91,7 +92,7 @@ def run_function(
91
92
  LABELS = "is_error"
92
93
  MODEL_CLASS = "sklearn.ensemble.RandomForestClassifier"
93
94
  DATA_PATH = "s3://bigdata/data.parquet"
94
- function = mlrun.import_function("hub://auto_trainer")
95
+ function = mlrun.import_function("hub://auto-trainer")
95
96
  run1 = run_function(function, params={"label_columns": LABELS, "model_class": MODEL_CLASS},
96
97
  inputs={"dataset": DATA_PATH})
97
98
 
@@ -100,7 +101,7 @@ def run_function(
100
101
  # create a project with two functions (local and from marketplace)
101
102
  project = mlrun.new_project(project_name, "./proj)
102
103
  project.set_function("mycode.py", "myfunc", image="mlrun/mlrun")
103
- project.set_function("hub://auto_trainer", "train")
104
+ project.set_function("hub://auto-trainer", "train")
104
105
 
105
106
  # run functions (refer to them by name)
106
107
  run1 = run_function("myfunc", params={"x": 7})
@@ -143,6 +144,7 @@ def run_function(
143
144
  see this link for help:
144
145
  https://apscheduler.readthedocs.io/en/3.x/modules/triggers/cron.html#module-apscheduler.triggers.cron
145
146
  :param artifact_path: path to store artifacts, when running in a workflow this will be set automatically
147
+ :param notifications: list of notifications to push when the run is completed
146
148
  :param returns: List of log hints - configurations for how to log the returning values from the handler's
147
149
  run (as artifacts or results). The list's length must be equal to the amount of returning
148
150
  objects. A log hint may be given as:
@@ -198,6 +200,7 @@ def run_function(
198
200
  or (project.artifact_path if project else None),
199
201
  auto_build=auto_build,
200
202
  schedule=schedule,
203
+ notifications=notifications,
201
204
  )
202
205
  if run_result:
203
206
  run_result._notified = False
@@ -705,7 +705,7 @@ class _LocalRunner(_PipelineRunner):
705
705
  trace = traceback.format_exc()
706
706
  logger.error(trace)
707
707
  project.notifiers.push(
708
- f"Workflow {workflow_id} run failed!, error: {e}\n{trace}", "error"
708
+ f":x: Workflow {workflow_id} run failed!, error: {e}\n{trace}", "error"
709
709
  )
710
710
  state = mlrun.run.RunStatuses.failed
711
711
  mlrun.run.wait_for_runs_completion(pipeline_context.runs_map.values())
@@ -874,7 +874,8 @@ class _RemoteRunner(_PipelineRunner):
874
874
  trace = traceback.format_exc()
875
875
  logger.error(trace)
876
876
  project.notifiers.push(
877
- f"Workflow {workflow_name} run failed!, error: {e}\n{trace}", "error"
877
+ f":x: Workflow {workflow_name} run failed!, error: {e}\n{trace}",
878
+ "error",
878
879
  )
879
880
  state = mlrun.run.RunStatuses.failed
880
881
  return _PipelineRunStatus(
@@ -1033,7 +1034,7 @@ def load_and_run(
1033
1034
  try:
1034
1035
  notification_pusher.push(
1035
1036
  message=message,
1036
- severity=mlrun.utils.notifications.NotificationSeverity.ERROR,
1037
+ severity=mlrun.api.schemas.NotificationSeverity.ERROR,
1037
1038
  )
1038
1039
 
1039
1040
  except Exception as exc:
mlrun/projects/project.py CHANGED
@@ -117,7 +117,7 @@ def new_project(
117
117
  # create a project with local and marketplace functions, a workflow, and an artifact
118
118
  project = mlrun.new_project("myproj", "./", init_git=True, description="my new project")
119
119
  project.set_function('prep_data.py', 'prep-data', image='mlrun/mlrun', handler='prep_data')
120
- project.set_function('hub://auto_trainer', 'train')
120
+ project.set_function('hub://auto-trainer', 'train')
121
121
  project.set_artifact('data', Artifact(target_path=data_url))
122
122
  project.set_workflow('main', "./myflow.py")
123
123
  project.save()
@@ -169,7 +169,13 @@ def new_project(
169
169
  # Remove original owner name for avoiding possible conflicts
170
170
  project.spec.owner = None
171
171
  else:
172
- project = MlrunProject(name=name)
172
+ project = MlrunProject.from_dict(
173
+ {
174
+ "metadata": {
175
+ "name": name,
176
+ }
177
+ }
178
+ )
173
179
  project.spec.context = context
174
180
  project.spec.subpath = subpath or project.spec.subpath
175
181
 
@@ -419,9 +425,15 @@ def _load_project_dir(context, name="", subpath=""):
419
425
 
420
426
  elif path.isfile(path.join(context, subpath_str, "function.yaml")):
421
427
  func = import_function(path.join(context, subpath_str, "function.yaml"))
422
- project = MlrunProject(
423
- name=func.metadata.project,
424
- functions=[{"url": "function.yaml", "name": func.metadata.name}],
428
+ project = MlrunProject.from_dict(
429
+ {
430
+ "metadata": {
431
+ "name": func.metadata.project,
432
+ },
433
+ "spec": {
434
+ "functions": [{"url": "function.yaml", "name": func.metadata.name}],
435
+ },
436
+ }
425
437
  )
426
438
  else:
427
439
  raise mlrun.errors.MLRunNotFoundError(
@@ -769,6 +781,7 @@ class MlrunProject(ModelObj):
769
781
 
770
782
  def __init__(
771
783
  self,
784
+ # TODO: remove all arguments except metadata and spec in 1.6.0
772
785
  name=None,
773
786
  description=None,
774
787
  params=None,
@@ -777,7 +790,7 @@ class MlrunProject(ModelObj):
777
790
  artifacts=None,
778
791
  artifact_path=None,
779
792
  conda=None,
780
- # all except these 2 are for backwards compatibility with MlrunProjectLegacy
793
+ # all except these metadata and spec are for backwards compatibility with MlrunProjectLegacy
781
794
  metadata=None,
782
795
  spec=None,
783
796
  default_requirements: typing.Union[str, typing.List[str]] = None,
@@ -789,6 +802,26 @@ class MlrunProject(ModelObj):
789
802
  self._status = None
790
803
  self.status = None
791
804
 
805
+ if any(
806
+ [
807
+ name,
808
+ description,
809
+ params,
810
+ functions,
811
+ workflows,
812
+ artifacts,
813
+ artifact_path,
814
+ conda,
815
+ default_requirements,
816
+ ]
817
+ ):
818
+ # TODO: remove in 1.6.0 along with all arguments except metadata and spec
819
+ warnings.warn(
820
+ "Project constructor arguments are deprecated in 1.4.0 and will be removed in 1.6.0,"
821
+ " use metadata and spec instead",
822
+ FutureWarning,
823
+ )
824
+
792
825
  # Handling the fields given in the legacy way
793
826
  self.metadata.name = name or self.metadata.name
794
827
  self.spec.description = description or self.spec.description
@@ -1436,12 +1469,15 @@ class MlrunProject(ModelObj):
1436
1469
  with open(f"{temp_dir}/_body", "rb") as fp:
1437
1470
  artifact.spec._body = fp.read()
1438
1471
  artifact.target_path = ""
1472
+
1473
+ # if the dataitem is not a file, it means we downloaded it from a remote source to a temp file,
1474
+ # so we need to remove it after we're done with it
1475
+ dataitem.remove_local()
1476
+
1439
1477
  return self.log_artifact(
1440
1478
  artifact, local_path=temp_dir, artifact_path=artifact_path
1441
1479
  )
1442
1480
 
1443
- if dataitem.kind != "file":
1444
- remove(item_file)
1445
1481
  else:
1446
1482
  raise ValueError("unsupported file suffix, use .yaml, .json, or .zip")
1447
1483
 
@@ -1489,7 +1525,7 @@ class MlrunProject(ModelObj):
1489
1525
 
1490
1526
  object (s3://, v3io://, ..)
1491
1527
  MLRun DB e.g. db://project/func:ver
1492
- functions hub/market: e.g. hub://auto_trainer:master
1528
+ functions hub/market: e.g. hub://auto-trainer:master
1493
1529
 
1494
1530
  examples::
1495
1531
 
@@ -1536,6 +1572,7 @@ class MlrunProject(ModelObj):
1536
1572
  func = path.relpath(func, self.spec.context)
1537
1573
 
1538
1574
  func = func or ""
1575
+ name = mlrun.utils.normalize_name(name) if name else name
1539
1576
  if isinstance(func, str):
1540
1577
  # in hub or db functions name defaults to the function name
1541
1578
  if not name and not (func.startswith("db://") or func.startswith("hub://")):
@@ -2151,6 +2188,7 @@ class MlrunProject(ModelObj):
2151
2188
  auto_build: bool = None,
2152
2189
  schedule: typing.Union[str, mlrun.api.schemas.ScheduleCronTrigger] = None,
2153
2190
  artifact_path: str = None,
2191
+ notifications: typing.List[mlrun.model.Notification] = None,
2154
2192
  returns: Optional[List[Union[str, Dict[str, str]]]] = None,
2155
2193
  ) -> typing.Union[mlrun.model.RunObject, kfp.dsl.ContainerOp]:
2156
2194
  """Run a local or remote task as part of a local/kubeflow pipeline
@@ -2160,7 +2198,7 @@ class MlrunProject(ModelObj):
2160
2198
  # create a project with two functions (local and from marketplace)
2161
2199
  project = mlrun.new_project(project_name, "./proj")
2162
2200
  project.set_function("mycode.py", "myfunc", image="mlrun/mlrun")
2163
- project.set_function("hub://auto_trainer", "train")
2201
+ project.set_function("hub://auto-trainer", "train")
2164
2202
 
2165
2203
  # run functions (refer to them by name)
2166
2204
  run1 = project.run_function("myfunc", params={"x": 7})
@@ -2192,6 +2230,7 @@ class MlrunProject(ModelObj):
2192
2230
  see this link for help:
2193
2231
  https://apscheduler.readthedocs.io/en/3.x/modules/triggers/cron.html#module-apscheduler.triggers.cron
2194
2232
  :param artifact_path: path to store artifacts, when running in a workflow this will be set automatically
2233
+ :param notifications: list of notifications to push when the run is completed
2195
2234
  :param returns: List of log hints - configurations for how to log the returning values from the
2196
2235
  handler's run (as artifacts or results). The list's length must be equal to the amount
2197
2236
  of returning objects. A log hint may be given as:
@@ -2226,6 +2265,7 @@ class MlrunProject(ModelObj):
2226
2265
  auto_build=auto_build,
2227
2266
  schedule=schedule,
2228
2267
  artifact_path=artifact_path,
2268
+ notifications=notifications,
2229
2269
  returns=returns,
2230
2270
  )
2231
2271
 
mlrun/run.py CHANGED
@@ -57,7 +57,6 @@ from .runtimes import (
57
57
  RemoteSparkRuntime,
58
58
  RuntimeKinds,
59
59
  ServingRuntime,
60
- Spark2Runtime,
61
60
  Spark3Runtime,
62
61
  get_runtime_class,
63
62
  )
@@ -127,6 +126,7 @@ def run_local(
127
126
  artifact_path: str = "",
128
127
  mode: str = None,
129
128
  allow_empty_resources=None,
129
+ notifications: List[mlrun.model.Notification] = None,
130
130
  returns: list = None,
131
131
  ):
132
132
  """Run a task on function/code (.py, .ipynb or .yaml) locally,
@@ -216,6 +216,7 @@ def run_local(
216
216
  inputs=inputs,
217
217
  returns=returns,
218
218
  artifact_path=artifact_path,
219
+ notifications=notifications,
219
220
  )
220
221
 
221
222
 
@@ -235,7 +236,7 @@ def function_to_module(code="", workdir=None, secrets=None, silent=False):
235
236
  mod.my_job(context, p1=1, p2='x')
236
237
  print(context.to_yaml())
237
238
 
238
- fn = mlrun.import_function('hub://open_archive')
239
+ fn = mlrun.import_function('hub://open-archive')
239
240
  mod = mlrun.function_to_module(fn)
240
241
  data = mlrun.run.get_dataitem("https://fpsignals-public.s3.amazonaws.com/catsndogs.tar.gz")
241
242
  context = mlrun.get_or_create_ctx('myfunc')
@@ -457,7 +458,7 @@ def import_function(url="", secrets=None, db="", project=None, new_name=None):
457
458
 
458
459
  examples::
459
460
 
460
- function = mlrun.import_function("hub://auto_trainer")
461
+ function = mlrun.import_function("hub://auto-trainer")
461
462
  function = mlrun.import_function("./func.yaml")
462
463
  function = mlrun.import_function("https://raw.githubusercontent.com/org/repo/func.yaml")
463
464
 
@@ -700,7 +701,6 @@ def code_to_function(
700
701
  DaskCluster,
701
702
  KubejobRuntime,
702
703
  LocalRuntime,
703
- Spark2Runtime,
704
704
  Spark3Runtime,
705
705
  RemoteSparkRuntime,
706
706
  ]:
@@ -793,6 +793,7 @@ def code_to_function(
793
793
 
794
794
  def update_common(fn, spec):
795
795
  fn.spec.image = image or get_in(spec, "spec.image", "")
796
+ fn.spec.filename = filename or get_in(spec, "spec.filename", "")
796
797
  fn.spec.build.base_image = get_in(spec, "spec.build.baseImage")
797
798
  fn.spec.build.commands = get_in(spec, "spec.build.commands")
798
799
  fn.spec.build.secret = get_in(spec, "spec.build.secret")
@@ -47,7 +47,7 @@ from .mpijob import ( # noqa
47
47
  from .nuclio import nuclio_init_hook
48
48
  from .remotesparkjob import RemoteSparkRuntime, RemoteSparkRuntimeHandler
49
49
  from .serving import ServingRuntime, new_v2_model_server
50
- from .sparkjob import Spark2Runtime, Spark3Runtime, SparkRuntimeHandler
50
+ from .sparkjob import Spark3Runtime, SparkRuntimeHandler
51
51
 
52
52
  # for legacy imports (MLModelServer moved from here to /serving)
53
53
  from ..serving import MLModelServer, new_v1_model_server # noqa isort: skip
@@ -260,11 +260,7 @@ def get_runtime_class(kind: str):
260
260
  return crd_version_to_runtime[mpijob_crd_version]
261
261
 
262
262
  if kind == RuntimeKinds.spark:
263
- spark_operator_version = resolve_spark_operator_version()
264
- if spark_operator_version == 2:
265
- return Spark2Runtime
266
- elif spark_operator_version == 3:
267
- return Spark3Runtime
263
+ return Spark3Runtime
268
264
 
269
265
  kind_runtime_map = {
270
266
  RuntimeKinds.remote: RemoteRuntime,