nextmv 1.0.0__py3-none-any.whl → 1.0.0.dev0__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.
Files changed (140) hide show
  1. nextmv/__about__.py +1 -1
  2. nextmv/__entrypoint__.py +2 -1
  3. nextmv/__init__.py +4 -0
  4. nextmv/cli/CONTRIBUTING.md +40 -112
  5. nextmv/cli/cloud/__init__.py +0 -4
  6. nextmv/cli/cloud/acceptance/create.py +22 -20
  7. nextmv/cli/cloud/acceptance/delete.py +12 -8
  8. nextmv/cli/cloud/acceptance/get.py +10 -9
  9. nextmv/cli/cloud/acceptance/list.py +3 -3
  10. nextmv/cli/cloud/acceptance/update.py +6 -6
  11. nextmv/cli/cloud/account/__init__.py +3 -3
  12. nextmv/cli/cloud/account/create.py +11 -11
  13. nextmv/cli/cloud/account/delete.py +8 -7
  14. nextmv/cli/cloud/account/get.py +3 -3
  15. nextmv/cli/cloud/account/update.py +5 -5
  16. nextmv/cli/cloud/app/create.py +26 -25
  17. nextmv/cli/cloud/app/delete.py +7 -6
  18. nextmv/cli/cloud/app/exists.py +2 -2
  19. nextmv/cli/cloud/app/get.py +2 -2
  20. nextmv/cli/cloud/app/list.py +3 -3
  21. nextmv/cli/cloud/app/push.py +54 -349
  22. nextmv/cli/cloud/app/update.py +12 -12
  23. nextmv/cli/cloud/batch/create.py +28 -26
  24. nextmv/cli/cloud/batch/delete.py +10 -6
  25. nextmv/cli/cloud/batch/get.py +9 -9
  26. nextmv/cli/cloud/batch/list.py +3 -3
  27. nextmv/cli/cloud/batch/metadata.py +4 -4
  28. nextmv/cli/cloud/batch/update.py +6 -6
  29. nextmv/cli/cloud/data/__init__.py +1 -1
  30. nextmv/cli/cloud/data/upload.py +15 -15
  31. nextmv/cli/cloud/ensemble/__init__.py +0 -2
  32. nextmv/cli/cloud/ensemble/create.py +22 -21
  33. nextmv/cli/cloud/ensemble/delete.py +10 -6
  34. nextmv/cli/cloud/ensemble/get.py +4 -4
  35. nextmv/cli/cloud/ensemble/update.py +9 -9
  36. nextmv/cli/cloud/input_set/__init__.py +0 -2
  37. nextmv/cli/cloud/input_set/create.py +22 -22
  38. nextmv/cli/cloud/input_set/get.py +3 -3
  39. nextmv/cli/cloud/input_set/list.py +3 -3
  40. nextmv/cli/cloud/input_set/update.py +24 -24
  41. nextmv/cli/cloud/instance/create.py +15 -14
  42. nextmv/cli/cloud/instance/delete.py +7 -6
  43. nextmv/cli/cloud/instance/exists.py +2 -2
  44. nextmv/cli/cloud/instance/get.py +2 -2
  45. nextmv/cli/cloud/instance/list.py +3 -3
  46. nextmv/cli/cloud/instance/update.py +14 -14
  47. nextmv/cli/cloud/managed_input/create.py +16 -14
  48. nextmv/cli/cloud/managed_input/delete.py +8 -7
  49. nextmv/cli/cloud/managed_input/get.py +3 -3
  50. nextmv/cli/cloud/managed_input/list.py +3 -3
  51. nextmv/cli/cloud/managed_input/update.py +9 -9
  52. nextmv/cli/cloud/run/cancel.py +2 -2
  53. nextmv/cli/cloud/run/create.py +40 -34
  54. nextmv/cli/cloud/run/get.py +8 -8
  55. nextmv/cli/cloud/run/input.py +4 -4
  56. nextmv/cli/cloud/run/list.py +6 -6
  57. nextmv/cli/cloud/run/logs.py +10 -9
  58. nextmv/cli/cloud/run/metadata.py +4 -4
  59. nextmv/cli/cloud/run/track.py +33 -32
  60. nextmv/cli/cloud/scenario/create.py +21 -21
  61. nextmv/cli/cloud/scenario/delete.py +10 -6
  62. nextmv/cli/cloud/scenario/get.py +9 -9
  63. nextmv/cli/cloud/scenario/list.py +3 -3
  64. nextmv/cli/cloud/scenario/metadata.py +4 -4
  65. nextmv/cli/cloud/scenario/update.py +6 -6
  66. nextmv/cli/cloud/secrets/create.py +17 -17
  67. nextmv/cli/cloud/secrets/delete.py +10 -6
  68. nextmv/cli/cloud/secrets/get.py +4 -4
  69. nextmv/cli/cloud/secrets/list.py +3 -3
  70. nextmv/cli/cloud/secrets/update.py +20 -17
  71. nextmv/cli/cloud/upload/create.py +2 -2
  72. nextmv/cli/cloud/version/create.py +10 -9
  73. nextmv/cli/cloud/version/delete.py +7 -6
  74. nextmv/cli/cloud/version/exists.py +2 -2
  75. nextmv/cli/cloud/version/get.py +2 -2
  76. nextmv/cli/cloud/version/list.py +3 -3
  77. nextmv/cli/cloud/version/update.py +8 -8
  78. nextmv/cli/community/__init__.py +1 -1
  79. nextmv/cli/community/clone.py +204 -20
  80. nextmv/cli/community/list.py +125 -60
  81. nextmv/cli/configuration/config.py +10 -43
  82. nextmv/cli/configuration/create.py +7 -7
  83. nextmv/cli/configuration/delete.py +8 -8
  84. nextmv/cli/configuration/list.py +3 -3
  85. nextmv/cli/main.py +36 -26
  86. nextmv/cli/message.py +54 -71
  87. nextmv/cli/options.py +0 -28
  88. nextmv/cli/version.py +1 -1
  89. nextmv/cloud/__init__.py +38 -14
  90. nextmv/cloud/acceptance_test.py +65 -1
  91. nextmv/cloud/account.py +6 -1
  92. nextmv/cloud/application/__init__.py +75 -18
  93. nextmv/cloud/application/_acceptance.py +8 -13
  94. nextmv/cloud/application/_batch_scenario.py +19 -4
  95. nextmv/cloud/application/_input_set.py +6 -42
  96. nextmv/cloud/application/_instance.py +3 -3
  97. nextmv/cloud/application/_managed_input.py +2 -2
  98. nextmv/cloud/application/_version.py +3 -4
  99. nextmv/cloud/batch_experiment.py +1 -3
  100. nextmv/cloud/integration.py +4 -7
  101. nextmv/deprecated.py +3 -5
  102. nextmv/input.py +52 -0
  103. nextmv/local/runner.py +1 -1
  104. nextmv/model.py +11 -50
  105. nextmv/options.py +256 -11
  106. nextmv/output.py +62 -0
  107. nextmv/run.py +10 -1
  108. nextmv/status.py +51 -1
  109. {nextmv-1.0.0.dist-info → nextmv-1.0.0.dev0.dist-info}/METADATA +4 -5
  110. nextmv-1.0.0.dev0.dist-info/RECORD +158 -0
  111. nextmv/cli/cloud/ensemble/list.py +0 -63
  112. nextmv/cli/cloud/input_set/delete.py +0 -64
  113. nextmv/cli/cloud/shadow/__init__.py +0 -33
  114. nextmv/cli/cloud/shadow/create.py +0 -184
  115. nextmv/cli/cloud/shadow/delete.py +0 -64
  116. nextmv/cli/cloud/shadow/get.py +0 -61
  117. nextmv/cli/cloud/shadow/list.py +0 -63
  118. nextmv/cli/cloud/shadow/metadata.py +0 -66
  119. nextmv/cli/cloud/shadow/start.py +0 -43
  120. nextmv/cli/cloud/shadow/stop.py +0 -53
  121. nextmv/cli/cloud/shadow/update.py +0 -96
  122. nextmv/cli/cloud/switchback/__init__.py +0 -33
  123. nextmv/cli/cloud/switchback/create.py +0 -151
  124. nextmv/cli/cloud/switchback/delete.py +0 -64
  125. nextmv/cli/cloud/switchback/get.py +0 -62
  126. nextmv/cli/cloud/switchback/list.py +0 -63
  127. nextmv/cli/cloud/switchback/metadata.py +0 -68
  128. nextmv/cli/cloud/switchback/start.py +0 -43
  129. nextmv/cli/cloud/switchback/stop.py +0 -53
  130. nextmv/cli/cloud/switchback/update.py +0 -96
  131. nextmv/cli/confirm.py +0 -34
  132. nextmv/cloud/application/_shadow.py +0 -320
  133. nextmv/cloud/application/_switchback.py +0 -332
  134. nextmv/cloud/community.py +0 -446
  135. nextmv/cloud/shadow.py +0 -254
  136. nextmv/cloud/switchback.py +0 -228
  137. nextmv-1.0.0.dist-info/RECORD +0 -185
  138. nextmv-1.0.0.dist-info/entry_points.txt +0 -2
  139. {nextmv-1.0.0.dist-info → nextmv-1.0.0.dev0.dist-info}/WHEEL +0 -0
  140. {nextmv-1.0.0.dist-info → nextmv-1.0.0.dev0.dist-info}/licenses/LICENSE +0 -0
