nextmv 0.34.0.dev2__tar.gz → 0.34.1.dev0__tar.gz

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 (88) hide show
  1. {nextmv-0.34.0.dev2 → nextmv-0.34.1.dev0}/PKG-INFO +1 -1
  2. nextmv-0.34.1.dev0/nextmv/__about__.py +1 -0
  3. {nextmv-0.34.0.dev2 → nextmv-0.34.1.dev0}/nextmv/local/application.py +13 -13
  4. {nextmv-0.34.0.dev2 → nextmv-0.34.1.dev0}/nextmv/local/executor.py +9 -2
  5. {nextmv-0.34.0.dev2 → nextmv-0.34.1.dev0}/nextmv/manifest.py +101 -21
  6. {nextmv-0.34.0.dev2 → nextmv-0.34.1.dev0}/tests/local/test_executor.py +44 -12
  7. nextmv-0.34.0.dev2/nextmv/__about__.py +0 -1
  8. {nextmv-0.34.0.dev2 → nextmv-0.34.1.dev0}/.gitignore +0 -0
  9. {nextmv-0.34.0.dev2 → nextmv-0.34.1.dev0}/LICENSE +0 -0
  10. {nextmv-0.34.0.dev2 → nextmv-0.34.1.dev0}/README.md +0 -0
  11. {nextmv-0.34.0.dev2 → nextmv-0.34.1.dev0}/nextmv/__entrypoint__.py +0 -0
  12. {nextmv-0.34.0.dev2 → nextmv-0.34.1.dev0}/nextmv/__init__.py +0 -0
  13. {nextmv-0.34.0.dev2 → nextmv-0.34.1.dev0}/nextmv/_serialization.py +0 -0
  14. {nextmv-0.34.0.dev2 → nextmv-0.34.1.dev0}/nextmv/base_model.py +0 -0
  15. {nextmv-0.34.0.dev2 → nextmv-0.34.1.dev0}/nextmv/cloud/__init__.py +0 -0
  16. {nextmv-0.34.0.dev2 → nextmv-0.34.1.dev0}/nextmv/cloud/acceptance_test.py +0 -0
  17. {nextmv-0.34.0.dev2 → nextmv-0.34.1.dev0}/nextmv/cloud/account.py +0 -0
  18. {nextmv-0.34.0.dev2 → nextmv-0.34.1.dev0}/nextmv/cloud/application.py +0 -0
  19. {nextmv-0.34.0.dev2 → nextmv-0.34.1.dev0}/nextmv/cloud/batch_experiment.py +0 -0
  20. {nextmv-0.34.0.dev2 → nextmv-0.34.1.dev0}/nextmv/cloud/client.py +0 -0
  21. {nextmv-0.34.0.dev2 → nextmv-0.34.1.dev0}/nextmv/cloud/ensemble.py +0 -0
  22. {nextmv-0.34.0.dev2 → nextmv-0.34.1.dev0}/nextmv/cloud/input_set.py +0 -0
  23. {nextmv-0.34.0.dev2 → nextmv-0.34.1.dev0}/nextmv/cloud/instance.py +0 -0
  24. {nextmv-0.34.0.dev2 → nextmv-0.34.1.dev0}/nextmv/cloud/package.py +0 -0
  25. {nextmv-0.34.0.dev2 → nextmv-0.34.1.dev0}/nextmv/cloud/scenario.py +0 -0
  26. {nextmv-0.34.0.dev2 → nextmv-0.34.1.dev0}/nextmv/cloud/secrets.py +0 -0
  27. {nextmv-0.34.0.dev2 → nextmv-0.34.1.dev0}/nextmv/cloud/url.py +0 -0
  28. {nextmv-0.34.0.dev2 → nextmv-0.34.1.dev0}/nextmv/cloud/version.py +0 -0
  29. {nextmv-0.34.0.dev2 → nextmv-0.34.1.dev0}/nextmv/default_app/.gitignore +0 -0
  30. {nextmv-0.34.0.dev2 → nextmv-0.34.1.dev0}/nextmv/default_app/README.md +0 -0
  31. {nextmv-0.34.0.dev2 → nextmv-0.34.1.dev0}/nextmv/default_app/app.yaml +0 -0
  32. {nextmv-0.34.0.dev2 → nextmv-0.34.1.dev0}/nextmv/default_app/input.json +0 -0
  33. {nextmv-0.34.0.dev2 → nextmv-0.34.1.dev0}/nextmv/default_app/main.py +0 -0
  34. {nextmv-0.34.0.dev2 → nextmv-0.34.1.dev0}/nextmv/default_app/requirements.txt +0 -0
  35. {nextmv-0.34.0.dev2 → nextmv-0.34.1.dev0}/nextmv/default_app/src/__init__.py +0 -0
  36. {nextmv-0.34.0.dev2 → nextmv-0.34.1.dev0}/nextmv/default_app/src/main.py +0 -0
  37. {nextmv-0.34.0.dev2 → nextmv-0.34.1.dev0}/nextmv/default_app/src/visuals.py +0 -0
  38. {nextmv-0.34.0.dev2 → nextmv-0.34.1.dev0}/nextmv/deprecated.py +0 -0
  39. {nextmv-0.34.0.dev2 → nextmv-0.34.1.dev0}/nextmv/input.py +0 -0
  40. {nextmv-0.34.0.dev2 → nextmv-0.34.1.dev0}/nextmv/local/__init__.py +0 -0
  41. {nextmv-0.34.0.dev2 → nextmv-0.34.1.dev0}/nextmv/local/geojson_handler.py +0 -0
  42. {nextmv-0.34.0.dev2 → nextmv-0.34.1.dev0}/nextmv/local/local.py +0 -0
  43. {nextmv-0.34.0.dev2 → nextmv-0.34.1.dev0}/nextmv/local/plotly_handler.py +0 -0
  44. {nextmv-0.34.0.dev2 → nextmv-0.34.1.dev0}/nextmv/local/runner.py +0 -0
  45. {nextmv-0.34.0.dev2 → nextmv-0.34.1.dev0}/nextmv/logger.py +0 -0
  46. {nextmv-0.34.0.dev2 → nextmv-0.34.1.dev0}/nextmv/model.py +0 -0
  47. {nextmv-0.34.0.dev2 → nextmv-0.34.1.dev0}/nextmv/options.py +0 -0
  48. {nextmv-0.34.0.dev2 → nextmv-0.34.1.dev0}/nextmv/output.py +0 -0
  49. {nextmv-0.34.0.dev2 → nextmv-0.34.1.dev0}/nextmv/polling.py +0 -0
  50. {nextmv-0.34.0.dev2 → nextmv-0.34.1.dev0}/nextmv/run.py +0 -0
  51. {nextmv-0.34.0.dev2 → nextmv-0.34.1.dev0}/nextmv/safe.py +0 -0
  52. {nextmv-0.34.0.dev2 → nextmv-0.34.1.dev0}/nextmv/status.py +0 -0
  53. {nextmv-0.34.0.dev2 → nextmv-0.34.1.dev0}/pyproject.toml +0 -0
  54. {nextmv-0.34.0.dev2 → nextmv-0.34.1.dev0}/tests/__init__.py +0 -0
  55. {nextmv-0.34.0.dev2 → nextmv-0.34.1.dev0}/tests/cloud/__init__.py +0 -0
  56. {nextmv-0.34.0.dev2 → nextmv-0.34.1.dev0}/tests/cloud/app.yaml +0 -0
  57. {nextmv-0.34.0.dev2 → nextmv-0.34.1.dev0}/tests/cloud/test_client.py +0 -0
  58. {nextmv-0.34.0.dev2 → nextmv-0.34.1.dev0}/tests/cloud/test_package.py +0 -0
  59. {nextmv-0.34.0.dev2 → nextmv-0.34.1.dev0}/tests/cloud/test_scenario.py +0 -0
  60. {nextmv-0.34.0.dev2 → nextmv-0.34.1.dev0}/tests/local/__init__.py +0 -0
  61. {nextmv-0.34.0.dev2 → nextmv-0.34.1.dev0}/tests/local/test_application.py +0 -0
  62. {nextmv-0.34.0.dev2 → nextmv-0.34.1.dev0}/tests/local/test_runner.py +0 -0
  63. {nextmv-0.34.0.dev2 → nextmv-0.34.1.dev0}/tests/scripts/__init__.py +0 -0
  64. {nextmv-0.34.0.dev2 → nextmv-0.34.1.dev0}/tests/scripts/options1.py +0 -0
  65. {nextmv-0.34.0.dev2 → nextmv-0.34.1.dev0}/tests/scripts/options2.py +0 -0
  66. {nextmv-0.34.0.dev2 → nextmv-0.34.1.dev0}/tests/scripts/options3.py +0 -0
  67. {nextmv-0.34.0.dev2 → nextmv-0.34.1.dev0}/tests/scripts/options4.py +0 -0
  68. {nextmv-0.34.0.dev2 → nextmv-0.34.1.dev0}/tests/scripts/options5.py +0 -0
  69. {nextmv-0.34.0.dev2 → nextmv-0.34.1.dev0}/tests/scripts/options6.py +0 -0
  70. {nextmv-0.34.0.dev2 → nextmv-0.34.1.dev0}/tests/scripts/options7.py +0 -0
  71. {nextmv-0.34.0.dev2 → nextmv-0.34.1.dev0}/tests/scripts/options_deprecated.py +0 -0
  72. {nextmv-0.34.0.dev2 → nextmv-0.34.1.dev0}/tests/test_base_model.py +0 -0
  73. {nextmv-0.34.0.dev2 → nextmv-0.34.1.dev0}/tests/test_entrypoint/__init__.py +0 -0
  74. {nextmv-0.34.0.dev2 → nextmv-0.34.1.dev0}/tests/test_entrypoint/test_entrypoint.py +0 -0
  75. {nextmv-0.34.0.dev2 → nextmv-0.34.1.dev0}/tests/test_input.py +0 -0
  76. {nextmv-0.34.0.dev2 → nextmv-0.34.1.dev0}/tests/test_inputs/test_data.csv +0 -0
  77. {nextmv-0.34.0.dev2 → nextmv-0.34.1.dev0}/tests/test_inputs/test_data.json +0 -0
  78. {nextmv-0.34.0.dev2 → nextmv-0.34.1.dev0}/tests/test_inputs/test_data.txt +0 -0
  79. {nextmv-0.34.0.dev2 → nextmv-0.34.1.dev0}/tests/test_logger.py +0 -0
  80. {nextmv-0.34.0.dev2 → nextmv-0.34.1.dev0}/tests/test_manifest.py +0 -0
  81. {nextmv-0.34.0.dev2 → nextmv-0.34.1.dev0}/tests/test_model.py +0 -0
  82. {nextmv-0.34.0.dev2 → nextmv-0.34.1.dev0}/tests/test_options.py +0 -0
  83. {nextmv-0.34.0.dev2 → nextmv-0.34.1.dev0}/tests/test_output.py +0 -0
  84. {nextmv-0.34.0.dev2 → nextmv-0.34.1.dev0}/tests/test_polling.py +0 -0
  85. {nextmv-0.34.0.dev2 → nextmv-0.34.1.dev0}/tests/test_run.py +0 -0
  86. {nextmv-0.34.0.dev2 → nextmv-0.34.1.dev0}/tests/test_safe.py +0 -0
  87. {nextmv-0.34.0.dev2 → nextmv-0.34.1.dev0}/tests/test_serialization.py +0 -0
  88. {nextmv-0.34.0.dev2 → nextmv-0.34.1.dev0}/tests/test_version.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: nextmv
