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/options.py CHANGED
@@ -24,155 +24,6 @@ from dataclasses import dataclass
24
24
  from typing import Any
25
25
 
26
26
  from nextmv.base_model import BaseModel
27
- from nextmv.deprecated import deprecated
28
-
29
-
30
- @dataclass
31
- class Parameter:
32
- """
33
- !!! warning
34
- `Parameter` is deprecated, use `Option` instead.
35
-
36
- Parameter that is used in a `Configuration`. When a parameter is required,
37
- it is a good practice to provide a default value for it. This is because
38
- the configuration will raise an error if a required parameter is not
39
- provided through a command-line argument, an environment variable or a
40
- default value.
41
-
42
- Parameters
43
- ----------
44
- name : str
45
- The name of the parameter.
46
-
47
- param_type : type
48
- The type of the parameter.
49
-
50
- default : Any, optional
51
- The default value of the parameter. Even though this is optional, it is
52
- recommended to provide a default value for all parameters.
53
-
54
- description : str, optional
55
- An optional description of the parameter. This is useful for generating
56
- help messages for the configuration.
57
-
58
- required : bool, optional
59
- Whether the parameter is required. If a parameter is required, it will
60
- be an error to not provide a value for it, either through a command-line
61
- argument, an environment variable or a default value.
62
-
63
- choices : list[Optional[Any]], optional
64
- Limits values to a specific set of choices.
65
-
66
- Examples
67
- --------
68
- >>> from nextmv.options import Parameter
69
- >>> parameter = Parameter("timeout", int, 60, "The maximum timeout in seconds", required=True)
70
- """
71
-
72
- name: str
73
- """The name of the parameter."""
74
- param_type: type
75
- """The type of the parameter."""
76
-
77
- default: Any | None = None
78
- """The default value of the parameter. Even though this is optional, it is
79
- recommended to provide a default value for all parameters."""
80
- description: str | None = None
81
- """An optional description of the parameter. This is useful for generating
82
- help messages for the configuration."""
83
- required: bool = False
84
- """Whether the parameter is required. If a parameter is required, it will
85
- be an error to not provide a value for it, either trough a command-line
86
- argument, an environment variable or a default value."""
87
- choices: list[Any | None] = None
88
- """Limits values to a specific set of choices."""
89
-
90
- def __post_init__(self):
91
- """
92
- Post-initialization hook that marks this class as deprecated.
93
-
94
- This method is automatically called after the object is initialized.
95
- It displays a deprecation warning to inform users to use the `Option` class instead.
96
- """
97
- deprecated(
98
- name="Parameter",
99
- reason="`Parameter` is deprecated, use `Option` instead",
100
- )
101
-
102
- @classmethod
103
- def from_dict(cls, data: dict[str, Any]) -> "Parameter":
104
- """
105
- !!! warning
106
- `Parameter` is deprecated, use `Option` instead.
107
- `Parameter.from_dict` -> `Option.from_dict`
108
-
109
- Creates an instance of `Parameter` from a dictionary.
110
-
111
- Parameters
112
- ----------
113
- data : dict[str, Any]
114
- The dictionary representation of a parameter.
115
-
116
- Returns
117
- -------
118
- Parameter
119
- An instance of `Parameter`.
120
- """
121
-
122
- deprecated(
123
- name="Parameter.from_dict",
124
- reason="`Parameter` is deprecated, use `Option` instead. Parameter.from_dict -> Option.from_dict",
125
- )
126
-
127
- param_type_string = data["param_type"]
128
- param_type = getattr(builtins, param_type_string.split("'")[1])
129
-
130
- return Parameter(
131
- name=data["name"],
132
- param_type=param_type,
133
- default=data.get("default"),
134
- description=data.get("description"),
135
- required=data.get("required", False),
136
- choices=data.get("choices"),
137
- )
138
-
139
- def to_dict(self) -> dict[str, Any]:
140
- """
141
- !!! warning
142
- `Parameter` is deprecated, use `Option` instead.
143
- `Parameter.to_dict` -> `Option.to_dict`
144
-
145
- Converts the parameter to a dict.
146
-
147
- Returns
148
- -------
149
- dict[str, Any]
150
- The parameter as a dict with its name, type, default value,
151
- description, required flag, and choices.
152
-
153
- Examples
154
- --------
155
- >>> param = Parameter("timeout", int, 60, "Maximum time in seconds", True)
156
- >>> param_dict = param.to_dict()
157
- >>> param_dict["name"]
158
- 'timeout'
159
- >>> param_dict["default"]
160
- 60
161
- """
162
-
163
- deprecated(
164
- name="Parameter.to_dict",
165
- reason="`Parameter` is deprecated, use `Option` instead. Parameter.to_dict -> Option.to_dict",
166
- )
167
-
168
- return {
169
- "name": self.name,
170
- "param_type": str(self.param_type),
171
- "default": self.default,
172
- "description": self.description,
173
- "required": self.required,
174
- "choices": self.choices,
175
- }
176
27
 
