nextmv 0.24.0__py3-none-any.whl → 0.26.0__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/__about__.py CHANGED
@@ -1 +1 @@
1
- __version__ = "v0.24.0"
1
+ __version__ = "v0.26.0"
@@ -14,7 +14,12 @@ import requests
14
14
  from nextmv.base_model import BaseModel
15
15
  from nextmv.cloud import package
16
16
  from nextmv.cloud.acceptance_test import AcceptanceTest, ExperimentStatus, Metric
17
- from nextmv.cloud.batch_experiment import BatchExperiment, BatchExperimentMetadata, BatchExperimentRun
17
+ from nextmv.cloud.batch_experiment import (
18
+ BatchExperiment,
19
+ BatchExperimentInformation,
20
+ BatchExperimentMetadata,
21
+ BatchExperimentRun,
22
+ )
18
23
  from nextmv.cloud.client import Client, get_size
19
24
  from nextmv.cloud.input_set import InputSet, ManagedInput
20
25
  from nextmv.cloud.instance import Instance, InstanceConfiguration
@@ -1799,7 +1804,7 @@ class Application:
1799
1804
 
1800
1805
  return self.batch_experiment(batch_id=scenario_test_id)
1801
1806
 
1802
- def track_run(self, tracked_run: TrackedRun) -> str:
1807
+ def track_run(self, tracked_run: TrackedRun, instance_id: Optional[str] = None) -> str:
1803
1808
  """
1804
1809
  Track an external run.
1805
1810
 
@@ -1812,6 +1817,9 @@ class Application:
1812
1817
  ----------
1813
1818
  tracked_run : TrackedRun
1814
1819
  The run to track.
1820
+ instance_id: Optional[str]
1821
+ Optional instance ID if you want to associate your tracked run with
1822
+ an instance.
1815
1823
 
1816
1824
  Returns
1817
1825
  -------
@@ -1855,12 +1863,17 @@ class Application:
1855
1863
  if tracked_run.error is not None and tracked_run.error != "":
1856
1864
  external_result.error_message = tracked_run.error
1857
1865
 
1858
- return self.new_run(upload_id=url_input.upload_id, external_result=external_result)
1866
+ return self.new_run(
1867
+ upload_id=url_input.upload_id,
1868
+ external_result=external_result,
1869
+ instance_id=instance_id,
1870
+ )
1859
1871
 
1860
1872
  def track_run_with_result(
1861
1873
  self,
1862
1874
  tracked_run: TrackedRun,
1863
1875
  polling_options: PollingOptions = _DEFAULT_POLLING_OPTIONS,
1876
+ instance_id: Optional[str] = None,
1864
1877
  ) -> RunResult:
1865
1878
  """
1866
1879
  Track an external run and poll for the result. This is a convenience
@@ -1874,6 +1887,9 @@ class Application:
1874
1887
  The run to track.
1875
1888
  polling_options : PollingOptions
1876
1889
  Options to use when polling for the run result.
1890
+ instance_id: Optional[str]
1891
+ Optional instance ID if you want to associate your tracked run with
1892
+ an instance.
1877
1893
 
1878
1894
  Returns
1879
1895
  -------
@@ -1893,7 +1909,7 @@ class Application:
1893
1909
  If the run does not succeed after the polling strategy is
1894
1910
  exhausted based on number of tries.