@@ -21,7 +21,7 @@ list_application
21
21
  import json
22
22
  import shutil
23
23
  import sys
24
- from datetime import datetime
24
+ from datetime import datetime, timezone
25
25
  from enum import Enum
26
26
  from typing import Any
27
27
 
@@ -41,8 +41,6 @@ from nextmv.cloud.application._instance import ApplicationInstanceMixin
41
41
  from nextmv.cloud.application._managed_input import ApplicationManagedInputMixin
42
42
  from nextmv.cloud.application._run import ApplicationRunMixin
43
43
  from nextmv.cloud.application._secrets import ApplicationSecretsMixin
44
- from nextmv.cloud.application._shadow import ApplicationShadowMixin
45
- from nextmv.cloud.application._switchback import ApplicationSwitchbackMixin
46
44
  from nextmv.cloud.application._utils import _is_not_exist_error
47
45
  from nextmv.cloud.application._version import ApplicationVersionMixin
48
46
  from nextmv.cloud.client import Client
@@ -102,8 +100,6 @@ class Application(
102
100
  ApplicationVersionMixin,
103
101
  ApplicationInputSetMixin,
104
102
  ApplicationManagedInputMixin,
105
- ApplicationShadowMixin,
106
- ApplicationSwitchbackMixin,
107
103
  ):
