This commit is contained in:
2026-02-02 02:32:28 -08:00
parent f0a82c2403
commit 4f7bac464f
6 changed files with 128 additions and 62 deletions

View File

@@ -653,6 +653,28 @@ def set_last_result_table(
logger.exception("Failed to sort overlay result_table and reorder items")
def set_last_result_table_overlay(
result_table: Optional[Any],
items: Optional[List[Any]] = None,
subject: Optional[Any] = None
) -> None:
"""Store a display table and items WITHOUT affecting history stack.
Used by action cmdlets (get-metadata, get-tag, get-url) to display detail
panels or filtered results without disrupting the primary search-result history.
"""
state = _get_pipeline_state()
state.display_table = result_table
state.display_items = items or []
state.display_subject = subject
def set_last_result_table_preserve_history(
result_table: Optional[Any],
items: Optional[List[Any]] = None,
subject: Optional[Any] = None
) -> None:
"""Compatibility alias for set_last_result_table_overlay."""
set_last_result_table_overlay(result_table, items=items, subject=subject)
def set_last_result_items_only(items: Optional[List[Any]]) -> None:
"""
@@ -1835,6 +1857,8 @@ class PipelineExecutor:
except Exception:
stage_table = None
debug(f"@N: stage_table={stage_table is not None}, display_table={display_table is not None}")
# Prefer selecting from the last selectable *table* (search/playlist)
# rather than from display-only emitted items, unless we're explicitly
# selecting from an overlay table.
@@ -1846,9 +1870,11 @@ class PipelineExecutor:
items_list = ctx.get_last_selectable_result_items() or []
else:
items_list = ctx.get_last_result_items() or []
except Exception:
except Exception as exc:
debug(f"@N: Exception getting items_list: {exc}")
items_list = []
debug(f"@N: selection_indices={selection_indices}, items_list length={len(items_list)}")
resolved_items = items_list if items_list else []
if items_list:
filtered = [
@@ -1920,12 +1946,15 @@ class PipelineExecutor:
filtered = track_items
table_type_hint = "tidal.track"
debug(f"@N: calling _maybe_run_class_selector with filtered={len(filtered)} items, stage_is_last={not stages}")
if PipelineExecutor._maybe_run_class_selector(
ctx,
config,
filtered,
stage_is_last=(not stages)):
debug(f"@N: _maybe_run_class_selector returned True, returning False")
return False, None
debug(f"@N: _maybe_run_class_selector returned False, continuing")
from cmdlet._shared import coerce_to_pipe_object
@@ -1934,6 +1963,7 @@ class PipelineExecutor:
filtered_pipe_objs
if len(filtered_pipe_objs) > 1 else filtered_pipe_objs[0]
)
debug(f"@N: coerced piped_result, stages={stages}")
if pipeline_session and worker_manager:
try:
@@ -2069,10 +2099,12 @@ class PipelineExecutor:
return False
if not stages:
debug(f"@N: stages is empty, checking auto_stage and metadata")
if isinstance(table_type, str) and table_type.startswith("metadata."):
print("Auto-applying metadata selection via get-tag")
stages.append(["get-tag"])
elif auto_stage:
debug(f"@N: Found auto_stage={auto_stage}, appending")
try:
print(f"Auto-running selection via {auto_stage[0]}")
except Exception:
@@ -2084,9 +2116,7 @@ class PipelineExecutor:
stages.append(list(auto_stage))
debug(f"Inserted auto stage before row action: {stages[-1]}")
# If the caller included a selection (e.g., @1) try to attach
# the selection args immediately to the inserted auto stage so
# the expansion is effective in a single pass.
# Attach selection args to auto stage
if selection_indices:
try:
if not _apply_row_action_to_stage(len(stages) - 1):
@@ -2115,61 +2145,56 @@ class PipelineExecutor:
except Exception:
logger.exception("Failed to attach selection args to auto-inserted stage")
# If no auto stage inserted and there are selection-action tokens available
# for the single selected row, apply it as the pipeline stage so a bare
# `@N` runs the intended action (e.g., get-file for hash-backed rows).
if not stages and selection_indices and len(selection_indices) == 1:
# Look for row_action in payload if still no stages
if not stages and selection_indices and len(selection_indices) == 1:
debug(f"@N: No stages and no auto_stage, looking for row_action in payload")
try:
idx = selection_indices[0]
debug(f"@N: idx={idx}, looking for row_action")
row_action = None
try:
idx = selection_indices[0]
debug(f"@N initial selection idx={idx} last_items={len(ctx.get_last_result_items() or [])}")
row_action = ctx.get_current_stage_table_row_selection_action(idx)
debug(f"@N: row_action from table={row_action}")
except Exception as exc:
debug(f"@N: Exception getting row_selection_action: {exc}")
row_action = None
if not row_action:
debug(f"@N: row_action not found from table, checking payload")
try:
row_action = ctx.get_current_stage_table_row_selection_action(idx)
except Exception:
logger.exception("Failed to get current_stage_table row selection action for idx %s", idx)
items = ctx.get_last_result_items() or []
debug(f"@N: got items, length={len(items)}")
if 0 <= idx < len(items):
maybe = items[idx]
try:
if isinstance(maybe, dict):
debug(f"@N: payload is dict with _selection_action={maybe.get('_selection_action')}")
else:
debug(f"@N: payload type={type(maybe).__name__}")
except Exception:
pass
if isinstance(maybe, dict):
candidate = maybe.get("_selection_action")
if isinstance(candidate, (list, tuple)):
row_action = [str(x) for x in candidate if x is not None]
debug(f"@N: extracted row_action from payload={row_action}")
except Exception as exc:
debug(f"@N: Exception checking payload: {exc}")
row_action = None
if not row_action:
if row_action:
debug(f"@N: FOUND row_action, appending {row_action}")
stages.append(row_action)
if pipeline_session and worker_manager:
try:
items = ctx.get_last_result_items() or []
if 0 <= idx < len(items):
maybe = items[idx]
try:
if isinstance(maybe, dict):
debug(f"@N payload: hash={maybe.get('hash')} store={maybe.get('store')} _selection_args={maybe.get('_selection_args')} _selection_action={maybe.get('_selection_action')}")
else:
debug(f"@N payload object type: {type(maybe).__name__}")
except Exception:
logger.exception("Failed to debug selection payload for index %s", idx)
if isinstance(maybe, dict):
candidate = maybe.get("_selection_action")
if isinstance(candidate, (list, tuple)):
row_action = [str(x) for x in candidate if x is not None]
worker_manager.log_step(
pipeline_session.worker_id,
f"@N applied row action -> {' '.join(row_action)}",
)
except Exception:
row_action = None
if row_action:
debug(f"@N applying row action -> {row_action}")
stages.append(row_action)
if pipeline_session and worker_manager:
try:
worker_manager.log_step(
pipeline_session.worker_id,
f"@N applied row action -> {' '.join(row_action)}",
)
except Exception:
logger.exception("Failed to record pipeline log step for applied row action (pipeline_session=%r)", getattr(pipeline_session, 'worker_id', None))
except Exception:
logger.exception("Failed to apply single-row selection action")
stages.append(row_action)
if pipeline_session and worker_manager:
try:
worker_manager.log_step(
pipeline_session.worker_id,
f"@N applied row action -> {' '.join(row_action)}",
)
except Exception:
logger.exception("Failed to record pipeline log step for applied row action (pipeline_session=%r)", getattr(pipeline_session, 'worker_id', None))
logger.exception("Failed to record pipeline log step for applied row action (pipeline_session=%r)", getattr(pipeline_session, 'worker_id', None))
except Exception:
logger.exception("Failed to apply single-row selection action")
else:
first_cmd = stages[0][0] if stages and stages[0] else None
if isinstance(table_type, str) and table_type.startswith("metadata.") and first_cmd not in (
@@ -2224,6 +2249,7 @@ class PipelineExecutor:
# selection-expansion logic can still run (e.g., for example selectors).
return True, piped_result
else:
debug(f"@N: No items to select from (items_list empty)")
print("No previous results to select from\n")
return False, None
@@ -2342,6 +2368,7 @@ class PipelineExecutor:
ctx = sys.modules[__name__]
try:
debug(f"execute_tokens: tokens={tokens}")
self._try_clear_pipeline_stop(ctx)
# REPL guard: stage-local tables should not persist across independent