nextmv 0.35.0.dev1__py3-none-any.whl → 0.36.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.
@@ -17,7 +17,7 @@ import tempfile
17
17
  import webbrowser
18
18
  from dataclasses import dataclass
19
19
  from datetime import datetime, timezone
20
- from typing import Any, Optional, Union
20
+ from typing import Any
21
21
 
22
22
  from nextmv import cloud
23
23
  from nextmv._serialization import deflated_serialize_json
@@ -92,9 +92,9 @@ class Application:
92
92
  manifest.
93
93
  """
94
94
 
95
- description: Optional[str] = None
95
+ description: str | None = None
96
96
  """Description of the application."""
97
- manifest: Optional[Manifest] = None
97
+ manifest: Manifest | None = None
98
98
  """
99
99
  Manifest of the application. A manifest is a file named `app.yaml` that
100
100
  must be present at the root of the application's `src` directory. If the
@@ -131,9 +131,9 @@ class Application:
131
131
  @classmethod
132
132
  def initialize(
133
133
  cls,
134
- src: Optional[str] = None,
135
- description: Optional[str] = None,
136
- destination: Optional[str] = None,
134
+ src: str | None = None,
135
+ description: str | None = None,
136
+ destination: str | None = None,
137
137
  ) -> "Application":
138
138
  """
139
139
  Initialize a sample Nextmv application, locally.
@@ -224,13 +224,13 @@ class Application:
224
224
 
225
225
  def new_run(
226
226
  self,
227
- input: Union[Input, dict[str, Any], BaseModel, str] = None,
228
- name: Optional[str] = None,
229
- description: Optional[str] = None,
230
- options: Optional[Union[Options, dict[str, str]]] = None,
231
- configuration: Optional[Union[RunConfiguration, dict[str, Any]]] = None,
232
- json_configurations: Optional[dict[str, Any]] = None,
233
- input_dir_path: Optional[str] = None,
227
+ input: Input | dict[str, Any] | BaseModel | str = None,
228
+ name: str | None = None,
229
+ description: str | None = None,
230
+ options: Options | dict[str, str] | None = None,
231
+ configuration: RunConfiguration | dict[str, Any] | None = None,
232
+ json_configurations: dict[str, Any] | None = None,
233
+ input_dir_path: str | None = None,
234
234
  ) -> str:
235
235
  """
236
236
  Run the application locally with the provided input.
@@ -361,15 +361,15 @@ class Application:
361
361
 
362
362
  def new_run_with_result(
363
363
  self,
364
- input: Union[Input, dict[str, Any], BaseModel, str] = None,
365
- name: Optional[str] = None,
366
- description: Optional[str] = None,
367
- run_options: Optional[Union[Options, dict[str, str]]] = None,
364
+ input: Input | dict[str, Any] | BaseModel | str = None,
365
+ name: str | None = None,
366
+ description: str | None = None,
367
+ run_options: Options | dict[str, str] | None = None,
368
368
  polling_options: PollingOptions = DEFAULT_POLLING_OPTIONS,
369
- configuration: Optional[Union[RunConfiguration, dict[str, Any]]] = None,
370
- json_configurations: Optional[dict[str, Any]] = None,
371
- input_dir_path: Optional[str] = None,
372
- output_dir_path: Optional[str] = ".",
369
+ configuration: RunConfiguration | dict[str, Any] | None = None,
370
+ json_configurations: dict[str, Any] | None = None,
371
+ input_dir_path: str | None = None,
372
+ output_dir_path: str | None = ".",
373
373
  ) -> RunResult:
374
374
  """
375
375
  Submit an input to start a new local run of the application and poll
@@ -594,7 +594,7 @@ class Application:
594
594
 
595
595
  return info
596
596
 
597
- def run_result(self, run_id: str, output_dir_path: Optional[str] = ".") -> RunResult:
597
+ def run_result(self, run_id: str, output_dir_path: str | None = ".") -> RunResult:
598
598
  """
599
599
  Get the local result of a run.
600
600
 
@@ -644,7 +644,7 @@ class Application:
644
644
  self,
645
645
  run_id: str,
646
646
  polling_options: PollingOptions = DEFAULT_POLLING_OPTIONS,
647
- output_dir_path: Optional[str] = ".",
647
+ output_dir_path: str | None = ".",
648
648
  ) -> RunResult:
