Cat Feeder  1.0.0
The Cat feeder project
Loading...
Searching...
No Matches
redis_args.py
Go to the documentation of this file.
1r"""
2# +==== BEGIN CatFeeder =================+
3# LOGO:
4# ..............(..../\
5# ...............)..(.')
6# ..............(../..)
7# ...............\‍(__)|
8# Inspired by Joan Stark
9# source https://www.asciiart.eu/
10# animals/cats
11# /STOP
12# PROJECT: CatFeeder
13# FILE: redis_args.py
14# CREATION DATE: 15-11-2025
15# LAST Modified: 14:51:26 19-12-2025
16# DESCRIPTION:
17# This is the backend server in charge of making the actual website work.
18# /STOP
19# COPYRIGHT: (c) Cat Feeder
20# PURPOSE: This is the file that will contain the dataclass with the optional redis initialisation information.
21# // AR
22# +==== END CatFeeder =================+
23"""
24
25import os
26from platform import system
27from dataclasses import dataclass
28from typing import Mapping, List, Optional, Callable, Union, Type, TYPE_CHECKING
29
30from redis import ConnectionPool
31from redis.retry import Retry
32from redis.cache import CacheConfig, CacheInterface
33from redis.event import EventDispatcher
34from redis.backoff import ExponentialWithJitterBackoff
35from redis.credentials import CredentialProvider
36from redis.maint_notifications import MaintNotificationsConfig
37from redis.utils import get_lib_version
38
39from .redis_constants import (
40 REDIS_PASSWORD_KEY,
41 REDIS_SOCKET_DEFAULT,
42 REDIS_SOCKET_KEY,
43 REDIS_HOST_KEY,
44 REDIS_HOST_DEFAULT,
45 REDIS_PORT_KEY,
46 REDIS_PORT_DEFAULT,
47)
48
49if TYPE_CHECKING:
50 import ssl
51 import OpenSSL
52
53
54@dataclass
56 """
57 This is the config section to allow the server to easily set up the parameters for the redis instance.
58 """
59 host: str = "localhost"
60 port: int = 6379
61 db: int = 0
62 password: Optional[str] = None
63 socket_timeout: Optional[float] = None
64 socket_connect_timeout: Optional[float] = None
65 socket_keepalive: Optional[bool] = None
66 socket_keepalive_options: Optional[Mapping[int, Union[int, bytes]]] = None
67 connection_pool: Optional[ConnectionPool] = None
68 unix_socket_path: Optional[str] = None
69 encoding: str = "utf-8"
70 encoding_errors: str = "strict"
71 decode_responses: bool = False
72 retry_on_timeout: bool = False
73 retry: Retry = Retry(
74 backoff=ExponentialWithJitterBackoff(base=1, cap=10), retries=3
75 )
76 retry_on_error: Optional[List[Type[Exception]]] = None
77 ssl: bool = False
78 ssl_keyfile: Optional[str] = None
79 ssl_certfile: Optional[str] = None
80 ssl_cert_reqs: Union[str, "ssl.VerifyMode"] = "required"
81 ssl_include_verify_flags: Optional[List["ssl.VerifyFlags"]] = None
82 ssl_exclude_verify_flags: Optional[List["ssl.VerifyFlags"]] = None
83 ssl_ca_certs: Optional[str] = None
84 ssl_ca_path: Optional[str] = None
85 ssl_ca_data: Optional[str] = None
86 ssl_check_hostname: bool = True
87 ssl_password: Optional[str] = None
88 ssl_validate_ocsp: bool = False
89 ssl_validate_ocsp_stapled: bool = False
90 ssl_ocsp_context: Optional["OpenSSL.SSL.Context"] = None
91 ssl_ocsp_expected_cert: Optional[str] = None
92 ssl_min_version: Optional["ssl.TLSVersion"] = None
93 ssl_ciphers: Optional[str] = None
94 max_connections: Optional[int] = None
95 single_connection_client: bool = False
96 health_check_interval: int = 0
97 client_name: Optional[str] = None
98 lib_name: Optional[str] = "redis-py"
99 lib_version: Optional[str] = get_lib_version()
100 username: Optional[str] = None
101 redis_connect_func: Optional[Callable[[], None]] = None
102 credential_provider: Optional[CredentialProvider] = None
103 protocol: Optional[int] = 2
104 cache: Optional[CacheInterface] = None
105 cache_config: Optional[CacheConfig] = None
106 event_dispatcher: Optional[EventDispatcher] = None
107 maint_notifications_config: Optional[MaintNotificationsConfig] = None
108
109
110def build_redis_args() -> RedisArgs:
111 """Create and return a configured Redis client.
112
113 This function does not connect immediately; the client will establish
114 a connection upon first command execution.
115
116 Connection Priority:
117 1. Unix socket (if REDIS_SOCKET is set and file exists) - preferred for docker-compose
118 2. TCP connection (REDIS_HOST:REDIS_PORT) - fallback for CI/testing environments
119
120 Environment Variables:
121 - REDIS_SOCKET: Path to Unix socket (default: /run/redis/redis.sock)
122 - REDIS_HOST: Redis host for TCP connection (default: 127.0.0.1)
123 - REDIS_PORT: Redis port for TCP connection (default: 6379)
124 - REDIS_PASSWORD: Redis password (required)
125
126 Returns:
127 redis.Redis: Configured Redis client instance.
128 """
129 socket_path = os.getenv(REDIS_SOCKET_KEY, REDIS_SOCKET_DEFAULT)
130 redis_host = os.getenv(REDIS_HOST_KEY, REDIS_HOST_DEFAULT)
131
132 # Parse redis_port with validation
133 redis_port_str = os.getenv(REDIS_PORT_KEY, str(REDIS_PORT_DEFAULT))
134 try:
135 redis_port = int(redis_port_str)
136 if not (1 <= redis_port <= 65535):
137 raise ValueError(
138 f"Port must be between 1 and 65535, got {redis_port}")
139 except (ValueError, TypeError) as e:
140 raise ValueError(
141 f"Invalid REDIS_PORT value '{redis_port_str}': must be a valid integer between 1-65535"
142 ) from e
143
144 password = os.getenv(REDIS_PASSWORD_KEY)
145
146 # Determine connection method: socket (preferred) or TCP (fallback)
147 unix_socket_path = None
148 use_tcp = True
149
150 # Try Unix socket first (not on Windows, and only if socket file exists)
151 if system().lower() != "windows":
152 if os.path.exists(socket_path):
153 unix_socket_path = socket_path
154 use_tcp = False # Socket available, don't use TCP
155
156 # Build RedisArgs with appropriate connection method
157 if use_tcp:
158 # TCP connection for CI/testing or when socket unavailable
159 node: RedisArgs = RedisArgs(
160 host=redis_host,
161 port=redis_port,
162 password=password,
163 decode_responses=True
164 )
165 else:
166 # Unix socket connection for docker-compose
167 node: RedisArgs = RedisArgs(
168 unix_socket_path=unix_socket_path,
169 password=password,
170 decode_responses=True
171 )
172 return node