108
104
  """
109
105
  A published decision model that can be executed.
@@ -250,7 +246,7 @@ class Application(
250
246
  def new(
251
247
  cls,
252
248
  client: Client,
253
- name: str | None = None,
249
+ name: str,
254
250
  id: str | None = None,
255
251
  description: str | None = None,
256
252
  is_workflow: bool | None = None,
@@ -268,13 +264,13 @@ class Application(
268
264
  ----------
269
265
  client : Client
270
266
  Client to use for interacting with the Nextmv Cloud API.
271
- name : str | None = None
272
- Name of the application. Uses the ID as the name if not provided.
273
- id : str | None = None
267
+ name : str
268
+ Name of the application.
269
+ id : str, optional
274
270
  ID of the application. Will be generated if not provided.
275
- description : str | None = None
271
+ description : str, optional
276
272
  Description of the application.
277
- is_workflow : bool | None = None
273
+ is_workflow : bool, optional
278
274
  Whether the application is a Decision Workflow.
279
275
  exist_ok : bool, default=False
280
276
  If True and an application with the same ID already exists,
@@ -296,10 +292,7 @@ class Application(
296
292
  >>> app = Application.new(client=client, name="My New App", id="my-app")
297
293
  """
298
294
 
299
- if exist_ok and (id is None or id == ""):
300
- raise ValueError("If exist_ok is True, id must be provided")
301
-
302
- if id is None or id == "":
295
+ if id is None:
303
296
  id = safe_id("app")
304
297
 
305
298
  if exist_ok and cls.exists(client=client, id=id):
