nextmv 0.27.0__py3-none-any.whl → 0.28.1__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/manifest.py CHANGED
@@ -1,4 +1,35 @@
1
- """Module with the logic for handling an app manifest."""
1
+ """Module with the logic for handling an app manifest.
2
+
3
+ This module provides classes and functions for managing Nextmv app manifests.
4
+ Manifest files (app.yaml) define how an application is built, run, and deployed
5
+ on the Nextmv Cloud platform.
6
+
7
+ Classes
8
+ -------
9
+ ManifestType
10
+ Enum for application types based on programming language.
11
+ ManifestRuntime
12
+ Enum for runtime environments where apps run on Nextmv Cloud.
13
+ ManifestBuild
14
+ Class for build-specific attributes in the manifest.
15
+ ManifestPythonModel
16
+ Class for model-specific instructions for Python apps.
17
+ ManifestPython
18
+ Class for Python-specific instructions in the manifest.
19
+ ManifestOption
20
+ Class representing an option for the decision model in the manifest.
21
+ ManifestOptions
22
+ Class containing a list of options for the decision model.
23
+ ManifestConfiguration
24
+ Class for configuration settings for the decision model.
25
+ Manifest
26
+ Main class representing an app manifest for Nextmv Cloud.
27
+
28
+ Constants
29
+ --------
30
+ FILE_NAME
31
+ Name of the app manifest file.
32
+ """
2
33
 
3
34
  import os
4
35
  from enum import Enum
@@ -11,23 +42,53 @@ from nextmv.base_model import BaseModel
11
42
  from nextmv.model import _REQUIREMENTS_FILE, ModelConfiguration
12
43
  from nextmv.options import Option, Options
13
44
 
14
- FILE_NAME = "app.yaml"
15
- """Name of the app manifest file."""
45
+ MANIFEST_FILE_NAME = "app.yaml"
46
+ """Name of the app manifest file.
47
+
48
+ This constant defines the standard filename for Nextmv app manifest files.
49
+
50
+ You can import the `FILE_NAME` constant directly from `cloud`:
51
+
52
+ ```python
53
+ from nextmv.cloud import FILE_NAME
54
+ ```
55
+
56
+ Notes
57
+ -----
58
+ All Nextmv applications must include an app.yaml file for proper deployment.
59
+ """
16
60
 
17
61
 
18
62
  class ManifestType(str, Enum):
19
63
  """
20
- Type of application in the manifest, based on the programming
21
- language.
64
+ Type of application in the manifest, based on the programming language.
65
+
66
+ You can import the `ManifestType` class directly from `cloud`:
67
+
68
+ ```python
69
+ from nextmv.cloud import ManifestType
70
+ ```
71
+
72
+ This enum defines the supported programming languages for applications
73
+ that can be deployed on Nextmv Cloud.
22
74
 
23
75
  Attributes
24
76
  ----------
25
- PYTHON: str
26
- Python format
27
- GO: str
28
- Go format
29
- JAVA: str
30
- Java format
77
+ PYTHON : str
78
+ Python format, used for Python applications.
79
+ GO : str
80
+ Go format, used for Go applications.
81
+ JAVA : str
82
+ Java format, used for Java applications.
83
+
84
+ Examples
85
+ --------
86
+ >>> from nextmv.cloud import ManifestType
87
+ >>> manifest_type = ManifestType.PYTHON
88
+ >>> manifest_type
89
+ <ManifestType.PYTHON: 'python'>
90
+ >>> str(manifest_type)
91
+ 'python'
31
92
  """
32
93
 
33
94
  PYTHON = "python"
@@ -42,19 +103,37 @@ class ManifestRuntime(str, Enum):
42
103
  """
43
104
  Runtime (environment) where the app will be run on Nextmv Cloud.
44
105
 
106
+ You can import the `ManifestRuntime` class directly from `cloud`:
107
+
108
+ ```python
109
+ from nextmv.cloud import ManifestRuntime
110
+ ```
111
+
112
+ This enum defines the supported runtime environments for applications
113
+ that can be deployed on Nextmv Cloud.
114
+
45
115
  Attributes
46
116
  ----------
47
- DEFAULT: str
117
+ DEFAULT : str
48
118
  This runtime is used to run compiled applications such as Go binaries.
49
- PYTHON: str
119
+ PYTHON : str
50
120
  This runtime is used as the basis for all other Python runtimes and
51
121
  Python applications.
52
- JAVA: str
122
+ JAVA : str
53
123
  This runtime is used to run Java applications.
54
- PYOMO: str
124
+ PYOMO : str
55
125
  This runtime provisions Python packages to run Pyomo applications.
56
- HEXALY: str
126
+ HEXALY : str
57
127
  This runtime provisions Python packages to run Hexaly applications.
128
+
129
+ Examples
130
+ --------
131
+ >>> from nextmv.cloud import ManifestRuntime
132
+ >>> runtime = ManifestRuntime.PYTHON
133
+ >>> runtime
134
+ <ManifestRuntime.PYTHON: 'ghcr.io/nextmv-io/runtime/python:3.11'>
135
+ >>> str(runtime)
136
+ 'ghcr.io/nextmv-io/runtime/python:3.11'
58
137
  """
