huge refactor of plugin system

This commit is contained in:
2026-04-30 18:56:22 -07:00
parent ea3ead248b
commit be5a11da97
99 changed files with 7603 additions and 11320 deletions
+72 -1
View File
@@ -13,7 +13,7 @@ Features:
from __future__ import annotations
from dataclasses import dataclass, field
from typing import Any, Dict, List, Optional, Callable, Set
from typing import Any, Dict, List, Optional, Callable, Set, Tuple
from pathlib import Path
import json
import re
@@ -94,6 +94,42 @@ def _partition_detail_tags(tags: Any) -> tuple[List[str], List[str]]:
return namespace_tags, freeform_tags
def _extract_namespace_sort_values(tags: Any, namespace: str) -> List[str]:
wanted = str(namespace or "").strip().casefold()
if not wanted:
return []
values: List[str] = []
for tag in _normalize_detail_tags(tags):
ns, sep, value = str(tag).partition(":")
if not sep:
continue
if ns.strip().casefold() != wanted:
continue
clean_value = value.strip()
if clean_value:
values.append(clean_value)
return values
def _namespace_sort_key(tags: Any, namespace: str, *, numeric: bool = False) -> Tuple[int, Any, str]:
values = _extract_namespace_sort_values(tags, namespace)
if not values:
return (1, float("inf") if numeric else "", "")
primary = values[0]
if numeric:
match = re.search(r"-?\d+(?:\.\d+)?", primary)
if match:
try:
return (0, float(match.group(0)), primary.casefold())
except Exception:
pass
return (0, float("inf"), primary.casefold())
return (0, primary.casefold(), primary.casefold())
def _chunk_detail_tags(tags: List[str], columns: int) -> List[List[str]]:
column_count = max(1, int(columns or 1))
rows: List[List[str]] = []
@@ -954,6 +990,41 @@ class Table:
return self
def sort_by_tag_namespace(self, namespace: str, *, numeric: bool = False, reverse: bool = False) -> "Table":
"""Sort rows by the first value found for a tag namespace.
Looks first at row payload tag metadata, then falls back to the visible Tag column.
When ``numeric`` is True, the first numeric token inside the namespace value is used.
"""
if getattr(self, "preserve_order", False):
return self
wanted = str(namespace or "").strip()
if not wanted:
return self
def _row_tags(row: Row) -> Any:
payload = getattr(row, "payload", None)
if isinstance(payload, dict):
for key in ("tag", "tags", "tag_summary"):
value = payload.get(key)
if value:
return value
metadata = payload.get("metadata")
if isinstance(metadata, dict):
for key in ("tag", "tags", "tag_summary"):
value = metadata.get(key)
if value:
return value
tag_column = row.get_column("Tag")
return tag_column or []
self.rows.sort(
key=lambda row: _namespace_sort_key(_row_tags(row), wanted, numeric=bool(numeric)),
reverse=bool(reverse),
)
return self
def add_result(self, result: Any) -> "Table":
"""Add a result object (SearchResult, PipeObject, ResultItem, TagItem, or dict) as a row.