3
- Version: 0.34.0.dev2
3
+ Version: 0.34.1.dev0
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://nextmv-py.docs.nextmv.io/en/latest/nextmv/
@@ -0,0 +1 @@
1
+ __version__ = "v0.34.1.dev0"
@@ -892,9 +892,6 @@ class Application:
892
892
  Auxiliary function to validate the directory path and configuration.
893
893
  """
894
894
 
895
- if input_dir_path is None or input_dir_path == "":
896
- return
897
-
898
895
  if configuration is None:
899
896
  if self.manifest.configuration is not None and self.manifest.configuration.content is not None:
900
897
  configuration = RunConfiguration(
@@ -904,18 +901,21 @@ class Application:
904
901
  ),
905
902
  ),
906
903
  )
907
- else:
908
- raise ValueError(
909
- "If `dir_path` is provided, either a `RunConfiguration` must also be provided or "
910
- "the application's manifest (app.yaml) must include the format under "
911
- "`configuration.content.format`.",
912
- )
913
-
914
- # Forcefully turn the configuration into a RunConfiguration object to
915
- # make it easier to deal with in the other functions.
916
- if isinstance(configuration, dict):
904
+ elif isinstance(configuration, dict):
905
+ # Forcefully turn the configuration into a RunConfiguration object to
906
+ # make it easier to deal with in the other functions.
917
907
  configuration = RunConfiguration.from_dict(configuration)
918
908
 
909
+ if input_dir_path is None or input_dir_path == "":
910
+ return configuration
911
+
912
+ if configuration is None:
913
+ raise ValueError(
914
+ "If `dir_path` is provided, either a `RunConfiguration` must also be provided or "
915
+ "the application's manifest (app.yaml) must include the format under "
916
+ "`configuration.content.format`.",
917
+ )
918
+
919
919
  config_format = configuration.format
920
920
  if config_format is None:
921
921
  raise ValueError(
@@ -988,7 +988,7 @@ def _remove_empty_directories(directory: str) -> None:
988
988
 
989
989
  def _should_copy_file(src_file: str, dst_file: str) -> bool:
990
990
  """
