nextmv 0.34.0.dev2__py3-none-any.whl → 0.34.1.dev0__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 +1 -1
- nextmv/local/application.py +13 -13
- nextmv/local/executor.py +9 -2
- nextmv/manifest.py +101 -21
- {nextmv-0.34.0.dev2.dist-info → nextmv-0.34.1.dev0.dist-info}/METADATA +1 -1
- {nextmv-0.34.0.dev2.dist-info → nextmv-0.34.1.dev0.dist-info}/RECORD +8 -8
- {nextmv-0.34.0.dev2.dist-info → nextmv-0.34.1.dev0.dist-info}/WHEEL +0 -0
- {nextmv-0.34.0.dev2.dist-info → nextmv-0.34.1.dev0.dist-info}/licenses/LICENSE +0 -0
nextmv/__about__.py
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
__version__ = "v0.34.
|
|
1
|
+
__version__ = "v0.34.1.dev0"
|
nextmv/local/application.py
CHANGED
|
@@ -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
|
-
|
|
908
|
-
|
|
909
|
-
|
|
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(
|
nextmv/local/executor.py
CHANGED
|
@@ -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
|
|
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
|
-
|
|
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
|
|
nextmv/manifest.py
CHANGED
|
@@ -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
|
-
"""
|
|
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
|
-
|
|
1028
|
-
"""
|
|
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
|
-
|
|
1085
|
+
python: Optional[ManifestPython] = None
|
|
1036
1086
|
"""
|
|
1037
|
-
|
|
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(
|
|
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
|
-
|
|
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
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
nextmv/__about__.py,sha256=
|
|
1
|
+
nextmv/__about__.py,sha256=qZQUeOL0DxE9dvXL1t-gJW2C054cwibCgKGAIHdG_zA,29
|
|
2
2
|
nextmv/__entrypoint__.py,sha256=dA0iwwHtrq6Z9w9FxmxKLoBGLyhe7jWtUAU-Y3PEgHg,1094
|
|
3
3
|
nextmv/__init__.py,sha256=C2f8MteVvvOX1Wj-0GFjfUt-0RzCT0zpLpLI2yyZQw8,3796
|
|
4
4
|
nextmv/_serialization.py,sha256=JlSl6BL0M2Esf7F89GsGIZ__Pp8RnFRNM0UxYhuuYU4,2853
|
|
@@ -6,7 +6,7 @@ nextmv/base_model.py,sha256=qmJ4AsYr9Yv01HQX_BERrn3229gyoZrYyP9tcyqNfeU,2311
|
|
|
6
6
|
nextmv/deprecated.py,sha256=kEVfyQ-nT0v2ePXTNldjQG9uH5IlfQVy3L4tztIxwmU,1638
|
|
7
7
|
nextmv/input.py,sha256=m9sVfO9ZL3F5i1l8amEtlWlbkekyUP4C3y9DduHWGFs,40211
|
|
8
8
|
nextmv/logger.py,sha256=kNIbu46MisrzYe4T0hNMpWfRTKKacDVvbtQcNys_c_E,2513
|
|
9
|
-
nextmv/manifest.py,sha256=
|
|
9
|
+
nextmv/manifest.py,sha256=vo4GJk2yF6-6cA1M5YrynlXKqupH2vQE7HByph6ne4k,49256
|
|
10
10
|
nextmv/model.py,sha256=vI3pSV3iTwjRPflar7nAg-6h98XRUyi9II5O2J06-Kc,15018
|
|
11
11
|
nextmv/options.py,sha256=yPJu5lYMbV6YioMwAXv7ctpZUggLXKlZc9CqIbUFvE4,37895
|
|
12
12
|
nextmv/output.py,sha256=HdvWYG3gIzwoXquulaEVI4LLchXJDjkbag0BkBPM0vQ,55128
|
|
@@ -38,13 +38,13 @@ nextmv/default_app/src/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3
|
|
|
38
38
|
nextmv/default_app/src/main.py,sha256=WWeN_xl_mcPhICl3rSCvdEjRkFXGmAnej88FhS-fAmc,884
|
|
39
39
|
nextmv/default_app/src/visuals.py,sha256=WYK_YBnLmYo3TpVev1CpoNCuW5R7hk9QIkeCmvMn1Fs,1014
|
|
40
40
|
nextmv/local/__init__.py,sha256=6BsoqlK4dw6X11_uKzz9gBPfxKpdiol2FYO8R3X73SE,116
|
|
41
|
-
nextmv/local/application.py,sha256=
|
|
42
|
-
nextmv/local/executor.py,sha256=
|
|
41
|
+
nextmv/local/application.py,sha256=yJDlbQB_mh29Y541Mt6a6zyT3XutdtzjqSd0mlOeteo,47002
|
|
42
|
+
nextmv/local/executor.py,sha256=DIY74nVkXxW09BxZQlrFs-_r18Si59_nHTe2jR8MMmI,36907
|
|
43
43
|
nextmv/local/geojson_handler.py,sha256=7FavJdkUonop-yskjis0x3qFGB8A5wZyoBUblw-bVhw,12540
|
|
44
44
|
nextmv/local/local.py,sha256=cp56UpI8h19Ob6Jvb_Ni0ceXH5Vv3ET_iPTDe6ftq3Y,2617
|
|
45
45
|
nextmv/local/plotly_handler.py,sha256=bLb50e3AkVr_W-F6S7lXfeRdN60mG2jk3UElNmhoMWU,1930
|
|
46
46
|
nextmv/local/runner.py,sha256=hwkITHrQG_J9TzxufnaP1mjLWG-iSsNQD66UFZY4pp4,8602
|
|
47
|
-
nextmv-0.34.
|
|
48
|
-
nextmv-0.34.
|
|
49
|
-
nextmv-0.34.
|
|
50
|
-
nextmv-0.34.
|
|
47
|
+
nextmv-0.34.1.dev0.dist-info/METADATA,sha256=IHE5mJ96yUsCNvLvfWtyhMNlCuxOHpNHGnEy3j3y0k8,16013
|
|
48
|
+
nextmv-0.34.1.dev0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
|
49
|
+
nextmv-0.34.1.dev0.dist-info/licenses/LICENSE,sha256=ZIbK-sSWA-OZprjNbmJAglYRtl5_K4l9UwAV3PGJAPc,11349
|
|
50
|
+
nextmv-0.34.1.dev0.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|