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,303 @@
1
+ """
2
+ Application mixin for managing app versions.
3
+ """
4
+
5
+ from typing import TYPE_CHECKING
6
+
7
+ import requests
8
+
9
+ from nextmv.cloud.application._utils import _is_not_exist_error
10
+ from nextmv.cloud.version import Version
11
+ from nextmv.safe import safe_id
12
+
13
+ if TYPE_CHECKING:
14
+ from . import Application
15
+
16
+
17
+ class ApplicationVersionMixin:
18
+ """
19
+ Mixin class for managing app versions within an application.
20
+ """
21
+
22
+ def delete_version(self: "Application", version_id: str) -> None:
23
+ """
24
+ Delete a version.
25
+
26
+ Permanently removes the specified version from the application.
27
+
28
+ Parameters
29
+ ----------
30
+ version_id : str
31
+ ID of the version to delete.
32
+
33
+ Raises
34
+ ------
35
+ requests.HTTPError
36
+ If the response status code is not 2xx.
37
+
38
+ Examples
39
+ --------
40
+ >>> app.delete_version("v1.0.0") # Permanently deletes the version
41
+ """
42
+
43
+ _ = self.client.request(
44
+ method="DELETE",
45
+ endpoint=f"{self.endpoint}/versions/{version_id}",
46
+ )
47
+
48
+ def list_versions(self: "Application") -> list[Version]:
49
+ """
50
+ List all versions.
51
+
52
+ Returns
53
+ -------
54
+ list[Version]
55
+ List of all versions associated with this application.
56
+
57
+ Raises
58
+ ------
59
+ requests.HTTPError
60
+ If the response status code is not 2xx.
61
+
62
+ Examples
63
+ --------
64
+ >>> versions = app.list_versions()
65
+ >>> for version in versions:
66
+ ... print(version.name)
67
+ 'v1.0.0'
68
+ 'v1.1.0'
69
+ """
70
+
71
+ response = self.client.request(
72
+ method="GET",
73
+ endpoint=f"{self.endpoint}/versions",
74
+ )
75
+
76
+ return [Version.from_dict(version) for version in response.json()]
77
+
78
+ def new_version(
79
+ self: "Application",
80
+ id: str | None = None,
81
+ name: str | None = None,
82
+ description: str | None = None,
83
+ exist_ok: bool = False,
84
+ ) -> Version:
85
+ """
86
+ Create a new version using the latest pushed executable.
87
+
88
+ This method creates a new version of the application using the current development
89
+ binary. Application versions represent different iterations of your application's
90
+ code and configuration that can be deployed.
91
+
92
+ Parameters
93
+ ----------
94
+ id : Optional[str], default=None
95
+ ID of the version. If not provided, a unique ID will be generated.
96
+ name : Optional[str], default=None
97
+ Name of the version. If not provided, a name will be generated.
98
+ description : Optional[str], default=None
99
+ Description of the version. If not provided, a description will be generated.
100
+ exist_ok : bool, default=False
101
+ If True and a version with the same ID already exists,
102
+ return the existing version instead of creating a new one.
103
+ If True, the 'id' parameter must be provided.
104
+
105
+ Returns
106
+ -------
107
+ Version
108
+ The newly created (or existing) version.
109
+
110
+ Raises
111
+ ------
112
+ ValueError
113
+ If exist_ok is True and id is None.
114
+ requests.HTTPError
115
+ If the response status code is not 2xx.
116
+
117
+ Examples
118
+ --------
119
+ >>> # Create a new version
120
+ >>> version = app.new_version(
121
+ ... id="v1.0.0",
122
+ ... name="Initial Release",
123
+ ... description="First stable version"
124
+ ... )
125
+ >>> print(version.id)
126
+ 'v1.0.0'
127
+
128
+ >>> # Get or create a version with exist_ok
129
+ >>> version = app.new_version(
130
+ ... id="v1.0.0",
131
+ ... exist_ok=True
132
+ ... )
133
+ """
134
+
135
+ if exist_ok and id is None:
136
+ raise ValueError("If exist_ok is True, id must be provided")
137
+
138
+ if exist_ok and self.version_exists(version_id=id):
139
+ return self.version(version_id=id)
140
+
141
+ if id is None:
142
+ id = safe_id(prefix="version")
143
+ if name is None:
144
+ name = id
145
+
146
+ payload = {
147
+ "id": id,
148
+ "name": name,
149
+ }
150
+
151
+ if description is not None:
152
+ payload["description"] = description
153
+
154
+ response = self.client.request(
155
+ method="POST",
156
+ endpoint=f"{self.endpoint}/versions",
157
+ payload=payload,
158
+ )
159
+
160
+ return Version.from_dict(response.json())
161
+
162
+ def update_version(
163
+ self: "Application",
164
+ version_id: str,
165
+ name: str | None = None,
166
+ description: str | None = None,
167
+ ) -> Version:
168
+ """
169
+ Update a version.
170
+
171
+ This method updates a specific version of the application. It mimics a
172
+ PATCH operation by allowing you to update only the name and/or description
173
+ fields while preserving all other fields.
174
+
175
+ Parameters
176
+ ----------
177
+ version_id : str
178
+ ID of the version to update.
179
+ name : Optional[str], default=None
180
+ Optional new name for the version.
181
+ description : Optional[str], default=None
182
+ Optional new description for the version.
183
+
184
+ Returns
185
+ -------
186
+ Version
187
+ The updated version object.
188
+
189
+ Raises
190
+ ------
191
+ requests.HTTPError
192
+ If the response status code is not 2xx.
193
+
194
+ Examples
195
+ --------
196
+ >>> # Update a version's name
197
+ >>> updated = app.update_version("v1.0.0", name="Version 1.0")
198
+ >>> print(updated.name)
199
+ 'Version 1.0'
200
+
201
+ >>> # Update a version's description
202
+ >>> updated = app.update_version("v1.0.0", description="Initial release")
203
+ >>> print(updated.description)
204
+ 'Initial release'
205
+ """
206
+
207
+ version = self.version(version_id=version_id)
208
+ version_dict = version.to_dict()
209
+ payload = version_dict.copy()
210
+
211
+ if name is not None:
212
+ payload["name"] = name
213
+ if description is not None:
214
+ payload["description"] = description
215
+
216
+ response = self.client.request(
217
+ method="PUT",
218
+ endpoint=f"{self.endpoint}/versions/{version_id}",
219
+ payload=payload,
220
+ )
221
+
222
+ return Version.from_dict(response.json())
223
+
224
+ def version(self: "Application", version_id: str) -> Version:
225
+ """
226
+ Get a version.
227
+
228
+ Retrieves a specific version of the application by its ID. Application versions
229
+ represent different iterations of your application's code and configuration.
230
+
231
+ Parameters
232
+ ----------
233
+ version_id : str
234
+ ID of the version to retrieve.
235
+
236
+ Returns
237
+ -------
238
+ Version
239
+ The version object containing details about the requested application version.
240
+
241
+ Raises
242
+ ------
243
+ requests.HTTPError
244
+ If the response status code is not 2xx.
245
+
246
+ Examples
247
+ --------
248
+ >>> # Retrieve a specific version
249
+ >>> version = app.version("v1.0.0")
250
+ >>> print(version.id)
251
+ 'v1.0.0'
252
+ >>> print(version.name)
253
+ 'Initial Release'
254
+ """
255
+
256
+ response = self.client.request(
257
+ method="GET",
258
+ endpoint=f"{self.endpoint}/versions/{version_id}",
259
+ )
260
+
261
+ return Version.from_dict(response.json())
262
+
263
+ def version_exists(self: "Application", version_id: str) -> bool:
264
+ """
265
+ Check if a version exists.
266
+
267
+ This method checks if a specific version of the application exists by
268
+ attempting to retrieve it. It handles HTTP errors for non-existent versions
269
+ and returns a boolean indicating existence.
270
+
271
+ Parameters
272
+ ----------
273
+ version_id : str
274
+ ID of the version to check for existence.
275
+
276
+ Returns
277
+ -------
278
+ bool
279
+ True if the version exists, False otherwise.
280
+
281
+ Raises
282
+ ------
283
+ requests.HTTPError
284
+ If an HTTP error occurs that is not related to the non-existence
285
+ of the version.
286
+
287
+ Examples
288
+ --------
289
+ >>> # Check if a version exists
290
+ >>> exists = app.version_exists("v1.0.0")
291
+ >>> if exists:
292
+ ... print("Version exists!")
293
+ ... else:
294
+ ... print("Version does not exist.")
295
+ """
296
+
297
+ try:
298
+ self.version(version_id=version_id)
299
+ return True
300
+ except requests.HTTPError as e:
301
+ if _is_not_exist_error(e):
302
+ return False
303
+ raise e
nextmv/cloud/assets.py ADDED
@@ -0,0 +1,48 @@
1
+ from nextmv.base_model import BaseModel
2
+ from nextmv.output import Visual
3
+
4
+
5
+ class RunAsset(BaseModel):
6
+ """
7
+ Represents an asset associated with a Nextmv Cloud run.
8
+
9
+ You can import the `RunAsset` class from `nextmv.cloud`:
10
+
11
+ ```python
12
+ from nextmv.cloud import RunAsset
13
+ ```
14
+
15
+ A run asset represents metadata about an asset associated with a Nextmv Cloud run.
16
+
17
+ Parameters
18
+ ----------
19
+ id : str
20
+ Unique identifier of the asset.
21
+ run_id : str
22
+ Identifier of the run associated with the asset.
23
+ name : str
24
+ Name of the asset.
25
+ created_at : str
26
+ Timestamp of when the asset was created.
27
+ size : int
28
+ Size of the asset content in bytes.
29
+ content_type : str
30
+ Content type of the asset. Only `json` is allowed at the moment.
31
+ visual : Visual | None, optional
32
+ Visual schema of the asset, by default None.
33
+ """
34
+
35
+ id: str
36
+ """Unique identifier of the asset."""
37
+ run_id: str
38
+ """Identifier of the run associated with the asset."""
39
+ name: str
40
+ """Name of the asset."""
41
+ created_at: str
42
+ """Timestamp of when the asset was created."""
43
+ size: int
44
+ """Size of the asset content in bytes."""
45
+ content_type: str
46
+ """Content type of the asset. Only `json` is allowed at the moment."""
47
+ visual: Visual | None = None
48
+ """Visual schema of the asset."""