nextmv 0.27.0__py3-none-any.whl → 0.28.1.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.
nextmv/options.py CHANGED
@@ -1,4 +1,19 @@
1
- """Configuration for a run."""
1
+ """
2
+ Configuration management for application runs.
3
+
4
+ This module provides classes for handling configuration options for
5
+ applications. It supports reading options from command-line arguments,
6
+ environment variables, and default values in a prioritized manner. The module
7
+ includes classes for defining individual options (`Option`) and managing
8
+ collections of options (`Options`).
9
+
10
+ Classes
11
+ -------
12
+ Option
13
+ Class for defining individual options for configuration.
14
+ Options
15
+ Class for managing collections of options.
16
+ """
2
17
 
3
18
  import argparse
4
19
  import builtins
@@ -15,9 +30,8 @@ from nextmv.deprecated import deprecated
15
30
  @dataclass
16
31
  class Parameter:
17
32
  """
18
- DEPRECATION WARNING
19
- ----------
20
- `Parameter` is deprecated, use `Option` instead.
33
+ !!! warning
34
+ `Parameter` is deprecated, use `Option` instead.
21
35
 
22
36
  Parameter that is used in a `Configuration`. When a parameter is required,
23
37
  it is a good practice to provide a default value for it. This is because
@@ -29,20 +43,30 @@ class Parameter:
29
43
  ----------
30
44
  name : str
31
45
  The name of the parameter.
46
+
32
47
  param_type : type
33
48
  The type of the parameter.
49
+
34
50
  default : Any, optional
35
51
  The default value of the parameter. Even though this is optional, it is
36
52
  recommended to provide a default value for all parameters.
53
+
37
54
  description : str, optional
38
55
  An optional description of the parameter. This is useful for generating
39
56
  help messages for the configuration.
57
+
40
58
  required : bool, optional
41
59
  Whether the parameter is required. If a parameter is required, it will
42
- be an error to not provide a value for it, either trough a command-line
60
+ be an error to not provide a value for it, either through a command-line
43
61
  argument, an environment variable or a default value.
62
+
44
63
  choices : list[Optional[Any]], optional
45
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)
46
70
  """
47
71
 
48
72
  name: str
@@ -64,6 +88,12 @@ class Parameter:
64
88
  """Limits values to a specific set of choices."""
65
89
 
66
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
+ """
67
97
  deprecated(
68
98
  name="Parameter",
69
99
  reason="`Parameter` is deprecated, use `Option` instead",
@@ -72,10 +102,9 @@ class Parameter:
72
102
  @classmethod
73
103
  def from_dict(cls, data: dict[str, Any]) -> "Parameter":
74
104
  """
75
- DEPRECATION WARNING
76
- ----------
77
- `Parameter` is deprecated, use `Option` instead. Parameter.from_dict ->
78
- Option.from_dict
105
+ !!! warning
106
+ `Parameter` is deprecated, use `Option` instead.
107
+ `Parameter.from_dict` -> `Option.from_dict`
79
108
 
80
109
  Creates an instance of `Parameter` from a dictionary.
81
110
 
@@ -109,17 +138,26 @@ class Parameter:
109
138
 
110
139
  def to_dict(self) -> dict[str, Any]:
111
140
  """
112
- DEPRECATION WARNING
113
- ----------
114
- `Parameter` is deprecated, use `Option` instead. Parameter.to_dict ->
115
- Option.to_dict
141
+ !!! warning
142
+ `Parameter` is deprecated, use `Option` instead.
143
+ `Parameter.to_dict` -> `Option.to_dict`
116
144
 
117
145
  Converts the parameter to a dict.
118
146
 
119
147
  Returns
120
148
  -------
121
149
  dict[str, Any]
122
- The parameter as a dict.
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
123
161
  """
124
162
 