177
28
 
178
29
  @dataclass
@@ -425,8 +276,7 @@ class Options:
425
276
  If a required option is not provided through a command-line
426
277
  argument, an environment variable or a default value.
427
278
  TypeError
428
- If an option is not either an `Option` or `Parameter` (deprecated)
429
- object.
279
+ If an option is not an `Option`
430
280
  ValueError
431
281
  If an environment variable is not of the type of the corresponding
432
282
  parameter.
@@ -522,27 +372,6 @@ class Options:
522
372
 
523
373
  return cloud_dict
524
374
 
525
- def parameters_dict(self) -> list[dict[str, Any]]:
526
- """
527
- !!! warning
528
- `Parameter` is deprecated, use `Option` instead. `Options.parameters_dict` -> `Options.options_dict`
529
-
530
- Converts the options to a list of dicts. Each dict is the dict
531
- representation of a `Parameter`.
532
-
533
- Returns
534
- -------
535
- list[dict[str, Any]]
536
- The list of dictionaries (parameter entries).
537
- """
538
-
539
- deprecated(
540
- name="Options.parameters_dict",
541
- reason="`Parameter` is deprecated, use `Option` instead. Options.parameters_dict -> Options.options_dict",
542
- )
543
-
544
- return [param.to_dict() for param in self.options]
545
-
546
375
  def options_dict(self) -> list[dict[str, Any]]:
547
376
  """
548
377
  Converts the `Options` to a list of dicts. Each dict is the dict
@@ -606,7 +435,7 @@ class Options:
606
435
  If a required option is not provided through a command-line
607
436
  argument, an environment variable or a default value.
608
437
  TypeError
609
- If an option is not an `Option` or `Parameter` (deprecated) object.
438
+ If an option is not an `Option` object.
610
439
  ValueError
611
440
  If an environment variable is not of the type of the corresponding
612
441
  parameter.
@@ -728,41 +557,6 @@ class Options:
728
557
 
729
558
  return cls(*options)
730
559
 
731
- @classmethod
732
- def from_parameters_dict(cls, parameters_dict: list[dict[str, Any]]) -> "Options":
733
- """
734
- !!! warning
735
-
736
- `Parameter` is deprecated, use `Option` instead.
737
- `Options.from_parameters_dict` -> `Options.from_options_dict`
738
-
739
- Creates an instance of `Options` from parameters in dict form. Each
740
- entry is the dict representation of a `Parameter`.
741
-
742
- Parameters
743
- ----------
744
- parameters_dict : list[dict[str, Any]]
745
- The list of dictionaries (parameter entries).
746
-
747
- Returns
748
- -------
749
- Options
750
- An instance of `Options`.
751
- """
752
-
753
- deprecated(
754
- name="Options.from_parameters_dict",
755
- reason="`Parameter` is deprecated, use `Option` instead. "
756
- "Options.from_parameters_dict -> Options.from_options_dict",
757
- )
758
-
759
- parameters = []
760
- for parameter_dict in parameters_dict:
761
- parameter = Parameter.from_dict(parameter_dict)
762
- parameters.append(parameter)
763
-
764
- return cls(*parameters)
765
-
766
560
  @classmethod
767
561
  def from_options_dict(cls, options_dict: list[dict[str, Any]]) -> "Options":
768
562
  """
@@ -837,7 +631,7 @@ class Options:
837
631
  If a required option is not provided through a command-line
838
632
  argument, an environment variable or a default value.
839
633
  TypeError
