nextmv 1.0.0.dev4__py3-none-any.whl → 1.0.0.dev5__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.
nextmv/cloud/__init__.py CHANGED
@@ -1,42 +1,5 @@
1
1
  """Functionality for interacting with the Nextmv Cloud."""
2
2
 
3
- # These imports are kept for backwards compatibility but the preferred import path is
4
- # from nextmv directly. These imports will be removed in a future release.
5
- from nextmv.manifest import MANIFEST_FILE_NAME as MANIFEST_FILE_NAME
6
- from nextmv.manifest import Manifest as Manifest
7
- from nextmv.manifest import ManifestBuild as ManifestBuild
8
- from nextmv.manifest import ManifestContent as ManifestContent
9
- from nextmv.manifest import ManifestContentMultiFile as ManifestContentMultiFile
10
- from nextmv.manifest import ManifestContentMultiFileInput as ManifestContentMultiFileInput
11
- from nextmv.manifest import ManifestContentMultiFileOutput as ManifestContentMultiFileOutput
12
- from nextmv.manifest import ManifestOption as ManifestOption
13
- from nextmv.manifest import ManifestPython as ManifestPython
14
- from nextmv.manifest import ManifestPythonModel as ManifestPythonModel
15
- from nextmv.manifest import ManifestRuntime as ManifestRuntime
16
- from nextmv.manifest import ManifestType as ManifestType
17
- from nextmv.polling import PollingOptions as PollingOptions
18
- from nextmv.polling import poll as poll
19
- from nextmv.run import ErrorLog as ErrorLog
20
- from nextmv.run import ExternalRunResult as ExternalRunResult
21
- from nextmv.run import Format as Format
22
- from nextmv.run import FormatInput as FormatInput
23
- from nextmv.run import FormatOutput as FormatOutput
24
- from nextmv.run import Metadata as Metadata
25
- from nextmv.run import RunConfiguration as RunConfiguration
26
- from nextmv.run import RunInformation as RunInformation
27
- from nextmv.run import RunLog as RunLog
28
- from nextmv.run import RunQueuing as RunQueuing
29
- from nextmv.run import RunResult as RunResult
30
- from nextmv.run import RunType as RunType
31
- from nextmv.run import RunTypeConfiguration as RunTypeConfiguration
32
- from nextmv.run import TrackedRun as TrackedRun
33
- from nextmv.run import TrackedRunStatus as TrackedRunStatus
34
- from nextmv.run import run_duration as run_duration
35
- from nextmv.safe import safe_id as safe_id
36
- from nextmv.safe import safe_name_and_id as safe_name_and_id
37
- from nextmv.status import Status as Status
38
- from nextmv.status import StatusV2 as StatusV2
39
-
40
3
  from .acceptance_test import AcceptanceTest as AcceptanceTest
41
4
  from .acceptance_test import AcceptanceTestResults as AcceptanceTestResults
42
5
  from .acceptance_test import Comparison as Comparison
@@ -52,7 +15,6 @@ from .acceptance_test import MetricToleranceType as MetricToleranceType
52
15
  from .acceptance_test import MetricType as MetricType
53
16
  from .acceptance_test import ResultStatistics as ResultStatistics
54
17
  from .acceptance_test import StatisticType as StatisticType
55
- from .acceptance_test import ToleranceType as ToleranceType
56
18
  from .account import Account as Account
57
19
  from .account import AccountMember as AccountMember
58
20
  from .account import Queue as Queue
@@ -13,7 +13,7 @@ StatisticType : Enum
13
13
  Type of statistical process for collapsing multiple values of a metric.
14
14
  Comparison : Enum
15
15
  Comparison operators to use for comparing two metrics.
16
- ToleranceType : Enum
16
+ MetricToleranceType : Enum
17
17
  Type of tolerance used for a metric.
18
18
  ExperimentStatus : Enum
19
19
  Status of an acceptance test experiment.