59
138
 
60
139
  DEFAULT = "ghcr.io/nextmv-io/runtime/default:latest"
@@ -79,29 +158,45 @@ class ManifestBuild(BaseModel):
79
158
  """
80
159
  Build-specific attributes.
81
160
 
82
- Attributes
161
+ You can import the `ManifestBuild` class directly from `cloud`:
162
+
163
+ ```python
164
+ from nextmv.cloud import ManifestBuild
165
+ ```
166
+
167
+ Parameters
83
168
  ----------
84
- command: Optional[str]
169
+ command : Optional[str], default=None
85
170
  The command to run to build the app. This command will be executed
86
171
  without a shell, i.e., directly. The command must exit with a status of
87
172
  0 to continue the push process of the app to Nextmv Cloud. This command
88
173
  is executed prior to the pre-push command.
89
- environment: Optional[dict[str, Any]]
174
+ environment : Optional[dict[str, Any]], default=None
90
175
  Environment variables to set when running the build command given as
91
176
  key-value pairs.
177
+
178
+ Examples
179
+ --------
180
+ >>> from nextmv.cloud import ManifestBuild
181
+ >>> build_config = ManifestBuild(
182
+ ... command="make build",
183
+ ... environment={"DEBUG": "true"}
184
+ ... )
185
+ >>> build_config.command
186
+ 'make build'
92
187
  """
93
188
 
94
189
  command: Optional[str] = None
95
- """
96
- The command to run to build the app. This command will be executed without
97
- a shell, i.e., directly. The command must exit with a status of 0 to
98
- continue the push process of the app to Nextmv Cloud. This command is
99
- executed prior to the pre-push command.
190
+ """The command to run to build the app.
191
+
192
+ This command will be executed without a shell, i.e., directly. The command
193
+ must exit with a status of 0 to continue the push process of the app to
194
+ Nextmv Cloud. This command is executed prior to the pre-push command.
100
195
  """
101
196
  environment: Optional[dict[str, Any]] = None
102
- """
103
- Environment variables to set when running the build command given as
104
- key-value pairs.
197
+ """Environment variables to set when running the build command.
198
+
199
+ Given as key-value pairs.
105
200
  """
106
201
 
107
202
  def environment_to_dict(self) -> dict[str, str]:
@@ -111,8 +206,18 @@ class ManifestBuild(BaseModel):
111
206
  Returns
112
207
  -------
113
208
  dict[str, str]
114
- The environment variables as a dictionary.
115
-
209
+ The environment variables as a dictionary of string key-value pairs.
210
+ Returns an empty dictionary if no environment variables are set.
211
+
212
+ Examples
213
+ --------
214
+ >>> from nextmv.cloud import ManifestBuild
215
+ >>> build_config = ManifestBuild(environment={"COUNT": 1, "NAME": "test"})
216
+ >>> build_config.environment_to_dict()
217
+ {'COUNT': '1', 'NAME': 'test'}
218
+ >>> build_config_empty = ManifestBuild()
219
+ >>> build_config_empty.environment_to_dict()
220
+ {}
116
221
  """
117
222
 
118
223
  if self.environment is None:
@@ -125,15 +230,31 @@ class ManifestPythonModel(BaseModel):
125
230
  """
126
231
  Model-specific instructions for a Python app.
127
232
 
128
- Attributes
233
+ You can import the `ManifestPythonModel` class directly from `cloud`:
234
+
235
+ ```python
236
+ from nextmv.cloud import ManifestPythonModel
237
+ ```
238
+
239
+ Parameters
129
240
  ----------
130
- name: str
241
+ name : str
131
242
  The name of the decision model.
132
- options: Optional[list[dict[str, Any]]]
243
+ options : Optional[list[dict[str, Any]]], default=None
133
244
  Options for the decision model. This is a data representation of the
134
245
  `nextmv.Options` class. It consists of a list of dicts. Each dict