840
- If an option is not an `Option` or `Parameter` (deprecated) object.
634
+ If an option is not an `Option` object.
841
635
  ValueError
842
636
  If an environment variable is not of the type of the corresponding
843
637
  parameter.
@@ -858,10 +652,8 @@ class Options:
858
652
  options_by_field_name: dict[str, Option] = {}
859
653
 
860
654
  for ix, option in enumerate(self.options):
861
- if not isinstance(option, Option) and not isinstance(option, Parameter):
862
- raise TypeError(
863
- f"expected an <Option> (or deprecated <Parameter>) object, but got {type(option)} in index {ix}"
864
- )
655
+ if not isinstance(option, Option):
656
+ raise TypeError(f"expected an <Option> object, but got {type(option)} in index {ix}")
865
657
 
866
658
  # See comment below about ipykernel adding a `-f` argument. We
867
659
  # restrict options from having the name 'f' or 'fff' for that
@@ -876,7 +668,7 @@ class Options:
876
668
  option.name = option.name.lstrip("-")
877
669
 
878
670
  kwargs = {
879
- "type": self._option_type(option) if self._option_type(option) is not bool else str,
671
+ "type": option.option_type if option.option_type is not bool else str,
880
672
  "help": self._description(option),
881
673
  }
882
674
 
@@ -925,12 +717,10 @@ class Options:
925
717
  env_value = os.getenv(upper_name)
926
718
  if env_value is not None:
927
719
  try:
928
- typed_env_value = (
929
- self._option_type(option)(env_value) if self._option_type(option) is not bool else env_value
930
- )
720
+ typed_env_value = option.option_type(env_value) if option.option_type is not bool else env_value
931
721
  except ValueError:
932
722
  raise ValueError(
933
- f'environment variable "{upper_name}" is not of type {self._option_type(option)}'
723
+ f'environment variable "{upper_name}" is not of type {option.option_type}'
934
724
  ) from None
935
725
 
936
726
  value = self._option_value(option, typed_env_value)
@@ -968,8 +758,6 @@ class Options:
968
758
  """
969
759
 
970
760
  description = ""
971
- if isinstance(option, Parameter):
972
- description = "DEPRECATED (initialized with <Parameter>, use <Option> instead) "
973
761
 
974
762
  description += f"[env var: {option.name.upper()}]"
975
763
 
@@ -979,7 +767,7 @@ class Options:
979
767
  if option.default is not None:
980
768
  description += f" (default: {option.default})"
981
769
 
982
- description += f" (type: {self._option_type(option).__name__})"
770
+ description += f" (type: {option.option_type.__name__})"
983
771
 
984
772
  if isinstance(option, Option) and option.additional_attributes is not None:
985
773
  description += f" (additional attributes: {option.additional_attributes})"
@@ -1020,7 +808,7 @@ class Options:
1020
808
  other values are converted to False.
1021
809
  """
1022
810
 
1023
- opt_type = self._option_type(option)
811
+ opt_type = option.option_type
1024
812
  if opt_type is not bool:
1025
813
  return value
1026
814
 
@@ -1031,39 +819,6 @@ class Options:
1031
819
 
1032
820
  return False
1033
821
 
1034
- @staticmethod
1035
- def _option_type(option: Option | Parameter) -> type:
1036
- """
1037
- Get the type of an option.
1038
-
1039
- This auxiliary function was introduced for backwards compatibility with
1040
- the deprecated `Parameter` class. Once `Parameter` is removed, this function
1041
- can be removed as well. When the function is removed, use the
1042
- `option.option_type` attribute directly, instead of calling this function.
1043
-
1044
- Parameters
1045
- ----------
1046
- option : Union[Option, Parameter]
1047
- The option to get the type for.
1048
-
1049
- Returns
1050
- -------
1051
- type
1052
- The type of the option.
1053
-
1054
- Raises
1055
- ------
1056
- TypeError
1057
- If the option is not an `Option` or `Parameter` object.
1058
- """
1059
-
1060
- if isinstance(option, Option):
1061
- return option.option_type
1062
- elif isinstance(option, Parameter):
1063
- return option.param_type
1064
- else:
1065
- raise TypeError(f"expected an <Option> (or deprecated <Parameter>) object, but got {type(option)}")
1066
-
1067
822
 