1895
1911
  """
1896
- run_id = self.track_run(tracked_run=tracked_run)
1912
+ run_id = self.track_run(tracked_run=tracked_run, instance_id=instance_id)
1897
1913
 
1898
1914
  return self.run_result_with_polling(
1899
1915
  run_id=run_id,
@@ -1944,6 +1960,47 @@ class Application:
1944
1960
 
1945
1961
  return Instance.from_dict(response.json())
1946
1962
 
1963
+ def update_batch_experiment(
1964
+ self,
1965
+ batch_experiment_id: str,
1966
+ name: str,
1967
+ description: str,
1968
+ ) -> BatchExperimentInformation:
1969
+ """
1970
+ Update a batch experiment.
1971
+
1972
+ Parameters
1973
+ ----------
1974
+ batch_experiment_id : str
1975
+ ID of the batch experiment to update.
1976
+ name : str
1977
+ Name of the batch experiment.
1978
+ description : str
1979
+ Description of the batch experiment.
1980
+
1981
+ Returns
1982
+ -------
1983
+ BatchExperimentInformation
1984
+ The information with the updated batch experiment.
1985
+
1986
+ Raises
1987
+ ------
1988
+ requests.HTTPError
1989
+ If the response status code is not 2xx.
1990
+ """
1991
+
1992
+ payload = {
1993
+ "name": name,
1994
+ "description": description,
1995
+ }
1996
+ response = self.client.request(
1997
+ method="PATCH",
1998
+ endpoint=f"{self.experiments_endpoint}/batch/{batch_experiment_id}",
1999
+ payload=payload,
2000
+ )
2001
+
2002
+ return BatchExperimentInformation.from_dict(response.json())
2003
+
1947
2004
  def update_managed_input(
1948
2005
  self,
1949
2006
  managed_input_id: str,
@@ -1983,6 +2040,43 @@ class Application:
1983
2040
  payload=payload,
1984
2041
  )
1985
2042
 
2043
+ def update_scenario_test(
2044
+ self,
2045
+ scenario_test_id: str,
2046
+ name: str,
2047
+ description: str,
2048
+ ) -> BatchExperimentInformation:
2049
+ """
2050
+ Update a scenario test. Scenario tests use the batch experiments API,
2051
+ so this method calls the `update_batch_experiment` method, and thus the
2052
+ return type is the same.
2053
+
2054
+ Parameters
2055
+ ----------
2056
+ scenario_test_id : str
2057
+ ID of the scenario test to update.
2058
+ name : str
2059
+ Name of the scenario test.
2060
+ description : str
2061
+ Description of the scenario test.
2062
+
2063
+ Returns
2064
+ -------
2065
+ BatchExperimentInformation
2066
+ The information with the updated scenario test.
2067
+
2068
+ Raises
2069
+ ------
2070
+ requests.HTTPError
2071
+ If the response status code is not 2xx.
2072
+ """
2073
+
2074
+ return self.update_batch_experiment(
2075
+ batch_experiment_id=scenario_test_id,
2076
+ name=name,
2077
+ description=description,
2078
+ )
2079
+
1986
2080
  def update_secrets_collection(
1987
2081
  self,
1988
2082
  secrets_collection_id: str,
@@ -18,9 +18,9 @@ class BatchExperimentInformation(BaseModel):
18
18
  """Creation date of the batch experiment."""
19
19
  updated_at: datetime
20
20
  """Last update date of the batch experiment."""
21
- status: str
22
- """Status of the batch experiment."""
23
21
 
22
+ status: Optional[str] = None
23
+ """Status of the batch experiment."""
24
24
  description: Optional[str] = None
25
25
  """Description of the batch experiment."""
26
26
  number_of_requested_runs: Optional[int] = None
@@ -101,5 +101,5 @@ class BatchExperimentRun(BaseModel):
101
101
  class BatchExperimentMetadata(BatchExperimentInformation):
102
102
  """Metadata of a batch experiment."""
103
103
 
104
- app_id: str
104
+ app_id: Optional[str] = None
105
105
  """ID of the application used for the batch experiment."""
nextmv/output.py CHANGED
@@ -349,7 +349,7 @@ class Output:
349
349
  class OutputWriter:
350
350
  """Base class for writing outputs."""
351
351
 
352
- def write(self, output: Output, *args, **kwargs) -> None:
352
+ def write(self, output: Union[Output, dict[str, Any], BaseModel], *args, **kwargs) -> None:
353
353
  """
354
354
  Write the output data. This method should be implemented by subclasses.
