Add YAPF style + ignore, and format tracked Python files
This commit is contained in:
210
models.py
210
models.py
@@ -117,7 +117,9 @@ class PipeObject:
|
||||
import pipeline as ctx
|
||||
|
||||
current = (
|
||||
ctx.get_current_cmdlet_name("") if hasattr(ctx, "get_current_cmdlet_name") else ""
|
||||
ctx.get_current_cmdlet_name("")
|
||||
if hasattr(ctx,
|
||||
"get_current_cmdlet_name") else ""
|
||||
)
|
||||
if current:
|
||||
cmdlet_name = current
|
||||
@@ -142,10 +144,11 @@ class PipeObject:
|
||||
|
||||
def to_dict(self) -> Dict[str, Any]:
|
||||
"""Serialize to dictionary, excluding None and empty values."""
|
||||
data: Dict[str, Any] = {
|
||||
"hash": self.hash,
|
||||
"store": self.store,
|
||||
}
|
||||
data: Dict[str,
|
||||
Any] = {
|
||||
"hash": self.hash,
|
||||
"store": self.store,
|
||||
}
|
||||
|
||||
if self.provider:
|
||||
data["provider"] = self.provider
|
||||
@@ -176,7 +179,10 @@ class PipeObject:
|
||||
data["parent_hash"] = self.parent_hash
|
||||
|
||||
# Add extra fields
|
||||
data.update({k: v for k, v in self.extra.items() if v is not None})
|
||||
data.update({
|
||||
k: v
|
||||
for k, v in self.extra.items() if v is not None
|
||||
})
|
||||
return data
|
||||
|
||||
|
||||
@@ -190,7 +196,9 @@ class FileRelationshipTracker:
|
||||
"""
|
||||
|
||||
def __init__(self) -> None:
|
||||
self.relationships: Dict[str, Dict[str, Any]] = {}
|
||||
self.relationships: Dict[str,
|
||||
Dict[str,
|
||||
Any]] = {}
|
||||
|
||||
def register_king(self, file_path: str, file_hash: str) -> None:
|
||||
"""Register a file as the king (primary) version."""
|
||||
@@ -271,7 +279,8 @@ class DownloadOptions:
|
||||
debug_log: Optional[Path] = None
|
||||
native_progress: bool = False
|
||||
clip_sections: Optional[str] = None
|
||||
playlist_items: Optional[str] = None # yt-dlp --playlist-items format (e.g., "1-3,5,8")
|
||||
playlist_items: Optional[
|
||||
str] = None # yt-dlp --playlist-items format (e.g., "1-3,5,8")
|
||||
no_playlist: bool = False # If True, pass --no-playlist to yt-dlp
|
||||
quiet: bool = False # If True, suppress all console output (progress, debug logs)
|
||||
embed_chapters: bool = False # If True, pass yt-dlp --embed-chapters / embedchapters
|
||||
@@ -281,7 +290,8 @@ class DownloadOptions:
|
||||
class SendFunc(Protocol):
|
||||
"""Protocol for event sender function."""
|
||||
|
||||
def __call__(self, event: str, **payload: Any) -> None: ...
|
||||
def __call__(self, event: str, **payload: Any) -> None:
|
||||
...
|
||||
|
||||
|
||||
@dataclass(slots=True)
|
||||
@@ -324,7 +334,11 @@ class DebugLogger:
|
||||
if self.session_started:
|
||||
return
|
||||
self.session_started = True
|
||||
self.write_record("session-start", {"pid": os.getpid(), "exe": sys.executable})
|
||||
self.write_record("session-start",
|
||||
{
|
||||
"pid": os.getpid(),
|
||||
"exe": sys.executable
|
||||
})
|
||||
|
||||
def write_raw(self, text: str) -> None:
|
||||
"""Write raw text to debug log."""
|
||||
@@ -334,7 +348,12 @@ class DebugLogger:
|
||||
self.file.write(text + "\n")
|
||||
self.file.flush()
|
||||
|
||||
def write_record(self, event: str, payload: Optional[Dict[str, Any]] = None) -> None:
|
||||
def write_record(
|
||||
self,
|
||||
event: str,
|
||||
payload: Optional[Dict[str,
|
||||
Any]] = None
|
||||
) -> None:
|
||||
"""Write a structured event record to debug log."""
|
||||
record = {
|
||||
"timestamp": datetime.datetime.utcnow().isoformat(timespec="seconds") + "Z",
|
||||
@@ -353,7 +372,12 @@ class DebugLogger:
|
||||
self.file = None
|
||||
|
||||
|
||||
def _sanitise_for_json(value: Any, *, max_depth: int = 8, _seen: Optional[set[int]] = None) -> Any:
|
||||
def _sanitise_for_json(
|
||||
value: Any,
|
||||
*,
|
||||
max_depth: int = 8,
|
||||
_seen: Optional[set[int]] = None
|
||||
) -> Any:
|
||||
"""Best-effort conversion to JSON-serialisable types without raising on cycles."""
|
||||
import math
|
||||
from dataclasses import asdict, is_dataclass
|
||||
@@ -386,16 +410,24 @@ def _sanitise_for_json(value: Any, *, max_depth: int = 8, _seen: Optional[set[in
|
||||
try:
|
||||
if isinstance(value, dict):
|
||||
return {
|
||||
str(key): _sanitise_for_json(val, max_depth=max_depth - 1, _seen=_seen)
|
||||
str(key): _sanitise_for_json(val,
|
||||
max_depth=max_depth - 1,
|
||||
_seen=_seen)
|
||||
for key, val in value.items()
|
||||
}
|
||||
if isinstance(value, (list, tuple, set)):
|
||||
iterable = value if not isinstance(value, set) else list(value)
|
||||
return [
|
||||
_sanitise_for_json(item, max_depth=max_depth - 1, _seen=_seen) for item in iterable
|
||||
_sanitise_for_json(item,
|
||||
max_depth=max_depth - 1,
|
||||
_seen=_seen) for item in iterable
|
||||
]
|
||||
if is_dataclass(value) and not isinstance(value, type):
|
||||
return _sanitise_for_json(asdict(value), max_depth=max_depth - 1, _seen=_seen)
|
||||
return _sanitise_for_json(
|
||||
asdict(value),
|
||||
max_depth=max_depth - 1,
|
||||
_seen=_seen
|
||||
)
|
||||
finally:
|
||||
_seen.discard(obj_id)
|
||||
|
||||
@@ -423,13 +455,21 @@ class ProgressBar:
|
||||
self._pipeline_ui: Any = None
|
||||
self._pipeline_label: Optional[str] = None
|
||||
|
||||
def _ensure_started(self, *, label: str, total: Optional[int], file: Any = None) -> None:
|
||||
def _ensure_started(
|
||||
self,
|
||||
*,
|
||||
label: str,
|
||||
total: Optional[int],
|
||||
file: Any = None
|
||||
) -> None:
|
||||
if self._pipeline_ui is not None and self._pipeline_label:
|
||||
# Pipeline-backed transfer task is already registered; update its total if needed.
|
||||
try:
|
||||
if total is not None and total > 0:
|
||||
self._pipeline_ui.update_transfer(
|
||||
label=self._pipeline_label, completed=None, total=int(total)
|
||||
label=self._pipeline_label,
|
||||
completed=None,
|
||||
total=int(total)
|
||||
)
|
||||
except Exception:
|
||||
pass
|
||||
@@ -445,13 +485,17 @@ class ProgressBar:
|
||||
import pipeline as pipeline_context
|
||||
|
||||
ui = pipeline_context.get_live_progress()
|
||||
if ui is not None and hasattr(ui, "begin_transfer") and hasattr(ui, "update_transfer"):
|
||||
if ui is not None and hasattr(ui,
|
||||
"begin_transfer") and hasattr(
|
||||
ui,
|
||||
"update_transfer"):
|
||||
self._pipeline_ui = ui
|
||||
self._pipeline_label = str(label or "download")
|
||||
try:
|
||||
ui.begin_transfer(
|
||||
label=self._pipeline_label,
|
||||
total=int(total) if isinstance(total, int) and total > 0 else None,
|
||||
total=int(total) if isinstance(total,
|
||||
int) and total > 0 else None,
|
||||
)
|
||||
except Exception:
|
||||
# If pipeline integration fails, fall back to standalone progress.
|
||||
@@ -508,7 +552,8 @@ class ProgressBar:
|
||||
self._pipeline_ui.update_transfer(
|
||||
label=self._pipeline_label,
|
||||
completed=int(downloaded or 0) if downloaded is not None else None,
|
||||
total=int(total) if isinstance(total, int) and total > 0 else None,
|
||||
total=int(total) if isinstance(total,
|
||||
int) and total > 0 else None,
|
||||
)
|
||||
except Exception:
|
||||
pass
|
||||
@@ -518,10 +563,17 @@ class ProgressBar:
|
||||
return
|
||||
if total is not None and total > 0:
|
||||
self._progress.update(
|
||||
self._task_id, completed=int(downloaded or 0), total=int(total), refresh=True
|
||||
self._task_id,
|
||||
completed=int(downloaded or 0),
|
||||
total=int(total),
|
||||
refresh=True
|
||||
)
|
||||
else:
|
||||
self._progress.update(self._task_id, completed=int(downloaded or 0), refresh=True)
|
||||
self._progress.update(
|
||||
self._task_id,
|
||||
completed=int(downloaded or 0),
|
||||
refresh=True
|
||||
)
|
||||
|
||||
def finish(self) -> None:
|
||||
if self._pipeline_ui is not None and self._pipeline_label:
|
||||
@@ -660,13 +712,14 @@ class ProgressFileReader:
|
||||
|
||||
|
||||
def _pipeline_progress_item_label(value: Any, *, max_len: int = 72) -> str:
|
||||
|
||||
def _clip(text: str) -> str:
|
||||
text = str(text or "").strip()
|
||||
if not text:
|
||||
return "(item)"
|
||||
if len(text) <= max_len:
|
||||
return text
|
||||
return text[: max(0, max_len - 1)] + "…"
|
||||
return text[:max(0, max_len - 1)] + "…"
|
||||
|
||||
try:
|
||||
if isinstance(value, PipeObject):
|
||||
@@ -715,18 +768,23 @@ class PipelineLiveProgress:
|
||||
self._overall_task: Optional[TaskID] = None
|
||||
self._pipe_tasks: List[TaskID] = []
|
||||
|
||||
self._transfer_tasks: Dict[str, TaskID] = {}
|
||||
self._transfer_tasks: Dict[str,
|
||||
TaskID] = {}
|
||||
|
||||
# Per-pipe status line shown below the pipe bars.
|
||||
self._status_tasks: Dict[int, TaskID] = {}
|
||||
self._status_tasks: Dict[int,
|
||||
TaskID] = {}
|
||||
|
||||
# When a pipe is operating on a single item, allow percent-based progress
|
||||
# updates on the pipe bar (0..100) so it doesn't sit at 0% until emit().
|
||||
self._pipe_percent_mode: Dict[int, bool] = {}
|
||||
self._pipe_percent_mode: Dict[int,
|
||||
bool] = {}
|
||||
|
||||
# Per-pipe step counters used for status lines and percent mapping.
|
||||
self._pipe_step_total: Dict[int, int] = {}
|
||||
self._pipe_step_done: Dict[int, int] = {}
|
||||
self._pipe_step_total: Dict[int,
|
||||
int] = {}
|
||||
self._pipe_step_done: Dict[int,
|
||||
int] = {}
|
||||
|
||||
# Per-pipe state
|
||||
self._pipe_totals: List[int] = [0 for _ in self._pipe_labels]
|
||||
@@ -782,7 +840,12 @@ class PipelineLiveProgress:
|
||||
body_parts.append(status)
|
||||
body_parts.append(transfers)
|
||||
|
||||
yield Group(Panel(Group(*body_parts), title=self._title_text(), expand=False), overall)
|
||||
yield Group(
|
||||
Panel(Group(*body_parts),
|
||||
title=self._title_text(),
|
||||
expand=False),
|
||||
overall
|
||||
)
|
||||
|
||||
def _render_group(self) -> Group:
|
||||
# Backward-compatible helper (some callers may still expect a Group).
|
||||
@@ -797,7 +860,12 @@ class PipelineLiveProgress:
|
||||
if status is not None and self._status_tasks:
|
||||
body_parts.append(status)
|
||||
body_parts.append(transfers)
|
||||
return Group(Panel(Group(*body_parts), title=self._title_text(), expand=False), overall)
|
||||
return Group(
|
||||
Panel(Group(*body_parts),
|
||||
title=self._title_text(),
|
||||
expand=False),
|
||||
overall
|
||||
)
|
||||
|
||||
def start(self) -> None:
|
||||
if not self._enabled:
|
||||
@@ -875,10 +943,16 @@ class PipelineLiveProgress:
|
||||
|
||||
self._overall_task = self._overall.add_task(
|
||||
f"Pipeline: 0/{len(self._pipe_labels)} pipes completed",
|
||||
total=max(1, len(self._pipe_labels)),
|
||||
total=max(1,
|
||||
len(self._pipe_labels)),
|
||||
)
|
||||
|
||||
self._live = Live(self, console=self._console, refresh_per_second=10, transient=True)
|
||||
self._live = Live(
|
||||
self,
|
||||
console=self._console,
|
||||
refresh_per_second=10,
|
||||
transient=True
|
||||
)
|
||||
self._live.start()
|
||||
|
||||
def pause(self) -> None:
|
||||
@@ -896,17 +970,18 @@ class PipelineLiveProgress:
|
||||
return
|
||||
if self._live is not None:
|
||||
return
|
||||
if (
|
||||
self._console is None
|
||||
or self._pipe_progress is None
|
||||
or self._subtasks is None
|
||||
or self._transfers is None
|
||||
or self._overall is None
|
||||
):
|
||||
if (self._console is None or self._pipe_progress is None
|
||||
or self._subtasks is None or self._transfers is None
|
||||
or self._overall is None):
|
||||
# Not initialized yet; start fresh.
|
||||
self.start()
|
||||
return
|
||||
self._live = Live(self, console=self._console, refresh_per_second=10, transient=True)
|
||||
self._live = Live(
|
||||
self,
|
||||
console=self._console,
|
||||
refresh_per_second=10,
|
||||
transient=True
|
||||
)
|
||||
self._live.start()
|
||||
|
||||
def stop(self) -> None:
|
||||
@@ -1116,7 +1191,11 @@ class PipelineLiveProgress:
|
||||
pass
|
||||
|
||||
def update_transfer(
|
||||
self, *, label: str, completed: Optional[int], total: Optional[int] = None
|
||||
self,
|
||||
*,
|
||||
label: str,
|
||||
completed: Optional[int],
|
||||
total: Optional[int] = None
|
||||
) -> None:
|
||||
if not self._enabled:
|
||||
return
|
||||
@@ -1129,7 +1208,8 @@ class PipelineLiveProgress:
|
||||
if task_id is None:
|
||||
return
|
||||
try:
|
||||
kwargs: Dict[str, Any] = {}
|
||||
kwargs: Dict[str,
|
||||
Any] = {}
|
||||
if completed is not None:
|
||||
kwargs["completed"] = int(completed)
|
||||
if total is not None and total > 0:
|
||||
@@ -1160,7 +1240,11 @@ class PipelineLiveProgress:
|
||||
return True
|
||||
|
||||
def begin_pipe(
|
||||
self, pipe_index: int, *, total_items: int, items_preview: Optional[List[Any]] = None
|
||||
self,
|
||||
pipe_index: int,
|
||||
*,
|
||||
total_items: int,
|
||||
items_preview: Optional[List[Any]] = None
|
||||
) -> None:
|
||||
if not self._ensure_pipe(pipe_index):
|
||||
return
|
||||
@@ -1192,7 +1276,11 @@ class PipelineLiveProgress:
|
||||
self._pipe_percent_mode[pipe_index] = percent_mode
|
||||
|
||||
pipe_task = self._pipe_tasks[pipe_index]
|
||||
pipe_progress.update(pipe_task, completed=0, total=(100 if percent_mode else total_items))
|
||||
pipe_progress.update(
|
||||
pipe_task,
|
||||
completed=0,
|
||||
total=(100 if percent_mode else total_items)
|
||||
)
|
||||
# Start the per-pipe timer now that the pipe is actually running.
|
||||
try:
|
||||
pipe_progress.start_task(pipe_task)
|
||||
@@ -1212,7 +1300,10 @@ class PipelineLiveProgress:
|
||||
for i in range(total_items):
|
||||
suffix = labels[i] if i < len(labels) else f"item {i + 1}/{total_items}"
|
||||
# Use start=False so elapsed time starts when we explicitly start_task().
|
||||
sub_id = subtasks.add_task(f"{self._pipe_labels[pipe_index]}: {suffix}", start=False)
|
||||
sub_id = subtasks.add_task(
|
||||
f"{self._pipe_labels[pipe_index]}: {suffix}",
|
||||
start=False
|
||||
)
|
||||
subtasks.update(sub_id, visible=False)
|
||||
self._subtask_ids[pipe_index].append(sub_id)
|
||||
|
||||
@@ -1223,7 +1314,9 @@ class PipelineLiveProgress:
|
||||
subtasks.start_task(first)
|
||||
try:
|
||||
t = subtasks.tasks[first]
|
||||
self._active_subtask_text = str(getattr(t, "description", "") or "").strip() or None
|
||||
self._active_subtask_text = str(getattr(t,
|
||||
"description",
|
||||
"") or "").strip() or None
|
||||
except Exception:
|
||||
self._active_subtask_text = None
|
||||
|
||||
@@ -1262,7 +1355,8 @@ class PipelineLiveProgress:
|
||||
# If we didn’t have a preview label, set it now.
|
||||
subtasks.update(
|
||||
current,
|
||||
description=f"{self._pipe_labels[pipe_index]}: {_pipeline_progress_item_label(emitted)}",
|
||||
description=
|
||||
f"{self._pipe_labels[pipe_index]}: {_pipeline_progress_item_label(emitted)}",
|
||||
)
|
||||
except Exception:
|
||||
pass
|
||||
@@ -1298,7 +1392,9 @@ class PipelineLiveProgress:
|
||||
subtasks.start_task(nxt)
|
||||
try:
|
||||
t = subtasks.tasks[nxt]
|
||||
self._active_subtask_text = str(getattr(t, "description", "") or "").strip() or None
|
||||
self._active_subtask_text = str(getattr(t,
|
||||
"description",
|
||||
"") or "").strip() or None
|
||||
except Exception:
|
||||
self._active_subtask_text = None
|
||||
else:
|
||||
@@ -1360,16 +1456,18 @@ class PipelineLiveProgress:
|
||||
completed = 0
|
||||
try:
|
||||
completed = sum(
|
||||
1
|
||||
for i in range(len(self._pipe_labels))
|
||||
1 for i in range(len(self._pipe_labels))
|
||||
if self._pipe_done[i] >= max(1, self._pipe_totals[i])
|
||||
)
|
||||
except Exception:
|
||||
completed = 0
|
||||
overall.update(
|
||||
self._overall_task,
|
||||
completed=min(completed, max(1, len(self._pipe_labels))),
|
||||
description=f"Pipeline: {completed}/{len(self._pipe_labels)} pipes completed",
|
||||
completed=min(completed,
|
||||
max(1,
|
||||
len(self._pipe_labels))),
|
||||
description=
|
||||
f"Pipeline: {completed}/{len(self._pipe_labels)} pipes completed",
|
||||
)
|
||||
|
||||
|
||||
@@ -1382,7 +1480,8 @@ class PipelineStageContext:
|
||||
total_stages: int,
|
||||
pipe_index: Optional[int] = None,
|
||||
worker_id: Optional[str] = None,
|
||||
on_emit: Optional[Callable[[Any], None]] = None,
|
||||
on_emit: Optional[Callable[[Any],
|
||||
None]] = None,
|
||||
):
|
||||
self.stage_index = stage_index
|
||||
self.total_stages = total_stages
|
||||
@@ -1505,7 +1604,10 @@ class ResultColumn:
|
||||
|
||||
def to_dict(self) -> Dict[str, str]:
|
||||
"""Convert to dictionary."""
|
||||
return {"name": self.name, "value": self.value}
|
||||
return {
|
||||
"name": self.name,
|
||||
"value": self.value
|
||||
}
|
||||
|
||||
|
||||
@dataclass
|
||||
|
||||
Reference in New Issue
Block a user