649
649
  """
650
650
  Get the result of a local run with polling.
@@ -761,9 +761,9 @@ class Application:
761
761
  def sync( # noqa: C901
762
762
  self,
763
763
  target: cloud.Application,
764
- run_ids: Optional[list[str]] = None,
765
- instance_id: Optional[str] = None,
766
- verbose: Optional[bool] = False,
764
+ run_ids: list[str] | None = None,
765
+ instance_id: str | None = None,
766
+ verbose: bool | None = False,
767
767
  ) -> None:
768
768
  """
769
769
  Sync the local application to a Nextmv Cloud application target.
@@ -867,7 +867,7 @@ class Application:
867
867
  self,
868
868
  run_id: str,
869
869
  run_information: RunInformation,
870
- output_dir_path: Optional[str] = ".",
870
+ output_dir_path: str | None = ".",
871
871
  ) -> RunResult:
872
872
  """
873
873
  Get the result of a local run.
@@ -931,8 +931,8 @@ class Application:
931
931
 
932
932
  def __validate_input_dir_path_and_configuration(
933
933
  self,
934
- input_dir_path: Optional[str],
935
- configuration: Optional[Union[RunConfiguration, dict[str, Any]]],
934
+ input_dir_path: str | None,
935
+ configuration: RunConfiguration | dict[str, Any] | None,
936
936
  ) -> RunConfiguration:
937
937
  """
938
938
  Auxiliary function to validate the directory path and configuration.
@@ -984,8 +984,8 @@ class Application:
984
984
 
985
985
  def __extract_input_data(
986
986
  self,
987
- input: Union[Input, dict[str, Any], BaseModel, str] = None,
988
- ) -> Optional[Union[dict[str, Any], str]]:
987
+ input: Input | dict[str, Any] | BaseModel | str = None,
988
+ ) -> dict[str, Any] | str | None:
989
989
  """
990
990
  Auxiliary function to extract the input data from the input, based on
991
991
  its type.
@@ -1003,8 +1003,8 @@ class Application:
1003
1003
 
1004
1004
  def __extract_options_dict(
1005
1005
  self,
1006
- options: Optional[Union[Options, dict[str, str]]] = None,
1007
- json_configurations: Optional[dict[str, Any]] = None,
1006
+ options: Options | dict[str, str] | None = None,
1007
+ json_configurations: dict[str, Any] | None = None,
1008
1008
  ) -> dict[str, str]:
1009
1009
  """
1010
1010
  Auxiliary function to extract the options that will be sent to the
@@ -1026,9 +1026,9 @@ class Application:
1026
1026
 
1027
1027
  def __extract_run_config(
1028
1028
  self,
1029
- input: Union[Input, dict[str, Any], BaseModel, str] = None,
1030
- configuration: Optional[Union[RunConfiguration, dict[str, Any]]] = None,
1031
- dir_path: Optional[str] = None,
1029
+ input: Input | dict[str, Any] | BaseModel | str = None,
1030
+ configuration: RunConfiguration | dict[str, Any] | None = None,
1031
+ dir_path: str | None = None,
1032
1032
  ) -> dict[str, Any]:
1033
1033
  """
1034
1034
  Auxiliary function to extract the run configuration that will be sent
@@ -1053,8 +1053,8 @@ class Application:
1053
1053
  run_id: str,
1054
1054
  runs_dir: str,
1055
1055
  temp_dir: str,
1056
- instance_id: Optional[str] = None,
1057
- verbose: Optional[bool] = False,
1056
+ instance_id: str | None = None,
1057
+ verbose: bool | None = False,
1058
1058
  ) -> bool:
1059
1059
  """
1060
1060
  Syncs a local run to a Nextmv Cloud target application. Returns True if
nextmv/local/executor.py CHANGED
@@ -43,7 +43,7 @@ import subprocess
43
43
  import sys
44
44
  import tempfile
45
45
  from datetime import datetime, timezone
46
- from typing import Any, Optional, Union
46
+ from typing import Any
47
47
 
48
48
  from nextmv.input import INPUTS_KEY, InputFormat, load
49
49
  from nextmv.local.geojson_handler import handle_geojson_visual
@@ -56,7 +56,7 @@ from nextmv.local.local import (
56
56
  calculate_files_size,
57
57
  )
58
58
  from nextmv.local.plotly_handler import handle_plotly_visual
59
- from nextmv.manifest import Manifest
59
+ from nextmv.manifest import Manifest, ManifestType
60
60
  from nextmv.output import ASSETS_KEY, OUTPUTS_KEY, SOLUTIONS_KEY, STATISTICS_KEY, Asset, OutputFormat, VisualSchema
