nextpipe 0.6.2.dev3__py3-none-any.whl → 0.6.2.dev5__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.
- nextpipe/__about__.py +1 -1
- nextpipe/decorators.py +15 -0
- nextpipe/flow.py +29 -7
- nextpipe/utils.py +8 -2
- {nextpipe-0.6.2.dev3.dist-info → nextpipe-0.6.2.dev5.dist-info}/METADATA +2 -2
- {nextpipe-0.6.2.dev3.dist-info → nextpipe-0.6.2.dev5.dist-info}/RECORD +8 -8
- {nextpipe-0.6.2.dev3.dist-info → nextpipe-0.6.2.dev5.dist-info}/WHEEL +0 -0
- {nextpipe-0.6.2.dev3.dist-info → nextpipe-0.6.2.dev5.dist-info}/licenses/LICENSE.md +0 -0
nextpipe/__about__.py
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
__version__ = "v0.6.2-dev.
|
|
1
|
+
__version__ = "v0.6.2-dev.5"
|
nextpipe/decorators.py
CHANGED
|
@@ -769,6 +769,11 @@ class App:
|
|
|
769
769
|
The configuration to apply when running the app.
|
|
770
770
|
polling_options : Optional[nextmv.PollingOptions]
|
|
771
771
|
Options for polling for the results of the app run.
|
|
772
|
+
bypass_result : typing.Any
|
|
773
|
+
A value that can be provided to bypass the actual execution of the app.
|
|
774
|
+
emit_live_logs : bool
|
|
775
|
+
Whether to emit live logs from the application run. If set to `True`, logs from
|
|
776
|
+
the sub-run will be relayed to the workflow.
|
|
772
777
|
"""
|
|
773
778
|
|
|
774
779
|
def __init__(
|
|
@@ -782,6 +787,7 @@ class App:
|
|
|
782
787
|
run_configuration: nextmv.RunConfiguration = None,
|
|
783
788
|
polling_options: nextmv.PollingOptions | None = nextmv.DEFAULT_POLLING_OPTIONS,
|
|
784
789
|
bypass_result: typing.Any = None,
|
|
790
|
+
emit_live_logs: bool = False,
|
|
785
791
|
):
|
|
786
792
|
"""
|
|
787
793
|
Initialize an App object.
|
|
@@ -803,6 +809,9 @@ class App:
|
|
|
803
809
|
bypass_result : typing.Any, optional
|
|
804
810
|
A value that can be provided to bypass the actual execution of the app.
|
|
805
811
|
This is mainly used for testing purposes, by default None.
|
|
812
|
+
emit_live_logs : bool, optional
|
|
813
|
+
Whether to emit live logs from the application run. If set to `True`, logs
|
|
814
|
+
from the sub-run will be relayed to the workflow. Default is False.
|
|
806
815
|
"""
|
|
807
816
|
|
|
808
817
|
if input_type:
|
|
@@ -829,6 +838,7 @@ class App:
|
|
|
829
838
|
self.run_configuration = run_configuration
|
|
830
839
|
self.polling_options = polling_options
|
|
831
840
|
self.bypass_result = bypass_result
|
|
841
|
+
self.emit_live_logs = emit_live_logs
|
|
832
842
|
|
|
833
843
|
def __repr__(self):
|
|
834
844
|
"""Return a string representation of the app.
|
|
@@ -852,6 +862,7 @@ def app(
|
|
|
852
862
|
run_configuration: nextmv.RunConfiguration = None,
|
|
853
863
|
polling_options: nextmv.PollingOptions | None = nextmv.DEFAULT_POLLING_OPTIONS,
|
|
854
864
|
bypass_result: typing.Any = None,
|
|
865
|
+
emit_live_logs: bool = False,
|
|
855
866
|
):
|
|
856
867
|
"""
|
|
857
868
|
Decorator to mark a step as a Nextmv Application (external application)
|
|
@@ -894,6 +905,9 @@ def app(
|
|
|
894
905
|
bypass_result : typing.Any, optional
|
|
895
906
|
A value that can be provided to bypass the actual execution of the app.
|
|
896
907
|
This is mainly used for testing purposes, by default None.
|
|
908
|
+
emit_live_logs : bool
|
|
909
|
+
Whether to emit live logs from the application run. If set to `True`, logs from
|
|
910
|
+
the sub-run will be relayed to the workflow.
|
|
897
911
|
|
|
898
912
|
Example
|
|
899
913
|
-------
|
|
@@ -967,6 +981,7 @@ def app(
|
|
|
967
981
|
run_configuration=run_configuration,
|
|
968
982
|
polling_options=polling_options,
|
|
969
983
|
bypass_result=bypass_result,
|
|
984
|
+
emit_live_logs=emit_live_logs,
|
|
970
985
|
)
|
|
971
986
|
wrapper.step.type = StepType.APP
|
|
972
987
|
|
nextpipe/flow.py
CHANGED
|
@@ -25,9 +25,11 @@ The module also defines status constants used throughout pipeline execution.
|
|
|
25
25
|
import ast
|
|
26
26
|
import base64
|
|
27
27
|
import copy
|
|
28
|
+
import datetime
|
|
28
29
|
import inspect
|
|
29
30
|
import io
|
|
30
31
|
import os
|
|
32
|
+
import pathlib
|
|
31
33
|
import random
|
|
32
34
|
import shutil
|
|
33
35
|
import tempfile
|
|
@@ -864,12 +866,12 @@ class Runner:
|
|
|
864
866
|
"""
|
|
865
867
|
|
|
866
868
|
reference: FlowNode = job.reference
|
|
867
|
-
failed = job.error
|
|
869
|
+
failed = job.error or reference.error
|
|
868
870
|
reference.status = STATUS_SUCCEEDED if not failed else STATUS_FAILED
|
|
869
871
|
reference.result = job.result
|
|
870
872
|
# Check if the job failed and mark the flow as failed if it did
|
|
871
873
|
with self.lock_fail:
|
|
872
|
-
if job.error
|
|
874
|
+
if job.error and not self.fail:
|
|
873
875
|
self.fail = True
|
|
874
876
|
self.fail_reason = f"Step {reference.parent.definition.get_id()} failed: {job.error}"
|
|
875
877
|
# Mark the node as done (and its parent if all nodes are done)
|
|
@@ -1014,16 +1016,29 @@ class Runner:
|
|
|
1014
1016
|
# Run the application
|
|
1015
1017
|
try:
|
|
1016
1018
|
if app_step.bypass_result is None:
|
|
1019
|
+
# Start the run.
|
|
1017
1020
|
run_id = app.new_run(*run_args[0], **run_args[1])
|
|
1018
1021
|
console_url = f"{client.console_url}/app/{app_step.app_id}/run/{run_id}?view=details"
|
|
1019
1022
|
utils.log_internal(f"Started app step {node.id} run, find it at {console_url}")
|
|
1020
1023
|
node.run_id = run_id
|
|
1021
1024
|
update_dag() # Update the node with the run_id before we start polling for results
|
|
1025
|
+
|
|
1026
|
+
# Emit live logs if requested.
|
|
1027
|
+
if app_step.emit_live_logs:
|
|
1028
|
+
utils.log_internal(f"Emitting live logs for app step {node.id} ...")
|
|
1029
|
+
app.run_logs_with_polling(
|
|
1030
|
+
run_id=run_id,
|
|
1031
|
+
verbose=True,
|
|
1032
|
+
log_func=lambda msg: utils.log(message=msg.log, step_name=node.id),
|
|
1033
|
+
)
|
|
1034
|
+
|
|
1035
|
+
# Wait for run to finish and get the result.
|
|
1022
1036
|
result = app.run_result_with_polling(
|
|
1023
1037
|
run_id=run_id, polling_options=polling_options, output_dir_path=temp_dir
|
|
1024
1038
|
)
|
|
1025
1039
|
else:
|
|
1026
1040
|
utils.log_internal(f"Bypassing app step {node.id} run, using bypass result")
|
|
1041
|
+
|
|
1027
1042
|
# We wrap the bypass result in a dummy RunResult to ensure consistent behavior.
|
|
1028
1043
|
result = nextmv.RunResult(
|
|
1029
1044
|
id="bypassed-run",
|
|
@@ -1034,8 +1049,8 @@ class Runner:
|
|
|
1034
1049
|
application_id="unavailable",
|
|
1035
1050
|
application_instance_id="unavailable",
|
|
1036
1051
|
application_version_id="unavailable",
|
|
1037
|
-
created_at=
|
|
1038
|
-
duration=
|
|
1052
|
+
created_at=datetime.datetime.now(),
|
|
1053
|
+
duration=0.0,
|
|
1039
1054
|
error="unavailable",
|
|
1040
1055
|
input_size=0.0,
|
|
1041
1056
|
output_size=0.0,
|
|
@@ -1048,12 +1063,19 @@ class Runner:
|
|
|
1048
1063
|
),
|
|
1049
1064
|
status_v2=nextmv.StatusV2.succeeded,
|
|
1050
1065
|
),
|
|
1051
|
-
output=app_step.bypass_result,
|
|
1052
1066
|
)
|
|
1067
|
+
result.output = app_step.bypass_result
|
|
1068
|
+
|
|
1053
1069
|
# If the user supplied a bypass result, we need to write it to the temp dir if it's in dir mode
|
|
1054
1070
|
if isinstance(app_step.bypass_result, str) and os.path.isdir(app_step.bypass_result):
|
|
1055
|
-
|
|
1056
|
-
|
|
1071
|
+
src = pathlib.Path(app_step.bypass_result)
|
|
1072
|
+
dst = pathlib.Path(temp_dir)
|
|
1073
|
+
for item in src.iterdir():
|
|
1074
|
+
if item.is_file():
|
|
1075
|
+
shutil.copy2(item, dst / item.name)
|
|
1076
|
+
elif item.is_dir():
|
|
1077
|
+
shutil.copytree(item, dst / item.name)
|
|
1078
|
+
|
|
1057
1079
|
finally: # Make sure we clean up temp dir on failure too
|
|
1058
1080
|
# If the temp dir is empty, remove it
|
|
1059
1081
|
dir_result = False
|
nextpipe/utils.py
CHANGED
|
@@ -46,7 +46,10 @@ def __get_step_name() -> str:
|
|
|
46
46
|
return "main"
|
|
47
47
|
|
|
48
48
|
|
|
49
|
-
def log(
|
|
49
|
+
def log(
|
|
50
|
+
message: str,
|
|
51
|
+
step_name: str | None = None,
|
|
52
|
+
) -> None:
|
|
50
53
|
"""
|
|
51
54
|
Logs a message using stderr. Furthermore, prepends the name of the calling function if it is a step.
|
|
52
55
|
|
|
@@ -62,6 +65,9 @@ def log(message: str) -> None:
|
|
|
62
65
|
----------
|
|
63
66
|
message : str
|
|
64
67
|
The message to log.
|
|
68
|
+
step_name : str, optional
|
|
69
|
+
The name of the step to include in the log message. If not provided, it will
|
|
70
|
+
attempt to determine it automatically.
|
|
65
71
|
|
|
66
72
|
Examples
|
|
67
73
|
--------
|
|
@@ -72,7 +78,7 @@ def log(message: str) -> None:
|
|
|
72
78
|
>>> # [step_name] Processing data
|
|
73
79
|
"""
|
|
74
80
|
|
|
75
|
-
step_name = __get_step_name()
|
|
81
|
+
step_name = step_name or __get_step_name()
|
|
76
82
|
if step_name:
|
|
77
83
|
print(f"[{step_name}] {message}", file=sys.stderr)
|
|
78
84
|
else:
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: nextpipe
|
|
3
|
-
Version: 0.6.2.
|
|
3
|
+
Version: 0.6.2.dev5
|
|
4
4
|
Summary: Framework for Decision Pipeline modeling and execution
|
|
5
5
|
Project-URL: Homepage, https://www.nextmv.io
|
|
6
6
|
Project-URL: Documentation, https://nextpipe.docs.nextmv.io/en/latest/
|
|
@@ -105,7 +105,7 @@ Classifier: Programming Language :: Python :: 3.13
|
|
|
105
105
|
Classifier: Programming Language :: Python :: 3.14
|
|
106
106
|
Requires-Python: >=3.10
|
|
107
107
|
Requires-Dist: dataclasses-json>=0.6.7
|
|
108
|
-
Requires-Dist: nextmv>=
|
|
108
|
+
Requires-Dist: nextmv>=1.2.2
|
|
109
109
|
Requires-Dist: requests>=2.31.0
|
|
110
110
|
Provides-Extra: dev
|
|
111
111
|
Requires-Dist: goldie>=0.1.8; extra == 'dev'
|
|
@@ -1,15 +1,15 @@
|
|
|
1
|
-
nextpipe/__about__.py,sha256=
|
|
1
|
+
nextpipe/__about__.py,sha256=mOiwTNqXBqNcb4rptxgI-9gGg1FsAGtcakJZLdc-Im0,29
|
|
2
2
|
nextpipe/__init__.py,sha256=SzRhi2z33zmvskfgqoRHwaKvRnt8G1_JJR1M7BXlda0,694
|
|
3
3
|
nextpipe/config.py,sha256=JSShEaK3fnqieGofalQysKxEJUkyrWIRbwhdyH9yYZM,1659
|
|
4
4
|
nextpipe/constants.py,sha256=ws1_khUbZFau_kShi6nweLhg24_2fjPWyXY6VPVAH7E,667
|
|
5
|
-
nextpipe/decorators.py,sha256=
|
|
6
|
-
nextpipe/flow.py,sha256=
|
|
5
|
+
nextpipe/decorators.py,sha256=kLYOVYqx0QWtTbVkgw6US8ZZSa0YijZ5qzMX0mRiO7k,26539
|
|
6
|
+
nextpipe/flow.py,sha256=TAjZ6kuUw_1m6wi-HfB9pgbCVClOBODFXLGNa-3n4Fw,43259
|
|
7
7
|
nextpipe/graph.py,sha256=Oi8HQ4fgvBYxpRK6xBoLtoxftGb-gCOb09BqFwzrwWs,2867
|
|
8
8
|
nextpipe/schema.py,sha256=HsJnzpYhemXUa8IZvFEChPB-_5YXTCbkYSPf5zpPqo8,3263
|
|
9
9
|
nextpipe/threads.py,sha256=5s7jF-hgVF_Eqfxgf6hsF6dhXsRSfqTsq5F0JBy4hNw,9039
|
|
10
10
|
nextpipe/uplink.py,sha256=oW8rIt6byUqKhex5-Nk0KwTOBuPvRAMtuDfP-fmM6_E,11475
|
|
11
|
-
nextpipe/utils.py,sha256
|
|
12
|
-
nextpipe-0.6.2.
|
|
13
|
-
nextpipe-0.6.2.
|
|
14
|
-
nextpipe-0.6.2.
|
|
15
|
-
nextpipe-0.6.2.
|
|
11
|
+
nextpipe/utils.py,sha256=jAeWautNhUZsjuF00K7BVaU5grLRWlBmtLkcNnPYnXg,8279
|
|
12
|
+
nextpipe-0.6.2.dev5.dist-info/METADATA,sha256=2075MaDBj6IyaZP_jiLemxdRw4H-o6-BFF8NBTIuFZM,8759
|
|
13
|
+
nextpipe-0.6.2.dev5.dist-info/WHEEL,sha256=QccIxa26bgl1E6uMy58deGWi-0aeIkkangHcxk2kWfw,87
|
|
14
|
+
nextpipe-0.6.2.dev5.dist-info/licenses/LICENSE.md,sha256=FpVScuRcgdbeBNVsMzoPyBbKL2eCB1vFor1vfNNFEj4,4106
|
|
15
|
+
nextpipe-0.6.2.dev5.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|