1068
823
  class OptionsEnforcement:
1069
824
  """
nextmv/output.py CHANGED
@@ -8,9 +8,9 @@ destinations.
8
8
  Classes
9
9
  -------
10
10
  RunStatistics
11
- Statistics about a general run.
11
+ Deprecated: Statistics about a general run.
12
12
  ResultStatistics
13
- Statistics about a specific result.
13
+ Deprecated: Statistics about a specific result.
14
14
  DataPoint
15
15
  A data point representing a 2D coordinate.
16
16
  Series
@@ -18,7 +18,10 @@ Series
18
18
  SeriesData
19
19
  Data container for multiple series of data points.
20
20
  Statistics
21
- Complete statistics container for a solution, including run metrics and result data.
21
+ Deprecated: Use metrics instead. Complete statistics container for a
22
+ solution, including run metrics and result data.
23
+ Metrics
24
+ Metrics container for a solution.
22
25
  OutputFormat
23
26
  Enumeration of supported output formats.
24
27
  SolutionFile
@@ -46,7 +49,9 @@ Attributes
46
49
  ASSETS_KEY : str
47
50
  Assets key constant used for identifying assets in the run output.
48
51
  STATISTICS_KEY : str
49
- Statistics key constant used for identifying statistics in the run output.
52
+ Deprecated: Use METRICS_KEY instead. Statistics key constant used for identifying statistics in the run output.
53
+ METRICS_KEY : str
54
+ Metrics key constant used for identifying metrics in the run output.
50
55
  SOLUTIONS_KEY : str
51
56
  Solutions key constant used for identifying solutions in the run output.
52
57
  OUTPUTS_KEY : str
@@ -66,7 +71,6 @@ from pydantic import AliasChoices, Field
66
71
 
67
72
  from nextmv._serialization import serialize_json
68
73
  from nextmv.base_model import BaseModel
69
- from nextmv.deprecated import deprecated
70
74
  from nextmv.logger import reset_stdout
71
75
  from nextmv.options import Options
72
76
 
@@ -76,7 +80,11 @@ Assets key constant used for identifying assets in the run output.
76
80
  """
77
81
  STATISTICS_KEY = "statistics"
78
82
  """
79
- Statistics key constant used for identifying statistics in the run output.
83
+ Deprecated: Use METRICS_KEY instead. Statistics key constant used for identifying statistics in the run output.
84
+ """
85
+ METRICS_KEY = "metrics"
86
+ """
87
+ Metrics key constant used for identifying metrics in the run output.
80
88
  """
81
89
  SOLUTIONS_KEY = "solutions"
82
90
  """
@@ -90,7 +98,7 @@ Outputs key constant used for identifying outputs in the run output.
90
98
 
91
99
  class RunStatistics(BaseModel):
92
100
  """
93
- Statistics about a general run.
101
+ Deprecated: Statistics about a general run.
94
102
 
95
103
  You can import the `RunStatistics` class directly from `nextmv`:
96
104
 
@@ -130,7 +138,7 @@ class RunStatistics(BaseModel):
130
138
 
131
139
  class ResultStatistics(BaseModel):
132
140
  """
133
- Statistics about a specific result.
141
+ Deprecated: Statistics about a specific result.
134
142
 
135
143
  You can import the `ResultStatistics` class directly from `nextmv`:
136
144
 
@@ -170,7 +178,7 @@ class ResultStatistics(BaseModel):
170
178
 
171
179
  class DataPoint(BaseModel):
172
180
  """
173
- A data point representing a 2D coordinate.
181
+ Deprecated: A data point representing a 2D coordinate.
174
182
 
175
183
  You can import the `DataPoint` class directly from `nextmv`:
176
184
 
@@ -203,7 +211,7 @@ class DataPoint(BaseModel):
203
211
 
204
212
  class Series(BaseModel):
205
213
  """
206
- A series of data points for visualization or analysis.
214
+ Deprecated: A series of data points for visualization or analysis.
207
215
 
208
216
  You can import the `Series` class directly from `nextmv`:
209
217
 
@@ -237,7 +245,7 @@ class Series(BaseModel):
237
245
 
238
246
  class SeriesData(BaseModel):
239
247
  """