125
163
  deprecated(
@@ -140,16 +178,24 @@ class Parameter:
140
178
  @dataclass
141
179
  class Option:
142
180
  """
143
- `Option` that is used in `Options`. When an `Option` is required,
144
- it is a good practice to provide a default value for it. This is because
145
- the `Options` will raise an error if a required `Option` is not
181
+ An option that is used in `Options`.
182
+
183
+ You can import the `Option` class directly from `nextmv`:
184
+
185
+ ```python
186
+ from nextmv import Option
187
+ ```
188
+
189
+ Options provide a way to configure application behavior. When an `Option`
190
+ is required, it is a good practice to provide a default value for it. This
191
+ is because the `Options` will raise an error if a required `Option` is not
146
192
  provided through a command-line argument, an environment variable or a
147
193
  default value.
148
194
 
149
- Attributes
195
+ Parameters
150
196
  ----------
151
197
  name : str
152
- The name of the option.
198
+ `name`. The name of the option.
153
199
  option_type : type
154
200
  The type of the option.
155
201
  default : Any, optional
@@ -160,7 +206,7 @@ class Option:
160
206
  help messages for the `Options`.
161
207
  required : bool, optional
162
208
  Whether the option is required. If an option is required, it will
163
- be an error to not provide a value for it, either trough a command-line
209
+ be an error to not provide a value for it, either through a command-line
164
210
  argument, an environment variable or a default value.
165
211
  choices : list[Optional[Any]], optional
166
212
  Limits values to a specific set of choices.
@@ -169,6 +215,15 @@ class Option:
169
215
  perform validation on these attributes. For example, the maximum length
170
216
  of a string or the maximum value of an integer. These additional
171
217
  attributes will be shown in the help message of the `Options`.
218
+
219
+ Examples
220
+ --------
221
+ ```python
222
+ from nextmv.options import Option
223
+ opt = Option("duration", str, "30s", description="solver duration", required=False)
224
+ opt.name
225
+ opt.default
226
+ ```
172
227
  """
173
228
 
174
229
  name: str
@@ -209,13 +264,23 @@ class Option:
209
264
 
210
265
  Parameters
211
266
  ----------
212
- data : dict[str, Any]
267
+
268
+ data: dict[str, Any]
213
269
  The dictionary representation of an option.
214
270
 
215
271
  Returns
216
272
  -------
217
273
  Option
218
274
  An instance of `Option`.
275
+
276
+ Examples
277
+ --------
278
+ >>> opt_dict = {"name": "timeout", "option_type": "<class 'int'>", "default": 60}
279
+ >>> option = Option.from_dict(opt_dict)
280
+ >>> option.name
281
+ 'timeout'
282
+ >>> option.default
283
+ 60
219
284
  """
220
285
 
221
286
  option_type_string = data["option_type"]
@@ -238,7 +303,16 @@ class Option:
238
303
  Returns
239
304
  -------
240
305
  dict[str, Any]
241
- The option as a dict.
306
+ The option as a dict with all its attributes.
307
+
308
+ Examples
309
+ --------
310
+ >>> opt = Option("duration", str, "30s", description="solver duration")
311
+ >>> opt_dict = opt.to_dict()
312
+ >>> opt_dict["name"]
313
+ 'duration'
314
+ >>> opt_dict["default"]
315
+ '30s'
242
316
  """
243
317
 
244
318
  return {
@@ -254,10 +328,17 @@ class Option:
254
328
 
255
329
  class Options:
256
330
  """
257
- Options for a run. To initialize options, pass in one or more `Option`
258
- objects. The options will look for the values of the given parameters in
259
- the following order: command-line arguments, environment variables, default
260
- values.
331
+ Options container for application configuration.
332
+
333
+ You can import the `Options` class directly from `nextmv`:
334
+
335
+ ```python
336
+ from nextmv import Options
337
+ ```
338
+
339
+ To initialize options, pass in one or more `Option` objects. The options
340
+ will look for the values of the given parameters in the following order:
341
+ command-line arguments, environment variables, default values.
261
342
 
262
343
  Once the `Options` are initialized, you can access the underlying options as
263
344
  attributes of the `Options` object. For example, if you have an
@@ -278,7 +359,7 @@ class Options:
278
359
  be merged with other options. After options are parsed, you may get the
279
360
  help message by running the script with the `-h/--help` flag.
280
361
 
281
- Attributes
362
+ Parameters
282
363
  ----------
283
364
  *options : Option
284
365
  The list of `Option` objects that are used in the options. At least one
@@ -294,7 +375,6 @@ class Options:
294
375
  ... )
295
376
  >>>
296
377
  >>> print(options.duration, options.threads, options.to_dict())
297
-
298
378
  30s 4 {"duration": "30s", "threads": 4}
299
379
 
300
380
  Raises
@@ -313,8 +393,14 @@ class Options:
313
393
  PARSED = False
314
394
 
315
395
  def __init__(self, *options: Option):
316
- """Initializes the options."""
396
+ """
397
+ Initialize an Options instance with the provided option objects.
317
398
 
399
+ Parameters
400
+ ----------
401
+ *options : Option
402
+ The option objects to include in this Options instance.
403
+ """
318
404
  self.options = copy.deepcopy(options)
319
405
 
320
406
  def to_dict(self) -> dict[str, Any]:
@@ -326,7 +412,17 @@ class Options:
326
412
  Returns
327
413
  -------
328
414
  dict[str, Any]
329
- The options as a dict.
415
+ The options as a dict where keys are option names and values
416
+ are the corresponding option values.
417
+
418
+ Examples
419
+ --------
420
+ >>> options = Options(Option("duration", str, "30s"), Option("threads", int, 4))
421
+ >>> options_dict = options.to_dict()
422
+ >>> options_dict["duration"]
423
+ '30s'
424
+ >>> options_dict["threads"]
425
+ 4
330
426
  """
331
427
 
332
428
  if not self.PARSED:
@@ -349,16 +445,28 @@ class Options:
349
445
  def to_dict_cloud(self) -> dict[str, str]:
350
446
  """
351
447
  Converts the options to a dict that can be used in the Nextmv Cloud.
448
+
352
449
  Cloud has a hard requirement that options are passed as strings. This
353
450
  method converts the options to a dict with string values. This is
354
451
  useful for passing options to the Nextmv Cloud.
452
+
355
453
  As a side effect, this method parses the options if they have not been
356
454
  parsed yet. See the `parse` method for more information.
357
455
 
358
456
  Returns
359
457
  -------
360
458
  dict[str, str]
361
- The options as a dict with string values.
459
+ The options as a dict with string values where non-string values
460
+ are JSON-encoded.
461
+
462
+ Examples
463
+ --------
464
+ >>> options = Options(Option("duration", str, "30s"), Option("threads", int, 4))
465
+ >>> cloud_dict = options.to_dict_cloud()
466
+ >>> cloud_dict["duration"]
467
+ '30s'
468
+ >>> cloud_dict["threads"]
469
+ '4'
362
470
  """
363
471
 
364
472
  options_dict = self.to_dict()
@@ -374,10 +482,8 @@ class Options:
374
482
 
375
483
  def parameters_dict(self) -> list[dict[str, Any]]:
376
484
  """
377
- DEPRECATION WARNING
378
- ----------
379
- `Parameter` is deprecated, use `Option` instead. Options.parameters_dict
380
- -> Options.options_dict
485
+ !!! warning
486
+ `Parameter` is deprecated, use `Option` instead. `Options.parameters_dict` -> `Options.options_dict`
381
487
 
382
488
  Converts the options to a list of dicts. Each dict is the dict
383
489
  representation of a `Parameter`.
@@ -404,6 +510,15 @@ class Options:
404
510
  -------
405
511
  list[dict[str, Any]]
406
512
  The list of dictionaries (`Option` entries).
513
+
514
+ Examples
515
+ --------
516
+ >>> options = Options(Option("duration", str, "30s"), Option("threads", int, 4))
517
+ >>> opt_dicts = options.options_dict()
518
+ >>> opt_dicts[0]["name"]
519
+ 'duration'
520
+ >>> opt_dicts[1]["name"]
521
+ 'threads'
407
522
  """
408
523
 
409
524
  return [opt.to_dict() for opt in self.options]
@@ -422,7 +537,7 @@ class Options:
422
537
  After Options have been parsed, they cannot be merged with other
423
538
  Options. If you need to merge Options, do so before parsing them.
424
539
 
425
- Example 1
540
+ Examples
426
541
  -------
427
542
  >>> import nextmv
428
543
  >>>
@@ -432,8 +547,6 @@ class Options:
432
547
  ... )
433
548
  >>> options.parse() # Does not raise an exception.
434
549
 
435
- Example 2
436
- -------
437
550
  >>> import nextmv
438
551
  >>>
439
552
  >>> options = nextmv.Options(
@@ -464,16 +577,22 @@ class Options:
464
577
 
465
578
  def merge(self, new: "Options") -> "Options":
466
579
  """
467
- Merges the current options with the new options. This method cannot be
468
- used if any of the options have been parsed. When options are parsed,
469
- values are read from the command-line arguments, environment variables
470
- and default values. Merging options after parsing would result in
471
- unpredictable behavior.
580
+ Merges the current options with the new options.
581
+
582
+ This method cannot be used if any of the options have been parsed. When
583
+ options are parsed, values are read from the command-line arguments,
584
+ environment variables and default values. Merging options after parsing
585
+ would result in unpredictable behavior.
472
586
 
473
587
  Parameters
474
588
  ----------
475
589
  new : Options
476
- The new options to merge.
590
+ The new options to merge with the current options.
591
+
592
+ Returns
593
+ -------
594
+ Options
595
+ The merged options object (self).
477
596
 
478
597
  Raises
479
598
  ------
@@ -482,10 +601,15 @@ class Options:
482
601
  RuntimeError
483
602
  If the new options have already been parsed.
484
603
 
485
- Returns
486
- -------
487
- Options
488
- The merged options.
604
+ Examples
605
+ --------
606
+ >>> opt1 = Options(Option("duration", str, "30s"))
607
+ >>> opt2 = Options(Option("threads", int, 4))
608
+ >>> merged = opt1.merge(opt2)
609
+ >>> merged.duration
610
+ '30s'
611
+ >>> merged.threads
612
+ 4
489
613
  """
490
614
 
491
615
  if self.PARSED:
@@ -507,13 +631,16 @@ class Options:
507
631
  @classmethod
508
632
  def from_dict(cls, data: dict[str, Any]) -> "Options":
509
633
  """
510
- Creates an instance of `Options` from a dictionary. The dictionary
511
- should have the following structure:
634
+ Creates an instance of `Options` from a dictionary.
635
+
636
+ The dictionary should have the following structure:
512
637
 
638
+ ```python
513
639
  {
514
640
  "duration": "30",
515
641
  "threads": 4,
516
642
  }
643
+ ```
517
644
 
518
645
  Parameters
519
646
  ----------
@@ -523,7 +650,16 @@ class Options:
523
650
  Returns
524
651
  -------
525
652
  Options
526
- An instance of `Options`.
653
+ An instance of `Options` with options created from the dictionary.
654
+
655
+ Examples
656
+ --------
657
+ >>> data = {"duration": "30s", "threads": 4}
658
+ >>> options = Options.from_dict(data)
659
+ >>> options.duration
660
+ '30s'
661
+ >>> options.threads
662
+ 4
527
663
  """
528
664
 
529
665
  options = []
@@ -536,17 +672,17 @@ class Options:
536
672
  @classmethod
537
673
  def from_parameters_dict(cls, parameters_dict: list[dict[str, Any]]) -> "Options":
538
674
  """
539
- DEPRECATION WARNING
540
- ----------
541
- `Parameter` is deprecated, use `Option` instead. Options.from_parameters_dict
542
- -> Options.from_options_dict
675
+ !!! warning
676
+
677
+ `Parameter` is deprecated, use `Option` instead.
678
+ `Options.from_parameters_dict` -> `Options.from_options_dict`
543
679
 
544
680
  Creates an instance of `Options` from parameters in dict form. Each
545
681
  entry is the dict representation of a `Parameter`.
546
682
 
547
683
  Parameters
548
684
  ----------
549
- data : list[dict[str, Any]]
685
+ parameters_dict : list[dict[str, Any]]
550
686
  The list of dictionaries (parameter entries).
551
687
 
552
688
  Returns
@@ -576,13 +712,25 @@ class Options:
576
712
 
577
713
  Parameters
578
714
  ----------
579
- data : list[dict[str, Any]]
715
+ options_dict : list[dict[str, Any]]
580
716
  The list of dictionaries (`Option` entries).
581
717
 
582
718
  Returns
583
719
  -------
584
720
  Options
585
721
  An instance of `Options`.
722
+
723
+ Examples
724
+ --------
725
+ >>> options_dict = [
726
+ ... {"name": "duration", "option_type": "<class 'str'>", "default": "30s"},
727
+ ... {"name": "threads", "option_type": "<class 'int'>", "default": 4}
728
+ ... ]
729
+ >>> options = Options.from_options_dict(options_dict)
730
+ >>> options.duration
731
+ '30s'
732
+ >>> options.threads
733
+ 4
586
734
  """
587
735
 
588
736
  options = []
@@ -594,8 +742,20 @@ class Options:
594
742
 
595
743
  def __getattr__(self, name: str) -> Any:
596
744
  """
597
- Gets an attribute of the options. This is called when an attribute
598
- is accessed. It parses the options if they have not been parsed yet.
745
+ Gets an attribute of the options.
746
+
747
+ This is called when an attribute is accessed. It parses the options
748
+ if they have not been parsed yet.
749
+
750
+ Parameters
751
+ ----------
752
+ name : str
753
+ The name of the attribute to get.
754
+
755
+ Returns
756
+ -------
757
+ Any
758
+ The value of the attribute.
599
759
  """
600
760
 
601
761
  if not self.PARSED:
@@ -608,6 +768,10 @@ class Options:
608
768
  Parses the options using command-line arguments, environment variables
609
769
  and default values.
610
770
 
771
+ This is an internal method that is called by `parse()` and `__getattr__()`.
772
+ It sets the `PARSED` flag to True and sets the values of the options
773
+ based on command-line arguments, environment variables, and default values.
774
+
611
775
  Raises
612
776
  ------
613
777
  ValueError
@@ -727,7 +891,22 @@ class Options:
727
891
  )
728
892
 
729
893
  def _description(self, option: Option) -> str:
730
- """Returns a description for an option."""
894
+ """
895
+ Returns a description for an option.
896
+
897
+ This is an internal method used to create the help text for options
898
+ in the command-line argument parser.
899
+
900
+ Parameters
901
+ ----------
902
+ option : Option
903
+ The option to get the description for.
904
+
905
+ Returns
906
+ -------
907
+ str
908
+ A formatted description string for the option.
909
+ """
731
910
 
732
911
  description = ""
733
912
  if isinstance(option, Parameter):
@@ -752,7 +931,26 @@ class Options:
752
931
  return description
753
932
 
754
933
  def _option_value(self, option: Option, value: Any) -> Any:
755
- """Handles how the value of an option is extracted."""
934
+ """
935
+ Handles how the value of an option is extracted.
936
+
937
+ This is an internal method that converts string values to boolean
938
+ values for boolean options.
939
+
940
+ Parameters
941
+ ----------
942
+ option : Option
943
+ The option to extract the value for.
944
+ value : Any
945
+ The value to extract.
946
+
947
+ Returns
948
+ -------
949
+ Any
950
+ The extracted value. For boolean options, string values like
951
+ "true", "1", "t", "y", and "yes" are converted to True, and
952
+ other values are converted to False.
953
+ """
756
954
 
757
955
  opt_type = self._option_type(option)
758
956
  if opt_type is not bool:
@@ -767,11 +965,28 @@ class Options:
767
965
 
768
966
  @staticmethod
769
967
  def _option_type(option: Union[Option, Parameter]) -> type:
770
- """Auxiliary function for handling the type of an option. This function
771
- was introduced for backwards compatibility with the deprecated
772
- `Parameter` class. Once `Parameter` is removed, this function can be removed
773
- as well. When the function is removed, use the `option.option_type`
774
- attribute directly, instead of calling this function.
968
+ """
969
+ Get the type of an option.
970
+
971
+ This auxiliary function was introduced for backwards compatibility with
972
+ the deprecated `Parameter` class. Once `Parameter` is removed, this function
973
+ can be removed as well. When the function is removed, use the
974
+ `option.option_type` attribute directly, instead of calling this function.
975
+
976
+ Parameters
977
+ ----------
978
+ option : Union[Option, Parameter]
979
+ The option to get the type for.
980
+
981
+ Returns
982
+ -------
983
+ type
984
+ The type of the option.
985
+
986
+ Raises
987
+ ------
988
+ TypeError
989
+ If the option is not an `Option` or `Parameter` object.
775
990
  """
776
991
 
777
992
  if isinstance(option, Option):