991
- Determine if a file should be copied based on existence and content.
991
+ Determine if a file should be copied based on existence, content, and modification time.
992
992
 
993
993
  Parameters
994
994
  ----------
@@ -1006,9 +1006,16 @@ def _should_copy_file(src_file: str, dst_file: str) -> bool:
1006
1006
  return True
1007
1007
 
1008
1008
  try:
1009
+ # First check if content is different
1009
1010
  src_checksum = _calculate_file_checksum(src_file)
1010
1011
  dst_checksum = _calculate_file_checksum(dst_file)
1011
- return src_checksum != dst_checksum
1012
+ if src_checksum != dst_checksum:
1013
+ return True
1014
+
1015
+ # If content is the same, check if source file is newer
1016
+ src_mtime = os.path.getmtime(src_file)
1017
+ dst_mtime = os.path.getmtime(dst_file)
1018
+ return src_mtime > dst_mtime
1012
1019
  except OSError:
1013
1020
  return True
1014
1021
 
@@ -11,6 +11,8 @@ ManifestType
11
11
  Enum for application types based on programming language.
12
12
  ManifestRuntime
13
13
  Enum for runtime environments where apps run on Nextmv.
14
+ ManifestPythonArch
15
+ Enum for target architecture for bundling Python apps.
14
16
  ManifestBuild