240
- Data container for multiple series of data points.
248
+ Deprecated: Data container for multiple series of data points.
241
249
 
242
250
  You can import the `SeriesData` class directly from `nextmv`:
243
251
 
@@ -272,6 +280,9 @@ class SeriesData(BaseModel):
272
280
 
273
281
  class Statistics(BaseModel):
274
282
  """
283
+ !!! warning
284
+ `Statistics` is deprecated, use `Metrics` instead.
285
+
275
286
  Complete statistics container for a solution, including run metrics and
276
287
  result data.
277
288
 
@@ -878,7 +889,9 @@ class Output:
878
889
  The solution to the decision problem. The type must match the
879
890
  `output_format`. Default is None.
880
891
  statistics : Optional[Union[Statistics, dict[str, Any]]], optional
881
- Statistics of the solution. Default is None.
892
+ Deprecated: Use Metrics instead. Statistics of the solution. Default is None.
893
+ metrics : Optional[dict[str, Any]], optional
894
+ Metrics of the solution. Default is None.
882
895
  csv_configurations : Optional[dict[str, Any]], optional
883
896
  Configuration for writing CSV files. Default is None.
884
897
  json_configurations : Optional[dict[str, Any]], optional
@@ -917,17 +930,16 @@ class Output:
917
930
  Examples
918
931
  --------
919
932
  >>> from nextmv.output import Output, OutputFormat, Statistics, RunStatistics
920
- >>> run_stats = RunStatistics(duration=30.0, iterations=100)
921
- >>> stats = Statistics(run=run_stats)
933
+ >>> metrics = {"duration": 30.0, "iterations": 100}
922
934
  >>> solution = {"routes": [{"vehicle": 1, "stops": [1, 2, 3]}, {"vehicle": 2, "stops": [4, 5]}]}
923
935
  >>> output = Output(
924
936
  ... output_format=OutputFormat.JSON,
925
937
  ... solution=solution,
926
- ... statistics=stats,
938
+ ... metrics=metrics,
927
939
  ... json_configurations={"indent": 4}
928
940
  ... )
929
941
  >>> output_dict = output.to_dict()
930
- >>> "solution" in output_dict and "statistics" in output_dict
942
+ >>> "solution" in output_dict and "metrics" in output_dict
931
943
  True
932
944
  """
933
945
 
@@ -970,11 +982,16 @@ class Output:
970
982
  """
971
983
  statistics: Statistics | dict[str, Any] | None = None
972
984
  """
985
+ Deprecated: Use Metrics instead.
973
986
  Statistics of the solution. These statistics can be of type `Statistics` or a
974
987
  simple dictionary. If the statistics are of type `Statistics`, they will be
975
988
  serialized to a dictionary using the `to_dict` method. If they are a
976
- dictionary, they will be used as is. If the statistics are not provided, an
977
- empty dictionary will be used.
989
+ dictionary, they will be used as is.
990
+ """
991
+ metrics: dict[str, Any] | None = None
992
+ """
993
+ Metrics of the solution. These metrics should be provided as a simple or
994
+ nested dictionary.
978
995
  """
979
996
  csv_configurations: dict[str, Any] | None = None
980
997
  """
@@ -1089,7 +1106,7 @@ class Output:
1089
1106
  # Statistics need to end up as a dict, so we achieve that based on the
1090
1107
  # type of statistics that were used to create the class.
1091
1108
  if self.statistics is None:
1092
- statistics = {}
1109
+ statistics = None
1093
1110
  elif isinstance(self.statistics, Statistics):
1094
1111
  statistics = self.statistics.to_dict()
1095
1112
  elif isinstance(self.statistics, dict):
@@ -1099,6 +1116,18 @@ class Output:
1099
1116
  f"unsupported statistics type: {type(self.statistics)}, supported types are `Statistics` or `dict`"
1100
1117
  )
1101
1118
 
1119
+ if self.metrics is None:
1120
+ metrics = None
1121
+ elif isinstance(self.metrics, dict):
1122
+ metrics = self.metrics
1123
+ else:
1124
+ raise TypeError(f"unsupported metrics type: {type(self.metrics)}, supported type is `dict`")
1125
+
1126
+ # if both metrics and statistics are None, set statistics to an
1127
+ # empty dict for backward compatibility
1128
+ if statistics is None and metrics is None:
1129
+ statistics = {}
1130
+
1102
1131
  # Assets need to end up as a list of dicts, so we achieve that based on