@@ -310,9 +303,6 @@ class Application(
310
303
 
311
304
  return cls.from_dict({"client": client} | response.json())
312
305
 
313
- if name is None or name == "":
314
- name = id
315
-
316
306
  payload = {
317
307
  "name": name,
318
308
  "id": id,
@@ -405,6 +395,10 @@ class Application(
405
395
  model: Model | None = None,
406
396
  model_configuration: ModelConfiguration | None = None,
407
397
  rich_print: bool = False,
398
+ no_version: bool = False,
399
+ version_id: str | None = None,
400
+ version_name: str | None = None,
401
+ version_description: str | None = None,
408
402
  ) -> None:
409
403
  """
410
404
  Push an app to Nextmv Cloud.
@@ -422,6 +416,15 @@ class Application(
422
416
  `nextmv.Model`. The model is encoded, some dependencies and
423
417
  accompanying files are packaged, and the app is pushed to Nextmv Cloud.
424
418
 
419
+ The default behavior of this function is to create a new application
420
+ version _after_ the app has been pushed. You can set the `no_version`
421
+ argument to `True` to skip this step. The `version_id`, `version_name`,
422
+ and `version_description` arguments can be used to customize the version
423
+ that is created. If the `version_id` is not specified, a randomly
424
+ generated ID will be used. If the `version_name` is not specified, a
425
+ generic name with a timestamp will be used. Lastly, if no description is
426
+ specified, then a generic description will also be used.
427
+
425
428
  Parameters
426
429
  ----------
427
430
  manifest : Optional[Manifest], default=None
@@ -440,6 +443,21 @@ class Application(
440
443
  with `model`.
441
444
  rich_print : bool, default=False
442
445
  Whether to use rich printing when verbose output is enabled.
446
+ no_version : bool, default=False
447
+ If True, do not create a new version after pushing the app.
448
+ version_id : Optional[str], default=None
449
+ ID of the version to create after pushing the app. If None, a unique
450
+ ID will be generated.
451
+ version_name : Optional[str], default=None
452
+ Name of the version to create after pushing the app. If None, a name
453
+ with a timestamp will be generated.
454
+ version_description : Optional[str], default=None
455
+ Description of the version to create after pushing the app. If None, a
456
+ generic description with a timestamp will be generated.
457
+
458
+ Returns
459
+ -------
460
+ None
443
461
 
444
462
  Raises
445
463
  ------
@@ -543,6 +561,45 @@ class Application(
543
561
  except OSError as e:
544
562
  raise Exception(f"error deleting output directory: {e}") from e
545
563
 
564
+ if no_version:
565
+ if verbose:
566
+ if rich_print:
567
+ rich.print(
568
+ f":white_check_mark: Push completed for Nextmv application [magenta]{self.id}[/magenta] "
569
+ "without creating a new version.",
570
+ file=sys.stderr,
571
+ )
572
+ else:
573
+ log("✅ Push completed without creating a new version for Nextmv application.")
574
+
575
+ return
576
+
577
+ now = datetime.now(timezone.utc)
578
+ if version_id is None:
579
+ version_id = safe_id(prefix="version") + f"-{now.strftime('%Y%m%d-%H%M%S')}"
580
+ if version_name is None:
581
+ version_name = f"Version {version_id}"
582
+ if version_description is None:
583
+ version_description = f"Version created automatically from push at {now.strftime('%Y-%m-%dT%H:%M:%SZ')}"
584
+
585
+ version = self.new_version(
586
+ id=version_id,
587
+ name=version_name,
588
+ description=version_description,
589
+ )
590
+ version_dict = version.to_dict()
591
+
592
+ if verbose:
593
+ if rich_print:
594
+ rich.print(
595
+ f":white_check_mark: Automatically created new version [magenta]{version.id}[/magenta].",
596
+ file=sys.stderr,
597
+ )
598
+ rich.print_json(data=version_dict)
599
+ else:
600
+ log(f'✅ Automatically created new version "{version.id}".')
601
+ log(json.dumps(version_dict, indent=2))
602
+
546
603
  def update(
547
604
  self,
548
605
  name: str | None = None,
@@ -9,7 +9,6 @@ import requests
9
9
  from nextmv.cloud.acceptance_test import AcceptanceTest, Metric
10
10
  from nextmv.cloud.batch_experiment import BatchExperimentRun, ExperimentStatus
11
11
  from nextmv.polling import DEFAULT_POLLING_OPTIONS, PollingOptions, poll
12
- from nextmv.safe import safe_id
13
12
 
14
13
  if TYPE_CHECKING:
15
14
  from . import Application
@@ -164,8 +163,8 @@ class ApplicationAcceptanceMixin:
164
163
  self: "Application",
165
164
  candidate_instance_id: str,
166
165
  baseline_instance_id: str,
166
+ id: str,
167
167
  metrics: list[Metric | dict[str, Any]],
168
- id: str | None = None,
169
168
  name: str | None = None,
170
169
  input_set_id: str | None = None,
171
170
  description: str | None = None,
@@ -186,8 +185,8 @@ class ApplicationAcceptanceMixin:
186
185
  ID of the candidate instance.
187
186
  baseline_instance_id : str
188
187
  ID of the baseline instance.
189
- id : str | None, default=None
190
- ID of the acceptance test. Will be generated if not provided.
188
+ id : str
189
+ ID of the acceptance test.
191
190
  metrics : list[Union[Metric, dict[str, Any]]]
192
191
  List of metrics to use for the acceptance test.
193
192
  name : Optional[str], default=None
@@ -211,11 +210,6 @@ class ApplicationAcceptanceMixin:
211
210
  If the batch experiment ID does not match the acceptance test ID.
212
211
  """
213
212
 
214
- # Generate ID if not provided
215
- if id is None or id == "":
216
- id = safe_id("acceptance")
217
-
218
- # Use ID as name if name not provided
219
213
  if name is None or name == "":
220
214
  name = id
221
215
 
@@ -271,10 +265,11 @@ class ApplicationAcceptanceMixin:
271
265
  "metrics": payload_metrics,
272
266
  "experiment_id": batch_experiment_id,
273
267
  "name": name,
274
- "id": id,
275
268
  }
276
269
  if description is not None:
277
270
  payload["description"] = description
271
+ if id is not None:
272
+ payload["id"] = id
278
273
 
279
274
  response = self.client.request(
280
275
  method="POST",
@@ -288,8 +283,8 @@ class ApplicationAcceptanceMixin:
288
283
  self: "Application",
289
284
  candidate_instance_id: str,
290
285
  baseline_instance_id: str,
286
+ id: str,
291
287
  metrics: list[Metric | dict[str, Any]],
292
- id: str | None = None,
293
288
  name: str | None = None,
294
289
  input_set_id: str | None = None,
295
290
  description: str | None = None,
@@ -307,8 +302,8 @@ class ApplicationAcceptanceMixin:
307
302
  ID of the candidate instance.
308
303
  baseline_instance_id : str
309
304
  ID of the baseline instance.
310
- id : str | None, default=None
311
- ID of the acceptance test. Will be generated if not provided.
305
+ id : str
306
+ ID of the acceptance test.
312
307
  metrics : list[Union[Metric, dict[str, Any]]]
313
308
  List of metrics to use for the acceptance test.
314
309
  name : Optional[str], default=None
@@ -10,6 +10,7 @@ from nextmv.cloud.batch_experiment import (
10
10
  BatchExperimentMetadata,
11
11
  BatchExperimentRun,
12
12
  ExperimentStatus,
13
+ to_runs,
13
14
  )
14
15
  from nextmv.cloud.input_set import InputSet, ManagedInput
15
16
  from nextmv.cloud.scenario import Scenario, ScenarioInputType, _option_sets, _scenarios_by_id
@@ -257,6 +258,7 @@ class ApplicationBatchMixin:
257
258
  self: "Application",
258
259
  name: str | None = None,
259
260
  input_set_id: str | None = None,
261
+ instance_ids: list[str] | None = None,
260
262
  description: str | None = None,
261
263
  id: str | None = None,
262
264
  option_sets: dict[str, dict[str, str]] | None = None,
@@ -272,6 +274,9 @@ class ApplicationBatchMixin:
272
274
  Name of the batch experiment. If not provided, the ID will be used as the name.
273
275
  input_set_id: str | None
274
276
  ID of the input set to use for the batch experiment.
277
+ instance_ids: list[str]
278
+ This argument is deprecated, use `runs` instead.
279
+ List of instance IDs to use for the batch experiment.
275
280
  description: Optional[str]
276
281
  Optional description of the batch experiment.
277
282
  id: Optional[str]
@@ -299,11 +304,11 @@ class ApplicationBatchMixin:
299
304
  """
300
305
 
301
306
  # Generate ID if not provided
302
- if id is None or id == "":
307
+ if id is None:
303
308
  id = safe_id("batch")
304
309
 
305
310
  # Use ID as name if name not provided
306
- if name is None or name == "":
311
+ if name is None:
307
312
  name = id
308
313
 
309
314
  payload = {
@@ -312,6 +317,11 @@ class ApplicationBatchMixin:
312
317
  }
313
318
  if input_set_id is not None:
314
319
  payload["input_set_id"] = input_set_id
320
+ if instance_ids is not None:
321
+ input_set = self.input_set(input_set_id)
322
+ runs = to_runs(instance_ids, input_set)
323
+ payload_runs = [run.to_dict() for run in runs]
324
+ payload["runs"] = payload_runs
315
325
  if description is not None:
316
326
  payload["description"] = description
317
327
  if option_sets is not None:
@@ -336,6 +346,7 @@ class ApplicationBatchMixin:
336
346
  self: "Application",
337
347
  name: str | None = None,
338
348
  input_set_id: str | None = None,
349
+ instance_ids: list[str] | None = None,
339
350
  description: str | None = None,
340
351
  id: str | None = None,
341
352
  option_sets: dict[str, dict[str, str]] | None = None,
@@ -357,6 +368,9 @@ class ApplicationBatchMixin:
357
368
  Name of the batch experiment. If not provided, the ID will be used as the name.
358
369
  input_set_id: str
359
370
  ID of the input set to use for the batch experiment.
371
+ instance_ids: list[str]
372
+ List of instance IDs to use for the batch experiment. This argument
373
+ is deprecated, use `runs` instead.
360
374
  description: Optional[str]
361
375
  Optional description of the batch experiment.
362
376
  id: Optional[str]
@@ -388,6 +402,7 @@ class ApplicationBatchMixin:
388
402
  batch_id = self.new_batch_experiment(
389
403
  name=name,
390
404
  input_set_id=input_set_id,
405
+ instance_ids=instance_ids,
391
406
  description=description,
392
407
  id=id,
393
408
  option_sets=option_sets,
@@ -466,11 +481,11 @@ class ApplicationBatchMixin:
466
481
  raise ValueError("At least one scenario must be provided")
467
482
 
468
483
  # Generate ID if not provided
469
- if id is None or id == "":
484
+ if id is None:
470
485
  id = safe_id("scenario")
471
486
 
472
487
  # Use ID as name if name not provided
473
- if name is None or name == "":
488
+ if name is None:
474
489
  name = id
475
490
 
476
491
  scenarios_by_id = _scenarios_by_id(scenarios)
@@ -6,7 +6,6 @@ from datetime import datetime
6
6
  from typing import TYPE_CHECKING
7
7
 
8
8
  from nextmv.cloud.input_set import InputSet, ManagedInput
9
- from nextmv.safe import safe_id
10
9
 
11
10
  if TYPE_CHECKING:
12
11
  from . import Application
@@ -17,32 +16,6 @@ class ApplicationInputSetMixin:
17
16
  Mixin class for managing app input sets within an application.
18
17
  """
19
18
 
20
- def delete_input_set(self: "Application", input_set_id: str) -> None:
21
- """
22
- Delete an input set.
23
-
24
- Deletes an input set along with all the associated information.
25
-
26
- Parameters
27
- ----------
28
- input_set_id : str
29
- ID of the input set to delete.
30
-
31
- Raises
32
- ------
33
- requests.HTTPError
34
- If the response status code is not 2xx.
35
-
36
- Examples
37
- --------
38
- >>> app.delete_input_set("input-set-123")
39
- """
40
-
41
- _ = self.client.request(
42
- method="DELETE",
43
- endpoint=f"{self.experiments_endpoint}/inputsets/{input_set_id}",
44
- )
45
-
46
19
  def input_set(self: "Application", input_set_id: str) -> InputSet:
47
20
  """
48
21
  Get an input set.
@@ -108,8 +81,8 @@ class ApplicationInputSetMixin:
108
81
 
109
82
  def new_input_set(
110
83
  self: "Application",
111
- id: str | None = None,
112
- name: str | None = None,
84
+ id: str,
85
+ name: str,
113
86
  description: str | None = None,
114
87
  end_time: datetime | None = None,
115
88
  instance_id: str | None = None,
@@ -134,11 +107,10 @@ class ApplicationInputSetMixin:
134
107
 
135
108
  Parameters
136
109
  ----------
137
- id: str | None = None
138
- ID of the input set, will be generated if not provided.
139
- name: str | None = None
140
- Name of the input set. If not provided, the ID will be used as
141
- the name.
110
+ id: str
111
+ ID of the input set
112
+ name: str
113
+ Name of the input set.
142
114
  description: Optional[str]
143
115
  Optional description of the input set.
144
116
  end_time: Optional[datetime]
@@ -173,14 +145,6 @@ class ApplicationInputSetMixin:
173
145
  If the response status code is not 2xx.
174
146
  """
175
147
 
176
- # Generate ID if not provided
177
- if id is None or id == "":
178
- id = safe_id("input-set")
179
-
180
- # Use ID as name if name not provided
181
- if name is None or name == "":
182
- name = id
183
-
184
148
  payload = {
185
149
  "id": id,
186
150
  "name": name,
@@ -194,15 +194,15 @@ class ApplicationInstanceMixin:
194
194
  'Production Instance'
195
195
  """
196
196
 
197
- if exist_ok and (id is None or id == ""):
197
+ if exist_ok and id is None:
198
198
  raise ValueError("If exist_ok is True, id must be provided")
199
199
 
200
200
  if exist_ok and self.instance_exists(instance_id=id):
201
201
  return self.instance(instance_id=id)
202
202
 
203
- if id is None or id == "":
203
+ if id is None:
204
204
  id = safe_id(prefix="instance")
205
- if name is None or name == "":
205
+ if name is None:
206
206
  name = id
207
207
 
208
208
  payload = {
@@ -147,9 +147,9 @@ class ApplicationManagedInputMixin:
147
147
  if upload_id is None and run_id is None:
148
148
  raise ValueError("Either upload_id or run_id must be specified")
149
149
 
150
- if id is None or id == "":
150
+ if id is None:
151
151
  id = safe_id(prefix="managed-input")
152
- if name is None or name == "":
152
+ if name is None:
153
153
  name = id
154
154
 
155
155
  payload = {
@@ -132,16 +132,15 @@ class ApplicationVersionMixin:
132
132
  ... )
133
133
  """
134
134
 
135
- if exist_ok and (id is None or id == ""):
135
+ if exist_ok and id is None:
136
136
  raise ValueError("If exist_ok is True, id must be provided")
137
137
 
138
138
  if exist_ok and self.version_exists(version_id=id):
139
139
  return self.version(version_id=id)
140
140
 
141
- if id is None or id == "":
141
+ if id is None:
142
142
  id = safe_id(prefix="version")
143
-
144
- if name is None or name == "":
143
+ if name is None:
145
144
  name = id
146
145
 
147
146
  payload = {
@@ -30,9 +30,7 @@ class ExperimentStatus(str, Enum):
30
30
 
31
31
  You can import the `ExperimentStatus` class directly from `cloud`:
32
32
 
33
- ```python
34
- from nextmv.cloud import ExperimentStatus
35
- ```
33
+ ```python from nextmv.cloud import ExperimentStatus ```
36
34
 
37
35
  This enum represents the comprehensive set of possible states for an
38
36
  experiment in Nextmv Cloud.
@@ -225,12 +225,12 @@ class Integration(BaseModel):
225
225
  def new( # noqa: C901
226
226
  cls,
227
227
  client: Client,
228
+ name: str,
228
229
  integration_type: IntegrationType | str,
229
230
  exec_types: list[ManifestType | str],
230
231
  provider: IntegrationProvider | str,
231
232
  provider_config: dict[str, Any],
232
233
  integration_id: str | None = None,
233
- name: str | None = None,
234
234
  description: str | None = None,
235
235
  is_global: bool = False,
236
236
  application_ids: list[str] | None = None,
@@ -243,6 +243,8 @@ class Integration(BaseModel):
243
243
  ----------
244
244
  client : Client
245
245
  Client to use for interacting with the Nextmv Cloud API.
246
+ name : str
247
+ The name of the integration.
246
248
  integration_type : IntegrationType | str
247
249
  The type of the integration. Please refer to the `IntegrationType`
248
250
  enum for possible values.
@@ -257,9 +259,6 @@ class Integration(BaseModel):
257
259
  integration_id : str, optional
258
260
  The unique identifier of the integration. If not provided,
259
261
  it will be generated automatically.
260
- name : str | None, optional
261
- The name of the integration. If not provided, the integration ID
262
- will be used as the name.
263
262
  description : str, optional
264
263
  An optional description of the integration.
265
264
  is_global : bool, optional, default=False
@@ -303,10 +302,8 @@ class Integration(BaseModel):
303
302
  elif not is_global and application_ids is None:
304
303
  raise ValueError("A non-global integration must have specific application IDs.")
305
304
 
306
- if integration_id is None or integration_id == "":
305
+ if integration_id is None:
307
306
  integration_id = safe_id("integration")
308
- if name is None or name == "":
309
- name = integration_id
310
307
 
311
308
  if exist_ok:
312
309
  try:
nextmv/deprecated.py CHANGED
@@ -1,5 +1,4 @@
1
- """
2
- Utilities for handling deprecated functionality within the Nextmv Python SDK.
1
+ """Utilities for handling deprecated functionality within the Nextmv Python SDK.
3
2
 
4
3
  This module provides tools to mark functions, methods, or features as deprecated,
5
4
  emitting appropriate warnings to users. These warnings inform users that the
@@ -14,8 +13,7 @@ import warnings
14
13
 
15
14
 
16
15
  def deprecated(name: str, reason: str) -> None:
17
- """
18
- Mark functionality as deprecated with a warning message.
16
+ """Mark functionality as deprecated with a warning message.
19
17
 
20
18
  This function emits a DeprecationWarning when called, indicating that
21
19
  the functionality will be removed in a future release.
@@ -42,7 +40,7 @@ def deprecated(name: str, reason: str) -> None:
42
40
 
43
41
  warnings.simplefilter("always", DeprecationWarning)
44
42
  warnings.warn(
45
- f"{name}: {reason}. This functionality will be removed in the next major release.",
43
+ f"{name}: {reason}. This functionality will be removed in a future release",
46
44
  category=DeprecationWarning,
47
45
  stacklevel=2,
48
46
  )
nextmv/input.py CHANGED
@@ -38,6 +38,7 @@ from enum import Enum
38
38
  from typing import Any
39
39
 
40
40
  from nextmv._serialization import serialize_json
41
+ from nextmv.deprecated import deprecated
41
42
  from nextmv.options import Options
42
43
 
43
44
  INPUTS_KEY = "inputs"
@@ -940,6 +941,57 @@ class LocalInputLoader(InputLoader):
940
941
  return data
941
942
 
942
943
 
944
+ def load_local(
945
+ input_format: InputFormat | None = InputFormat.JSON,
946
+ options: Options | None = None,
947
+ path: str | None = None,
948
+ csv_configurations: dict[str, Any] | None = None,
949
+ ) -> Input:
950
+ """
951
+ !!! warning
952
+ `load_local` is deprecated, use `load` instead.
953
+
954
+ Load input data from local sources.
955
+
956
+ This is a convenience function for instantiating a `LocalInputLoader`
957
+ and calling its `load` method.
958
+
959
+ Parameters
960
+ ----------
961
+ input_format : InputFormat, optional
962
+ Format of the input data. Default is `InputFormat.JSON`.
963
+ options : Options, optional
964
+ Options for loading the input data.
965
+ path : str, optional
966
+ Path to the input data.
967
+ csv_configurations : dict[str, Any], optional
968
+ Configurations for loading CSV files. Custom kwargs for
969
+ Python's `csv.DictReader`.
970
+
971
+ Returns
972
+ -------
973
+ Input
974
+ The loaded input data in an Input object.
975
+
976
+ Raises
977
+ ------
978
+ ValueError
979
+ If the path is invalid or data format is incorrect.
980
+
981
+ See Also
982
+ --------
983
+ load : The recommended function to use instead.
984
+ """
985
+
986
+ deprecated(
987
+ name="load_local",
988
+ reason="`load_local` is deprecated, use `load` instead",
989
+ )
990
+
991
+ loader = LocalInputLoader()
992
+ return loader.load(input_format, options, path, csv_configurations)
993
+
994
+
943
995
  _LOCAL_INPUT_LOADER = LocalInputLoader()
944
996
  """Default instance of LocalInputLoader used by the load function."""
945
997
 
nextmv/local/runner.py CHANGED
@@ -204,7 +204,7 @@ def new_run(
204
204
  if description is None:
205
205
  description = f"Local run created at {created_at.isoformat().replace('+00:00', 'Z')}"
206
206
 
207
- if name is None or name == "":
207
+ if name is None:
208
208
  name = f"local run {run_id}"
209
209
 
210
210
  information = RunInformation(