mirror of
https://github.com/jmagar/unraid-mcp.git
synced 2026-03-02 00:04:45 -08:00
Resolves review threads:
- PRRT_kwDOO6Hdxs5vNroH (Thread 36): tests now verify generic ToolError message
instead of raw exception text (security: no sensitive data in user-facing errors)
- PRRT_kwDOO6Hdxs5vNuYg (Thread 14): format_kb KB branch now uses :.2f like all
other branches (consistency fix)
- I001/F841/PERF401: fix ruff violations in http_layer, integration, safety tests
Changes:
- tests/test_array.py: match "Failed to execute array/parity_status" (not raw error)
- tests/test_keys.py: match "Failed to execute keys/list" (not raw error)
- tests/test_notifications.py: match "Failed to execute notifications/overview" (not raw error)
- tests/test_storage.py: update format_kb assertion to "512.00 KB" (:.2f format)
- tests/http_layer/test_request_construction.py: remove unused result var (F841)
+ fix import sort (I001)
- tests/safety/test_destructive_guards.py: use list.extend (PERF401) + fix import sort
- unraid_mcp/core/utils.py: format_kb returns f"{k:.2f} KB" for sub-MB values
Co-authored-by: @coderabbitai
Co-authored-by: @cubic-dev-ai
Co-authored-by: @copilot-pull-request-reviewer
70 lines
1.9 KiB
Python
70 lines
1.9 KiB
Python
"""Shared utility functions for Unraid MCP tools."""
|
|
|
|
from typing import Any
|
|
|
|
|
|
def safe_get(data: dict[str, Any], *keys: str, default: Any = None) -> Any:
|
|
"""Safely traverse nested dict keys, handling None intermediates.
|
|
|
|
Args:
|
|
data: The root dictionary to traverse.
|
|
*keys: Sequence of keys to follow.
|
|
default: Value to return if any key is missing or None.
|
|
|
|
Returns:
|
|
The value at the end of the key chain, or default if unreachable.
|
|
Explicit ``None`` values at the final key also return ``default``.
|
|
"""
|
|
current = data
|
|
for key in keys:
|
|
if not isinstance(current, dict):
|
|
return default
|
|
current = current.get(key)
|
|
return current if current is not None else default
|
|
|
|
|
|
def format_bytes(bytes_value: int | None) -> str:
|
|
"""Format byte values into human-readable sizes.
|
|
|
|
Args:
|
|
bytes_value: Number of bytes, or None.
|
|
|
|
Returns:
|
|
Human-readable string like "1.00 GB" or "N/A" if input is None/invalid.
|
|
"""
|
|
if bytes_value is None:
|
|
return "N/A"
|
|
try:
|
|
value = float(int(bytes_value))
|
|
except (ValueError, TypeError):
|
|
return "N/A"
|
|
for unit in ["B", "KB", "MB", "GB", "TB", "PB"]:
|
|
if value < 1024.0:
|
|
return f"{value:.2f} {unit}"
|
|
value /= 1024.0
|
|
return f"{value:.2f} EB"
|
|
|
|
|
|
def format_kb(k: Any) -> str:
|
|
"""Format kilobyte values into human-readable sizes.
|
|
|
|
Args:
|
|
k: Number of kilobytes, or None.
|
|
|
|
Returns:
|
|
Human-readable string like "1.00 GB" or "N/A" if input is None/invalid.
|
|
"""
|
|
if k is None:
|
|
return "N/A"
|
|
try:
|
|
k = int(k)
|
|
except (ValueError, TypeError):
|
|
return "N/A"
|
|
if k >= 1024 * 1024 * 1024:
|
|
return f"{k / (1024 * 1024 * 1024):.2f} TB"
|
|
if k >= 1024 * 1024:
|
|
return f"{k / (1024 * 1024):.2f} GB"
|
|
if k >= 1024:
|
|
return f"{k / 1024:.2f} MB"
|
|
return f"{k:.2f} KB"
|