15
17
  Class for build-specific attributes in the manifest.
16
18
  ManifestPythonModel
@@ -38,6 +40,11 @@ ManifestConfiguration
38
40
  Manifest
39
41
  Main class representing an app manifest for Nextmv.
40
42
 
43
+ Functions
44
+ ---------
45
+ default_python_manifest
46
+ Creates a default Python manifest as a starting point for applications.
47
+
41
48
  Constants
42
49
  --------
43
50
  MANIFEST_FILE_NAME
@@ -331,7 +338,7 @@ class ManifestPython(BaseModel):
331
338
 
332
339
  Parameters
333
340
  ----------
334
- pip_requirements : Optional[str], default=None
341
+ pip_requirements : Optional[Union[str, list[str]]], default=None
335
342
  Path to a requirements.txt file containing (additional) Python
336
343
  dependencies that will be bundled with the app. Alternatively, you can provide a
337
344
  list of strings, each representing a package to install, e.g.,
@@ -358,10 +365,11 @@ class ManifestPython(BaseModel):
358
365
  default=None,
359
366
  )
360
367
  """
361
- Path to a requirements.txt file.
368
+ Path to a requirements.txt file or list of packages.
362
369
 
363
370
  Contains (additional) Python dependencies that will be bundled with the
364
- app.
371
+ app. Can be either a string path to a requirements.txt file or a list
372
+ of package specifications.
365
373
  """
