From ee0f111fc9cf00eb2244ed228cd4f71d03a1610f Mon Sep 17 00:00:00 2001 From: Qubasa Date: Tue, 16 Sep 2025 11:48:59 +0200 Subject: [PATCH 1/2] clan-app: change ApiBridge ABC class to Protocol --- pkgs/clan-app/clan_app/api/api_bridge.py | 14 +++++--------- .../clan_app/deps/webview/webview_bridge.py | 7 +++++-- 2 files changed, 10 insertions(+), 11 deletions(-) diff --git a/pkgs/clan-app/clan_app/api/api_bridge.py b/pkgs/clan-app/clan_app/api/api_bridge.py index 52d8ab913..2c7c0ab17 100644 --- a/pkgs/clan-app/clan_app/api/api_bridge.py +++ b/pkgs/clan-app/clan_app/api/api_bridge.py @@ -1,9 +1,8 @@ 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 @@ -30,16 +29,13 @@ 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.""" 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..76a5de54a 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.api.middleware 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.""" From 864b13101071a46b538e2a095095e42764a00bbb Mon Sep 17 00:00:00 2001 From: Qubasa Date: Tue, 16 Sep 2025 14:51:11 +0200 Subject: [PATCH 2/2] clan-app: Move middleware to it's own folder --- pkgs/clan-app/clan_app/api/api_bridge.py | 4 ++-- pkgs/clan-app/clan_app/api/middleware.py | 20 +++++++++++++++++++ pkgs/clan-app/clan_app/app.py | 6 +++--- .../clan_app/deps/http/http_bridge.py | 2 +- .../clan_app/deps/http/http_server.py | 2 +- .../clan_app/deps/http/test_http_api.py | 4 ++-- .../clan-app/clan_app/deps/webview/webview.py | 2 +- .../clan_app/deps/webview/webview_bridge.py | 2 +- .../clan_app/{api => }/middleware/__init__.py | 2 +- .../{api => }/middleware/argument_parsing.py | 0 .../clan_app/{api => }/middleware/base.py | 0 .../clan_app/{api => }/middleware/logging.py | 0 .../{api => }/middleware/method_execution.py | 0 13 files changed, 32 insertions(+), 12 deletions(-) create mode 100644 pkgs/clan-app/clan_app/api/middleware.py rename pkgs/clan-app/clan_app/{api => }/middleware/__init__.py (84%) rename pkgs/clan-app/clan_app/{api => }/middleware/argument_parsing.py (100%) rename pkgs/clan-app/clan_app/{api => }/middleware/base.py (100%) rename pkgs/clan-app/clan_app/{api => }/middleware/logging.py (100%) rename pkgs/clan-app/clan_app/{api => }/middleware/method_execution.py (100%) diff --git a/pkgs/clan-app/clan_app/api/api_bridge.py b/pkgs/clan-app/clan_app/api/api_bridge.py index 2c7c0ab17..a29e18bfe 100644 --- a/pkgs/clan-app/clan_app/api/api_bridge.py +++ b/pkgs/clan-app/clan_app/api/api_bridge.py @@ -9,7 +9,7 @@ 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__) @@ -39,7 +39,7 @@ class ApiBridge(Protocol): 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 76a5de54a..130e689b7 100644 --- a/pkgs/clan-app/clan_app/deps/webview/webview_bridge.py +++ b/pkgs/clan-app/clan_app/deps/webview/webview_bridge.py @@ -9,7 +9,7 @@ from clan_lib.api.tasks import WebThread 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 from .webview import Webview 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