@@ -46,7 +46,6 @@ from enum import Enum
46
46
 
47
47
  from nextmv.base_model import BaseModel
48
48
  from nextmv.cloud.batch_experiment import ExperimentStatus
49
- from nextmv.deprecated import deprecated
50
49
 
51
50
 
52
51
  class MetricType(str, Enum):
@@ -212,69 +211,6 @@ class Comparison(str, Enum):
212
211
  """Not equal to metric type."""
213
212
 
214
213
 
215
- class ToleranceType(str, Enum):
216
- """
217
- !!! warning
218
- `ToleranceType` is deprecated, use `MetricToleranceType` instead.
219
-
220
- Type of tolerance used for a metric.
221
-
222
- You can import the `ToleranceType` class directly from `cloud`:
223
-
224
- ```python
225
- from nextmv.cloud import ToleranceType
226
- ```
227
-
228
- This enumeration defines the different types of tolerances that can be used
229
- when comparing metrics in acceptance tests.
230
-
231
- Attributes
232
- ----------
233
- undefined : str
234
- Undefined tolerance type (empty string).
235
- absolute : str
236
- Absolute tolerance type, using a fixed value.
237
- relative : str
238
- Relative tolerance type, using a percentage.
239
-
240
- Examples
241
- --------
242
- >>> from nextmv.cloud import ToleranceType
243
- >>> tol_type = ToleranceType.absolute
244
- >>> tol_type
245
- <ToleranceType.absolute: 'absolute'>
246
- """
247
-
248
- undefined = ""
249
- """ToleranceType is deprecated, please use MetricToleranceType instead.
250
- Undefined tolerance type."""
251
- absolute = "absolute"
252
- """ToleranceType is deprecated, please use MetricToleranceType instead.
253
- Absolute tolerance type."""
254
- relative = "relative"
255
- """ToleranceType is deprecated, please use MetricToleranceType instead.
256
- Relative tolerance type."""
257
-
258
-
259
- # Override __getattribute__ to emit deprecation warnings when enum values are accessed
260
- _original_getattribute = ToleranceType.__class__.__getattribute__
261
-
262
-
263
- def _deprecated_getattribute(cls, name: str):
264
- # Only emit deprecation warning if this is specifically the ToleranceType class
265
- if cls is ToleranceType and name in ("undefined", "absolute", "relative"):
266
- deprecated(
267
- f"ToleranceType.{name}",
268
- "ToleranceType is deprecated and will be removed in a future version. "
269
- "Please use MetricToleranceType instead",
270
- )
271
-
272
- return _original_getattribute(cls, name)
273
-
274
-
275
- ToleranceType.__class__.__getattribute__ = _deprecated_getattribute
276
-
277
-
278
214
  class MetricToleranceType(str, Enum):
