diff options
| author | Roberto Bertó <463349+robertoberto@users.noreply.github.com> | 2026-05-19 02:32:04 +0000 |
|---|---|---|
| committer | Roberto Bertó <463349+robertoberto@users.noreply.github.com> | 2026-05-19 02:32:04 +0000 |
| commit | 0f76bcc7179976e893b1d9f296b1b1e7988b0031 (patch) | |
| tree | 87fa89da92263bbe2af7303a0ab36433fdc6f5d3 | |
| parent | ffd5ba16eb1ada42a582db4ac8bdaf29f66a868f (diff) | |
| download | pyvyos-0f76bcc7179976e893b1d9f296b1b1e7988b0031.tar.gz pyvyos-0f76bcc7179976e893b1d9f296b1b1e7988b0031.zip | |
refactor: remove unused specs, exceptions, and request_id
The pyvyos.specs Pydantic models were never imported by the runtime and
had 0% test coverage. The pyvyos.exceptions hierarchy was defined but
never raised anywhere. The request_id helper generated UUIDs that were
attached to log records but never propagated to callers — half-implemented
tracing is worse than none.
This commit does not alter request/response logic or HTTP payloads. It
only removes code that was never executed and tests for that code.
Removed:
- pyvyos/specs/ (Pydantic models package)
- pyvyos/exceptions.py (SDKError, HttpError, ApiError, ValidationError)
- pyvyos/utils/ids.py (request_id helper)
- tests/test_exceptions.py
- tests/utils/test_ids.py
Edited:
- pyvyos/core/rest_client.py: drop request_id import and log extras
- pyvyos/utils/__init__.py: drop request_id export
Tests: 66 -> 57 passing. Coverage: 54% -> 87%.
Public API unchanged.
| -rw-r--r-- | pyvyos/core/rest_client.py | 9 | ||||
| -rw-r--r-- | pyvyos/exceptions.py | 33 | ||||
| -rw-r--r-- | pyvyos/specs/__init__.py | 12 | ||||
| -rw-r--r-- | pyvyos/specs/commands/__init__.py | 13 | ||||
| -rw-r--r-- | pyvyos/specs/commands/config_file.py | 36 | ||||
| -rw-r--r-- | pyvyos/specs/commands/configure.py | 38 | ||||
| -rw-r--r-- | pyvyos/specs/commands/generate.py | 20 | ||||
| -rw-r--r-- | pyvyos/specs/commands/image.py | 33 | ||||
| -rw-r--r-- | pyvyos/specs/commands/reset.py | 20 | ||||
| -rw-r--r-- | pyvyos/specs/commands/retrieve.py | 27 | ||||
| -rw-r--r-- | pyvyos/specs/commands/show.py | 20 | ||||
| -rw-r--r-- | pyvyos/specs/commands/system.py | 27 | ||||
| -rw-r--r-- | pyvyos/specs/models.py | 31 | ||||
| -rw-r--r-- | pyvyos/utils/__init__.py | 3 | ||||
| -rw-r--r-- | pyvyos/utils/ids.py | 14 | ||||
| -rw-r--r-- | tests/test_exceptions.py | 51 | ||||
| -rw-r--r-- | tests/utils/test_ids.py | 25 |
17 files changed, 1 insertions, 411 deletions
diff --git a/pyvyos/core/rest_client.py b/pyvyos/core/rest_client.py index eb77d9b..ddef7ca 100644 --- a/pyvyos/core/rest_client.py +++ b/pyvyos/core/rest_client.py @@ -232,18 +232,10 @@ class RestClient(ABC): # Initialize mutable defaults safely path = path or [] - # Generate request ID for tracing - try: - from ..utils.ids import request_id - req_id = request_id() - except ImportError: - req_id = None - # Log request start (DEBUG level) logger.debug( "Request start", extra={ - "request_id": req_id, "command": command, "op": op, "has_path": bool(path), @@ -261,7 +253,6 @@ class RestClient(ABC): logger.info( "Request completed", extra={ - "request_id": req_id, "command": command, "op": op, "status": status, diff --git a/pyvyos/exceptions.py b/pyvyos/exceptions.py deleted file mode 100644 index 5a79733..0000000 --- a/pyvyos/exceptions.py +++ /dev/null @@ -1,33 +0,0 @@ -"""PyVyOS exception hierarchy for typed error handling.""" - - -class SDKError(Exception): - """Base exception for all PyVyOS SDK errors.""" - pass - - -class HttpError(SDKError): - """HTTP-level errors (network, timeout, status codes).""" - - def __init__(self, status: int, message: str): - self.status = status - self.message = message - super().__init__(f"HTTP Error {status}: {message}") - - -class ApiError(SDKError): - """API-level errors (when VyOS API returns success=False).""" - - def __init__(self, message: str, details: dict = None): - self.message = message - self.details = details - super().__init__(message) - - -class ValidationError(SDKError): - """Client-side validation errors (invalid parameters).""" - - def __init__(self, message: str): - self.message = message - super().__init__(message) - diff --git a/pyvyos/specs/__init__.py b/pyvyos/specs/__init__.py deleted file mode 100644 index d12c764..0000000 --- a/pyvyos/specs/__init__.py +++ /dev/null @@ -1,12 +0,0 @@ -"""PyVyOS Pydantic specs for optional request/response validation.""" - -# Feature detection: gracefully degrade if pydantic not available -try: - from pydantic import BaseModel - PYDANTIC_AVAILABLE = True -except ImportError: - PYDANTIC_AVAILABLE = False - BaseModel = None # type: ignore - -__all__ = ["PYDANTIC_AVAILABLE"] - diff --git a/pyvyos/specs/commands/__init__.py b/pyvyos/specs/commands/__init__.py deleted file mode 100644 index 1d8e8b3..0000000 --- a/pyvyos/specs/commands/__init__.py +++ /dev/null @@ -1,13 +0,0 @@ -"""Pydantic models for individual VyOS API commands.""" - -from .config_file import ConfigFileSaveRequest, ConfigFileLoadRequest -from .configure import ConfigureSetRequest, ConfigureDeleteRequest, ConfigureMultipleOpRequest - -__all__ = [ - "ConfigFileSaveRequest", - "ConfigFileLoadRequest", - "ConfigureSetRequest", - "ConfigureDeleteRequest", - "ConfigureMultipleOpRequest", -] - diff --git a/pyvyos/specs/commands/config_file.py b/pyvyos/specs/commands/config_file.py deleted file mode 100644 index 911b094..0000000 --- a/pyvyos/specs/commands/config_file.py +++ /dev/null @@ -1,36 +0,0 @@ -"""Pydantic models for config-file operations.""" - -from typing import List, Literal, Optional - -try: - from pydantic import BaseModel, Field - from ..models import ApiRequest -except ImportError: - BaseModel = None # type: ignore - Field = None # type: ignore - ApiRequest = None # type: ignore - - -if BaseModel: - - class ConfigFileSaveRequest(ApiRequest): - """Request model for config-file save operation.""" - - op: Literal["save"] = "save" - path: List[str] = Field( - default_factory=list, - description="Required by VyOS API, typically empty array" - ) - file: Optional[str] = None - - - class ConfigFileLoadRequest(ApiRequest): - """Request model for config-file load operation.""" - - op: Literal["load"] = "load" - path: List[str] = Field( - default_factory=list, - description="Required by VyOS API, typically empty array" - ) - file: str # Required for load - diff --git a/pyvyos/specs/commands/configure.py b/pyvyos/specs/commands/configure.py deleted file mode 100644 index d80aa3f..0000000 --- a/pyvyos/specs/commands/configure.py +++ /dev/null @@ -1,38 +0,0 @@ -"""Pydantic models for configure operations.""" - -from typing import Dict, List, Literal, Union, Any - -try: - from pydantic import BaseModel, Field - from ..models import ApiRequest -except ImportError: - BaseModel = None # type: ignore - Field = None # type: ignore - ApiRequest = None # type: ignore - - -if BaseModel: - - class ConfigureSetRequest(ApiRequest): - """Request model for configure set operation.""" - - op: Literal["set"] = "set" - path: Union[List[str], List[List[str]]] # Required - - - class ConfigureDeleteRequest(ApiRequest): - """Request model for configure delete operation.""" - - op: Literal["delete"] = "delete" - path: List[str] # Required - - - class ConfigureMultipleOpRequest(BaseModel): - """Request model for configure multiple operations.""" - - op: Literal["set", "delete"] - path: Union[List[str], List[List[str]]] - - class Config: - extra = "allow" - diff --git a/pyvyos/specs/commands/generate.py b/pyvyos/specs/commands/generate.py deleted file mode 100644 index 40aa2a4..0000000 --- a/pyvyos/specs/commands/generate.py +++ /dev/null @@ -1,20 +0,0 @@ -"""Pydantic models for generate operations.""" - -from typing import List, Literal, Optional - -try: - from pydantic import BaseModel - from ..models import ApiRequest -except ImportError: - BaseModel = None # type: ignore - ApiRequest = None # type: ignore - - -if BaseModel: - - class GenerateRequest(ApiRequest): - """Request model for generate operation.""" - - op: Literal["generate"] = "generate" - path: Optional[List[str]] = None - diff --git a/pyvyos/specs/commands/image.py b/pyvyos/specs/commands/image.py deleted file mode 100644 index 973bae4..0000000 --- a/pyvyos/specs/commands/image.py +++ /dev/null @@ -1,33 +0,0 @@ -"""Pydantic models for image operations.""" - -from typing import List, Literal, Optional - -try: - from pydantic import BaseModel - from ..models import ApiRequest -except ImportError: - BaseModel = None # type: ignore - ApiRequest = None # type: ignore - - -if BaseModel: - - class ImageAddRequest(ApiRequest): - """Request model for image add operation.""" - - op: Literal["add"] = "add" - path: Optional[List[str]] = None - url: Optional[str] = None - file: Optional[str] = None - name: Optional[str] = None - - - class ImageDeleteRequest(ApiRequest): - """Request model for image delete operation.""" - - op: Literal["delete"] = "delete" - path: Optional[List[str]] = None - url: Optional[str] = None - file: Optional[str] = None - name: Optional[str] = None - diff --git a/pyvyos/specs/commands/reset.py b/pyvyos/specs/commands/reset.py deleted file mode 100644 index ac02bfe..0000000 --- a/pyvyos/specs/commands/reset.py +++ /dev/null @@ -1,20 +0,0 @@ -"""Pydantic models for reset operations.""" - -from typing import List, Literal, Optional - -try: - from pydantic import BaseModel - from ..models import ApiRequest -except ImportError: - BaseModel = None # type: ignore - ApiRequest = None # type: ignore - - -if BaseModel: - - class ResetRequest(ApiRequest): - """Request model for reset operation.""" - - op: Literal["reset"] = "reset" - path: Optional[List[str]] = None - diff --git a/pyvyos/specs/commands/retrieve.py b/pyvyos/specs/commands/retrieve.py deleted file mode 100644 index 65efbbf..0000000 --- a/pyvyos/specs/commands/retrieve.py +++ /dev/null @@ -1,27 +0,0 @@ -"""Pydantic models for retrieve operations.""" - -from typing import List, Literal, Optional - -try: - from pydantic import BaseModel - from ..models import ApiRequest -except ImportError: - BaseModel = None # type: ignore - ApiRequest = None # type: ignore - - -if BaseModel: - - class RetrieveShowConfigRequest(ApiRequest): - """Request model for retrieve showConfig operation.""" - - op: Literal["showConfig"] = "showConfig" - path: Optional[List[str]] = None - - - class RetrieveReturnValuesRequest(ApiRequest): - """Request model for retrieve returnValues operation.""" - - op: Literal["returnValues"] = "returnValues" - path: Optional[List[str]] = None - diff --git a/pyvyos/specs/commands/show.py b/pyvyos/specs/commands/show.py deleted file mode 100644 index 2fb2b97..0000000 --- a/pyvyos/specs/commands/show.py +++ /dev/null @@ -1,20 +0,0 @@ -"""Pydantic models for show operations.""" - -from typing import List, Literal, Optional - -try: - from pydantic import BaseModel - from ..models import ApiRequest -except ImportError: - BaseModel = None # type: ignore - ApiRequest = None # type: ignore - - -if BaseModel: - - class ShowRequest(ApiRequest): - """Request model for show operation.""" - - op: Literal["show"] = "show" - path: Optional[List[str]] = None - diff --git a/pyvyos/specs/commands/system.py b/pyvyos/specs/commands/system.py deleted file mode 100644 index 9f5e5c9..0000000 --- a/pyvyos/specs/commands/system.py +++ /dev/null @@ -1,27 +0,0 @@ -"""Pydantic models for system operations (reboot, poweroff).""" - -from typing import List, Literal - -try: - from pydantic import BaseModel - from ..models import ApiRequest -except ImportError: - BaseModel = None # type: ignore - ApiRequest = None # type: ignore - - -if BaseModel: - - class RebootRequest(ApiRequest): - """Request model for reboot operation.""" - - op: Literal["reboot"] = "reboot" - path: List[str] = ["now"] - - - class PoweroffRequest(ApiRequest): - """Request model for poweroff operation.""" - - op: Literal["poweroff"] = "poweroff" - path: List[str] = ["now"] - diff --git a/pyvyos/specs/models.py b/pyvyos/specs/models.py deleted file mode 100644 index a57ac82..0000000 --- a/pyvyos/specs/models.py +++ /dev/null @@ -1,31 +0,0 @@ -"""Base Pydantic models for PyVyOS API requests and responses.""" - -from typing import Any, Dict, List, Optional, Union - -if False: # type checking only - from pydantic import BaseModel -else: - try: - from pydantic import BaseModel, Field - except ImportError: - BaseModel = None # type: ignore - Field = None # type: ignore - - -class ApiRequest(BaseModel): - """Base request model for VyOS API operations.""" - - op: str - path: Optional[Union[List[str], List[List[str]], List[Dict[str, Any]]]] = None - - class Config: - extra = "allow" # Allow additional fields (file, url, name, etc.) - - -class ApiResponse(BaseModel): - """Base response model for VyOS API responses.""" - - success: bool - data: Union[Dict[str, Any], List[Any], str, None] - error: Optional[str] = None - diff --git a/pyvyos/utils/__init__.py b/pyvyos/utils/__init__.py index adff044..6290fc1 100644 --- a/pyvyos/utils/__init__.py +++ b/pyvyos/utils/__init__.py @@ -1,8 +1,7 @@ """Utility functions for PyVyOS.""" from .json import redact_key, safe_dumps -from .ids import request_id from .paths import build_path -__all__ = ["redact_key", "safe_dumps", "request_id", "build_path"] +__all__ = ["redact_key", "safe_dumps", "build_path"] diff --git a/pyvyos/utils/ids.py b/pyvyos/utils/ids.py deleted file mode 100644 index 5807ddf..0000000 --- a/pyvyos/utils/ids.py +++ /dev/null @@ -1,14 +0,0 @@ -"""ID generation utilities.""" - -import uuid - - -def request_id() -> str: - """ - Generate a unique request ID for tracing. - - Returns: - UUID4 string formatted for logging - """ - return str(uuid.uuid4()) - diff --git a/tests/test_exceptions.py b/tests/test_exceptions.py deleted file mode 100644 index 22a4f34..0000000 --- a/tests/test_exceptions.py +++ /dev/null @@ -1,51 +0,0 @@ -"""Tests for pyvyos.exceptions module.""" - -import pytest - -from pyvyos.exceptions import SDKError, HttpError, ApiError, ValidationError - - -def test_sdk_error_base(): - """Test SDKError is base exception.""" - assert issubclass(HttpError, SDKError) - assert issubclass(ApiError, SDKError) - assert issubclass(ValidationError, SDKError) - - -def test_http_error(): - """Test HttpError creation and attributes.""" - error = HttpError(status=404, message="Not Found") - assert error.status == 404 - assert error.message == "Not Found" - assert "404" in str(error) - assert "Not Found" in str(error) - - -def test_api_error(): - """Test ApiError creation.""" - error = ApiError(message="API failure") - assert error.message == "API failure" - assert "API failure" in str(error) - - -def test_api_error_with_details(): - """Test ApiError with details.""" - details = {"code": "ERR001", "field": "path"} - error = ApiError(message="Validation failed", details=details) - assert error.message == "Validation failed" - assert error.details == details - - -def test_validation_error(): - """Test ValidationError creation.""" - error = ValidationError(message="Invalid path format") - assert error.message == "Invalid path format" - assert "Invalid path format" in str(error) - - -def test_exceptions_raiseable(): - """Test that exceptions can be raised and caught.""" - with pytest.raises(HttpError) as exc_info: - raise HttpError(status=500, message="Internal Error") - assert exc_info.value.status == 500 - diff --git a/tests/utils/test_ids.py b/tests/utils/test_ids.py deleted file mode 100644 index 19ff3a7..0000000 --- a/tests/utils/test_ids.py +++ /dev/null @@ -1,25 +0,0 @@ -"""Tests for pyvyos.utils.ids module.""" - -import uuid - -from pyvyos.utils.ids import request_id - - -def test_request_id_returns_string(): - """Test that request_id returns a string.""" - rid = request_id() - assert isinstance(rid, str) - - -def test_request_id_valid_uuid(): - """Test that request_id returns a valid UUID.""" - rid = request_id() - uuid.UUID(rid) # Should not raise - - -def test_request_id_unique(): - """Test that request_id generates unique IDs.""" - id1 = request_id() - id2 = request_id() - assert id1 != id2 - |