135
246
  represents the `nextmv.Option` class. It is used to be able to
136
247
  reconstruct an Options object from data when loading a decision model.
248
+
249
+ Examples
250
+ --------
251
+ >>> from nextmv.cloud import ManifestPythonModel
252
+ >>> python_model_config = ManifestPythonModel(
253
+ ... name="routing_model",
254
+ ... options=[{"name": "max_vehicles", "type": "int", "default": 10}]
255
+ ... )
256
+ >>> python_model_config.name
257
+ 'routing_model'
137
258
  """
138
259
 
139
260
  name: str
@@ -151,14 +272,31 @@ class ManifestPython(BaseModel):
151
272
  """
152
273
  Python-specific instructions.
153
274
 
154
- Attributes
275
+ You can import the `ManifestPython` class directly from `cloud`:
276
+
277
+ ```python
278
+ from nextmv.cloud import ManifestPython
279
+ ```
280
+
281
+ Parameters
155
282
  ----------
156
- pip_requirements: Optional[str]
283
+ pip_requirements : Optional[str], default=None
157
284
  Path to a requirements.txt file containing (additional) Python
158
285
  dependencies that will be bundled with the app.
159
- model: Optional[ManifestPythonModel]
160
- Information about an encoded decision model as handlded via mlflow. This
286
+ Aliases: `pip-requirements`.
287
+ model : Optional[ManifestPythonModel], default=None
288
+ Information about an encoded decision model as handled via mlflow. This
161
289
  information is used to load the decision model from the app bundle.
290
+
291
+ Examples
292
+ --------
293
+ >>> from nextmv.cloud import ManifestPython, ManifestPythonModel
294
+ >>> python_config = ManifestPython(
295
+ ... pip_requirements="requirements.txt",
296
+ ... model=ManifestPythonModel(name="my_model")
297
+ ... )
298
+ >>> python_config.pip_requirements
299
+ 'requirements.txt'
162
300
  """
163
301
 
164
302
  pip_requirements: Optional[str] = Field(
@@ -166,14 +304,16 @@ class ManifestPython(BaseModel):
166
304
  validation_alias=AliasChoices("pip-requirements", "pip_requirements"),
167
305
  default=None,
168
306
  )
169
- """
170
- Path to a requirements.txt file containing (additional) Python
171
- dependencies that will be bundled with the app.
307
+ """Path to a requirements.txt file.
308
+
309
+ Contains (additional) Python dependencies that will be bundled with the
310
+ app.
172
311
  """
173
312
  model: Optional[ManifestPythonModel] = None
174
- """
175
- Information about an encoded decision model as handlded via mlflow. This
176
- information is used to load the decision model from the app bundle.
313
+ """Information about an encoded decision model.
314
+
315
+ As handled via mlflow. This information is used to load the decision model
316
+ from the app bundle.
177
317
  """
178
318
 
179
319
 
@@ -181,28 +321,53 @@ class ManifestOption(BaseModel):
181
321
  """
182
322
  An option for the decision model that is recorded in the manifest.
183
323
 
184
- Attributes
324
+ You can import the `ManifestOption` class directly from `cloud`:
325
+
326
+ ```python
327
+ from nextmv.cloud import ManifestOption
328
+ ```
329
+
330
+ Parameters
185
331
  ----------
186
- name: str
332
+ name : str
187
333
  The name of the option.
188
- option_type: str
334
+ option_type : str
189
335
  The type of the option. This is a string representation of the
190
- `nextmv.Option` class.
191
- default: Optional[Any]
336
+ `nextmv.Option` class (e.g., "string", "int", "bool", "float").
337
+ Aliases: `type`.
338
+ default : Optional[Any], default=None
192
339
  The default value of the option.
193
- description: Optional[str]
340
+ description : Optional[str], default=""
194
341
  The description of the option.
195
- required: bool
342
+ required : bool, default=False
196
343
  Whether the option is required or not.