366
374
  arch: Optional[ManifestPythonArch] = None
367
375
  """
@@ -383,6 +391,31 @@ class ManifestPython(BaseModel):
383
391
  @field_validator("version", mode="before")
384
392
  @classmethod
385
393
  def validate_version(cls, v: Optional[Union[str, float]]) -> Optional[str]:
394
+ """
395
+ Validate and convert the Python version field to a string.
396
+
397
+ This validator allows the version to be specified as either a float or string
398
+ in the manifest for convenience, but ensures it's stored internally as a string.
399
+
400
+ Parameters
401
+ ----------
402
+ v : Optional[Union[str, float]]
403
+ The version value to validate. Can be None, a string, or a float.
404
+
405
+ Returns
406
+ -------
407
+ Optional[str]
408
+ The version as a string, or None if the input was None.
409
+
410
+ Examples
411
+ --------
412
+ >>> ManifestPython.validate_version(3.11)
413
+ '3.11'
414
+ >>> ManifestPython.validate_version("3.11")
415
+ '3.11'
416
+ >>> ManifestPython.validate_version(None) is None
417
+ True
418
+ """
386
419
  # We allow the version to be a float in the manifest for convenience, but we want
387
420
  # to store it as a string internally.
388
421
  if v is None:
@@ -917,7 +950,23 @@ class ManifestContent(BaseModel):
917
950
  """Configuration for multi-file content format."""
918
951
 
919
952
  def model_post_init(self, __context) -> None:
920
- """Post-initialization to validate fields."""
953
+ """
954
+ Post-initialization validation to ensure format field contains valid values.
955
+
956
+ This method is automatically called by Pydantic after the model is initialized
957
+ to validate that the format field contains one of the acceptable values.
958
+
959
+ Parameters
960
+ ----------
961
+ __context : Any
962
+ Pydantic context (unused in this implementation).
963
+
964
+ Raises
965
+ ------
966
+ ValueError
967
+ If the format field contains an invalid value that is not one of the
968
+ acceptable formats (JSON, MULTI_FILE, or CSV_ARCHIVE).
969
+ """
921
970
  acceptable_formats = [InputFormat.JSON, InputFormat.MULTI_FILE, InputFormat.CSV_ARCHIVE]
922
971
  if self.format not in acceptable_formats:
923
972
  raise ValueError(f"Invalid format: {self.format}. Must be one of {acceptable_formats}.")
@@ -1024,17 +1073,28 @@ class Manifest(BaseModel):
1024
1073
  ['main.py', 'model_logic/']
1025
1074
  """
1026
1075
 
