fix(plugins): one unreadable plugin child no longer aborts iter_plugin_dirs or memory-provider discovery
iter_plugin_dirs stat'd <child>/__init__.py inside every plugin dir, so a single mode-000 / ACL-denied $HERMES_HOME/plugins/<x> still raised PermissionError out of the loader, memory-provider discovery (dashboard memory settings, hermes memory setup, plugins memory picker) and the user cron-provider scan. Catch OSError per child and log the same 'Skipping unreadable plugin directory' warning the list path already emits. Part of #111804
This commit is contained in:
@@ -62,12 +62,13 @@ def _get_project_plugins_dir() -> Optional[Path]:
|
||||
def _is_memory_provider_dir(path: Path) -> bool:
|
||||
"""Cheap text heuristic (no import): ``__init__.py`` mentions the memory provider contract."""
|
||||
init_file = path / "__init__.py"
|
||||
if not init_file.exists():
|
||||
return False
|
||||
try:
|
||||
if not init_file.exists():
|
||||
return False
|
||||
source = init_file.read_text(errors="replace", encoding="utf-8")[:8192]
|
||||
return "register_memory_provider" in source or "MemoryProvider" in source
|
||||
except Exception:
|
||||
except OSError as exc: # one mode-000 / ACL-denied child must not abort discovery
|
||||
logger.warning("Skipping unreadable plugin directory %s: %s", path, exc)
|
||||
return False
|
||||
|
||||
|
||||
|
||||
@@ -13,6 +13,8 @@ import sys
|
||||
from pathlib import Path
|
||||
from typing import Any, Callable, List, Optional, Tuple
|
||||
|
||||
_log = logging.getLogger(__name__)
|
||||
|
||||
_PLUGINS_ROOT = Path(__file__).parent
|
||||
|
||||
|
||||
@@ -39,8 +41,16 @@ def iter_plugin_dirs(root: Path) -> List[Path]:
|
||||
"""Sorted child dirs of *root* that have an ``__init__.py`` (skips ``_``/``.`` names)."""
|
||||
if not root.is_dir():
|
||||
return []
|
||||
return [child for child in sorted(root.iterdir())
|
||||
if child.is_dir() and not child.name.startswith(("_", ".")) and (child / "__init__.py").exists()]
|
||||
dirs: List[Path] = []
|
||||
for child in sorted(root.iterdir()):
|
||||
if child.name.startswith(("_", ".")):
|
||||
continue
|
||||
try:
|
||||
if child.is_dir() and (child / "__init__.py").exists():
|
||||
dirs.append(child)
|
||||
except OSError as exc: # one mode-000 / ACL-denied child must not abort the listing
|
||||
_log.warning("Skipping unreadable plugin directory %s: %s", child, exc)
|
||||
return dirs
|
||||
|
||||
|
||||
def read_plugin_description(plugin_dir: Path) -> str:
|
||||
|
||||
Reference in New Issue
Block a user