344
+ additional_attributes : Optional[dict[str, Any]], default=None
345
+ Optional additional attributes for the option. The Nextmv Cloud may
346
+ perform validation on these attributes. For example, the maximum
347
+ length of a string or the maximum value of an integer. These
348
+ additional attributes will be shown in the help message of the
349
+ `Options`.
350
+
351
+ Examples
352
+ --------
353
+ >>> from nextmv.cloud import ManifestOption
354
+ >>> option = ManifestOption(
355
+ ... name="solve.duration",
356
+ ... option_type="string",
357
+ ... default="30s",
358
+ ... description="Maximum duration for the solver."
359
+ ... )
360
+ >>> option.name
361
+ 'solve.duration'
197
362
  """
198
363
 
199
364
  name: str
200
365
  """The name of the option"""
201
366
  option_type: str = Field(
202
- serialization_alias="type",
367
+ serialization_alias="option_type",
203
368
  validation_alias=AliasChoices("type", "option_type"),
204
369
  )
205
- """The type of the option"""
370
+ """The type of the option (e.g., "string", "int", "bool", "float)."""
206
371
 
207
372
  default: Optional[Any] = None
208
373
  """The default value of the option"""
@@ -211,11 +376,11 @@ class ManifestOption(BaseModel):
211
376
  required: bool = False
212
377
  """Whether the option is required or not"""
213
378
  additional_attributes: Optional[dict[str, Any]] = None
214
- """
215
- Optional additional attributes for the option. The Nextmv Cloud may
216
- perform validation on these attributes. For example, the maximum length of
217
- a string or the maximum value of an integer. These additional attributes
218
- will be shown in the help message of the `Options`.
379
+ """Optional additional attributes for the option.
380
+
381
+ The Nextmv Cloud may perform validation on these attributes. For example,
382
+ the maximum length of a string or the maximum value of an integer. These
383
+ additional attributes will be shown in the help message of the `Options`.
219
384
  """
220
385
 
221
386
  @classmethod
@@ -225,13 +390,29 @@ class ManifestOption(BaseModel):
225
390
 
226
391
  Parameters
227
392
  ----------
228
- option: Option
393
+ option : nextmv.options.Option
229
394
  The option to convert.
230
395
 
231
396
  Returns
232
397
  -------
233
398
  ManifestOption
234
399
  The converted option.
400
+
401
+ Raises
402
+ ------
403
+ ValueError
404
+ If the `option.option_type` is unknown.
405
+
406
+ Examples
407
+ --------
408
+ >>> from nextmv.options import Option
409
+ >>> from nextmv.cloud import ManifestOption
410
+ >>> sdk_option = Option(name="max_stops", option_type=int, default=100)
411
+ >>> manifest_opt = ManifestOption.from_option(sdk_option)
412
+ >>> manifest_opt.name
413
+ 'max_stops'
414
+ >>> manifest_opt.option_type
415
+ 'int'
235
416
  """
236
417
  option_type = option.option_type
237
418
  if option_type is str:
@@ -260,8 +441,23 @@ class ManifestOption(BaseModel):
260
441
 
261
442
  Returns
262
443
  -------
263
- Option
444
+ nextmv.options.Option
264
445
  The converted option.
446
+
447
+ Raises
448
+ ------
449
+ ValueError
450
+ If the `self.option_type` is unknown.
451
+
452
+ Examples
453
+ --------
454
+ >>> from nextmv.cloud import ManifestOption
455
+ >>> manifest_opt = ManifestOption(name="max_stops", option_type="int", default=100)
456
+ >>> sdk_option = manifest_opt.to_option()
457
+ >>> sdk_option.name
458
+ 'max_stops'
459
+ >>> sdk_option.option_type
460
+ <class 'int'>
265
461
  """
266
462
 
267
463
  option_type_string = self.option_type
@@ -290,21 +486,42 @@ class ManifestOptions(BaseModel):
290
486
  """
291
487
  Options for the decision model.
292
488
 
293
- Attributes
489
+ You can import the `ManifestOptions` class directly from `cloud`:
490
+
491
+ ```python
492
+ from nextmv.cloud import ManifestOptions
493
+ ```
494
+
495
+ Parameters
294
496
  ----------
295
- strict: bool
497
+ strict : Optional[bool], default=False
296
498
  If strict is set to `True`, only the listed options will be allowed.
297
- items: list[ManifestOption]
298
- Optional. The actual list of options for the decision model. An option
499
+ items : Optional[list[ManifestOption]], default=None
500
+ The actual list of options for the decision model. An option
299
501
  is a parameter that configures the decision model.
502
+
503
+ Examples
504
+ --------
505
+ >>> from nextmv.cloud import ManifestOptions, ManifestOption
506
+ >>> options_config = ManifestOptions(
507
+ ... strict=True,
508
+ ... items=[
509
+ ... ManifestOption(name="timeout", option_type="int", default=60),
510
+ ... ManifestOption(name="vehicle_capacity", option_type="float", default=100.0)
511
+ ... ]
512
+ ... )
513
+ >>> options_config.strict
514
+ True
515
+ >>> len(options_config.items)
516
+ 2
300
517
  """
301
518
 
302
519
  strict: Optional[bool] = False
303
520
  """If strict is set to `True`, only the listed options will be allowed."""
304
521
  items: Optional[list[ManifestOption]] = None
305
- """
306
- Optional. The actual list of options for the decision model. An option is a
307
- parameter that configures the decision model.
522
+ """The actual list of options for the decision model.
523
+
524
+ An option is a parameter that configures the decision model.
308
525
  """
309
526
 
310
527
 
@@ -312,11 +529,27 @@ class ManifestConfiguration(BaseModel):
312
529
  """
313
530
  Configuration for the decision model.
314
531
 
315
- Attributes
532
+ You can import the `ManifestConfiguration` class directly from `cloud`:
533
+
534
+ ```python
535
+ from nextmv.cloud import ManifestConfiguration
536
+ ```
537
+
538
+ Parameters
316
539
  ----------
317
- options: ManifestOptions
318
- Optional. The actual list of options for the decision model. An option
319
- is a parameter that configures the decision model.
540
+ options : ManifestOptions
541
+ Options for the decision model.
542
+
543
+ Examples
544
+ --------
545
+ >>> from nextmv.cloud import ManifestConfiguration, ManifestOptions, ManifestOption
546
+ >>> model_config = ManifestConfiguration(
547
+ ... options=ManifestOptions(
548
+ ... items=[ManifestOption(name="debug_mode", option_type="bool", default=False)]
549
+ ... )
550
+ ... )
551
+ >>> model_config.options.items[0].name
552
+ 'debug_mode'
320
553
  """
321
554
 
322
555
  options: ManifestOptions
@@ -325,6 +558,14 @@ class ManifestConfiguration(BaseModel):
325
558
 
326
559
  class Manifest(BaseModel):
327
560
  """
561
+ Represents an app manifest (`app.yaml`) for Nextmv Cloud.
562
+
563
+ You can import the `Manifest` class directly from `cloud`:
564
+
565
+ ```python
566
+ from nextmv.cloud import Manifest
567
+ ```
568
+
328
569
  An application that runs on the Nextmv Platform must contain a file named
329
570
  `app.yaml` which is known as the app manifest. This file is used to specify
330
571
  the execution environment for the app.
@@ -332,77 +573,95 @@ class Manifest(BaseModel):
332
573
  This class represents the app manifest and allows you to load it from a
333
574
  file or create it programmatically.
334
575
 
335
- Attributes
576
+ Parameters
336
577
  ----------
337
- files: list[str]
338
- Mandatory. The files to include (or exclude) in the app.
339
- runtime: ManifestRuntime
340
- Mandatory. The runtime to use for the app, it provides the environment
341
- in which the app runs.
342
- type: ManifestType
343
- Mandatory. Type of application, based on the programming language.
344
- build: Optional[ManifestBuild]
345
- Optional. Build-specific attributes. The build.command to run to build
578
+ files : list[str]
579
+ The files to include (or exclude) in the app. This is mandatory.
580
+ runtime : ManifestRuntime, default=ManifestRuntime.PYTHON
581
+ The runtime to use for the app, it provides the environment
582
+ in which the app runs. This is mandatory.
583
+ type : ManifestType, default=ManifestType.PYTHON
584
+ Type of application, based on the programming language. This is
585
+ mandatory.
586
+ build : Optional[ManifestBuild], default=None
587
+ Build-specific attributes. The `build.command` to run to build
346
588
  the app. This command will be executed without a shell, i.e., directly.
347
589
  The command must exit with a status of 0 to continue the push process of
348
590
  the app to Nextmv Cloud. This command is executed prior to the pre-push
349
- command. The build.environment is used to set environment variables when
591
+ command. The `build.environment` is used to set environment variables when
350
592
  running the build command given as key-value pairs.
351
- pre_push: Optional[str]
352
- Optional. A command to run before the app is pushed to the Nextmv Cloud.
593
+ pre_push : Optional[str], default=None
594
+ A command to run before the app is pushed to the Nextmv Cloud.
353
595
  This command can be used to compile a binary, run tests or similar tasks.
354
596
  One difference with what is specified under build, is that the command
355
- will be executed via
356
- python: Optional[ManifestPython]
357
- Optional. Only for Python apps. Contains further Python-specific
597
+ will be executed via the shell (i.e., `bash -c` on Linux & macOS or
598
+ `cmd /c` on Windows). The command must exit with a status of 0 to
599
+ continue the push process. This command is executed just before the app
600
+ gets bundled and pushed (after the build command).
601
+ Aliases: `pre-push`.
602
+ python : Optional[ManifestPython], default=None
603
+ Only for Python apps. Contains further Python-specific
358
604
  attributes.
359
- configuration: Optional[ManifestConfiguration]
360
- Optional. A list of options for the decision model. An option is a
605
+ configuration : Optional[ManifestConfiguration], default=None
606
+ A list of options for the decision model. An option is a
361
607
  parameter that configures the decision model.
608
+
609
+ Examples
610
+ --------
611
+ >>> from nextmv.cloud import Manifest, ManifestRuntime, ManifestType
612
+ >>> manifest = Manifest(
613
+ ... files=["main.py", "model_logic/"],
614
+ ... runtime=ManifestRuntime.PYTHON,
615
+ ... type=ManifestType.PYTHON,
616
+ ... )
617
+ >>> manifest.files
618
+ ['main.py', 'model_logic/']
362
619
  """
363
620
 
364
621
  files: list[str]
365
- """Mandatory. The files to include (or exclude) in the app."""
622
+ """The files to include (or exclude) in the app. This is mandatory."""
366
623
 
367
624
  runtime: ManifestRuntime = ManifestRuntime.PYTHON
368
- """
369
- Mandatory. The runtime to use for the app, it provides the environment in
370
- which the app runs.
625
+ """The runtime to use for the app.
626
+
627
+ It provides the environment in which the app runs. This is mandatory.
371
628
  """
372
629
  type: ManifestType = ManifestType.PYTHON
373
- """Mandatory. Type of application, based on the programming language."""
630
+ """Type of application, based on the programming language. This is mandatory."""
374
631
  build: Optional[ManifestBuild] = None
375
- """
376
- Optional. Build-specific attributes. The build.command to run to build the
377
- app. This command will be executed without a shell, i.e., directly. The
378
- command must exit with a status of 0 to continue the push process of the
379
- app to Nextmv Cloud. This command is executed prior to the pre-push
380
- command. The build.environment is used to set environment variables when
381
- running the build command given as key-value pairs.
632
+ """Build-specific attributes.
633
+
634
+ The `build.command` to run to build the app. This command will be executed
635
+ without a shell, i.e., directly. The command must exit with a status of 0
636
+ to continue the push process of the app to Nextmv Cloud. This command is
637
+ executed prior to the pre-push command. The `build.environment` is used to
638
+ set environment variables when running the build command given as key-value
639
+ pairs.
382
640
  """
383
641
  pre_push: Optional[str] = Field(
384
642
  serialization_alias="pre-push",
385
643
  validation_alias=AliasChoices("pre-push", "pre_push"),
386
644
  default=None,
387
645
  )
388
- """
389
- Optional. A command to run before the app is pushed to the Nextmv Cloud.
646
+ """A command to run before the app is pushed to the Nextmv Cloud.
647
+
390
648
  This command can be used to compile a binary, run tests or similar tasks.
391
649
  One difference with what is specified under build, is that the command will
392
- be executed via the shell (i.e., bash -c on Linux & macOS or cmd /c on
650
+ be executed via the shell (i.e., `bash -c` on Linux & macOS or `cmd /c` on
393
651
  Windows). The command must exit with a status of 0 to continue the push
394
652
  process. This command is executed just before the app gets bundled and
395
653
  pushed (after the build command).
396
654
  """
397
655
  python: Optional[ManifestPython] = None
398
- """
399
- Optional. Only for Python apps. Contains further Python-specific
400
- attributes.
656
+ """Python-specific attributes.
657
+
658
+ Only for Python apps. Contains further Python-specific attributes.
401
659
  """
402
660
  configuration: Optional[ManifestConfiguration] = None
403
- """
404
- Optional. A list of options for the decision model. An option is a
405
- parameter that configures the decision model.
661
+ """Configuration for the decision model.
662
+
663
+ A list of options for the decision model. An option is a parameter that
664
+ configures the decision model.
406
665
  """
407
666
 
408
667
  @classmethod
@@ -410,19 +669,44 @@ class Manifest(BaseModel):
410
669
  """
411
670
  Load a manifest from a YAML file.
412
671
 
672
+ The YAML file is expected to be named `app.yaml` and located in the
673
+ specified directory.
674
+
413
675
  Parameters
414
676
  ----------
415
- dirpath: str
416
- Path to the directory containing the app.yaml file.
677
+ dirpath : str
678
+ Path to the directory containing the `app.yaml` file.
417
679
 
418
680
  Returns
419
681
  -------
420
682
  Manifest
421
683
  The loaded manifest.
422
684
 
685
+ Raises
686
+ ------
687
+ FileNotFoundError
688
+ If the `app.yaml` file is not found in `dirpath`.
689
+ yaml.YAMLError
690
+ If there is an error parsing the YAML file.
691
+
692
+ Examples
693
+ --------
694
+ Assuming an `app.yaml` file exists in `./my_app_dir`:
695
+
696
+ ```yaml
697
+ # ./my_app_dir/app.yaml
698
+ files:
699
+ - main.py
700
+ runtime: ghcr.io/nextmv-io/runtime/python:3.11
701
+ type: python
702
+ ```
703
+
704
+ >>> from nextmv.cloud import Manifest
705
+ >>> # manifest = Manifest.from_yaml("./my_app_dir") # This would be run
706
+ >>> # assert manifest.type == "python"
423
707
  """
424
708
 
425
- with open(os.path.join(dirpath, FILE_NAME)) as file:
709
+ with open(os.path.join(dirpath, MANIFEST_FILE_NAME)) as file:
426
710
  raw_manifest = yaml.safe_load(file)
427
711
 
428
712
  return cls.from_dict(raw_manifest)
@@ -431,27 +715,63 @@ class Manifest(BaseModel):
431
715
  """
432
716
  Write the manifest to a YAML file.
433
717
 
718
+ The manifest will be written to a file named `app.yaml` in the
719
+ specified directory.
720
+
434
721
  Parameters
435
722
  ----------
436
- dirpath: str
437
- Path to the directory where the app.yaml file will be written.
438
-
723
+ dirpath : str
724
+ Path to the directory where the `app.yaml` file will be written.
725
+
726
+ Raises
727
+ ------
728
+ IOError
729
+ If there is an error writing the file.
730
+ yaml.YAMLError
731
+ If there is an error serializing the manifest to YAML.
732
+
733
+ Examples
734
+ --------
735
+ >>> from nextmv.cloud import Manifest
736
+ >>> manifest = Manifest(files=["solver.py"], type="python")
737
+ >>> # manifest.to_yaml("./output_dir") # This would create ./output_dir/app.yaml
439
738
  """
440
739
 
441
- with open(os.path.join(dirpath, FILE_NAME), "w") as file:
740
+ with open(os.path.join(dirpath, MANIFEST_FILE_NAME), "w") as file:
442
741
  yaml.dump(self.to_dict(), file)
443
742
 
444
743
  def extract_options(self) -> Optional[Options]:
445
744
  """
446
- Convert the manifest options to a `nextmv.Options` object. If the
447
- manifest does not have valid options defined in
448
- `.configuration.options.items`, this method simply returns a `None`.
745
+ Convert the manifest options to a `nextmv.Options` object.
746
+
747
+ If the manifest does not have valid options defined in
748
+ `.configuration.options.items`, this method returns `None`.
449
749
 
450
750
  Returns
451
751
  -------
452
- Optional[Options]
752
+ Optional[nextmv.options.Options]
453
753
  The options extracted from the manifest. If no options are found,
454
754
  `None` is returned.
755
+
756
+ Examples
757
+ --------
758
+ >>> from nextmv.cloud import Manifest, ManifestConfiguration, ManifestOptions, ManifestOption
759
+ >>> manifest = Manifest(
760
+ ... files=["main.py"],
761
+ ... configuration=ManifestConfiguration(
762
+ ... options=ManifestOptions(
763
+ ... items=[
764
+ ... ManifestOption(name="duration", option_type="string", default="10s")
765
+ ... ]
766
+ ... )
767
+ ... )
768
+ ... )
769
+ >>> sdk_options = manifest.extract_options()
770
+ >>> sdk_options.get_option("duration").default
771
+ '10s'
772
+ >>> empty_manifest = Manifest(files=["main.py"])
773
+ >>> empty_manifest.extract_options() is None
774
+ True
455
775
  """
456
776
 
457
777
  if self.configuration is None or self.configuration.options is None or self.configuration.options.items is None:
@@ -462,23 +782,52 @@ class Manifest(BaseModel):
462
782
  return Options(*options)
463
783
 
464
784
  @classmethod
465
- def from_model_configuration(cls, model_configuration: ModelConfiguration) -> "Manifest":
785
+ def from_model_configuration(
786
+ cls,
787
+ model_configuration: ModelConfiguration,
788
+ ) -> "Manifest":
466
789
  """
467
- Create a Python manifest from a Python model configuration. Note that
468
- the `ModelConfiguration` is almost always used in conjunction with the
469
- `nextmv.Model` class. If you are not implementing an instance of
470
- `nextmv.Model`, maybe you should use the `from_options` method instead,
471
- to initialize the manifest with the options of the model.
790
+ Create a Python manifest from a `nextmv.model.ModelConfiguration`.
791
+
792
+ Note that the `ModelConfiguration` is almost always used in
793
+ conjunction with the `nextmv.Model` class. If you are not
794
+ implementing an instance of `nextmv.Model`, consider using the
795
+ `from_options` method instead to initialize the manifest with the
796
+ options of the model.
797
+
798
+ The resulting manifest will have:
799
+
800
+ - `files` set to `["main.py", f"{model_configuration.name}/**"]`
801
+ - `runtime` set to `ManifestRuntime.PYTHON`
802
+ - `type` set to `ManifestType.PYTHON`
803
+ - `python.pip_requirements` set to the default requirements file name.
804
+ - `python.model.name` set to `model_configuration.name`.
805
+ - `python.model.options` populated from `model_configuration.options`.
806
+ - `configuration.options` populated from `model_configuration.options`.
472
807
 
473
808
  Parameters
474
809
  ----------
475
- model_configuration: ModelConfiguration
810
+ model_configuration : nextmv.model.ModelConfiguration
476
811
  The model configuration.
477
812
 
478
813
  Returns
479
814
  -------
480
815
  Manifest
481
816
  The Python manifest.
817
+
818
+ Examples
819
+ --------
820
+ >>> from nextmv.model import ModelConfiguration, Options, Option
821
+ >>> from nextmv.cloud import Manifest
822
+ >>> opts = Options(Option(name="vehicle_count", option_type=int, default=5))
823
+ >>> mc = ModelConfiguration(name="vehicle_router", options=opts)
824
+ >>> manifest = Manifest.from_model_configuration(mc)
825
+ >>> manifest.python.model.name
826
+ 'vehicle_router'
827
+ >>> manifest.files
828
+ ['main.py', 'vehicle_router/**']
829
+ >>> manifest.configuration.options.items[0].name
830
+ 'vehicle_count'
482
831
  """
483
832
 
484
833
  manifest_python_dict = {
@@ -512,21 +861,48 @@ class Manifest(BaseModel):
512
861
  @classmethod
513
862
  def from_options(cls, options: Options) -> "Manifest":
514
863
  """
515
- Create a basic Python manifest from `Options`. If you have more files
516
- than just a `main.py`, make sure you modify the `.files` attribute of
517
- the resulting manifest. This method assumes that requirements are
518
- specified in a `requirements.txt` file. You may also specify a
519
- different requirements file once you instantiate the manifest.
864
+ Create a basic Python manifest from `nextmv.options.Options`.
865
+
866
+ If you have more files than just a `main.py`, make sure you modify
867
+ the `.files` attribute of the resulting manifest. This method assumes
868
+ that requirements are specified in a `requirements.txt` file. You may
869
+ also specify a different requirements file once you instantiate the
870
+ manifest.
871
+
872
+ The resulting manifest will have:
873
+ - `files` set to `["main.py"]`
874
+ - `runtime` set to `ManifestRuntime.PYTHON`
875
+ - `type` set to `ManifestType.PYTHON`
876
+ - `python.pip_requirements` set to `"requirements.txt"`.
877
+ - `configuration.options` populated from the provided `options`.
520
878
 
521
879
  Parameters
522
880
  ----------
523
- options: Options
881
+ options : nextmv.options.Options
524
882
  The options to include in the manifest.
525
883
 
526
884
  Returns
527
885
  -------
528
886
  Manifest
529
887
  The manifest with the given options.
888
+
889
+ Examples
890
+ --------
891
+ >>> from nextmv.options import Options, Option
892
+ >>> from nextmv.cloud import Manifest
893
+ >>> opts = Options(
894
+ ... Option(name="max_runtime", option_type=str, default="60s"),
895
+ ... Option(name="use_heuristic", option_type=bool, default=True)
896
+ ... )
897
+ >>> manifest = Manifest.from_options(opts)
898
+ >>> manifest.files
899
+ ['main.py']
900
+ >>> manifest.python.pip_requirements
901
+ 'requirements.txt'
902
+ >>> len(manifest.configuration.options.items)
903
+ 2
904
+ >>> manifest.configuration.options.items[0].name
905
+ 'max_runtime'
530
906
  """
531
907
 
532
908
  manifest = cls(