diff --git a/pkgs/clan-app/clan_app/api/api_bridge.py b/pkgs/clan-app/clan_app/api/api_bridge.py index 52d8ab913..a29e18bfe 100644 --- a/pkgs/clan-app/clan_app/api/api_bridge.py +++ b/pkgs/clan-app/clan_app/api/api_bridge.py @@ -1,16 +1,15 @@ import logging import threading -from abc import ABC, abstractmethod from contextlib import ExitStack -from dataclasses import dataclass, field -from typing import TYPE_CHECKING, Any +from dataclasses import dataclass +from typing import TYPE_CHECKING, Any, Protocol from clan_lib.api import ApiError, ApiResponse, ErrorDataClass from clan_lib.api.tasks import WebThread from clan_lib.async_run import set_current_thread_opkey, set_should_cancel if TYPE_CHECKING: - from .middleware import Middleware + from clan_app.middleware.base import Middleware log = logging.getLogger(__name__) @@ -30,20 +29,17 @@ class BackendResponse: _op_key: str -@dataclass -class ApiBridge(ABC): +class ApiBridge(Protocol): """Generic interface for API bridges that can handle method calls from different sources.""" middleware_chain: tuple["Middleware", ...] - threads: dict[str, WebThread] = field(default_factory=dict) + threads: dict[str, WebThread] - @abstractmethod - def send_api_response(self, response: BackendResponse) -> None: - """Send response back to the client.""" + def send_api_response(self, response: BackendResponse) -> None: ... def process_request(self, request: BackendRequest) -> None: """Process an API request through the middleware chain.""" - from .middleware import MiddlewareContext # noqa: PLC0415 + from clan_app.middleware.base import MiddlewareContext # noqa: PLC0415 with ExitStack() as stack: context = MiddlewareContext( diff --git a/pkgs/clan-app/clan_app/api/middleware.py b/pkgs/clan-app/clan_app/api/middleware.py new file mode 100644 index 000000000..78cc138e0 --- /dev/null +++ b/pkgs/clan-app/clan_app/api/middleware.py @@ -0,0 +1,20 @@ +"""Compatibility wrapper for relocated middleware components. + +This module preserves the legacy import path ``clan_app.api.middleware`` while +the actual middleware implementations now live in ``clan_app.middleware``. +""" + +from __future__ import annotations + +from warnings import warn + +import clan_app.middleware as _middleware +from clan_app.middleware import * # noqa: F403 + +warn( + "clan_app.api.middleware is deprecated; use clan_app.middleware instead", + DeprecationWarning, + stacklevel=2, +) + +__all__ = _middleware.__all__ diff --git a/pkgs/clan-app/clan_app/app.py b/pkgs/clan-app/clan_app/app.py index 1da360cbf..3640cbe78 100644 --- a/pkgs/clan-app/clan_app/app.py +++ b/pkgs/clan-app/clan_app/app.py @@ -12,13 +12,13 @@ from clan_lib.log_manager import LogGroupConfig, LogManager from clan_lib.log_manager import api as log_manager_api from clan_app.api.file_gtk import get_clan_folder, get_system_file -from clan_app.api.middleware import ( +from clan_app.deps.http.http_server import HttpApiServer +from clan_app.deps.webview.webview import Size, SizeHint, Webview +from clan_app.middleware import ( ArgumentParsingMiddleware, LoggingMiddleware, MethodExecutionMiddleware, ) -from clan_app.deps.http.http_server import HttpApiServer -from clan_app.deps.webview.webview import Size, SizeHint, Webview log = logging.getLogger(__name__) diff --git a/pkgs/clan-app/clan_app/deps/http/http_bridge.py b/pkgs/clan-app/clan_app/deps/http/http_bridge.py index a6a3ea8fa..287f20459 100644 --- a/pkgs/clan-app/clan_app/deps/http/http_bridge.py +++ b/pkgs/clan-app/clan_app/deps/http/http_bridge.py @@ -21,7 +21,7 @@ from clan_lib.async_run import ( from clan_app.api.api_bridge import ApiBridge, BackendRequest, BackendResponse if TYPE_CHECKING: - from clan_app.api.middleware import Middleware + from clan_app.middleware.base import Middleware log = logging.getLogger(__name__) diff --git a/pkgs/clan-app/clan_app/deps/http/http_server.py b/pkgs/clan-app/clan_app/deps/http/http_server.py index 764e79789..ac374428c 100644 --- a/pkgs/clan-app/clan_app/deps/http/http_server.py +++ b/pkgs/clan-app/clan_app/deps/http/http_server.py @@ -8,7 +8,7 @@ from clan_lib.api import MethodRegistry from clan_lib.api.tasks import WebThread if TYPE_CHECKING: - from clan_app.api.middleware import Middleware + from clan_app.middleware.base import Middleware from .http_bridge import HttpBridge diff --git a/pkgs/clan-app/clan_app/deps/http/test_http_api.py b/pkgs/clan-app/clan_app/deps/http/test_http_api.py index 68e5841a7..d7fe43452 100644 --- a/pkgs/clan-app/clan_app/deps/http/test_http_api.py +++ b/pkgs/clan-app/clan_app/deps/http/test_http_api.py @@ -10,11 +10,11 @@ import pytest from clan_lib.api import MethodRegistry, tasks from clan_lib.async_run import is_async_cancelled -from clan_app.api.middleware import ( +from clan_app.deps.http.http_server import HttpApiServer +from clan_app.middleware import ( ArgumentParsingMiddleware, MethodExecutionMiddleware, ) -from clan_app.deps.http.http_server import HttpApiServer log = logging.getLogger(__name__) diff --git a/pkgs/clan-app/clan_app/deps/webview/webview.py b/pkgs/clan-app/clan_app/deps/webview/webview.py index 96f27215c..4e2f38114 100644 --- a/pkgs/clan-app/clan_app/deps/webview/webview.py +++ b/pkgs/clan-app/clan_app/deps/webview/webview.py @@ -19,7 +19,7 @@ from ._webview_ffi import ( from .webview_bridge import WebviewBridge if TYPE_CHECKING: - from clan_app.api.middleware import Middleware + from clan_app.middleware.base import Middleware log = logging.getLogger(__name__) diff --git a/pkgs/clan-app/clan_app/deps/webview/webview_bridge.py b/pkgs/clan-app/clan_app/deps/webview/webview_bridge.py index e818f2295..130e689b7 100644 --- a/pkgs/clan-app/clan_app/deps/webview/webview_bridge.py +++ b/pkgs/clan-app/clan_app/deps/webview/webview_bridge.py @@ -1,6 +1,6 @@ import json import logging -from dataclasses import dataclass +from dataclasses import dataclass, field from typing import TYPE_CHECKING from clan_lib.api import dataclass_to_dict @@ -9,6 +9,8 @@ from clan_lib.api.tasks import WebThread from clan_app.api.api_bridge import ApiBridge, BackendRequest, BackendResponse if TYPE_CHECKING: + from clan_app.middleware.base import Middleware + from .webview import Webview log = logging.getLogger(__name__) @@ -19,7 +21,8 @@ class WebviewBridge(ApiBridge): """Webview-specific implementation of the API bridge.""" webview: "Webview" - threads: dict[str, WebThread] # Inherited from ApiBridge + middleware_chain: tuple["Middleware", ...] + threads: dict[str, WebThread] = field(default_factory=dict) def send_api_response(self, response: BackendResponse) -> None: """Send response back to the webview client.""" diff --git a/pkgs/clan-app/clan_app/api/middleware/__init__.py b/pkgs/clan-app/clan_app/middleware/__init__.py similarity index 84% rename from pkgs/clan-app/clan_app/api/middleware/__init__.py rename to pkgs/clan-app/clan_app/middleware/__init__.py index 824c42ff1..7538488ed 100644 --- a/pkgs/clan-app/clan_app/api/middleware/__init__.py +++ b/pkgs/clan-app/clan_app/middleware/__init__.py @@ -1,4 +1,4 @@ -"""Middleware components for the webview API bridge.""" +"""Middleware components shared by API bridge implementations.""" from .argument_parsing import ArgumentParsingMiddleware from .base import Middleware, MiddlewareContext diff --git a/pkgs/clan-app/clan_app/api/middleware/argument_parsing.py b/pkgs/clan-app/clan_app/middleware/argument_parsing.py similarity index 100% rename from pkgs/clan-app/clan_app/api/middleware/argument_parsing.py rename to pkgs/clan-app/clan_app/middleware/argument_parsing.py diff --git a/pkgs/clan-app/clan_app/api/middleware/base.py b/pkgs/clan-app/clan_app/middleware/base.py similarity index 100% rename from pkgs/clan-app/clan_app/api/middleware/base.py rename to pkgs/clan-app/clan_app/middleware/base.py diff --git a/pkgs/clan-app/clan_app/api/middleware/logging.py b/pkgs/clan-app/clan_app/middleware/logging.py similarity index 100% rename from pkgs/clan-app/clan_app/api/middleware/logging.py rename to pkgs/clan-app/clan_app/middleware/logging.py diff --git a/pkgs/clan-app/clan_app/api/middleware/method_execution.py b/pkgs/clan-app/clan_app/middleware/method_execution.py similarity index 100% rename from pkgs/clan-app/clan_app/api/middleware/method_execution.py rename to pkgs/clan-app/clan_app/middleware/method_execution.py