279
215
  """
280
216
  Type of tolerance used for a metric.
nextmv/cloud/account.py CHANGED
@@ -21,7 +21,7 @@ from pydantic import AliasChoices, Field
21
21
 
22
22
  from nextmv.base_model import BaseModel
23
23
  from nextmv.cloud.client import Client
24
- from nextmv.status import Status, StatusV2
24
+ from nextmv.status import StatusV2
25
25
 
26
26
 
27
27
  class QueuedRun(BaseModel):
@@ -57,8 +57,6 @@ class QueuedRun(BaseModel):
57
57
  ID of the application version used for the run.
58
58
  execution_class : str
59
59
  Execution class used for the run.
60
- status : Status
61
- Deprecated: use status_v2.
62
60
  status_v2 : StatusV2
63
61
  Status of the run.
64
62
 
@@ -74,7 +72,6 @@ class QueuedRun(BaseModel):
74
72
  ... "application_instance_id": "appins-123456",
75
73
  ... "application_version_id": "appver-123456",
76
74
  ... "execution_class": "standard",
77
- ... "status": "RUNNING",
78
75
  ... "status_v2": "RUNNING"
79
76
  ... })
80
77
  >>> print(queued_run.name)
@@ -99,8 +96,6 @@ class QueuedRun(BaseModel):
99
96
  """ID of the application version used for the run."""
100
97
  execution_class: str
101
98
  """Execution class used for the run."""
102
- status: Status
103
- """Deprecated: use status_v2."""
104
99
  status_v2: StatusV2
105
100
  """Status of the run."""
106
101
 
@@ -867,6 +867,8 @@ class Application(
867
867
  output_config = multi_config["output_configuration"] = {}
868
868
  if content.multi_file.output.statistics:
869
869
  output_config["statistics_path"] = content.multi_file.output.statistics
870
+ if content.multi_file.output.metrics:
871
+ output_config["metrics_path"] = content.multi_file.output.metrics
870
872
  if content.multi_file.output.assets:
871
873
  output_config["assets_path"] = content.multi_file.output.assets
872
874
  if content.multi_file.output.solutions:
@@ -10,7 +10,6 @@ from nextmv.cloud.batch_experiment import (
10
10
  BatchExperimentMetadata,
11
11
  BatchExperimentRun,
12
12
  ExperimentStatus,
13
- to_runs,
14
13
  )
15
14
  from nextmv.cloud.input_set import InputSet, ManagedInput
16
15
  from nextmv.cloud.scenario import Scenario, ScenarioInputType, _option_sets, _scenarios_by_id
@@ -258,7 +257,6 @@ class ApplicationBatchMixin:
258
257
  self: "Application",
259
258
  name: str | None = None,
260
259
  input_set_id: str | None = None,
261
- instance_ids: list[str] | None = None,
262
260
  description: str | None = None,
263
261
  id: str | None = None,
264
262
  option_sets: dict[str, dict[str, str]] | None = None,
@@ -274,9 +272,6 @@ class ApplicationBatchMixin:
274
272
  Name of the batch experiment. If not provided, the ID will be used as the name.
275
273
  input_set_id: str | None
276
274
  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.
280
275
  description: Optional[str]
281
276
  Optional description of the batch experiment.
282
277
  id: Optional[str]
@@ -308,7 +303,7 @@ class ApplicationBatchMixin:
308
303
  id = safe_id("batch")
309
304
 
310
305
  # Use ID as name if name not provided
311
- if name is None:
306
+ if name is None or name == "":
312
307
  name = id
313
308
 
314
309
  payload = {
@@ -317,11 +312,6 @@ class ApplicationBatchMixin:
317
312
  }
318
313
  if input_set_id is not None:
319
314
  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
325
315
  if description is not None:
326
316
  payload["description"] = description
327
317
  if option_sets is not None:
@@ -346,7 +336,6 @@ class ApplicationBatchMixin:
346
336
  self: "Application",
347
337
  name: str | None = None,
348
338
  input_set_id: str | None = None,
349
- instance_ids: list[str] | None = None,
350
339
  description: str | None = None,
351
340
  id: str | None = None,
352
341
  option_sets: dict[str, dict[str, str]] | None = None,
@@ -368,9 +357,6 @@ class ApplicationBatchMixin:
368
357
  Name of the batch experiment. If not provided, the ID will be used as the name.
369
358
  input_set_id: str
370
359
  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.
374
360
  description: Optional[str]
375
361
  Optional description of the batch experiment.
376
362
  id: Optional[str]
@@ -402,7 +388,6 @@ class ApplicationBatchMixin:
402
388
  batch_id = self.new_batch_experiment(
403
389
  name=name,
404
390
  input_set_id=input_set_id,
405
- instance_ids=instance_ids,
406
391
  description=description,
407
392
  id=id,
408
393
  option_sets=option_sets,
@@ -485,7 +470,7 @@ class ApplicationBatchMixin:
485
470
  id = safe_id("scenario")
486
471
 
487
472
  # Use ID as name if name not provided
488
- if name is None:
473
+ if name is None or name == "":
489
474
  name = id
490
475
 
491
476
  scenarios_by_id = _scenarios_by_id(scenarios)
@@ -202,7 +202,7 @@ class ApplicationInstanceMixin:
202
202
 
203
203
  if id is None or id == "":
204
204
  id = safe_id(prefix="instance")
205
- if name is None:
205
+ if name is None or name == "":
206
206
  name = id
207
207
 
208
208
  payload = {
@@ -149,7 +149,7 @@ class ApplicationManagedInputMixin:
149
149
 
150
150
  if id is None or id == "":
151
151
  id = safe_id(prefix="managed-input")
152
- if name is None:
152
+ if name is None or name == "":
153
153
  name = id
154
154
 
155
155
  payload = {
@@ -21,7 +21,14 @@ from nextmv.cloud.url import DownloadURL
21
21
  from nextmv.input import Input, InputFormat
22
22
  from nextmv.logger import log
23
23
  from nextmv.options import Options
24
- from nextmv.output import ASSETS_KEY, STATISTICS_KEY, Asset, Output, OutputFormat, Statistics
24
+ from nextmv.output import (
25
+ ASSETS_KEY,
26
+ STATISTICS_KEY,
27
+ Asset,
28
+ Output,
29
+ OutputFormat,
30
+ Statistics,
31
+ )
25
32
  from nextmv.polling import DEFAULT_POLLING_OPTIONS, PollingOptions, poll
26
33
  from nextmv.run import (
27
34
  ExternalRunResult,
@@ -204,7 +204,7 @@ class ApplicationShadowMixin:
204
204
  shadow_test_id = safe_id("shadow")
205
205
 
206
206
  # Use ID as name if name not provided
207
- if name is None:
207
+ if name is None or name == "":
208
208
  name = shadow_test_id
209
209
 
210
210
  payload = {
@@ -210,7 +210,7 @@ class ApplicationSwitchbackMixin:
210
210
  switchback_test_id = safe_id("switchback")
211
211
 
212
212
  # Use ID as name if name not provided
213
- if name is None:
213
+ if name is None or name == "":
214
214
  name = switchback_test_id
215
215
 
216
216
  payload = {
@@ -141,7 +141,7 @@ class ApplicationVersionMixin:
141
141
  if id is None or id == "":
142
142
  id = safe_id(prefix="version")
143
143
 
144
- if name is None:
144
+ if name is None or name == "":
145
145
  name = id
146
146
 
147
147
  payload = {
nextmv/cloud/client.py CHANGED
@@ -303,7 +303,7 @@ class Client:
303
303
  if data is not None:
304
304
  kwargs["data"] = data
305
305
  if payload is not None:
306
- if isinstance(payload, (dict, list)):
306
+ if isinstance(payload, dict | list):
307
307
  data = deflated_serialize_json(payload, json_configurations=json_configurations)
308
308
  kwargs["data"] = data
309
309
  else:
@@ -26,12 +26,10 @@ assets = create_visuals(name, input.data["radius"], input.data["distance"])
26
26
  output = nextmv.Output(
27
27
  options=options,
28
28
  solution={"message": message},
29
- statistics=nextmv.Statistics(
30
- result=nextmv.ResultStatistics(
31
- value=1.23,
32
- custom={"message": message},
33
- ),
34
- ),
29
+ metrics={
30
+ "value": 1.23,
31
+ "custom": {"message": message},
32
+ },
35
33
  assets=assets,
36
34
  )
37
35
  nextmv.write(output)
nextmv/deprecated.py CHANGED
@@ -1,4 +1,5 @@
1
- """Utilities for handling deprecated functionality within the Nextmv Python SDK.
1
+ """
2
+ Utilities for handling deprecated functionality within the Nextmv Python SDK.
2
3
 
3
4
  This module provides tools to mark functions, methods, or features as deprecated,
4
5
  emitting appropriate warnings to users. These warnings inform users that the
@@ -13,7 +14,8 @@ import warnings
13
14
 
14
15
 
15
16
  def deprecated(name: str, reason: str) -> None:
16
- """Mark functionality as deprecated with a warning message.
17
+ """
18
+ Mark functionality as deprecated with a warning message.
17
19
 
18
20
  This function emits a DeprecationWarning when called, indicating that
19
21
  the functionality will be removed in a future release.
@@ -40,7 +42,7 @@ def deprecated(name: str, reason: str) -> None:
40
42
 
41
43
  warnings.simplefilter("always", DeprecationWarning)
42
44
  warnings.warn(
43
- f"{name}: {reason}. This functionality will be removed in a future release",
45
+ f"{name}: {reason}. This functionality will be removed in the next major release.",
44
46
  category=DeprecationWarning,
45
47
  stacklevel=2,
46
48
  )
nextmv/input.py CHANGED
@@ -38,7 +38,6 @@ 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
42
41
  from nextmv.options import Options
43
42
 
44
43
  INPUTS_KEY = "inputs"
@@ -941,57 +940,6 @@ class LocalInputLoader(InputLoader):
941
940
  return data
942
941
 
943
942
 
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
-
995
943
  _LOCAL_INPUT_LOADER = LocalInputLoader()
996
944
  """Default instance of LocalInputLoader used by the load function."""
997
945
 
nextmv/local/executor.py CHANGED
@@ -22,6 +22,8 @@ process_run_information
22
22
  Function to update run metadata including duration and status.
23
23
  process_run_logs
24
24
  Function to process and save run logs.
25
+ process_run_metrics
26
+ Function to process and save run metrics.
25
27
  process_run_statistics
26
28
  Function to process and save run statistics.
27
29
  process_run_assets
@@ -57,7 +59,16 @@ from nextmv.local.local import (
57
59
  )
58
60
  from nextmv.local.plotly_handler import handle_plotly_visual
59
61
  from nextmv.manifest import Manifest, ManifestType
60
- from nextmv.output import ASSETS_KEY, OUTPUTS_KEY, SOLUTIONS_KEY, STATISTICS_KEY, Asset, OutputFormat, VisualSchema
62
+ from nextmv.output import (
63
+ ASSETS_KEY,
64
+ METRICS_KEY,
65
+ OUTPUTS_KEY,
66
+ SOLUTIONS_KEY,
67
+ STATISTICS_KEY,
68
+ Asset,
69
+ OutputFormat,
70
+ VisualSchema,
71
+ )
61
72
  from nextmv.status import StatusV2
62
73
 
63
74
 
@@ -305,7 +316,7 @@ def process_run_output(
305
316
  ) -> None:
306
317
  """
