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,251 @@
1
+ """
2
+ Application mixin for managing app ensembles.
3
+ """
4
+
5
+ from typing import TYPE_CHECKING
6
+
7
+ from nextmv.cloud.ensemble import EnsembleDefinition, EvaluationRule, RunGroup
8
+ from nextmv.deprecated import deprecated
9
+ from nextmv.safe import safe_id
10
+
11
+ if TYPE_CHECKING:
12
+ from . import Application
13
+
14
+
15
+ class ApplicationEnsembleMixin:
16
+ """
17
+ Mixin class for managing app ensembles within an application.
18
+ """
19
+
20
+ def delete_ensemble_definition(self: "Application", ensemble_definition_id: str) -> None:
21
+ """
22
+ Delete an ensemble definition.
23
+
24
+ Parameters
25
+ ----------
26
+ ensemble_definition_id : str
27
+ ID of the ensemble definition to delete.
28
+
29
+ Raises
30
+ ------
31
+ requests.HTTPError
32
+ If the response status code is not 2xx.
33
+
34
+ Examples
35
+ --------
36
+ >>> app.delete_ensemble_definition("development-ensemble-definition")
37
+ """
38
+
39
+ _ = self.client.request(
40
+ method="DELETE",
41
+ endpoint=f"{self.ensembles_endpoint}/{ensemble_definition_id}",
42
+ )
43
+
44
+ def ensemble_definition(self: "Application", ensemble_definition_id: str) -> EnsembleDefinition:
45
+ """
46
+ Get an ensemble definition.
47
+
48
+ Parameters
49
+ ----------
50
+ ensemble_definition_id : str
51
+ ID of the ensemble definition to retrieve.
52
+
53
+ Returns
54
+ -------
55
+ EnsembleDefintion
56
+ The requested ensemble definition details.
57
+
58
+ Raises
59
+ ------
60
+ requests.HTTPError
61
+ If the response status code is not 2xx.
62
+
63
+ Examples
64
+ --------
65
+ >>> ensemble_definition = app.ensemble_definition("instance-123")
66
+ >>> print(ensemble_definition.name)
67
+ 'Production Ensemble Definition'
68
+ """
69
+
70
+ response = self.client.request(
71
+ method="GET",
72
+ endpoint=f"{self.ensembles_endpoint}/{ensemble_definition_id}",
73
+ )
74
+
75
+ return EnsembleDefinition.from_dict(response.json())
76
+
77
+ def list_ensemble_definitions(self: "Application") -> list[EnsembleDefinition]:
78
+ """
79
+ List all ensemble_definitions.
80
+
81
+ Returns
82
+ -------
83
+ list[EnsembleDefinition]
84
+ List of all ensemble definitions associated with this application.
85
+
86
+ Raises
87
+ ------
88
+ requests.HTTPError
89
+ If the response status code is not 2xx.
90
+
91
+ Examples
92
+ --------
93
+ >>> ensemble_definitions = app.list_ensemble_definitions()
94
+ >>> for ensemble_definition in ensemble_definitions:
95
+ ... print(ensemble_definition.name)
96
+ 'Development Ensemble Definition'
97
+ 'Production Ensemble Definition'
98
+ """
99
+
100
+ response = self.client.request(
101
+ method="GET",
102
+ endpoint=f"{self.ensembles_endpoint}",
103
+ )
104
+
105
+ return [EnsembleDefinition.from_dict(ensemble_definition) for ensemble_definition in response.json()["items"]]
106
+
107
+ def new_ensemble_defintion(
108
+ self: "Application",
109
+ id: str,
110
+ run_groups: list[RunGroup],
111
+ rules: list[EvaluationRule],
112
+ name: str | None = None,
113
+ description: str | None = None,
114
+ ) -> EnsembleDefinition:
115
+ """
116
+ !!! warning
117
+ `new_ensemble_defintion` is deprecated, use `new_ensemble_definition` instead.
118
+
119
+ Create a new ensemble definition.
120
+
121
+ Parameters
122
+ ----------
123
+ id: str
124
+ ID of the ensemble defintion.
125
+ run_groups: list[RunGroup]
126
+ Information to facilitate the execution of child runs.
127
+ rules: list[EvaluationRule]
128
+ Information to facilitate the selection of
129
+ a result for the ensemble run from child runs.
130
+ name: Optional[str]
131
+ Name of the ensemble definition.
132
+ description: Optional[str]
133
+ Description of the ensemble definition.
134
+ """
135
+
136
+ deprecated(
137
+ name="new_ensemble_defintion",
138
+ reason="`Application.new_ensemble_defintion` is deprecated, use `new_ensemble_definition` instead",
139
+ )
140
+
141
+ return self.new_ensemble_definition(
142
+ run_groups=run_groups,
143
+ rules=rules,
144
+ id=id,
145
+ name=name,
146
+ description=description,
147
+ )
148
+
149
+ def new_ensemble_definition(
150
+ self: "Application",
151
+ run_groups: list[RunGroup],
152
+ rules: list[EvaluationRule],
153
+ id: str | None = None,
154
+ name: str | None = None,
155
+ description: str | None = None,
156
+ ) -> EnsembleDefinition:
157
+ """
158
+ Create a new ensemble definition.
159
+
160
+ Parameters
161
+ ----------
162
+ run_groups: list[RunGroup]
163
+ Information to facilitate the execution of child runs.
164
+ rules: list[EvaluationRule]
165
+ Information to facilitate the selection of
166
+ a result for the ensemble run from child runs.
167
+ id: str | None, default=None
168
+ ID of the ensemble definition. If not provided, a unique ID will be
169
+ generated with the prefix 'ensemble-'.
170
+ name: Optional[str]
171
+ Name of the ensemble definition. If not provided, the ID will be used.
172
+ description: Optional[str]
173
+ Description of the ensemble definition. If not provided, the name will be used.
174
+ """
175
+
176
+ if len(run_groups) == 0:
177
+ raise ValueError("at least one run group must be defined to create an ensemble definition")
178
+
179
+ if len(rules) == 0:
180
+ raise ValueError("at least one evaluation rule must be defined to create an ensemble definition")
181
+
182
+ if id is None or id == "":
183
+ id = safe_id(prefix="ensemble")
184
+ if name is None or name == "":
185
+ name = id
186
+ if description is None or description == "":
187
+ description = name
188
+
189
+ payload = {
190
+ "id": id,
191
+ "run_groups": [run_group.to_dict() for run_group in run_groups],
192
+ "rules": [rule.to_dict() for rule in rules],
193
+ "name": name,
194
+ "description": description,
195
+ }
196
+
197
+ response = self.client.request(
198
+ method="POST",
199
+ endpoint=f"{self.ensembles_endpoint}",
200
+ payload=payload,
201
+ )
202
+
203
+ return EnsembleDefinition.from_dict(response.json())
204
+
205
+ def update_ensemble_definition(
206
+ self: "Application",
207
+ id: str,
208
+ name: str | None = None,
209
+ description: str | None = None,
210
+ ) -> EnsembleDefinition:
211
+ """
212
+ Update an ensemble definition.
213
+
214
+ Parameters
215
+ ----------
216
+ id : str
217
+ ID of the ensemble definition to update.
218
+ name : Optional[str], default=None
219
+ Optional name of the ensemble definition.
220
+ description : Optional[str], default=None
221
+ Optional description of the ensemble definition.
222
+
223
+ Returns
224
+ -------
225
+ EnsembleDefinition
226
+ The updated ensemble definition.
227
+
228
+ Raises
229
+ ------
230
+ ValueError
231
+ If neither name nor description is updated
232
+ requests.HTTPError
233
+ If the response status code is not 2xx.
234
+ """
235
+
236
+ payload = {}
237
+
238
+ if name is None and description is None:
239
+ raise ValueError("Must define at least one value among name and description to modify")
240
+ if name is not None:
241
+ payload["name"] = name
242
+ if description is not None:
243
+ payload["description"] = description
244
+
245
+ response = self.client.request(
246
+ method="PATCH",
247
+ endpoint=f"{self.ensembles_endpoint}/{id}",
248
+ payload=payload,
249
+ )
250
+
251
+ return EnsembleDefinition.from_dict(response.json())
@@ -0,0 +1,227 @@
1
+ """
2
+ Application mixin for managing app input sets.
3
+ """
4
+
5
+ from datetime import datetime
6
+ from typing import TYPE_CHECKING
7
+
8
+ from nextmv.cloud.input_set import InputSet, ManagedInput
9
+
10
+ if TYPE_CHECKING:
11
+ from . import Application
12
+
13
+
14
+ class ApplicationInputSetMixin:
15
+ """
16
+ Mixin class for managing app input sets within an application.
17
+ """
18
+
19
+ def input_set(self: "Application", input_set_id: str) -> InputSet:
20
+ """
21
+ Get an input set.
22
+
23
+ Parameters
24
+ ----------
25
+ input_set_id : str
26
+ ID of the input set to retrieve.
27
+
28
+ Returns
29
+ -------
30
+ InputSet
31
+ The requested input set.
32
+
33
+ Raises
34
+ ------
35
+ requests.HTTPError
36
+ If the response status code is not 2xx.
37
+
38
+ Examples
39
+ --------
40
+ >>> input_set = app.input_set("input-set-123")
41
+ >>> print(input_set.name)
42
+ 'My Input Set'
43
+ """
44
+
45
+ response = self.client.request(
46
+ method="GET",
47
+ endpoint=f"{self.experiments_endpoint}/inputsets/{input_set_id}",
48
+ )
49
+
50
+ return InputSet.from_dict(response.json())
51
+
52
+ def list_input_sets(self: "Application") -> list[InputSet]:
53
+ """
54
+ List all input sets.
55
+
56
+ Returns
57
+ -------
58
+ list[InputSet]
59
+ List of all input sets associated with this application.
60
+
61
+ Raises
62
+ ------
63
+ requests.HTTPError
64
+ If the response status code is not 2xx.
65
+
66
+ Examples
67
+ --------
68
+ >>> input_sets = app.list_input_sets()
69
+ >>> for input_set in input_sets:
70
+ ... print(input_set.name)
71
+ 'Input Set 1'
72
+ 'Input Set 2'
73
+ """
74
+
75
+ response = self.client.request(
76
+ method="GET",
77
+ endpoint=f"{self.experiments_endpoint}/inputsets",
78
+ )
79
+
80
+ return [InputSet.from_dict(input_set) for input_set in response.json()]
81
+
82
+ def new_input_set(
83
+ self: "Application",
84
+ id: str,
85
+ name: str,
86
+ description: str | None = None,
87
+ end_time: datetime | None = None,
88
+ instance_id: str | None = None,
89
+ maximum_runs: int | None = None,
90
+ run_ids: list[str] | None = None,
91
+ start_time: datetime | None = None,
92
+ inputs: list[ManagedInput] | None = None,
93
+ ) -> InputSet:
94
+ """
95
+ Create a new input set. You can create an input set from three
96
+ different methodologies:
97
+
98
+ 1. Using `instance_id`, `start_time`, `end_time` and `maximum_runs`.
99
+ Instance runs will be obtained from the application matching the
100
+ criteria of dates and maximum number of runs.
101
+ 2. Using `run_ids`. The input set will be created using the list of
102
+ runs specified by the user.
103
+ 3. Using `inputs`. The input set will be created using the list of
104
+ inputs specified by the user. This is useful for creating an input
105
+ set from a list of inputs that are already available in the
106
+ application.
107
+
108
+ Parameters
109
+ ----------
110
+ id: str
111
+ ID of the input set
112
+ name: str
113
+ Name of the input set.
114
+ description: Optional[str]
115
+ Optional description of the input set.
116
+ end_time: Optional[datetime]
117
+ End time of the input set. This is used to filter the runs
118
+ associated with the input set.
119
+ instance_id: Optional[str]
120
+ ID of the instance to use for the input set. This is used to
121
+ filter the runs associated with the input set. If not provided,
122
+ the application's `default_instance_id` is used.
123
+ maximum_runs: Optional[int]
124
+ Maximum number of runs to use for the input set. This is used to
125
+ filter the runs associated with the input set. If not provided,
126
+ all runs are used.
127
+ run_ids: Optional[list[str]]
128
+ List of run IDs to use for the input set.
129
+ start_time: Optional[datetime]
130
+ Start time of the input set. This is used to filter the runs
131
+ associated with the input set.
132
+ inputs: Optional[list[ManagedInput]]
133
+ List of inputs to use for the input set. This is used to create
134
+ the input set from a list of inputs that are already available in
135
+ the application.
136
+
137
+ Returns
138
+ -------
139
+ InputSet
140
+ The new input set.
141
+
142
+ Raises
143
+ ------
144
+ requests.HTTPError
145
+ If the response status code is not 2xx.
146
+ """
147
+
148
+ payload = {
149
+ "id": id,
150
+ "name": name,
151
+ }
152
+ if description is not None:
153
+ payload["description"] = description
154
+ if end_time is not None:
155
+ payload["end_time"] = end_time.isoformat()
156
+ if instance_id is not None:
157
+ payload["instance_id"] = instance_id
158
+ if maximum_runs is not None:
159
+ payload["maximum_runs"] = maximum_runs
160
+ if run_ids is not None:
161
+ payload["run_ids"] = run_ids
162
+ if start_time is not None:
163
+ payload["start_time"] = start_time.isoformat()
164
+ if inputs is not None:
165
+ payload["inputs"] = [input.to_dict() for input in inputs]
166
+
167
+ response = self.client.request(
168
+ method="POST",
169
+ endpoint=f"{self.experiments_endpoint}/inputsets",
170
+ payload=payload,
171
+ )
172
+
173
+ return InputSet.from_dict(response.json())
174
+
175
+ def update_input_set(
176
+ self: "Application",
177
+ id: str,
178
+ name: str | None = None,
179
+ description: str | None = None,
180
+ inputs: list[ManagedInput] | None = None,
181
+ ) -> InputSet:
182
+ """
183
+ Update an input set.
184
+
185
+ Parameters
186
+ ----------
187
+ id : str
188
+ ID of the input set to update.
189
+ name : Optional[str], default=None
190
+ Optional name of the input set.
191
+ description : Optional[str], default=None
192
+ Optional description of the input set.
193
+ inputs: Optional[list[ManagedInput]]
194
+ List of inputs to use for the input set. This is used to create
195
+ the input set from a list of inputs that are already available in
196
+ the application.
197
+
198
+ Returns
199
+ -------
200
+ Instance
201
+ The updated instance.
202
+
203
+ Raises
204
+ ------
205
+ requests.HTTPError
206
+ If the response status code is not 2xx.
207
+ """
208
+
209
+ # Get the input set as it currently exsits.
210
+ input_set = self.input_set(id)
211
+ input_set_dict = input_set.to_dict()
212
+ payload = input_set_dict.copy()
213
+
214
+ if name is not None:
215
+ payload["name"] = name
216
+ if description is not None:
217
+ payload["description"] = description
218
+ if inputs is not None:
219
+ payload["inputs"] = [input.to_dict() for input in inputs]
220
+
221
+ response = self.client.request(
222
+ method="PUT",
223
+ endpoint=f"{self.experiments_endpoint}/inputsets/{id}",
224
+ payload=payload,
225
+ )
226
+
227
+ return InputSet.from_dict(response.json())