1027
- files: list[str]
1028
- """The files to include (or exclude) in the app. This is mandatory."""
1029
-
1076
+ type: ManifestType = ManifestType.PYTHON
1077
+ """
1078
+ Type of application, based on the programming language. This is mandatory.
1079
+ """
1030
1080
  runtime: ManifestRuntime = ManifestRuntime.PYTHON
1031
1081
  """
1032
1082
  The runtime to use for the app. It provides the environment in which the
1033
1083
  app runs. This is mandatory.
1034
1084
  """
1035
- type: ManifestType = ManifestType.PYTHON
1085
+ python: Optional[ManifestPython] = None
1036
1086
  """
1037
- Type of application, based on the programming language. This is mandatory.
1087
+ Python-specific attributes. Only for Python apps. Contains further
1088
+ Python-specific attributes.
1089
+ """
1090
+ files: list[str] = Field(
1091
+ default_factory=list,
1092
+ )
1093
+ """The files to include (or exclude) in the app. This is mandatory."""
1094
+ configuration: Optional[ManifestConfiguration] = None
1095
+ """
1096
+ Configuration for the decision model. A list of options for the decision
1097
+ model. An option is a parameter that configures the decision model.
1038
1098
  """
1039
1099
  build: Optional[ManifestBuild] = None
1040
1100
  """
@@ -1062,16 +1122,6 @@ class Manifest(BaseModel):
1062
1122
  process. This command is executed just before the app gets bundled and
1063
1123
  pushed (after the build command).
1064
1124
  """
1065
- python: Optional[ManifestPython] = None
1066
- """
1067
- Python-specific attributes. Only for Python apps. Contains further
1068
- Python-specific attributes.
1069
- """
1070
- configuration: Optional[ManifestConfiguration] = None
1071
- """
1072
- Configuration for the decision model. A list of options for the decision
1073
- model. An option is a parameter that configures the decision model.
1074
- """
1075
1125
  entrypoint: Optional[str] = None
1076
1126
  """
1077
1127
  Optional entrypoint for the decision model. When not specified, the
@@ -1083,6 +1133,26 @@ class Manifest(BaseModel):
1083
1133
  """
1084
1134
 
1085
1135
  def model_post_init(self, __context) -> None:
1136
+ """
1137
+ Post-initialization to set default entrypoint based on runtime if not specified.
1138
+
1139
+ This method is automatically called by Pydantic after the model is initialized.
1140
+ If no entrypoint is provided, it sets a default entrypoint based on the runtime:
1141
+ - Python runtimes (PYTHON, HEXALY, PYOMO, CUOPT): "./main.py"
1142
+ - DEFAULT runtime: "./main"
1143
+ - JAVA runtime: "./main.jar"
1144
+
1145
+ Parameters
1146
+ ----------
1147
+ __context : Any
1148
+ Pydantic context (unused in this implementation).
1149
+
1150
+ Raises
1151
+ ------
1152
+ ValueError
1153
+ If no entrypoint is provided and the runtime cannot be resolved to
1154
+ establish a default entrypoint.
1155
+ """
1086
1156
  if self.entrypoint is None:
