refactor(download): remove ProviderCore/download.py, move sanitize_filename to SYS.utils, replace callers to use API.HTTP.HTTPClient
This commit is contained in:
@@ -10,10 +10,10 @@ from __future__ import annotations
|
||||
from dataclasses import dataclass
|
||||
from typing import Any, Callable, Dict, Iterable, List, Optional, Union
|
||||
|
||||
from SYS.result_table_api import ColumnSpec, ProviderAdapter, ResultModel
|
||||
from SYS.result_table_api import ColumnSpec, ProviderAdapter, ResultModel, ResultTable, ensure_result_model
|
||||
|
||||
|
||||
ColumnFactory = Callable[[Iterable[ResultModel]], List[ColumnSpec]]
|
||||
ColumnFactory = Callable[[List[ResultModel]], List[ColumnSpec]]
|
||||
SelectionFn = Callable[[ResultModel], List[str]]
|
||||
|
||||
|
||||
@@ -22,33 +22,57 @@ class Provider:
|
||||
name: str
|
||||
adapter: ProviderAdapter
|
||||
# columns can be a static list or a factory that derives columns from sample rows
|
||||
columns: Optional[Union[List[ColumnSpec], ColumnFactory]] = None
|
||||
selection_fn: Optional[SelectionFn] = None
|
||||
columns: Union[List[ColumnSpec], ColumnFactory]
|
||||
selection_fn: SelectionFn
|
||||
metadata: Optional[Dict[str, Any]] = None
|
||||
|
||||
def get_columns(self, rows: Optional[Iterable[ResultModel]] = None) -> List[ColumnSpec]:
|
||||
if self.columns is None:
|
||||
raise ValueError(f"provider '{self.name}' must define columns")
|
||||
|
||||
if callable(self.columns):
|
||||
try:
|
||||
rows_list = list(rows) if rows is not None else []
|
||||
return list(self.columns(rows_list))
|
||||
except Exception:
|
||||
# Fall back to a minimal Title column on errors
|
||||
return [ColumnSpec("title", "Title", lambda r: r.title)]
|
||||
if self.columns is not None:
|
||||
return list(self.columns)
|
||||
# Default minimal column set
|
||||
return [ColumnSpec("title", "Title", lambda r: r.title)]
|
||||
rows_list = list(rows) if rows is not None else []
|
||||
cols = list(self.columns(rows_list))
|
||||
else:
|
||||
cols = list(self.columns)
|
||||
|
||||
if not cols:
|
||||
raise ValueError(f"provider '{self.name}' produced no columns")
|
||||
|
||||
return cols
|
||||
|
||||
def selection_args(self, row: ResultModel) -> List[str]:
|
||||
if callable(self.selection_fn):
|
||||
try:
|
||||
return list(self.selection_fn(row))
|
||||
except Exception:
|
||||
return []
|
||||
# Default selector: prefer path flag, then title
|
||||
if getattr(row, "path", None):
|
||||
return ["-path", str(row.path)]
|
||||
return ["-title", str(row.title)]
|
||||
if not callable(self.selection_fn):
|
||||
raise ValueError(f"provider '{self.name}' must define a selection function")
|
||||
|
||||
sel = list(self.selection_fn(ensure_result_model(row)))
|
||||
return sel
|
||||
|
||||
def build_table(self, items: Iterable[Any]) -> ResultTable:
|
||||
"""Materialize adapter output into a ResultTable (strict, no legacy types)."""
|
||||
|
||||
try:
|
||||
rows = [ensure_result_model(r) for r in self.adapter(items)]
|
||||
except Exception as exc:
|
||||
raise RuntimeError(f"provider '{self.name}' adapter failed") from exc
|
||||
|
||||
cols = self.get_columns(rows)
|
||||
return ResultTable(provider=self.name, rows=rows, columns=cols, meta=self.metadata or {})
|
||||
|
||||
def serialize_row(self, row: ResultModel) -> Dict[str, Any]:
|
||||
r = ensure_result_model(row)
|
||||
return {
|
||||
"title": r.title,
|
||||
"path": r.path,
|
||||
"ext": r.ext,
|
||||
"size_bytes": r.size_bytes,
|
||||
"metadata": r.metadata or {},
|
||||
"source": r.source or self.name,
|
||||
"_selection_args": self.selection_args(r),
|
||||
}
|
||||
|
||||
def serialize_rows(self, rows: Iterable[ResultModel]) -> List[Dict[str, Any]]:
|
||||
return [self.serialize_row(r) for r in rows]
|
||||
|
||||
|
||||
_PROVIDERS: Dict[str, Provider] = {}
|
||||
@@ -58,8 +82,8 @@ def register_provider(
|
||||
name: str,
|
||||
adapter: ProviderAdapter,
|
||||
*,
|
||||
columns: Optional[Union[List[ColumnSpec], ColumnFactory]] = None,
|
||||
selection_fn: Optional[SelectionFn] = None,
|
||||
columns: Union[List[ColumnSpec], ColumnFactory],
|
||||
selection_fn: SelectionFn,
|
||||
metadata: Optional[Dict[str, Any]] = None,
|
||||
) -> Provider:
|
||||
name = str(name or "").strip().lower()
|
||||
@@ -67,13 +91,20 @@ def register_provider(
|
||||
raise ValueError("provider name required")
|
||||
if name in _PROVIDERS:
|
||||
raise ValueError(f"provider already registered: {name}")
|
||||
if columns is None:
|
||||
raise ValueError("provider registration requires columns")
|
||||
if selection_fn is None:
|
||||
raise ValueError("provider registration requires selection_fn")
|
||||
p = Provider(name=name, adapter=adapter, columns=columns, selection_fn=selection_fn, metadata=metadata)
|
||||
_PROVIDERS[name] = p
|
||||
return p
|
||||
|
||||
|
||||
def get_provider(name: str) -> Provider:
|
||||
return _PROVIDERS[name.lower()]
|
||||
normalized = str(name or "").lower()
|
||||
if normalized not in _PROVIDERS:
|
||||
raise KeyError(f"provider not registered: {name}")
|
||||
return _PROVIDERS[normalized]
|
||||
|
||||
|
||||
def list_providers() -> List[str]:
|
||||
|
||||
Reference in New Issue
Block a user