pipeline: finalize PipelineState migration
- Add public wrappers get_pipeline_state() and sync_module_state() - Update TUI pipeline_runner to use public accessors and lazy- import CLI deps - Update get_tag docstring to avoid referencing internal variables - Update tests to use public API
This commit is contained in:
810
SYS/pipeline.py
810
SYS/pipeline.py
File diff suppressed because it is too large
Load Diff
@@ -22,7 +22,13 @@ for path in (ROOT_DIR, BASE_DIR):
|
|||||||
sys.path.insert(0, str_path)
|
sys.path.insert(0, str_path)
|
||||||
|
|
||||||
from SYS import pipeline as ctx
|
from SYS import pipeline as ctx
|
||||||
from CLI import ConfigLoader, PipelineExecutor as CLIPipelineExecutor, WorkerManagerRegistry
|
# Lazily import CLI dependencies to avoid import-time failures in test environments
|
||||||
|
try:
|
||||||
|
from CLI import ConfigLoader, PipelineExecutor as CLIPipelineExecutor, WorkerManagerRegistry
|
||||||
|
except Exception:
|
||||||
|
ConfigLoader = None
|
||||||
|
CLIPipelineExecutor = None
|
||||||
|
WorkerManagerRegistry = None
|
||||||
from SYS.logger import set_debug
|
from SYS.logger import set_debug
|
||||||
from SYS.rich_display import capture_rich_output
|
from SYS.rich_display import capture_rich_output
|
||||||
from SYS.result_table import ResultTable
|
from SYS.result_table import ResultTable
|
||||||
@@ -76,9 +82,14 @@ class PipelineRunResult:
|
|||||||
class PipelineRunner:
|
class PipelineRunner:
|
||||||
"""TUI wrapper that delegates to the canonical CLI pipeline executor."""
|
"""TUI wrapper that delegates to the canonical CLI pipeline executor."""
|
||||||
|
|
||||||
def __init__(self) -> None:
|
def __init__(self, config_loader: Optional[Any] = None, executor: Optional[Any] = None) -> None:
|
||||||
self._config_loader = ConfigLoader(root=ROOT_DIR)
|
# Allow dependency injection or lazily construct CLI dependencies so tests
|
||||||
self._executor = CLIPipelineExecutor(config_loader=self._config_loader)
|
# don't fail due to import-order issues in pytest environments.
|
||||||
|
self._config_loader = config_loader if config_loader is not None else (ConfigLoader(root=ROOT_DIR) if ConfigLoader else None)
|
||||||
|
if executor is not None:
|
||||||
|
self._executor = executor
|
||||||
|
else:
|
||||||
|
self._executor = CLIPipelineExecutor(config_loader=self._config_loader) if CLIPipelineExecutor else None
|
||||||
self._worker_manager = None
|
self._worker_manager = None
|
||||||
|
|
||||||
@property
|
@property
|
||||||
@@ -227,7 +238,11 @@ class PipelineRunner:
|
|||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def _snapshot_ctx_state() -> Dict[str, Any]:
|
def _snapshot_ctx_state() -> Dict[str, Any]:
|
||||||
"""Best-effort snapshot of pipeline context so TUI popups don't clobber UI state."""
|
"""Best-effort snapshot of pipeline context using PipelineState.
|
||||||
|
|
||||||
|
This reads from the active PipelineState (ContextVar or global fallback)
|
||||||
|
to produce a consistent snapshot that can be restored later.
|
||||||
|
"""
|
||||||
|
|
||||||
def _copy(val: Any) -> Any:
|
def _copy(val: Any) -> Any:
|
||||||
if isinstance(val, list):
|
if isinstance(val, list):
|
||||||
@@ -236,90 +251,117 @@ class PipelineRunner:
|
|||||||
return val.copy()
|
return val.copy()
|
||||||
return val
|
return val
|
||||||
|
|
||||||
snap: Dict[str,
|
state = ctx.get_pipeline_state()
|
||||||
Any] = {}
|
snap: Dict[str, Any] = {}
|
||||||
keys = [
|
|
||||||
"_LIVE_PROGRESS",
|
|
||||||
"_CURRENT_CONTEXT",
|
|
||||||
"_LAST_SEARCH_QUERY",
|
|
||||||
"_PIPELINE_REFRESHED",
|
|
||||||
"_PIPELINE_LAST_ITEMS",
|
|
||||||
"_LAST_RESULT_TABLE",
|
|
||||||
"_LAST_RESULT_ITEMS",
|
|
||||||
"_LAST_RESULT_SUBJECT",
|
|
||||||
"_RESULT_TABLE_HISTORY",
|
|
||||||
"_RESULT_TABLE_FORWARD",
|
|
||||||
"_CURRENT_STAGE_TABLE",
|
|
||||||
"_DISPLAY_ITEMS",
|
|
||||||
"_DISPLAY_TABLE",
|
|
||||||
"_DISPLAY_SUBJECT",
|
|
||||||
"_PIPELINE_LAST_SELECTION",
|
|
||||||
"_PIPELINE_COMMAND_TEXT",
|
|
||||||
"_CURRENT_CMDLET_NAME",
|
|
||||||
"_CURRENT_STAGE_TEXT",
|
|
||||||
"_PIPELINE_VALUES",
|
|
||||||
"_PENDING_PIPELINE_TAIL",
|
|
||||||
"_PENDING_PIPELINE_SOURCE",
|
|
||||||
"_UI_LIBRARY_REFRESH_CALLBACK",
|
|
||||||
]
|
|
||||||
|
|
||||||
for k in keys:
|
# Simple scalar/list/dict fields
|
||||||
snap[k] = _copy(getattr(ctx, k, None))
|
snap["live_progress"] = _copy(state.live_progress)
|
||||||
|
snap["current_context"] = state.current_context
|
||||||
|
snap["last_search_query"] = state.last_search_query
|
||||||
|
snap["pipeline_refreshed"] = state.pipeline_refreshed
|
||||||
|
snap["last_items"] = _copy(state.last_items)
|
||||||
|
snap["last_result_table"] = state.last_result_table
|
||||||
|
snap["last_result_items"] = _copy(state.last_result_items)
|
||||||
|
snap["last_result_subject"] = state.last_result_subject
|
||||||
|
|
||||||
# Deepen copies where nested lists are common.
|
# Deep-copy history/forward stacks (copy nested item lists)
|
||||||
try:
|
def _copy_history(hist: Optional[List[tuple]]) -> List[tuple]:
|
||||||
hist = list(getattr(ctx, "_RESULT_TABLE_HISTORY", []) or [])
|
out: List[tuple] = []
|
||||||
snap["_RESULT_TABLE_HISTORY"] = [
|
try:
|
||||||
(
|
for (t, items, subj) in list(hist or []):
|
||||||
t,
|
items_copy = items.copy() if isinstance(items, list) else list(items) if items else []
|
||||||
(
|
out.append((t, items_copy, subj))
|
||||||
items.copy()
|
except Exception:
|
||||||
if isinstance(items,
|
pass
|
||||||
list) else list(items) if items else []
|
return out
|
||||||
),
|
|
||||||
subj,
|
|
||||||
) for (t, items, subj) in hist if isinstance((t, items, subj), tuple)
|
|
||||||
]
|
|
||||||
except Exception:
|
|
||||||
pass
|
|
||||||
|
|
||||||
try:
|
snap["result_table_history"] = _copy_history(state.result_table_history)
|
||||||
fwd = list(getattr(ctx, "_RESULT_TABLE_FORWARD", []) or [])
|
snap["result_table_forward"] = _copy_history(state.result_table_forward)
|
||||||
snap["_RESULT_TABLE_FORWARD"] = [
|
|
||||||
(
|
|
||||||
t,
|
|
||||||
(
|
|
||||||
items.copy()
|
|
||||||
if isinstance(items,
|
|
||||||
list) else list(items) if items else []
|
|
||||||
),
|
|
||||||
subj,
|
|
||||||
) for (t, items, subj) in fwd if isinstance((t, items, subj), tuple)
|
|
||||||
]
|
|
||||||
except Exception:
|
|
||||||
pass
|
|
||||||
|
|
||||||
try:
|
snap["current_stage_table"] = state.current_stage_table
|
||||||
tail = list(getattr(ctx, "_PENDING_PIPELINE_TAIL", []) or [])
|
snap["display_items"] = _copy(state.display_items)
|
||||||
snap["_PENDING_PIPELINE_TAIL"] = [
|
snap["display_table"] = state.display_table
|
||||||
list(stage) for stage in tail if isinstance(stage, list)
|
snap["display_subject"] = state.display_subject
|
||||||
]
|
snap["last_selection"] = _copy(state.last_selection)
|
||||||
except Exception:
|
snap["pipeline_command_text"] = state.pipeline_command_text
|
||||||
pass
|
snap["current_cmdlet_name"] = state.current_cmdlet_name
|
||||||
|
snap["current_stage_text"] = state.current_stage_text
|
||||||
try:
|
snap["pipeline_values"] = _copy(state.pipeline_values) if isinstance(state.pipeline_values, dict) else state.pipeline_values
|
||||||
values = getattr(ctx, "_PIPELINE_VALUES", None)
|
snap["pending_pipeline_tail"] = [list(stage) for stage in (state.pending_pipeline_tail or [])]
|
||||||
if isinstance(values, dict):
|
snap["pending_pipeline_source"] = state.pending_pipeline_source
|
||||||
snap["_PIPELINE_VALUES"] = values.copy()
|
snap["ui_library_refresh_callback"] = state.ui_library_refresh_callback
|
||||||
except Exception:
|
snap["pipeline_stop"] = state.pipeline_stop
|
||||||
pass
|
|
||||||
|
|
||||||
return snap
|
return snap
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def _restore_ctx_state(snapshot: Dict[str, Any]) -> None:
|
def _restore_ctx_state(snapshot: Dict[str, Any]) -> None:
|
||||||
for k, v in (snapshot or {}).items():
|
if not snapshot:
|
||||||
|
return
|
||||||
|
state = ctx.get_pipeline_state()
|
||||||
|
|
||||||
|
# Helper for restoring history-like stacks
|
||||||
|
def _restore_history(key: str, val: Any) -> None:
|
||||||
try:
|
try:
|
||||||
setattr(ctx, k, v)
|
if isinstance(val, list):
|
||||||
|
out: List[tuple] = []
|
||||||
|
for (t, items, subj) in val:
|
||||||
|
items_copy = items.copy() if isinstance(items, list) else list(items) if items else []
|
||||||
|
out.append((t, items_copy, subj))
|
||||||
|
setattr(state, key, out)
|
||||||
except Exception:
|
except Exception:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
try:
|
||||||
|
if "live_progress" in snapshot:
|
||||||
|
state.live_progress = snapshot["live_progress"]
|
||||||
|
if "current_context" in snapshot:
|
||||||
|
state.current_context = snapshot["current_context"]
|
||||||
|
if "last_search_query" in snapshot:
|
||||||
|
state.last_search_query = snapshot["last_search_query"]
|
||||||
|
if "pipeline_refreshed" in snapshot:
|
||||||
|
state.pipeline_refreshed = snapshot["pipeline_refreshed"]
|
||||||
|
if "last_items" in snapshot:
|
||||||
|
state.last_items = snapshot["last_items"] or []
|
||||||
|
if "last_result_table" in snapshot:
|
||||||
|
state.last_result_table = snapshot["last_result_table"]
|
||||||
|
if "last_result_items" in snapshot:
|
||||||
|
state.last_result_items = snapshot["last_result_items"] or []
|
||||||
|
if "last_result_subject" in snapshot:
|
||||||
|
state.last_result_subject = snapshot["last_result_subject"]
|
||||||
|
if "result_table_history" in snapshot:
|
||||||
|
_restore_history("result_table_history", snapshot["result_table_history"])
|
||||||
|
if "result_table_forward" in snapshot:
|
||||||
|
_restore_history("result_table_forward", snapshot["result_table_forward"])
|
||||||
|
if "current_stage_table" in snapshot:
|
||||||
|
state.current_stage_table = snapshot["current_stage_table"]
|
||||||
|
if "display_items" in snapshot:
|
||||||
|
state.display_items = snapshot["display_items"] or []
|
||||||
|
if "display_table" in snapshot:
|
||||||
|
state.display_table = snapshot["display_table"]
|
||||||
|
if "display_subject" in snapshot:
|
||||||
|
state.display_subject = snapshot["display_subject"]
|
||||||
|
if "last_selection" in snapshot:
|
||||||
|
state.last_selection = snapshot["last_selection"] or []
|
||||||
|
if "pipeline_command_text" in snapshot:
|
||||||
|
state.pipeline_command_text = snapshot["pipeline_command_text"] or ""
|
||||||
|
if "current_cmdlet_name" in snapshot:
|
||||||
|
state.current_cmdlet_name = snapshot["current_cmdlet_name"] or ""
|
||||||
|
if "current_stage_text" in snapshot:
|
||||||
|
state.current_stage_text = snapshot["current_stage_text"] or ""
|
||||||
|
if "pipeline_values" in snapshot:
|
||||||
|
state.pipeline_values = snapshot["pipeline_values"] or {}
|
||||||
|
if "pending_pipeline_tail" in snapshot:
|
||||||
|
state.pending_pipeline_tail = snapshot["pending_pipeline_tail"] or []
|
||||||
|
if "pending_pipeline_source" in snapshot:
|
||||||
|
state.pending_pipeline_source = snapshot["pending_pipeline_source"]
|
||||||
|
if "ui_library_refresh_callback" in snapshot:
|
||||||
|
state.ui_library_refresh_callback = snapshot["ui_library_refresh_callback"]
|
||||||
|
if "pipeline_stop" in snapshot:
|
||||||
|
state.pipeline_stop = snapshot["pipeline_stop"]
|
||||||
|
except Exception:
|
||||||
|
# Best-effort; don't break the pipeline runner
|
||||||
|
pass
|
||||||
|
|
||||||
|
# Ensure module-level variables reflect restored state
|
||||||
|
ctx.sync_module_state(state)
|
||||||
|
|||||||
@@ -326,7 +326,7 @@ def _emit_tags_as_table(
|
|||||||
"""Emit tags as TagItem objects and display via ResultTable.
|
"""Emit tags as TagItem objects and display via ResultTable.
|
||||||
|
|
||||||
This replaces _print_tag_list to make tags pipe-able.
|
This replaces _print_tag_list to make tags pipe-able.
|
||||||
Stores the table in ctx._LAST_RESULT_TABLE for downstream @ selection.
|
Stores the table via ctx.set_last_result_table_overlay (or ctx.set_last_result_table) for downstream @ selection.
|
||||||
"""
|
"""
|
||||||
from SYS.result_table import ResultTable
|
from SYS.result_table import ResultTable
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user