1103
1132
  # the type of each asset in the list.
1104
1133
  assets = []
@@ -1118,10 +1147,16 @@ class Output:
1118
1147
  output_dict = {
1119
1148
  "options": options,
1120
1149
  "solution": self.solution if self.solution is not None else {},
1121
- STATISTICS_KEY: statistics,
1122
1150
  ASSETS_KEY: assets,
1123
1151
  }
1124
1152
 
1153
+ # Only include statistics in output if it's not None
1154
+ if statistics is not None:
1155
+ output_dict[STATISTICS_KEY] = statistics
1156
+
1157
+ if metrics is not None:
1158
+ output_dict[METRICS_KEY] = metrics
1159
+
1125
1160
  # Add the auxiliary configurations to the output dictionary if they are
1126
1161
  # defined and not empty.
1127
1162
  if (
@@ -1194,9 +1229,9 @@ class LocalOutputWriter(OutputWriter):
1194
1229
 
1195
1230
  Examples
1196
1231
  --------
1197
- >>> from nextmv.output import LocalOutputWriter, Output, Statistics
1232
+ >>> from nextmv.output import LocalOutputWriter, Output, Metrics
1198
1233
  >>> writer = LocalOutputWriter()
1199
- >>> output = Output(solution={"result": 42}, statistics=Statistics())
1234
+ >>> output = Output(solution={"result": 42}, metrics={"time": 1.23})
1200
1235
  >>> # Write to stdout
1201
1236
  >>> writer.write(output, path=None)
1202
1237
  >>> # Write to a file
@@ -1518,67 +1553,6 @@ class LocalOutputWriter(OutputWriter):
1518
1553
  )
1519
1554
 
1520
1555
 
1521
- def write_local(
1522
- output: Output | dict[str, Any],
1523
- path: str | None = None,
1524
- skip_stdout_reset: bool = False,
1525
- ) -> None:
1526
- """
1527
- !!! warning
1528
- `write_local` is deprecated, use `write` instead.
1529
-
1530
- Write the output to the local filesystem or stdout.
1531
-
1532
- This is a convenience function for instantiating a `LocalOutputWriter` and
1533
- calling its `write` method.
1534
-
1535
- Parameters
1536
- ----------
1537
- output : Union[Output, dict[str, Any]]
1538
- Output data to write. Can be an Output object or a dictionary.
1539
- path : str, optional
1540
- Path to write the output data to. The interpretation depends on the
1541
- output format:
1542
-
1543
- - For `OutputFormat.JSON`: File path for the JSON output. If None or
1544
- empty, writes to stdout.
1545
- - For `OutputFormat.CSV_ARCHIVE`: Directory path for CSV files. If None
1546
- or empty, writes to a directory named "output" in the current working
1547
- directory.
1548
- skip_stdout_reset : bool, optional
1549
- Skip resetting stdout before writing the output data. Default is False.
1550
-
1551
- Raises
1552
- ------
1553
- ValueError
1554
- If the Output.output_format is not supported.
1555
- TypeError
1556
- If the output is of an unsupported type.
1557
-
1558
- Notes
1559
- -----
1560
- This function detects if stdout was redirected and resets it to avoid
1561
- unexpected behavior. If you want to skip this behavior, set the
1562
- skip_stdout_reset parameter to True.
1563
-
1564
- Examples
1565
- --------
1566
- >>> from nextmv.output import write_local, Output
1567
- >>> # Write JSON to a file
1568
- >>> write_local(Output(solution={"result": 42}), path="result.json")
1569
- >>> # Write JSON to stdout
1570
- >>> write_local({"simple": "data"})
1571
- """
1572
-
1573
- deprecated(
1574
- name="write_local",
1575
- reason="`write_local` is deprecated, use `write` instead",
1576
- )
1577
-
1578
- writer = LocalOutputWriter()
1579
- writer.write(output, path, skip_stdout_reset)
1580
-
1581
-
1582
1556
  _LOCAL_OUTPUT_WRITER = LocalOutputWriter()
1583
1557
  """Default LocalOutputWriter instance used by the write function."""
1584
1558