61
61
  from nextmv.status import StatusV2
62
62
 
@@ -87,9 +87,9 @@ def execute_run(
87
87
  manifest_dict: dict[str, Any],
88
88
  run_dir: str,
89
89
  run_config: dict[str, Any],
90
- inputs_dir_path: Optional[str] = None,
91
- options: Optional[dict[str, Any]] = None,
92
- input_data: Optional[Union[dict[str, Any], str]] = None,
90
+ inputs_dir_path: str | None = None,
91
+ options: dict[str, Any] | None = None,
92
+ input_data: dict[str, Any] | str | None = None,
93
93
  ) -> None:
94
94
  """
95
95
  Executes the decision model run using a subprocess to call the entrypoint
@@ -152,7 +152,8 @@ def execute_run(
152
152
  # Start a Python subprocess to execute the entrypoint. For now, we are
153
153
  # supporting a Python-first experience, so we are not summoning
154
154
  # applications that are not Python-based.
155
- entrypoint = os.path.join(temp_src, manifest.entrypoint)
155
+ entrypoint = os.path.join(temp_src, __determine_entrypoint(manifest))
156
+ cwd = __determine_cwd(manifest, default=temp_src)
156
157
  args = [sys.executable, entrypoint] + options_args(options)
157
158
 
158
159
  result = subprocess.run(
@@ -162,7 +163,7 @@ def execute_run(
162
163
  text=True,
163
164
  capture_output=True,
164
165
  input=stdin_input,
165
- cwd=temp_src,
166
+ cwd=cwd,
166
167
  )
167
168
 
168
169
  process_run_output(
@@ -190,7 +191,7 @@ def execute_run(
190
191
  f.truncate()
191
192
 
192
193
 
193
- def options_args(options: Optional[dict[str, Any]] = None) -> list[str]:
194
+ def options_args(options: dict[str, Any] | None = None) -> list[str]:
194
195
  """
195
196
  Converts options dictionary to a list of command-line arguments.
196
197
 
@@ -218,8 +219,8 @@ def process_run_input(
218
219
  temp_src: str,
219
220
  run_format: str,
220
221
  manifest: Manifest,
221
- input_data: Optional[Union[dict[str, Any], str]] = None,
222
- inputs_dir_path: Optional[str] = None,
222
+ input_data: dict[str, Any] | str | None = None,
223
+ inputs_dir_path: str | None = None,
223
224
  ) -> str:
224
225
  """
225
226
  In the temp source, writes the run input according to the run format. If
@@ -465,7 +466,7 @@ def process_run_logs(
465
466
  output_format: OutputFormat,
466
467
  run_dir: str,
467
468
  result: subprocess.CompletedProcess[str],
468
- stdout_output: Union[str, dict[str, Any]],
469
+ stdout_output: str | dict[str, Any],
469
470
  ) -> None:
470
471
  """
471
472
  Processes the logs of the run. Writes the logs to a logs directory.
