nextmv 0.18.0__py3-none-any.whl → 1.0.0.dev2__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (175) hide show
  1. nextmv/__about__.py +1 -1
  2. nextmv/__entrypoint__.py +8 -13
  3. nextmv/__init__.py +53 -0
  4. nextmv/_serialization.py +96 -0
  5. nextmv/base_model.py +54 -9
  6. nextmv/cli/CONTRIBUTING.md +511 -0
  7. nextmv/cli/__init__.py +0 -0
  8. nextmv/cli/cloud/__init__.py +47 -0
  9. nextmv/cli/cloud/acceptance/__init__.py +27 -0
  10. nextmv/cli/cloud/acceptance/create.py +393 -0
  11. nextmv/cli/cloud/acceptance/delete.py +68 -0
  12. nextmv/cli/cloud/acceptance/get.py +104 -0
  13. nextmv/cli/cloud/acceptance/list.py +62 -0
  14. nextmv/cli/cloud/acceptance/update.py +95 -0
  15. nextmv/cli/cloud/account/__init__.py +28 -0
  16. nextmv/cli/cloud/account/create.py +83 -0
  17. nextmv/cli/cloud/account/delete.py +60 -0
  18. nextmv/cli/cloud/account/get.py +66 -0
  19. nextmv/cli/cloud/account/update.py +70 -0
  20. nextmv/cli/cloud/app/__init__.py +35 -0
  21. nextmv/cli/cloud/app/create.py +141 -0
  22. nextmv/cli/cloud/app/delete.py +58 -0
  23. nextmv/cli/cloud/app/exists.py +44 -0
  24. nextmv/cli/cloud/app/get.py +66 -0
  25. nextmv/cli/cloud/app/list.py +61 -0
  26. nextmv/cli/cloud/app/push.py +137 -0
  27. nextmv/cli/cloud/app/update.py +124 -0
  28. nextmv/cli/cloud/batch/__init__.py +29 -0
  29. nextmv/cli/cloud/batch/create.py +454 -0
  30. nextmv/cli/cloud/batch/delete.py +68 -0
  31. nextmv/cli/cloud/batch/get.py +104 -0
  32. nextmv/cli/cloud/batch/list.py +63 -0
  33. nextmv/cli/cloud/batch/metadata.py +66 -0
  34. nextmv/cli/cloud/batch/update.py +95 -0
  35. nextmv/cli/cloud/data/__init__.py +26 -0
  36. nextmv/cli/cloud/data/upload.py +162 -0
  37. nextmv/cli/cloud/ensemble/__init__.py +31 -0
  38. nextmv/cli/cloud/ensemble/create.py +414 -0
  39. nextmv/cli/cloud/ensemble/delete.py +67 -0
  40. nextmv/cli/cloud/ensemble/get.py +65 -0
  41. nextmv/cli/cloud/ensemble/update.py +103 -0
  42. nextmv/cli/cloud/input_set/__init__.py +30 -0
  43. nextmv/cli/cloud/input_set/create.py +170 -0
  44. nextmv/cli/cloud/input_set/get.py +63 -0
  45. nextmv/cli/cloud/input_set/list.py +63 -0
  46. nextmv/cli/cloud/input_set/update.py +123 -0
  47. nextmv/cli/cloud/instance/__init__.py +35 -0
  48. nextmv/cli/cloud/instance/create.py +290 -0
  49. nextmv/cli/cloud/instance/delete.py +62 -0
  50. nextmv/cli/cloud/instance/exists.py +39 -0
  51. nextmv/cli/cloud/instance/get.py +62 -0
  52. nextmv/cli/cloud/instance/list.py +60 -0
  53. nextmv/cli/cloud/instance/update.py +216 -0
  54. nextmv/cli/cloud/managed_input/__init__.py +31 -0
  55. nextmv/cli/cloud/managed_input/create.py +146 -0
  56. nextmv/cli/cloud/managed_input/delete.py +65 -0
  57. nextmv/cli/cloud/managed_input/get.py +63 -0
  58. nextmv/cli/cloud/managed_input/list.py +60 -0
  59. nextmv/cli/cloud/managed_input/update.py +97 -0
  60. nextmv/cli/cloud/run/__init__.py +37 -0
  61. nextmv/cli/cloud/run/cancel.py +37 -0
  62. nextmv/cli/cloud/run/create.py +530 -0
  63. nextmv/cli/cloud/run/get.py +199 -0
  64. nextmv/cli/cloud/run/input.py +86 -0
  65. nextmv/cli/cloud/run/list.py +80 -0
  66. nextmv/cli/cloud/run/logs.py +167 -0
  67. nextmv/cli/cloud/run/metadata.py +67 -0
  68. nextmv/cli/cloud/run/track.py +501 -0
  69. nextmv/cli/cloud/scenario/__init__.py +29 -0
  70. nextmv/cli/cloud/scenario/create.py +451 -0
  71. nextmv/cli/cloud/scenario/delete.py +65 -0
  72. nextmv/cli/cloud/scenario/get.py +102 -0
  73. nextmv/cli/cloud/scenario/list.py +63 -0
  74. nextmv/cli/cloud/scenario/metadata.py +67 -0
  75. nextmv/cli/cloud/scenario/update.py +93 -0
  76. nextmv/cli/cloud/secrets/__init__.py +33 -0
  77. nextmv/cli/cloud/secrets/create.py +206 -0
  78. nextmv/cli/cloud/secrets/delete.py +67 -0
  79. nextmv/cli/cloud/secrets/get.py +66 -0
  80. nextmv/cli/cloud/secrets/list.py +60 -0
  81. nextmv/cli/cloud/secrets/update.py +147 -0
  82. nextmv/cli/cloud/shadow/__init__.py +33 -0
  83. nextmv/cli/cloud/shadow/create.py +184 -0
  84. nextmv/cli/cloud/shadow/delete.py +68 -0
  85. nextmv/cli/cloud/shadow/get.py +61 -0
  86. nextmv/cli/cloud/shadow/list.py +63 -0
  87. nextmv/cli/cloud/shadow/metadata.py +66 -0
  88. nextmv/cli/cloud/shadow/start.py +43 -0
  89. nextmv/cli/cloud/shadow/stop.py +43 -0
  90. nextmv/cli/cloud/shadow/update.py +95 -0
  91. nextmv/cli/cloud/upload/__init__.py +22 -0
  92. nextmv/cli/cloud/upload/create.py +39 -0
  93. nextmv/cli/cloud/version/__init__.py +33 -0
  94. nextmv/cli/cloud/version/create.py +97 -0
  95. nextmv/cli/cloud/version/delete.py +62 -0
  96. nextmv/cli/cloud/version/exists.py +39 -0
  97. nextmv/cli/cloud/version/get.py +62 -0
  98. nextmv/cli/cloud/version/list.py +60 -0
  99. nextmv/cli/cloud/version/update.py +92 -0
  100. nextmv/cli/community/__init__.py +24 -0
  101. nextmv/cli/community/clone.py +270 -0
  102. nextmv/cli/community/list.py +265 -0
  103. nextmv/cli/configuration/__init__.py +23 -0
  104. nextmv/cli/configuration/config.py +195 -0
  105. nextmv/cli/configuration/create.py +94 -0
  106. nextmv/cli/configuration/delete.py +67 -0
  107. nextmv/cli/configuration/list.py +77 -0
  108. nextmv/cli/main.py +188 -0
  109. nextmv/cli/message.py +153 -0
  110. nextmv/cli/options.py +206 -0
  111. nextmv/cli/version.py +38 -0
  112. nextmv/cloud/__init__.py +71 -17
  113. nextmv/cloud/acceptance_test.py +757 -51
  114. nextmv/cloud/account.py +406 -17
  115. nextmv/cloud/application/__init__.py +957 -0
  116. nextmv/cloud/application/_acceptance.py +419 -0
  117. nextmv/cloud/application/_batch_scenario.py +860 -0
  118. nextmv/cloud/application/_ensemble.py +251 -0
  119. nextmv/cloud/application/_input_set.py +227 -0
  120. nextmv/cloud/application/_instance.py +289 -0
  121. nextmv/cloud/application/_managed_input.py +227 -0
  122. nextmv/cloud/application/_run.py +1393 -0
  123. nextmv/cloud/application/_secrets.py +294 -0
  124. nextmv/cloud/application/_shadow.py +314 -0
  125. nextmv/cloud/application/_utils.py +54 -0
  126. nextmv/cloud/application/_version.py +303 -0
  127. nextmv/cloud/assets.py +48 -0
  128. nextmv/cloud/batch_experiment.py +294 -33
  129. nextmv/cloud/client.py +307 -66
  130. nextmv/cloud/ensemble.py +247 -0
  131. nextmv/cloud/input_set.py +120 -2
  132. nextmv/cloud/instance.py +133 -8
  133. nextmv/cloud/integration.py +533 -0
  134. nextmv/cloud/package.py +168 -53
  135. nextmv/cloud/scenario.py +410 -0
  136. nextmv/cloud/secrets.py +234 -0
  137. nextmv/cloud/shadow.py +190 -0
  138. nextmv/cloud/url.py +73 -0
  139. nextmv/cloud/version.py +132 -4
  140. nextmv/default_app/.gitignore +1 -0
  141. nextmv/default_app/README.md +32 -0
  142. nextmv/default_app/app.yaml +12 -0
  143. nextmv/default_app/input.json +5 -0
  144. nextmv/default_app/main.py +37 -0
  145. nextmv/default_app/requirements.txt +2 -0
  146. nextmv/default_app/src/__init__.py +0 -0
  147. nextmv/default_app/src/visuals.py +36 -0
  148. nextmv/deprecated.py +47 -0
  149. nextmv/input.py +861 -90
  150. nextmv/local/__init__.py +5 -0
  151. nextmv/local/application.py +1251 -0
  152. nextmv/local/executor.py +1042 -0
  153. nextmv/local/geojson_handler.py +323 -0
  154. nextmv/local/local.py +97 -0
  155. nextmv/local/plotly_handler.py +61 -0
  156. nextmv/local/runner.py +274 -0
  157. nextmv/logger.py +80 -9
  158. nextmv/manifest.py +1466 -0
  159. nextmv/model.py +241 -66
  160. nextmv/options.py +708 -115
  161. nextmv/output.py +1301 -274
  162. nextmv/polling.py +325 -0
  163. nextmv/run.py +1702 -0
  164. nextmv/safe.py +145 -0
  165. nextmv/status.py +122 -0
  166. nextmv-1.0.0.dev2.dist-info/METADATA +311 -0
  167. nextmv-1.0.0.dev2.dist-info/RECORD +170 -0
  168. {nextmv-0.18.0.dist-info → nextmv-1.0.0.dev2.dist-info}/WHEEL +1 -1
  169. nextmv-1.0.0.dev2.dist-info/entry_points.txt +2 -0
  170. nextmv/cloud/application.py +0 -1405
  171. nextmv/cloud/manifest.py +0 -234
  172. nextmv/cloud/status.py +0 -29
  173. nextmv-0.18.0.dist-info/METADATA +0 -770
  174. nextmv-0.18.0.dist-info/RECORD +0 -25
  175. {nextmv-0.18.0.dist-info → nextmv-1.0.0.dev2.dist-info}/licenses/LICENSE +0 -0