355
355
  """
@@ -364,7 +364,7 @@ class LocalOutputWriter(OutputWriter):
364
364
  """
365
365
 
366
366
  def _write_json(
367
- output: Union[Output, dict[str, Any]],
367
+ output: Union[Output, dict[str, Any], BaseModel],
368
368
  options: dict[str, Any],
369
369
  statistics: dict[str, Any],
370
370
  assets: list[dict[str, Any]],
@@ -372,6 +372,8 @@ class LocalOutputWriter(OutputWriter):
372
372
  ) -> None:
373
373
  if isinstance(output, dict):
374
374
  final_output = output
375
+ elif isinstance(output, BaseModel):
376
+ final_output = output.to_dict()
375
377
  else:
376
378
  solution = output.solution if output.solution is not None else {}
377
379
  final_output = {
@@ -447,7 +449,7 @@ class LocalOutputWriter(OutputWriter):
447
449
 
448
450
  def write(
449
451
  self,
450
- output: Union[Output, dict[str, Any]],
452
+ output: Union[Output, dict[str, Any], BaseModel],
451
453
  path: Optional[str] = None,
452
454
  skip_stdout_reset: bool = False,
453
455
  ) -> None:
@@ -497,8 +499,12 @@ class LocalOutputWriter(OutputWriter):
497
499
  output_format = output.output_format
498
500
  elif isinstance(output, dict):
499
501
  output_format = OutputFormat.JSON
502
+ elif isinstance(output, BaseModel):
503
+ output_format = OutputFormat.JSON
500
504
  else:
501
- raise TypeError(f"unsupported output type: {type(output)}, supported types are `Output` or `dict`")
505
+ raise TypeError(
506
+ f"unsupported output type: {type(output)}, supported types are `Output`, `dict`, `BaseModel`"
507
+ )
502
508
 
503
509
  statistics = self._extract_statistics(output)
504
510
  options = self._extract_options(output)
@@ -640,7 +646,7 @@ _LOCAL_OUTPUT_WRITER = LocalOutputWriter()
640
646
 
641
647
 
642
648
  def write(
643
- output: Union[Output, dict[str, Any]],
649
+ output: Union[Output, dict[str, Any], BaseModel],
644
650
  path: Optional[str] = None,
645
651
  skip_stdout_reset: bool = False,
646
652
  writer: Optional[OutputWriter] = _LOCAL_OUTPUT_WRITER,
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: nextmv
3
- Version: 0.24.0
3
+ Version: 0.26.0
4
4
  Summary: The all-purpose Python SDK for Nextmv
5
5
  Project-URL: Homepage, https://www.nextmv.io
6
6
  Project-URL: Documentation, https://www.nextmv.io/docs/python-sdks/nextmv/installation
@@ -1,4 +1,4 @@
1
- nextmv/__about__.py,sha256=fD27wZZANNFf0FJ1iOoxuLS18yRdvKwwSYfjMUoav6A,24
1
+ nextmv/__about__.py,sha256=eQqPiK0rRHZ8N7lI25IAshYTaILxk2NZzxVXgTPB1OY,24
2
2
  nextmv/__entrypoint__.py,sha256=5K058PICm5sx4sNMqx56auMh9yWgdIESVLzfvyIXdjs,1158
3
3
  nextmv/__init__.py,sha256=QN5e_BFkIdBkR8DiGr9T06N6mXtowT84eRUJj3_Vfrg,1459
4
4
  nextmv/base_model.py,sha256=mdaBe-epNK1cFgP4TxbOtn3So4pCi1vMTOrIBkCBp7A,1050
@@ -7,12 +7,12 @@ nextmv/input.py,sha256=tppXHiJM_EzR9Z1yJPc37joBvgC0RmiAFo0Ab1X5nPA,15480
7
7
  nextmv/logger.py,sha256=5qQ7E3Aaw3zzkIeiUuwYGzTcB7VhVqIzNZm5PHmdpUI,852
8
8
  nextmv/model.py,sha256=rwBdgmKSEp1DPv43zF0azj3QnbHO6O6wKs0PIGvVS40,9861
9
9
  nextmv/options.py,sha256=IPqAIUjoKMWmoPx_e5zDcnxu7S2HVxw-SLjjUduVvZM,25302
10
- nextmv/output.py,sha256=xag6SoNkd3oyKBgQCbvx6jsYLu4pzHPuLEAEtbCDolI,21984
10
+ nextmv/output.py,sha256=rcmDivGRsInyS944ivO6SBtVFSH3RC5Nwu0qFTFRRLI,22270
11
11
  nextmv/cloud/__init__.py,sha256=O34rjrgClnIwXDE63PK4iq5ESuGQ86dJ6FPSrcUnmk0,3680
12
12
  nextmv/cloud/acceptance_test.py,sha256=NtqGhj-UYibxGBbU2kfjr-lYcngojb_5VMvK2WZwibI,6620
13
13
  nextmv/cloud/account.py,sha256=mZUGzV-uMGBA5BC_FPtsiCMFuz5jxEZ3O1BbELZIm18,1841
14
- nextmv/cloud/application.py,sha256=tx_VgtW34cpYdOCdvWh6J5z6KpEEeU22nPdYvtRyuKI,79346
15
- nextmv/cloud/batch_experiment.py,sha256=xuItiW0hhaDt78BWSz7bcw8cbI8joCkw5KH2Gj5JRbQ,3734
14
+ nextmv/cloud/application.py,sha256=BoW-n_cJ4nxpmxOq_VMdJzv7B4RSxRhApkv0_oEcEYQ,81952
15
+ nextmv/cloud/batch_experiment.py,sha256=Ngm1XDvvAMaU9VYU5JFNxXFnt8xjE_34H5W54kO9V5c,3768
16
16
  nextmv/cloud/client.py,sha256=JUE3vD767_FFICl1vov5Mxmircci03TBL3KmT1BOZY4,9096
17
17
  nextmv/cloud/input_set.py,sha256=HTLA2acJridZbBCAVJNom8ldNo2Rfy40YQ8Coyz3bdo,1482
18
18
  nextmv/cloud/instance.py,sha256=UfyfZXfL1ugCGAB6zwZJIAi8qxI1JCUGsluwaGdjfN4,1223
@@ -24,7 +24,7 @@ nextmv/cloud/scenario.py,sha256=9gbdnQuvmerPUBCcJ-5QbLCwgbsIfBwKXE8c5359S8E,8120
24
24
  nextmv/cloud/secrets.py,sha256=kqlN4ceww_L4kVTrAU8BZykRzXINO3zhMT_BLYea6tk,1764
25
25
  nextmv/cloud/status.py,sha256=C-ax8cLw0jPeh7CPsJkCa0s4ImRyFI4NDJJxI0_1sr4,602
26
26
  nextmv/cloud/version.py,sha256=sjVRNRtohHA97j6IuyM33_DSSsXYkZPusYgpb6hlcrc,1244
27
- nextmv-0.24.0.dist-info/METADATA,sha256=i51XfzKArsSXtHLUce4KczcnWp5h5Uqq7_Iezk4Xsug,14557
28
- nextmv-0.24.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
29
- nextmv-0.24.0.dist-info/licenses/LICENSE,sha256=ZIbK-sSWA-OZprjNbmJAglYRtl5_K4l9UwAV3PGJAPc,11349
30
- nextmv-0.24.0.dist-info/RECORD,,
27
+ nextmv-0.26.0.dist-info/METADATA,sha256=c53wSmLfDD0omHMTPlxDkrx9DMb5RdvKMNHCByar1TA,14557
28
+ nextmv-0.26.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
29
+ nextmv-0.26.0.dist-info/licenses/LICENSE,sha256=ZIbK-sSWA-OZprjNbmJAglYRtl5_K4l9UwAV3PGJAPc,11349
30
+ nextmv-0.26.0.dist-info/RECORD,,