@@ -502,7 +503,7 @@ def process_run_logs(
502
503
  def process_run_statistics(
503
504
  temp_run_outputs_dir: str,
504
505
  outputs_dir: str,
505
- stdout_output: Union[str, dict[str, Any]],
506
+ stdout_output: str | dict[str, Any],
506
507
  temp_src: str,
507
508
  manifest: Manifest,
508
509
  ) -> None:
@@ -563,7 +564,7 @@ def process_run_statistics(
563
564
  def process_run_assets(
564
565
  temp_run_outputs_dir: str,
565
566
  outputs_dir: str,
566
- stdout_output: Union[str, dict[str, Any]],
567
+ stdout_output: str | dict[str, Any],
567
568
  temp_src: str,
568
569
  manifest: Manifest,
569
570
  ) -> None:
@@ -627,7 +628,7 @@ def process_run_solutions(
627
628
  temp_run_outputs_dir: str,
628
629
  temp_src: str,
629
630
  outputs_dir: str,
630
- stdout_output: Union[str, dict[str, Any]],
631
+ stdout_output: str | dict[str, Any],
631
632
  output_format: OutputFormat,
632
633
  manifest: Manifest,
633
634
  src: str,
@@ -756,7 +757,7 @@ def process_run_visuals(run_dir: str, outputs_dir: str) -> None:
756
757
  # so we ignore it for now.
757
758
 
758
759
 
759
- def resolve_stdout(result: subprocess.CompletedProcess[str]) -> Union[str, dict[str, Any]]:
760
+ def resolve_stdout(result: subprocess.CompletedProcess[str]) -> str | dict[str, Any]:
760
761
  """
761
762
  Resolves the stdout output of the subprocess run. If the stdout is valid
762
763
  JSON, it returns the parsed dictionary. Otherwise, it returns the raw
@@ -838,8 +839,8 @@ def _ignore_patterns(dir_path: str, names: list[str]) -> list[str]:
838
839
  def _copy_new_or_modified_files( # noqa: C901
839
840
  runtime_dir: str,
840
841
  dst_dir: str,
841
- original_src_dir: Optional[str] = None,
842
- exclusion_dirs: Optional[list[str]] = None,
842
+ original_src_dir: str | None = None,
843
+ exclusion_dirs: list[str] | None = None,
843
844
  ) -> None:
844
845
  """
845
846
  Copy only new or modified files from runtime directory to destination directory.
@@ -1006,5 +1007,34 @@ def _calculate_file_checksum(file_path: str) -> str:
1006
1007
  return hash_md5.hexdigest()
1007
1008
 
1008
1009
 
1010
+ def __determine_entrypoint(manifest: Manifest) -> str:
1011
+ """Returns the default entrypoint based on the runtime if not explicitly set."""
1012
+ if manifest.execution is not None and manifest.execution.entrypoint is not None:
1013
+ return manifest.execution.entrypoint
1014
+
1015
+ # Determine default entrypoint based on type
1016
+ if manifest.type == ManifestType.PYTHON:
1017
+ return "./main.py"
1018
+ elif manifest.type == ManifestType.GO:
1019
+ return "./main"
1020
+ elif manifest.type == ManifestType.JAVA:
1021
+ return "./main.jar"
1022
+ else:
1023
+ raise ValueError(
1024
+ f'entrypoint is not provided but the app type "{manifest.type}" could not '
1025
+ "be resolved to establish a default entrypoint"
1026
+ )
1027
+
1028
+
1029
+ def __determine_cwd(manifest: Manifest, default: str) -> str:
1030
+ """
1031
+ Returns the working directory based on the manifest if set, otherwise the default.
1032
+ """
1033
+ if manifest.execution is not None and manifest.execution.cwd is not None:
1034
+ return manifest.execution.cwd
1035
+
1036
+ return default
1037
+
1038
+
1009
1039
  if __name__ == "__main__":
1010
1040
  main()
nextmv/local/runner.py CHANGED
@@ -20,7 +20,7 @@ import shutil
20
20
  import subprocess
21
21
  import sys
22
22
  from datetime import datetime, timezone
23
- from typing import Any, Optional, Union
23
+ from typing import Any
24
24
 
25
25
  from nextmv.input import INPUTS_KEY
26
26
  from nextmv.local.local import DEFAULT_INPUT_JSON_FILE, NEXTMV_DIR, RUNS_KEY, calculate_files_size
@@ -34,11 +34,11 @@ def run(
34
34
  src: str,
35
35
  manifest: Manifest,
36
36
  run_config: dict[str, Any],
37
- name: Optional[str] = None,
38
- description: Optional[str] = None,
39
- input_data: Optional[Union[dict[str, Any], str]] = None,
40
- inputs_dir_path: Optional[str] = None,
41
- options: Optional[dict[str, Any]] = None,
37
+ name: str | None = None,
38
+ description: str | None = None,
39
+ input_data: dict[str, Any] | str | None = None,
40
+ inputs_dir_path: str | None = None,
41
+ options: dict[str, Any] | None = None,
42
42
  ) -> str:
43
43
  """
44
44
  Execute a local run.
@@ -145,8 +145,8 @@ def new_run(
145
145
  src: str,
146
146
  run_id: str,
147
147
  run_config: dict[str, Any],
148
- name: Optional[str] = None,
149
- description: Optional[str] = None,
148
+ name: str | None = None,
149
+ description: str | None = None,
150
150
  ) -> str:
151
151
  """
152
152
  Initializes a new run.
@@ -223,8 +223,8 @@ def new_run(
223
223
  def record_input(
224
224
  run_dir: str,
225
225
  run_id: str,
226
- input_data: Optional[Union[dict[str, Any], str]] = None,
227
- inputs_dir_path: Optional[str] = None,
226
+ input_data: dict[str, Any] | str | None = None,
227
+ inputs_dir_path: str | None = None,
228
228
  ) -> None:
229
229
  """
230
230
  Writes the input to the appropriate location.