@@ -0,0 +1,37 @@
1
+ """
2
+ This module defines the cloud run command tree for the Nextmv CLI.
3
+ """
4
+
5
+ import typer
6
+
7
+ from nextmv.cli.cloud.run.cancel import app as cancel_app
8
+ from nextmv.cli.cloud.run.create import app as create_app
9
+ from nextmv.cli.cloud.run.get import app as get_app
10
+ from nextmv.cli.cloud.run.input import app as input_app
11
+ from nextmv.cli.cloud.run.list import app as list_app
12
+ from nextmv.cli.cloud.run.logs import app as logs_app
13
+ from nextmv.cli.cloud.run.metadata import app as metadata_app
14
+ from nextmv.cli.cloud.run.track import app as track_app
15
+
16
+ # Set up subcommand application.
17
+ app = typer.Typer()
18
+ app.add_typer(cancel_app)
19
+ app.add_typer(create_app)
20
+ app.add_typer(get_app)
21
+ app.add_typer(input_app)
22
+ app.add_typer(list_app)
23
+ app.add_typer(logs_app)
24
+ app.add_typer(metadata_app)
25
+ app.add_typer(track_app)
26
+
27
+
28
+ @app.callback()
29
+ def callback() -> None:
30
+ """
31
+ Create and manage Nextmv Cloud application runs.
32
+
33
+ A run represents the execution of a decision model within a Nextmv Cloud
34
+ application. Each run takes an input, processes it using the decision model,
35
+ and produces an output.
36
+ """
37
+ pass
@@ -0,0 +1,37 @@
1
+ """
2
+ This module defines the cloud run cancel command for the Nextmv CLI.
3
+ """
4
+
5
+ import typer
6
+
7
+ from nextmv.cli.configuration.config import build_app
8
+ from nextmv.cli.message import in_progress, success
9
+ from nextmv.cli.options import AppIDOption, ProfileOption, RunIDOption
10
+
11
+ # Set up subcommand application.
12
+ app = typer.Typer()
13
+
14
+
15
+ @app.command()
16
+ def cancel(
17
+ app_id: AppIDOption,
18
+ run_id: RunIDOption,
19
+ profile: ProfileOption = None,
20
+ ) -> None:
21
+ """
22
+ Cancel a queued/running Nextmv Cloud application run.
23
+
24
+ [bold][underline]Examples[/underline][/bold]
25
+
26
+ - Cancel the run with ID [magenta]burrow-123[/magenta] belonging to an app with ID [magenta]hare-app[/magenta].
27
+ $ [green]nextmv cloud run cancel --app-id hare-app --run-id burrow-123[/green]
28
+
29
+ - Cancel the run with ID [magenta]burrow-123[/magenta] belonging to an app with ID [magenta]hare-app[/magenta].
30
+ Use the profile named [magenta]hare[/magenta].
31
+ $ [green]nextmv cloud run cancel --app-id hare-app --run-id burrow-123 --profile hare[/green]
32
+ """
33
+
34
+ cloud_app = build_app(app_id=app_id, profile=profile)
35
+ in_progress(msg=f"Canceling run [magenta]{run_id}[/magenta]...")
36
+ cloud_app.cancel_run(run_id)
37
+ success(f"Run [magenta]{run_id}[/magenta] canceled.")
@@ -0,0 +1,530 @@
1
+ """
2
+ This module defines the cloud run create command for the Nextmv CLI.
3
+ """
4
+
5
+ import json
6
+ import sys
7
+ import tarfile
8
+ from pathlib import Path
9
+ from typing import Annotated, Any
10
+
11
+ import typer
12
+
13
+ from nextmv.cli.cloud.run.get import handle_outputs
14
+ from nextmv.cli.cloud.run.logs import handle_logs
15
+ from nextmv.cli.configuration.config import build_app
16
+ from nextmv.cli.message import enum_values, error, print_json, success
17
+ from nextmv.cli.options import AppIDOption, ProfileOption
18
+ from nextmv.cloud.application import Application
19
+ from nextmv.input import InputFormat
20
+ from nextmv.polling import default_polling_options
21
+ from nextmv.run import Format, FormatInput, RunConfiguration, RunQueuing, RunType, RunTypeConfiguration
22
+
23
+ # Set up subcommand application.
24
+ app = typer.Typer()
25
+
26
+
27
+ @app.command()
28
+ def create(
29
+ app_id: AppIDOption,
30
+ # Options for controlling input.
31
+ input: Annotated[
32
+ str | None,
33
+ typer.Option(
34
+ "--input",
35
+ "-i",
36
+ help="The input path to use. File or directory depending on content format. "
37
+ "Uses [magenta]stdin[/magenta] if not defined. "
38
+ "Can be a [magenta].tar.gz[/magenta] file for multi-file content format.",
39
+ metavar="INPUT_PATH",
40
+ rich_help_panel="Input control",
41
+ ),
42
+ ] = None,
43
+ # Options for controlling output.
44
+ logs: Annotated[
45
+ str | None,
46
+ typer.Option(
47
+ "--logs",
48
+ "-l",
49
+ help="Waits for the run to complete and saves the logs to this location.",
50
+ metavar="LOGS_PATH",
51
+ rich_help_panel="Output control",
52
+ ),
53
+ ] = None,
54
+ output: Annotated[
55
+ str | None,
56
+ typer.Option(
57
+ "--output",
58
+ "-u",
59
+ help="Waits for the run to complete and save the output to this location. "
60
+ "A file or directory will be created depending on content format. ",
61
+ metavar="OUTPUT_PATH",
62
+ rich_help_panel="Output control",
63
+ ),
64
+ ] = None,
65
+ tail: Annotated[
66
+ bool,
67
+ typer.Option(
68
+ "--tail",
69
+ "-t",
70
+ help="Tail the logs until the run completes. Logs are streamed to [magenta]stderr[/magenta]. "
71
+ "Specify log output location with [code]--logs[/code].",
72
+ rich_help_panel="Output control",
73
+ ),
74
+ ] = False,
75
+ wait: Annotated[
76
+ bool,
77
+ typer.Option(
78
+ "--wait",
79
+ "-w",
80
+ help="Wait for the run to complete. Run result is printed to [magenta]stdout[/magenta] for "
81
+ "[magenta]json[/magenta], to a dir for [magenta]multi-file[/magenta]. "
82
+ "Specify output location with [code]--output[/code].",
83
+ rich_help_panel="Output control",
84
+ ),
85
+ ] = False,
86
+ # Options for run configuration.
87
+ content_format: Annotated[
88
+ InputFormat | None,
89
+ typer.Option(
90
+ "--content-format",
91
+ "-c",
92
+ help=f"The content format of the run to create. Allowed values are: {enum_values(InputFormat)}.",
93
+ metavar="CONTENT_FORMAT",
94
+ rich_help_panel="Run configuration",
95
+ ),
96
+ ] = None,
97
+ definition_id: Annotated[
98
+ str | None,
99
+ typer.Option(
100
+ "--definition-id",
101
+ "-d",
102
+ help="The definition ID to use for the run. Required for certain run types like ensemble runs.",
103
+ metavar="DEFINITION_ID",
104
+ rich_help_panel="Run configuration",
105
+ ),
106
+ ] = None,
107
+ description: Annotated[
108
+ str | None,
109
+ typer.Option(
110
+ help="An optional description for the new run.",
111
+ metavar="DESCRIPTION",
112
+ rich_help_panel="Run configuration",
113
+ ),
114
+ ] = None,
115
+ execution_class: Annotated[
116
+ str | None,
117
+ typer.Option(
118
+ "--execution-class",
119
+ "-e",
120
+ help="The execution class to use for the run, if applicable.",
121
+ metavar="EXECUTION_CLASS",
122
+ rich_help_panel="Run configuration",
123
+ ),
124
+ ] = None,
125
+ instance_id: Annotated[
126
+ str | None,
127
+ typer.Option(
128
+ help="The instance ID to use for the run.",
129
+ metavar="INSTANCE_ID",
130
+ rich_help_panel="Run configuration",
131
+ ),
132
+ ] = "latest",
133
+ integration_id: Annotated[
134
+ str | None,
135
+ typer.Option(
136
+ help="The integration ID to use for the run, if applicable.",
137
+ metavar="INTEGRATION_ID",
138
+ rich_help_panel="Run configuration",
139
+ ),
140
+ ] = None,
141
+ name: Annotated[
142
+ str | None,
143
+ typer.Option(
144
+ "--name",
145
+ "-n",
146
+ help="An optional name for the new run.",
147
+ metavar="NAME",
148
+ rich_help_panel="Run configuration",
149
+ ),
150
+ ] = None,
151
+ no_queuing: Annotated[
152
+ bool,
153
+ typer.Option(
154
+ "--no-queuing",
155
+ help="Do not queue run. Default is [magenta]False[/magenta], "
156
+ "meaning the run [italic]will[/italic] be queued.",
157
+ rich_help_panel="Run configuration",
158
+ ),
159
+ ] = False,
160
+ options: Annotated[
161
+ list[str] | None,
162
+ typer.Option(
163
+ "--options",
164
+ "-o",
165
+ help="Options passed to the run. Format: [magenta]key=value[/magenta]. "
166
+ "Pass multiple options by repeating the flag, or separating with commas.",
167
+ metavar="KEY=VALUE",
168
+ rich_help_panel="Run configuration",
169
+ ),
170
+ ] = None,
171
+ priority: Annotated[
172
+ int,
173
+ typer.Option(
174
+ help="The priority of the run. Priority is between 1 and 10, with 1 being the highest priority.",
175
+ metavar="PRIORITY",
176
+ rich_help_panel="Run configuration",
177
+ ),
178
+ ] = 6,
179
+ run_type: Annotated[
180
+ RunType,
181
+ typer.Option(
182
+ "--run-type",
183
+ "-r",
184
+ help=f"The type of run to create. Allowed values are: {enum_values(RunType)}.",
185
+ metavar="RUN_TYPE",
186
+ rich_help_panel="Run configuration",
187
+ ),
188
+ ] = RunType.STANDARD,
189
+ secret_collection_id: Annotated[
190
+ str | None,
191
+ typer.Option(
192
+ "--secret-collection-id",
193
+ "-s",
194
+ help="The secret collection ID to use for the run, if applicable.",
195
+ metavar="SECRET_COLLECTION_ID",
196
+ rich_help_panel="Run configuration",
197
+ ),
198
+ ] = None,
199
+ timeout: Annotated[
200
+ int,
201
+ typer.Option(
202
+ help="The maximum time in seconds to wait for results when polling. Poll indefinitely if not set.",
203
+ metavar="TIMEOUT_SECONDS",
204
+ rich_help_panel="Run configuration",
205
+ ),
206
+ ] = -1,
207
+ profile: ProfileOption = None,
208
+ ) -> None:
209
+ """
210
+ Create a new Nextmv Cloud application run.
211
+
212
+ Input for the run should be given through [magenta]stdin[/magenta] or the
213
+ [code]--input[/code] flag. When using the [code]--input[/code] flag, the
214
+ value can be one of the following:
215
+
216
+ - [green]<FILE_PATH>[/green]: path to a [magenta]file[/magenta] containing
217
+ the input data. Use with the [magenta]json[/magenta], and
218
+ [magenta]text[/magenta] content formats.
219
+ - [green]<DIR_PATH>[/green]: path to a [magenta]directory[/magenta]
220
+ containing the input data files. Use with the
221
+ [magenta]multi-file[/magenta] content format.
222
+ - [green]<.tar.gz_PATH>[/green]: path to a [magenta].tar.gz[/magenta] file
223
+ containing tarred input data files. Use with the
224
+ [magenta]multi-file[/magenta] content format.
225
+
226
+ The CLI determines how to send the input to the application based on the
227
+ value.
228
+
229
+ Use the [code]--wait[/code] flag to wait for the run to complete, polling
230
+ for results. Using the [code]--output[/code] flag will also activate
231
+ waiting, and allows you to specify a destination (file or dir) for the
232
+ output, depending on the content type.
233
+
234
+ Use the [code]--tail[/code] flag to stream logs to
235
+ [magenta]stderr[/magenta] until the run completes. Using the
236
+ [code]--logs[/code] flag will also activate waiting, and allows you to
237
+ specify a file to write the logs to.
238
+
239
+ An application run executes against a specific instance. An instance
240
+ represents the combination of executable code and configuration. You can
241
+ specify the instance with the [code]--instance-id[/code] flag. These are
242
+ the possible values for this flag:
243
+
244
+ - [green]latest[/green]: uses the special [magenta]latest[/magenta]
245
+ instance of the application. This corresponds to the latest pushed
246
+ executable. This is the default behavior.
247
+ - [green]default[/green]: if the application has a [italic]default[/italic]
248
+ instance configured, then it uses that instance. Setting the flag's value
249
+ to [code]''[/code] (empty string) has the same effect.
250
+ - [green]<INSTANCE_ID>[/green]: uses the instance with the given ID.
251
+
252
+ [bold][underline]Examples[/underline][/bold]
253
+
254
+ - Read a [magenta]json[/magenta] input via [magenta]stdin[/magenta], from an [magenta]input.json[/magenta] file,
255
+ and submit a run to an app with ID [magenta]hare-app[/magenta], using the [magenta]latest[/magenta] instance.
256
+ $ [green]cat input.json | nextmv cloud run create --app-id hare-app[/green]
257
+
258
+ - Read a [magenta]json[/magenta] input from an [magenta]input.json[/magenta] file, and
259
+ submit a run to an app with ID [magenta]hare-app[/magenta], using the [magenta]latest[/magenta] instance.
260
+ $ [green]nextmv cloud run create --app-id hare-app --input input.json[/green]
261
+
262
+ - Read a [magenta]json[/magenta] input from an [magenta]input.json[/magenta] file, and
263
+ submit a run to an app with ID [magenta]hare-app[/magenta], using the [magenta]latest[/magenta] instance.
264
+ Wait for the run to complete and print the result to [magenta]stdout[/magenta].
265
+ $ [green]nextmv cloud run create --app-id hare-app --input input.json --wait[/green]
266
+
267
+ - Read a [magenta]json[/magenta] input from an [magenta]input.json[/magenta] file, and
268
+ submit a run to an app with ID [magenta]hare-app[/magenta], using the [magenta]latest[/magenta] instance.
269
+ Tail the run's logs, streaming to [magenta]stderr[/magenta].
270
+ $ [green]nextmv cloud run create --app-id hare-app --input input.json --tail[/green]
271
+
272
+ - Read a [magenta]json[/magenta] input from an [magenta]input.json[/magenta] file, and
273
+ submit a run to an app with ID [magenta]hare-app[/magenta], using the [magenta]latest[/magenta] instance.
274
+ Wait for the run to complete and write the result to an [magenta]output.json[/magenta] file.
275
+ $ [green]nextmv cloud run create --app-id hare-app --input input.json --output output.json[/green]
276
+
277
+ - Read a [magenta]json[/magenta] input from an [magenta]input.json[/magenta] file, and
278
+ submit a run to an app with ID [magenta]hare-app[/magenta], using the [magenta]latest[/magenta] instance.
279
+ Wait for the run to complete, and write the logs to a [magenta]logs.log[/magenta] file.
280
+ $ [green]nextmv cloud run create --app-id hare-app --input input.json --logs logs.log[/green]
281
+
282
+ - Read a [magenta]json[/magenta] input from an [magenta]input.json[/magenta] file, and submit a run to an app with
283
+ ID [magenta]hare-app[/magenta], using the [magenta]latest[/magenta] instance. Wait for the run to complete. Tail
284
+ the run's logs, streaming to [magenta]stderr[/magenta]. Write the logs to a [magenta]logs.log[/magenta] file.
285
+ Write the result to an [magenta]output.json[/magenta] file.
286
+ $ [green]nextmv cloud run create --app-id hare-app --input input.json --tail --logs logs.log \\
287
+ --output output.json [/green]
288
+
289
+ - Read a [magenta]multi-file[/magenta] input from an [magenta]inputs[/magenta] directory, and
290
+ submit a run to an app with ID [magenta]hare-app[/magenta], using the [magenta]default[/magenta] instance.
291
+ $ [green]nextmv cloud run create --app-id hare-app --input inputs --instance-id default[/green]
292
+
293
+ - Read a [magenta]multi-file[/magenta] input from an [magenta]inputs[/magenta] directory, and
294
+ submit a run to an app with ID [magenta]hare-app[/magenta], using the [magenta]default[/magenta] instance.
295
+ Wait for the run to complete, and save the results to the default location (a directory named after the run ID).
296
+ $ [green]nextmv cloud run create --app-id hare-app --input inputs --instance-id default --wait[/green]
297
+
298
+ - Read a [magenta]multi-file[/magenta] input from an [magenta]inputs[/magenta] directory, and
299
+ submit a run to an app with ID [magenta]hare-app[/magenta], using the [magenta]burrow[/magenta] instance.
300
+ Wait for the run to complete and download the result files to an [magenta]outputs[/magenta] directory.
301
+ $ [green]nextmv cloud run create --app-id hare-app --input inputs --instance-id burrow --output outputs[/green]
302
+ """
303
+
304
+ # Validate that input is provided.
305
+ stdin = sys.stdin.read().strip() if sys.stdin.isatty() is False else None
306
+ if stdin is None and (input is None or input == ""):
307
+ error("Input data must be provided via the [code]--input[/code] flag or [magenta]stdin[/magenta].")
308
+
309
+ # Instantiate the basic requirements to start a new run.
310
+ cloud_app = build_app(app_id=app_id, profile=profile)
311
+ config = build_run_config(
312
+ run_type=run_type,
313
+ priority=priority,
314
+ no_queuing=no_queuing,
315
+ execution_class=execution_class,
316
+ content_format=content_format,
317
+ secret_collection_id=secret_collection_id,
318
+ integration_id=integration_id,
319
+ definition_id=definition_id,
320
+ )
321
+ run_options = build_run_options(options)
322
+
323
+ # Handles the default instance.
324
+ if instance_id == "default":
325
+ instance_id = ""
326
+
327
+ # Start the run before deciding if we should poll or not.
328
+ input_kwarg = resolve_input_kwarg(
329
+ stdin=stdin,
330
+ input=input,
331
+ cloud_app=cloud_app,
332
+ )
333
+ run_id = cloud_app.new_run(
334
+ **input_kwarg,
335
+ instance_id=instance_id,
336
+ name=name,
337
+ description=description,
338
+ options=run_options,
339
+ configuration=config,
340
+ )
341
+
342
+ # If we don't need to poll at all we are done.
343
+ if not wait and not tail and output is None and logs is None:
344
+ print_json({"run_id": run_id})
345
+
346
+ return
347
+
348
+ success(f"Run [magenta]{run_id}[/magenta] created.")
349
+
350
+ # Build the polling options.
351
+ polling_options = default_polling_options()
352
+ polling_options.max_duration = timeout
353
+
354
+ # Handle what happens after the run is created for logging and result
355
+ # retrieval.
356
+ handle_logs(
357
+ cloud_app=cloud_app,
358
+ run_id=run_id,
359
+ tail=tail,
360
+ logs=logs,
361
+ polling_options=polling_options,
362
+ file_output=True,
363
+ )
364
+ handle_outputs(
365
+ cloud_app=cloud_app,
366
+ run_id=run_id,
367
+ wait=wait,
368
+ output=output,
369
+ polling_options=polling_options,
370
+ skip_wait_check=False,
371
+ )
372
+
373
+
374
+ def build_run_config(
375
+ run_type: RunType,
376
+ priority: int,
377
+ no_queuing: bool,
378
+ execution_class: str | None = None,
379
+ content_format: InputFormat | None = None,
380
+ secret_collection_id: str | None = None,
381
+ integration_id: str | None = None,
382
+ definition_id: str | None = None,
383
+ ) -> RunConfiguration:
384
+ """
385
+ Builds the run configuration for the new run.
386
+
387
+ Parameters
388
+ ----------
389
+ run_type : RunType
390
+ The type of run to create.
391
+ priority : int
392
+ The priority of the run.
393
+ no_queuing : bool
394
+ Whether to disable queuing for the run.
395
+ execution_class : str | None
396
+ The execution class to use for the run, if applicable.
397
+ content_format : InputFormat | None
398
+ The content format of the run to create, if applicable.
399
+ secret_collection_id : str | None
400
+ The secret collection ID to use for the run, if applicable.
401
+ integration_id : str | None
402
+ The integration ID to use for the run, if applicable.
403
+ definition_id : str | None
404
+ The definition ID to use for the run, if applicable.
405
+
406
+ Returns
407
+ -------
408
+ RunConfiguration
409
+ The built run configuration.
410
+ """
411
+
412
+ config = RunConfiguration(
413
+ run_type=RunTypeConfiguration(
414
+ run_type=RunType(run_type),
415
+ ),
416
+ queuing=RunQueuing(
417
+ priority=priority,
418
+ disabled=no_queuing,
419
+ ),
420
+ )
421
+ if execution_class is not None:
422
+ config.execution_class = execution_class
423
+ if content_format is not None:
424
+ config.format = Format(
425
+ format_input=FormatInput(
426
+ input_type=InputFormat(content_format),
427
+ ),
428
+ )
429
+ if secret_collection_id is not None:
430
+ config.secrets_collection_id = secret_collection_id
431
+ if integration_id is not None:
432
+ config.integration_id = integration_id
433
+ if definition_id is not None:
434
+ config.run_type.definition_id = definition_id
435
+
436
+ return config
437
+
438
+
439
+ def build_run_options(options: list[str] | None) -> dict[str, str]:
440
+ """
441
+ Builds the run options for the new run. One can pass options by either
442
+ using the flag multiple times or by separating with commas in the same
443
+ flag. A combination of both is also possible.
444
+
445
+ Parameters
446
+ ----------
447
+ options : list[str] | None
448
+ The list of run options as strings.
449
+
450
+ Returns
451
+ -------
452
+ dict[str, str]
453
+ The built run options.
454
+ """
455
+
456
+ if options is None:
457
+ return None
458
+
459
+ run_options = {}
460
+ for opt in options:
461
+ # It is possible to pass multiple options separated by commas. The
462
+ # default way though is to use the flag multiple times to specify
463
+ # different options.
464
+ sub_opts = opt.split(",")
465
+ for sub_opt in sub_opts:
466
+ key_value = sub_opt.split("=", 1)
467
+ if len(key_value) != 2:
468
+ error(f"Invalid option format: {sub_opt}. Expected format is [magenta]key=value[/magenta].")
469
+
470
+ key, value = key_value
471
+ run_options[key] = value
472
+
473
+ return run_options
474
+
475
+
476
+ def resolve_input_kwarg(
477
+ stdin: str | None,
478
+ input: str | None,
479
+ cloud_app: Application,
480
+ ) -> dict[str, Any]:
481
+ """
482
+ Gets the keyword argument related to the input that is needed for the run
483
+ creation. It handles stdin, file, and directory inputs. It uploads the
484
+ input to the cloud application if needed.
485
+
486
+ Parameters
487
+ ----------
488
+ stdin : str | None
489
+ The stdin input data, if provided.
490
+ input : str | None
491
+ The input path, if provided.
492
+ cloud_app : Application
493
+ The cloud application instance.
494
+
495
+ Returns
496
+ -------
497
+ dict[str, Any]
498
+ The keyword argument with the resolved input.
499
+ """
500
+
501
+ if stdin is not None:
502
+ # Handle the case where stdin is provided as JSON for a JSON app.
503
+ try:
504
+ input_data = json.loads(stdin)
505
+ except json.JSONDecodeError:
506
+ input_data = stdin
507
+
508
+ return {"input": input_data}
509
+
510
+ input_path = Path(input)
511
+
512
+ # If the input is a file, we need to determine if it is a tar file or
513
+ # a regular file and upload it accordingly. If it is a regular file, we
514
+ # need to read its content.
515
+ if input_path.is_file():
516
+ upload_url = cloud_app.upload_url()
517
+ if tarfile.is_tarfile(input_path):
518
+ cloud_app.upload_data(data=None, upload_url=upload_url, tar_file=input_path)
519
+ else:
520
+ input_data = input_path.read_text()
521
+ cloud_app.upload_data(data=input_data, upload_url=upload_url)
522
+
523
+ return {"upload_id": upload_url.upload_id}
524
+
525
+ # If the input is a directory, we give the path directly to the run method.
526
+ # Internally, the files will be tarred and uploaded.
527
+ if input_path.is_dir():
528
+ return {"input_dir_path": input}
529
+
530
+ error(f"Input path [magenta]{input}[/magenta] does not exist.")