1087
1157
  if self.runtime in (
1088
1158
  ManifestRuntime.PYTHON,
@@ -1175,7 +1245,14 @@ class Manifest(BaseModel):
1175
1245
  """
1176
1246
 
1177
1247
  with open(os.path.join(dirpath, MANIFEST_FILE_NAME), "w") as file:
1178
- yaml.dump(self.to_dict(), file)
1248
+ yaml.dump(
1249
+ self.to_dict(),
1250
+ file,
1251
+ sort_keys=False,
1252
+ default_flow_style=False,
1253
+ indent=2,
1254
+ width=120,
1255
+ )
1179
1256
 
1180
1257
  def extract_options(self) -> Optional[Options]:
1181
1258
  """
@@ -1376,9 +1453,12 @@ def default_python_manifest() -> Manifest:
1376
1453
  A default Python manifest with common settings.
1377
1454
  """
1378
1455
 
1379
- return Manifest(
1456
+ m = Manifest(
1380
1457
  files=["main.py"],
1381
1458
  runtime=ManifestRuntime.PYTHON,
1382
1459
  type=ManifestType.PYTHON,
1383
1460
  python=ManifestPython(pip_requirements="requirements.txt"),
1384
1461
  )
1462
+ m.entrypoint = None # TODO: change this when we are ready for the entrypoint.
1463
+
1464
+ return m
@@ -274,11 +274,11 @@ class TestLocalExecutor(unittest.TestCase):
274
274
  logs_dir = os.path.join(self.run_dir, "logs")
275
275
  self.assertTrue(os.path.exists(logs_dir))
276
276
 
277
- # Check that stderr.log was created with correct content
278
- stderr_file = os.path.join(logs_dir, "stderr.log")
279
- self.assertTrue(os.path.exists(stderr_file))
277
+ # Check that logs.log was created with correct content
278
+ logs_file = os.path.join(logs_dir, "logs.log")
279
+ self.assertTrue(os.path.exists(logs_file))
280
280
 
281
- with open(stderr_file) as f:
281
+ with open(logs_file) as f:
282
282
  content = f.read()
283
283
 
284
284
  self.assertEqual(content, "Error line 1\nError line 2\n")
@@ -299,7 +299,12 @@ class TestLocalExecutor(unittest.TestCase):
299
299
  stdout_output = {}
300
300
 
301
301
  process_run_statistics(
302
- temp_outputs_dir, outputs_dir, stdout_output, temp_src=self.temp_src, manifest=self.mock_manifest
302
+ temp_outputs_dir,
303
+ outputs_dir,
304
+ stdout_output,
305
+ temp_src=self.temp_src,
306
+ manifest=self.mock_manifest,
307
+ src=self.test_dir,
303
308
  )
304
309
 
305
310
  # Check that statistics directory was copied
@@ -316,7 +321,12 @@ class TestLocalExecutor(unittest.TestCase):
316
321
  stdout_output = {STATISTICS_KEY: {"duration": 2.5, "iterations": 100}}
317
322
 
318
323
  process_run_statistics(
319
- temp_outputs_dir, outputs_dir, stdout_output, temp_src=self.temp_src, manifest=self.mock_manifest
324
+ temp_outputs_dir,
325
+ outputs_dir,
326
+ stdout_output,
327
+ temp_src=self.temp_src,
328
+ manifest=self.mock_manifest,
329
+ src=self.test_dir,
320
330
  )
321
331
 
322
332
  # Check that statistics.json was created
@@ -341,7 +351,12 @@ class TestLocalExecutor(unittest.TestCase):
341
351
  stdout_output = {}
342
352
 
343
353
  process_run_statistics(
344
- temp_outputs_dir, outputs_dir, stdout_output, temp_src=self.temp_src, manifest=self.mock_manifest
354
+ temp_outputs_dir,
355
+ outputs_dir,
356
+ stdout_output,
357
+ temp_src=self.temp_src,
358
+ manifest=self.mock_manifest,
359
+ src=self.test_dir,
345
360
  )
346
361
 
347
362
  # Check that statistics directory was not created
@@ -364,7 +379,12 @@ class TestLocalExecutor(unittest.TestCase):
364
379
  stdout_output = {}
365
380
 
366
381
  process_run_assets(
367
- temp_outputs_dir, outputs_dir, stdout_output, temp_src=self.temp_src, manifest=self.mock_manifest
382
+ temp_outputs_dir,
383
+ outputs_dir,
384
+ stdout_output,
385
+ temp_src=self.temp_src,
386
+ manifest=self.mock_manifest,
387
+ src=self.test_dir,
368
388
  )
369
389
 
370
390
  # Check that assets directory was copied
@@ -386,7 +406,12 @@ class TestLocalExecutor(unittest.TestCase):
386
406
  }
387
407
 
388
408
  process_run_assets(
389
- temp_outputs_dir, outputs_dir, stdout_output, temp_src=self.temp_src, manifest=self.mock_manifest
409
+ temp_outputs_dir,
410
+ outputs_dir,
411
+ stdout_output,
412
+ temp_src=self.temp_src,
413
+ manifest=self.mock_manifest,
414
+ src=self.test_dir,
390
415
  )
391
416
 
392
417
  # Check that assets.json was created
@@ -429,6 +454,7 @@ class TestLocalExecutor(unittest.TestCase):
429
454
  stdout_output,
430
455
  output_format=OutputFormat.CSV_ARCHIVE,
431
456
  manifest=self.mock_manifest,
457
+ src=self.test_dir,
432
458
  )
433
459
 
434
460
  # Check that solutions directory was created and files copied
@@ -463,6 +489,7 @@ class TestLocalExecutor(unittest.TestCase):
463
489
  stdout_output,
464
490
  output_format=OutputFormat.MULTI_FILE,
465
491
  manifest=self.mock_manifest,
492
+ src=self.test_dir,
466
493
  )
467
494
 
468
495
  # Check that solutions directory was created and files copied
@@ -490,6 +517,7 @@ class TestLocalExecutor(unittest.TestCase):
490
517
  stdout_output,
491
518
  output_format=self.mock_output_format,
492
519
  manifest=self.mock_manifest,
520
+ src=self.test_dir,
493
521
  )
494
522
 
495
523
  # Check that solution.json was created
@@ -524,6 +552,7 @@ class TestLocalExecutor(unittest.TestCase):
524
552
  stdout_output,
525
553
  output_format=self.mock_output_format,
526
554
  manifest=self.mock_manifest,
555
+ src=self.test_dir,
527
556
  )
528
557
 
529
558
  # Check that solutions directory was created
@@ -609,6 +638,7 @@ class TestLocalExecutor(unittest.TestCase):
609
638
  temp_src=temp_src,
610
639
  result=mock_result,
611
640
  run_dir="/test/run_dir",
641
+ src="/test/src",
612
642
  )
613
643
 
614
644
  def test_process_run_output_with_valid_json(self):
@@ -636,6 +666,7 @@ class TestLocalExecutor(unittest.TestCase):
636
666
  temp_src=self.temp_src,
637
667
  result=mock_result,
638
668
  run_dir=self.run_dir,
669
+ src=self.test_dir,
639
670
  )
640
671
 
641
672
  # Verify all processing functions were called
@@ -674,11 +705,12 @@ class TestLocalExecutor(unittest.TestCase):
674
705
  temp_src=self.temp_src,
675
706
  result=mock_result,
676
707
  run_dir=self.run_dir,
708
+ src=self.test_dir,
677
709
  )
678
710
 
679
- # Verify all processing functions were called with empty dict
711
+ # Verify all processing functions were called with empty string
680
712
  mock_logs.assert_called_once_with(
681
- output_format=unittest.mock.ANY, run_dir=self.run_dir, result=mock_result, stdout_output={}
713
+ output_format=unittest.mock.ANY, run_dir=self.run_dir, result=mock_result, stdout_output=""
682
714
  )
683
715
  mock_stats.assert_called_once()
684
716
  mock_assets.assert_called_once()
@@ -686,7 +718,7 @@ class TestLocalExecutor(unittest.TestCase):
686
718
 
687
719
  # Get the stdout_output that was passed to the functions
688
720
  stdout_output = mock_stats.call_args.kwargs["stdout_output"]
689
- self.assertEqual(stdout_output, {})
721
+ self.assertEqual(stdout_output, "")
690
722
 
691
723
 
692
724
  if __name__ == "__main__":
@@ -1 +0,0 @@
1
- __version__ = "v0.34.0.dev2"
File without changes
File without changes
File without changes
File without changes