307
318
  Processes the result of the subprocess run. This function is in charge of
308
- handling the run results, including solutions, statistics, logs, assets,
319
+ handling the run results, including solutions, statistics, metrics, logs, assets,
309
320
  and visuals.
310
321
 
311
322
  Parameters
@@ -347,6 +358,13 @@ def process_run_output(
347
358
  result=result,
348
359
  stdout_output=stdout_output,
349
360
  )
361
+ process_run_metrics(
362
+ temp_run_outputs_dir=temp_run_outputs_dir,
363
+ outputs_dir=outputs_dir,
364
+ stdout_output=stdout_output,
365
+ temp_src=temp_src,
366
+ manifest=manifest,
367
+ )
350
368
  process_run_statistics(
351
369
  temp_run_outputs_dir=temp_run_outputs_dir,
352
370
  outputs_dir=outputs_dir,
@@ -499,6 +517,65 @@ def process_run_logs(
499
517
 
500
518
  f.write(std_err)
501
519
 
520
+ def process_run_metrics(
521
+ temp_run_outputs_dir: str,
522
+ outputs_dir: str,
523
+ stdout_output: str | dict[str, Any],
524
+ temp_src: str,
525
+ manifest: Manifest,
526
+ ) -> None:
527
+ """
528
+ Processes the metrics of the run. Checks for an outputs/metrics folder
529
+ or custom metrics file location from manifest. If found, copies to run
530
+ directory. Otherwise, attempts to extract metrics from stdout.
531
+
532
+ Parameters
533
+ ----------
534
+ temp_run_outputs_dir : str
535
+ The path to the temporary outputs directory.
536
+ outputs_dir : str
537
+ The path to the outputs directory in the run directory.
538
+ stdout_output : Union[str, dict[str, Any]]
539
+ The stdout output of the run, either as raw string or parsed dictionary.
540
+ temp_src : str
541
+ The path to the temporary source directory.
542
+ manifest : Manifest
543
+ The application manifest containing configuration and custom paths.
544
+ """
545
+
546
+ metrics_dst = os.path.join(outputs_dir, METRICS_KEY)
547
+ os.makedirs(metrics_dst, exist_ok=True)
548
+ metrics_file = f"{METRICS_KEY}.json"
549
+
550
+ # Check for custom location in manifest and override metrics_src if needed.
551
+ if (
552
+ manifest.configuration is not None
553
+ and manifest.configuration.content is not None
554
+ and manifest.configuration.content.format == OutputFormat.MULTI_FILE
555
+ and manifest.configuration.content.multi_file is not None
556
+ ):
557
+ metrics_src_file = os.path.join(temp_src, manifest.configuration.content.multi_file.output.metrics)
558
+
559
+ # If the custom metrics file exists, copy it to the metrics destination
560
+ if os.path.exists(metrics_src_file) and os.path.isfile(metrics_src_file):
561
+ metrics_dst_file = os.path.join(metrics_dst, metrics_file)
562
+ shutil.copy2(metrics_src_file, metrics_dst_file)
563
+ return
564
+
565
+ metrics_src = os.path.join(temp_run_outputs_dir, METRICS_KEY)
566
+ if os.path.exists(metrics_src) and os.path.isdir(metrics_src):
567
+ shutil.copytree(metrics_src, metrics_dst, dirs_exist_ok=True)
568
+ return
569
+
570
+ if not isinstance(stdout_output, dict):
571
+ return
572
+
573
+ if METRICS_KEY not in stdout_output:
574
+ return
575
+
576
+ with open(os.path.join(metrics_dst, metrics_file), "w") as f:
577
+ metrics = {METRICS_KEY: stdout_output[METRICS_KEY]}
578
+ json.dump(metrics, f, indent=2)
502
579
 
503
580
  def process_run_statistics(
504
581
  temp_run_outputs_dir: str,
@@ -508,6 +585,9 @@ def process_run_statistics(
508
585
  manifest: Manifest,
509
586
  ) -> None:
510
587
  """
588
+ !!! warning
589
+ `process_run_statistics` is deprecated, use `process_run_metrics` instead.
590
+
511
591
  Processes the statistics of the run. Checks for an outputs/statistics folder
512
592
  or custom statistics file location from manifest. If found, copies to run
513
593
  directory. Otherwise, attempts to extract statistics from stdout.
@@ -848,7 +928,7 @@ def _copy_new_or_modified_files( # noqa: C901
848
928
  This function identifies files that are either new (not present in the original
849
929
  source) or have been modified (different content, checksum, or modification time)
850
930
  compared to the original source. It excludes files that exist in specified
851
- exclusion directories to avoid copying input data, statistics, or assets as
931
+ exclusion directories to avoid copying input data, statistics, metrics, or assets as
852
932
  solution outputs.
853
933
 
854
934
  Parameters
@@ -111,7 +111,7 @@ def extract_coordinates(coords, all_coords) -> None:
111
111
  like Polygons and MultiPolygons
112
112
  """
113
113
  if isinstance(coords, list):
114
- if len(coords) == 2 and isinstance(coords[0], (int, float)) and isinstance(coords[1], (int, float)):
114
+ if len(coords) == 2 and isinstance(coords[0], int | float) and isinstance(coords[1], int | float):
115
115
  # This is a coordinate pair [lon, lat]
116
116
  all_coords.append(coords)
117
117
  else:
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:
207
+ if name is None or name == "":
208
208
  name = f"local run {run_id}"
209
209
 
210
210
  information = RunInformation(
nextmv/manifest.py CHANGED
@@ -829,7 +829,9 @@ class ManifestContentMultiFileOutput(BaseModel):
829
829
  Parameters
830
830
  ----------
831
831
  statistics : Optional[str], default=""
832
- The path to the statistics file.
832
+ Deprecated: Use `metrics` instead. The path to the statistics file.
833
+ metrics : Optional[str], default=""
834
+ The path to the metrics file.
833
835
  assets : Optional[str], default=""
834
836
  The path to the assets file.
835
837
  solutions : Optional[str], default=""
@@ -839,16 +841,18 @@ class ManifestContentMultiFileOutput(BaseModel):
839
841
  --------
840
842
  >>> from nextmv import ManifestContentMultiFileOutput
841
843
  >>> output_config = ManifestContentMultiFileOutput(
842
- ... statistics="my-outputs/statistics.json",
844
+ ... metrics="my-outputs/metrics.json",
843
845
  ... assets="my-outputs/assets.json",
844
846
  ... solutions="my-outputs/solutions/"
845
847
  ... )
846
- >>> output_config.statistics
847
- 'my-outputs/statistics.json'
848
+ >>> output_config.metrics
849
+ 'my-outputs/metrics.json'
848
850
  """
849
851
 
850
852
  statistics: str | None = ""
851
- """The path to the statistics file."""
853
+ """Deprecated: Use `metrics` instead. The path to the statistics file."""
854
+ metrics: str | None = ""
855
+ """The path to the metrics file."""
852
856
  assets: str | None = ""
853
857
  """The path to the assets file."""
854
858
  solutions: str | None = ""
@@ -878,7 +882,7 @@ class ManifestContentMultiFile(BaseModel):
878
882
  >>> multi_file_config = ManifestContentMultiFile(
879
883
  ... input=ManifestContentMultiFileInput(path="data/input/"),
880
884
  ... output=ManifestContentMultiFileOutput(
881
- ... statistics="my-outputs/statistics.json",
885
+ ... metrics="my-outputs/metrics.json",
882
886
  ... assets="my-outputs/assets.json",
883
887
  ... solutions="my-outputs/solutions/"
884
888
  ... )
@@ -919,7 +923,7 @@ class ManifestContent(BaseModel):
919
923
  ... multi_file=ManifestContentMultiFile(
920
924
  ... input=ManifestContentMultiFileInput(path="data/input/"),
921
925
  ... output=ManifestContentMultiFileOutput(
922
- ... statistics="my-outputs/statistics.json",
926
+ ... metrics="my-outputs/metrics.json",
923
927
  ... assets="my-outputs/assets.json",
924
928
  ... solutions="my-outputs/solutions/"
925
929
  ... )
nextmv/model.py CHANGED
@@ -195,7 +195,7 @@ class Model:
195
195
  ... return nextmv.Output(
196
196
  ... options=input.options,
197
197
  ... solution=nextroute_output.solutions[0].to_dict(),
198
- ... statistics=nextroute_output.statistics.to_dict(),
198
+ ... metrics=nextroute_output.metrics.to_dict(),
199
199
  ... )
200
200
  """
201
201
 
@@ -234,7 +234,7 @@ class Model:
234
234
  ... return Output(
235
235
  ... options=input.options,
236
236
  ... solution=result,
237
- ... statistics={"processing_time": 0.5}
237
+ ... metrics={"processing_time": 0.5}
238
238
  ... )
239
239
  """
240
240