Coverage for seedboxsync/core/flask.py: 100%
50 statements
« prev ^ index » next coverage.py v7.15.2, created at 2026-07-26 17:46 +0000
« prev ^ index » next coverage.py v7.15.2, created at 2026-07-26 17:46 +0000
1#
2# Copyright (C) 2015-2026 Guillaume Kulakowski <guillaume@kulakowski.fr>
3#
4# For the full copyright and license information, please view the LICENSE
5# file that was distributed with this source code.
6#
7"""Flask core initer mobule."""
9from functools import cached_property
10from importlib import import_module
11from typing import Any, cast
12from flask import Flask, current_app
13from seedboxsync.core import Config
14from seedboxsync.core.exception import PingServiceError, SyncProtocoleError
15from seedboxsync.core.ping import AbstractPingClient
16from seedboxsync.core.sync import AbstractSyncClient
17from seedboxsync.core.taskmanager import Manager, task_manager
20class SeedboxSyncFlask(Flask):
21 """Flask application with SeedboxSync-specific configuration helpers."""
23 @property
24 def seedboxsync_config(self) -> dict[str, Any]:
25 """
26 Return the SeedboxSync configuration namespace.
28 Returns:
29 The SeedboxSync configuration with the namespace prefix removed
30 and keys converted to lowercase.
31 """
32 return self.config.get_namespace(Config.CONFIG_NAMESPACE)
34 @cached_property
35 def task_manager(self) -> Manager:
36 """
37 Return the task instance Manager.
39 Returns:
40 The task manager instance.
41 """
42 task_manager.init_app(self)
43 return task_manager
45 @cached_property
46 def sync(self) -> AbstractSyncClient:
47 """
48 Return the configured sync client instance.
50 Returns:
51 AbstractSyncClient: Client instance.
52 """
53 protocol = self.seedboxsync_config.get("seedbox_protocol", "")
54 client_class = protocol.title() + "Client"
56 self.logger.debug(f"Using SeedboxSync with sync ({protocol} / {client_class})")
58 # Load the client module dynamically
59 try:
60 client_module = import_module("seedboxsync.core.sync.client." + protocol)
61 except ImportError as exc:
62 raise SyncProtocoleError(f"Unsupported protocol: {protocol}: {exc!s}") from exc
64 # Get the client class from the module
65 try:
66 transfer_client = getattr(client_module, client_class)
67 except AttributeError as exc:
68 raise SyncProtocoleError(f'Unsupported protocol module! No class "{client_class}" in module "seedboxsync.core.sync.{protocol}_client"') from exc
70 # Instantiate the client
71 try:
72 sync_client = transfer_client()
73 except Exception as exc:
74 raise ConnectionError(f"{exc!s}\nFailed to establish a connection.") from exc
76 return sync_client # type: ignore[no-any-return]
78 @cached_property
79 def ping(self) -> AbstractPingClient:
80 """
81 Return the configured ping client instance.
83 Returns:
84 AbstractPingClient: Configured ping client instance.
85 """
86 ping_service = "healthchecks"
87 client_class = ping_service.title()
89 self.logger.debug(f"Using SeedboxSync with ping ({ping_service} / {client_class})")
91 # Load the client module dynamically
92 try:
93 client_module = import_module("seedboxsync.core.ping.client." + ping_service)
94 except ImportError as exc:
95 raise PingServiceError(f"Unsupported ping service: {ping_service}: {exc!s}") from exc
97 # Get the client class from the module
98 try:
99 ping_client = getattr(client_module, client_class)
100 except AttributeError as exc:
101 raise PingServiceError(
102 f'Unsupported ping service module! No class "{client_class}" in module "seedboxsync.core.ping.{ping_service}client"'
103 ) from exc
105 return ping_client() # type: ignore[no-any-return]
108seedboxsync_current_app = cast(SeedboxSyncFlask, current_app)