hypern 0.3.1__cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.whl → 0.3.3__cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- hypern/application.py +115 -16
- hypern/args_parser.py +34 -1
- hypern/caching/__init__.py +6 -0
- hypern/caching/backend.py +31 -0
- hypern/caching/redis_backend.py +200 -2
- hypern/caching/strategies.py +164 -71
- hypern/config.py +97 -0
- hypern/db/addons/__init__.py +5 -0
- hypern/db/addons/sqlalchemy/__init__.py +71 -0
- hypern/db/sql/__init__.py +13 -179
- hypern/db/sql/field.py +606 -0
- hypern/db/sql/model.py +116 -0
- hypern/db/sql/query.py +879 -0
- hypern/exceptions.py +10 -0
- hypern/gateway/__init__.py +6 -0
- hypern/gateway/aggregator.py +32 -0
- hypern/gateway/gateway.py +41 -0
- hypern/gateway/proxy.py +60 -0
- hypern/gateway/service.py +52 -0
- hypern/hypern.cpython-311-i386-linux-gnu.so +0 -0
- hypern/hypern.pyi +53 -18
- hypern/middleware/__init__.py +14 -2
- hypern/middleware/base.py +9 -14
- hypern/middleware/cache.py +177 -0
- hypern/middleware/compress.py +78 -0
- hypern/middleware/cors.py +6 -3
- hypern/middleware/limit.py +5 -4
- hypern/middleware/security.py +21 -16
- hypern/processpool.py +16 -32
- hypern/routing/__init__.py +2 -1
- hypern/routing/dispatcher.py +4 -0
- hypern/routing/queue.py +175 -0
- {hypern-0.3.1.dist-info → hypern-0.3.3.dist-info}/METADATA +3 -1
- hypern-0.3.3.dist-info/RECORD +84 -0
- hypern/caching/base/__init__.py +0 -8
- hypern/caching/base/backend.py +0 -3
- hypern/caching/base/key_maker.py +0 -8
- hypern/caching/cache_manager.py +0 -56
- hypern/caching/cache_tag.py +0 -10
- hypern/caching/custom_key_maker.py +0 -11
- hypern-0.3.1.dist-info/RECORD +0 -76
- /hypern/db/{sql/addons → addons/sqlalchemy/fields}/__init__.py +0 -0
- /hypern/db/{sql/addons → addons/sqlalchemy/fields}/color.py +0 -0
- /hypern/db/{sql/addons → addons/sqlalchemy/fields}/daterange.py +0 -0
- /hypern/db/{sql/addons → addons/sqlalchemy/fields}/datetime.py +0 -0
- /hypern/db/{sql/addons → addons/sqlalchemy/fields}/encrypted.py +0 -0
- /hypern/db/{sql/addons → addons/sqlalchemy/fields}/password.py +0 -0
- /hypern/db/{sql/addons → addons/sqlalchemy/fields}/ts_vector.py +0 -0
- /hypern/db/{sql/addons → addons/sqlalchemy/fields}/unicode.py +0 -0
- /hypern/db/{sql → addons/sqlalchemy}/repository.py +0 -0
- {hypern-0.3.1.dist-info → hypern-0.3.3.dist-info}/WHEEL +0 -0
- {hypern-0.3.1.dist-info → hypern-0.3.3.dist-info}/licenses/LICENSE +0 -0
hypern/application.py
CHANGED
@@ -2,25 +2,52 @@
|
|
2
2
|
from __future__ import annotations
|
3
3
|
|
4
4
|
import asyncio
|
5
|
+
from dataclasses import dataclass
|
5
6
|
from typing import Any, Callable, List, TypeVar
|
6
7
|
|
7
8
|
import orjson
|
9
|
+
import psutil
|
8
10
|
from typing_extensions import Annotated, Doc
|
9
11
|
|
12
|
+
from hypern.args_parser import ArgsConfig
|
10
13
|
from hypern.datastructures import Contact, HTTPMethod, Info, License
|
11
|
-
from hypern.hypern import FunctionInfo, Router,
|
14
|
+
from hypern.hypern import DatabaseConfig, FunctionInfo, MiddlewareConfig, Router, Server, WebsocketRouter
|
15
|
+
from hypern.hypern import Route as InternalRoute
|
16
|
+
from hypern.logging import logger
|
17
|
+
from hypern.middleware import Middleware
|
12
18
|
from hypern.openapi import SchemaGenerator, SwaggerUI
|
13
19
|
from hypern.processpool import run_processes
|
14
20
|
from hypern.response import HTMLResponse, JSONResponse
|
15
21
|
from hypern.routing import Route
|
16
22
|
from hypern.scheduler import Scheduler
|
17
|
-
from hypern.middleware import Middleware
|
18
|
-
from hypern.args_parser import ArgsConfig
|
19
23
|
from hypern.ws import WebsocketRoute
|
20
24
|
|
21
25
|
AppType = TypeVar("AppType", bound="Hypern")
|
22
26
|
|
23
27
|
|
28
|
+
@dataclass
|
29
|
+
class ThreadConfig:
|
30
|
+
workers: int
|
31
|
+
max_blocking_threads: int
|
32
|
+
|
33
|
+
|
34
|
+
class ThreadConfigurator:
|
35
|
+
def __init__(self):
|
36
|
+
self._cpu_count = psutil.cpu_count(logical=True)
|
37
|
+
self._memory_gb = psutil.virtual_memory().total / (1024**3)
|
38
|
+
|
39
|
+
def get_config(self, concurrent_requests: int = None) -> ThreadConfig:
|
40
|
+
"""Calculate optimal thread configuration based on system resources."""
|
41
|
+
workers = max(2, self._cpu_count)
|
42
|
+
|
43
|
+
if concurrent_requests:
|
44
|
+
max_blocking = min(max(32, concurrent_requests * 2), workers * 4, int(self._memory_gb * 8))
|
45
|
+
else:
|
46
|
+
max_blocking = min(workers * 4, int(self._memory_gb * 8), 256)
|
47
|
+
|
48
|
+
return ThreadConfig(workers=workers, max_blocking_threads=max_blocking)
|
49
|
+
|
50
|
+
|
24
51
|
class Hypern:
|
25
52
|
def __init__(
|
26
53
|
self: AppType,
|
@@ -190,6 +217,22 @@ class Hypern:
|
|
190
217
|
"""
|
191
218
|
),
|
192
219
|
] = None,
|
220
|
+
auto_compression: Annotated[
|
221
|
+
bool,
|
222
|
+
Doc(
|
223
|
+
"""
|
224
|
+
Enable automatic compression of responses.
|
225
|
+
"""
|
226
|
+
),
|
227
|
+
] = False,
|
228
|
+
database_config: Annotated[
|
229
|
+
DatabaseConfig | None,
|
230
|
+
Doc(
|
231
|
+
"""
|
232
|
+
The database configuration for the application.
|
233
|
+
"""
|
234
|
+
),
|
235
|
+
] = None,
|
193
236
|
*args: Any,
|
194
237
|
**kwargs: Any,
|
195
238
|
) -> None:
|
@@ -202,6 +245,11 @@ class Hypern:
|
|
202
245
|
self.middleware_after_request = []
|
203
246
|
self.response_headers = {}
|
204
247
|
self.args = ArgsConfig()
|
248
|
+
self.start_up_handler = None
|
249
|
+
self.shutdown_handler = None
|
250
|
+
self.auto_compression = auto_compression
|
251
|
+
self.database_config = database_config
|
252
|
+
self.thread_config = ThreadConfigurator().get_config()
|
205
253
|
|
206
254
|
for route in routes or []:
|
207
255
|
self.router.extend_route(route(app=self).routes)
|
@@ -287,10 +335,12 @@ class Hypern:
|
|
287
335
|
function: The decorator function that registers the middleware.
|
288
336
|
"""
|
289
337
|
|
338
|
+
logger.warning("This functin will be deprecated in version 0.4.0. Please use the middleware class instead.")
|
339
|
+
|
290
340
|
def decorator(func):
|
291
341
|
is_async = asyncio.iscoroutinefunction(func)
|
292
342
|
func_info = FunctionInfo(handler=func, is_async=is_async)
|
293
|
-
self.middleware_before_request.append(func_info)
|
343
|
+
self.middleware_before_request.append((func_info, MiddlewareConfig.default()))
|
294
344
|
return func
|
295
345
|
|
296
346
|
return decorator
|
@@ -306,11 +356,12 @@ class Hypern:
|
|
306
356
|
Returns:
|
307
357
|
function: The decorator function that registers the given function.
|
308
358
|
"""
|
359
|
+
logger.warning("This functin will be deprecated in version 0.4.0. Please use the middleware class instead.")
|
309
360
|
|
310
361
|
def decorator(func):
|
311
362
|
is_async = asyncio.iscoroutinefunction(func)
|
312
363
|
func_info = FunctionInfo(handler=func, is_async=is_async)
|
313
|
-
self.middleware_after_request.append(func_info)
|
364
|
+
self.middleware_after_request.append((func_info, MiddlewareConfig.default()))
|
314
365
|
return func
|
315
366
|
|
316
367
|
return decorator
|
@@ -346,11 +397,22 @@ class Hypern:
|
|
346
397
|
before_request = getattr(middleware, "before_request", None)
|
347
398
|
after_request = getattr(middleware, "after_request", None)
|
348
399
|
|
349
|
-
|
350
|
-
|
351
|
-
|
352
|
-
|
353
|
-
|
400
|
+
is_async = asyncio.iscoroutinefunction(before_request)
|
401
|
+
before_request = FunctionInfo(handler=before_request, is_async=is_async)
|
402
|
+
self.middleware_before_request.append((before_request, middleware.config))
|
403
|
+
|
404
|
+
is_async = asyncio.iscoroutinefunction(after_request)
|
405
|
+
after_request = FunctionInfo(handler=after_request, is_async=is_async)
|
406
|
+
self.middleware_after_request.append((after_request, middleware.config))
|
407
|
+
|
408
|
+
def set_database_config(self, config: DatabaseConfig):
|
409
|
+
"""
|
410
|
+
Sets the database configuration for the application.
|
411
|
+
|
412
|
+
Args:
|
413
|
+
config (DatabaseConfig): The database configuration to be set.
|
414
|
+
"""
|
415
|
+
self.database_config = config
|
354
416
|
|
355
417
|
def start(
|
356
418
|
self,
|
@@ -364,18 +426,36 @@ class Hypern:
|
|
364
426
|
if self.scheduler:
|
365
427
|
self.scheduler.start()
|
366
428
|
|
429
|
+
server = Server()
|
430
|
+
server.set_router(router=self.router)
|
431
|
+
server.set_websocket_router(websocket_router=self.websocket_router)
|
432
|
+
server.set_injected(injected=self.injectables)
|
433
|
+
server.set_before_hooks(hooks=self.middleware_before_request)
|
434
|
+
server.set_after_hooks(hooks=self.middleware_after_request)
|
435
|
+
server.set_response_headers(headers=self.response_headers)
|
436
|
+
server.set_auto_compression(enabled=self.auto_compression)
|
437
|
+
server.set_mem_pool_capacity(min_capacity=self.args.min_capacity, max_capacity=self.args.max_capacity)
|
438
|
+
|
439
|
+
server.optimize_routes()
|
440
|
+
|
441
|
+
if self.database_config:
|
442
|
+
server.set_database_config(config=self.database_config)
|
443
|
+
if self.start_up_handler:
|
444
|
+
server.set_startup_handler(self.start_up_handler)
|
445
|
+
if self.shutdown_handler:
|
446
|
+
server.set_shutdown_handler(self.shutdown_handler)
|
447
|
+
|
448
|
+
if self.args.auto_workers:
|
449
|
+
self.args.workers = self.thread_config.workers
|
450
|
+
self.args.max_blocking_threads = self.thread_config.max_blocking_threads
|
451
|
+
|
367
452
|
run_processes(
|
453
|
+
server=server,
|
368
454
|
host=self.args.host,
|
369
455
|
port=self.args.port,
|
370
456
|
workers=self.args.workers,
|
371
457
|
processes=self.args.processes,
|
372
458
|
max_blocking_threads=self.args.max_blocking_threads,
|
373
|
-
router=self.router,
|
374
|
-
websocket_router=self.websocket_router,
|
375
|
-
injectables=self.injectables,
|
376
|
-
before_request=self.middleware_before_request,
|
377
|
-
after_request=self.middleware_after_request,
|
378
|
-
response_headers=self.response_headers,
|
379
459
|
reload=self.args.reload,
|
380
460
|
)
|
381
461
|
|
@@ -403,3 +483,22 @@ class Hypern:
|
|
403
483
|
"""
|
404
484
|
for route in ws_route.routes:
|
405
485
|
self.websocket_router.add_route(route=route)
|
486
|
+
|
487
|
+
def on_startup(self, handler: Callable[..., Any]):
|
488
|
+
"""
|
489
|
+
Registers a function to be executed on application startup.
|
490
|
+
|
491
|
+
Args:
|
492
|
+
handler (Callable[..., Any]): The function to be executed on application startup.
|
493
|
+
"""
|
494
|
+
# decorator
|
495
|
+
self.start_up_handler = FunctionInfo(handler=handler, is_async=asyncio.iscoroutinefunction(handler))
|
496
|
+
|
497
|
+
def on_shutdown(self, handler: Callable[..., Any]):
|
498
|
+
"""
|
499
|
+
Registers a function to be executed on application shutdown.
|
500
|
+
|
501
|
+
Args:
|
502
|
+
handler (Callable[..., Any]): The function to be executed on application shutdown.
|
503
|
+
"""
|
504
|
+
self.shutdown_handler = FunctionInfo(handler=handler, is_async=asyncio.iscoroutinefunction(handler))
|
hypern/args_parser.py
CHANGED
@@ -49,11 +49,44 @@ class ArgsConfig:
|
|
49
49
|
action="store_true",
|
50
50
|
help="It restarts the server based on file changes.",
|
51
51
|
)
|
52
|
+
|
53
|
+
parser.add_argument(
|
54
|
+
"--auto-compression",
|
55
|
+
action="store_true",
|
56
|
+
help="It compresses the response automatically.",
|
57
|
+
)
|
58
|
+
|
59
|
+
parser.add_argument(
|
60
|
+
"--auto-workers",
|
61
|
+
action="store_true",
|
62
|
+
help="It sets the number of workers and max-blocking-threads automatically.",
|
63
|
+
)
|
64
|
+
|
65
|
+
parser.add_argument(
|
66
|
+
"--min-capacity",
|
67
|
+
type=int,
|
68
|
+
default=1,
|
69
|
+
required=False,
|
70
|
+
help="Choose the minimum memory pool capacity. [Default: 1]",
|
71
|
+
)
|
72
|
+
|
73
|
+
parser.add_argument(
|
74
|
+
"--max-capacity",
|
75
|
+
type=int,
|
76
|
+
default=100,
|
77
|
+
required=False,
|
78
|
+
help="Choose the maximum memory pool capacity. [Default: 100]",
|
79
|
+
)
|
80
|
+
|
52
81
|
args, _ = parser.parse_known_args()
|
53
82
|
|
54
83
|
self.host = args.host or "127.0.0.1"
|
55
84
|
self.port = args.port or 5000
|
56
|
-
self.max_blocking_threads = args.max_blocking_threads or
|
85
|
+
self.max_blocking_threads = args.max_blocking_threads or 32
|
57
86
|
self.processes = args.processes or 1
|
58
87
|
self.workers = args.workers or 1
|
59
88
|
self.reload = args.reload or False
|
89
|
+
self.auto_compression = args.auto_compression
|
90
|
+
self.auto_workers = args.auto_workers
|
91
|
+
self.min_capacity = args.min_capacity
|
92
|
+
self.max_capacity = args.max_capacity
|
hypern/caching/__init__.py
CHANGED
@@ -0,0 +1,6 @@
|
|
1
|
+
from .backend import BaseBackend
|
2
|
+
from .redis_backend import RedisBackend
|
3
|
+
|
4
|
+
from .strategies import CacheAsideStrategy, CacheEntry, CacheStrategy, StaleWhileRevalidateStrategy, cache_with_strategy
|
5
|
+
|
6
|
+
__all__ = ["BaseBackend", "RedisBackend", "CacheAsideStrategy", "CacheEntry", "CacheStrategy", "StaleWhileRevalidateStrategy", "cache_with_strategy"]
|
@@ -0,0 +1,31 @@
|
|
1
|
+
from abc import ABC, abstractmethod
|
2
|
+
from typing import Any, Optional
|
3
|
+
|
4
|
+
|
5
|
+
class BaseBackend(ABC):
|
6
|
+
@abstractmethod
|
7
|
+
async def get(self, key: str) -> Any: ...
|
8
|
+
|
9
|
+
@abstractmethod
|
10
|
+
async def set(self, key: str, value: Any, ttl: Optional[int] = None) -> None: ...
|
11
|
+
|
12
|
+
@abstractmethod
|
13
|
+
async def delete_pattern(self, pattern: str) -> None: ...
|
14
|
+
|
15
|
+
@abstractmethod
|
16
|
+
async def delete(self, key: str) -> None: ...
|
17
|
+
|
18
|
+
@abstractmethod
|
19
|
+
async def exists(self, key: str) -> bool: ...
|
20
|
+
|
21
|
+
@abstractmethod
|
22
|
+
async def set_nx(self, key: str, value: Any, ttl: Optional[int] = None) -> bool: ...
|
23
|
+
|
24
|
+
@abstractmethod
|
25
|
+
async def ttl(self, key: str) -> int: ...
|
26
|
+
|
27
|
+
@abstractmethod
|
28
|
+
async def incr(self, key: str) -> int: ...
|
29
|
+
|
30
|
+
@abstractmethod
|
31
|
+
async def clear(self) -> None: ...
|
hypern/caching/redis_backend.py
CHANGED
@@ -1,3 +1,201 @@
|
|
1
|
-
|
1
|
+
# src/hypern/cache/backends/redis.py
|
2
|
+
import pickle
|
3
|
+
from typing import Any, Optional
|
2
4
|
|
3
|
-
|
5
|
+
from redis import asyncio as aioredis
|
6
|
+
|
7
|
+
from hypern.logging import logger
|
8
|
+
|
9
|
+
from .backend import BaseBackend
|
10
|
+
|
11
|
+
|
12
|
+
class RedisBackend(BaseBackend):
|
13
|
+
def __init__(self, url: str = "redis://localhost:6379", encoding: str = "utf-8", decode_responses: bool = False, **kwargs):
|
14
|
+
"""
|
15
|
+
Initialize Redis backend with aioredis
|
16
|
+
|
17
|
+
Args:
|
18
|
+
url: Redis connection URL
|
19
|
+
encoding: Character encoding to use
|
20
|
+
decode_responses: Whether to decode response bytes to strings
|
21
|
+
**kwargs: Additional arguments passed to aioredis.from_url
|
22
|
+
"""
|
23
|
+
self.redis = aioredis.from_url(url, encoding=encoding, decode_responses=decode_responses, **kwargs)
|
24
|
+
self._encoding = encoding
|
25
|
+
|
26
|
+
async def get(self, key: str) -> Optional[Any]:
|
27
|
+
"""
|
28
|
+
Get value from Redis
|
29
|
+
|
30
|
+
Args:
|
31
|
+
key: Cache key
|
32
|
+
|
33
|
+
Returns:
|
34
|
+
Deserialized Python object or None if key doesn't exist
|
35
|
+
"""
|
36
|
+
try:
|
37
|
+
value = await self.redis.get(key)
|
38
|
+
if value is not None:
|
39
|
+
return pickle.loads(value)
|
40
|
+
return None
|
41
|
+
except Exception as e:
|
42
|
+
logger.error(f"Error getting cache key {key}: {e}")
|
43
|
+
return None
|
44
|
+
|
45
|
+
async def set(self, key: str, value: Any, ttl: Optional[int] = None) -> bool:
|
46
|
+
"""
|
47
|
+
Set value in Redis with optional TTL
|
48
|
+
|
49
|
+
Args:
|
50
|
+
key: Cache key
|
51
|
+
value: Python object to store
|
52
|
+
ttl: Time to live in seconds
|
53
|
+
|
54
|
+
Returns:
|
55
|
+
bool: True if successful, False otherwise
|
56
|
+
"""
|
57
|
+
try:
|
58
|
+
serialized = pickle.dumps(value)
|
59
|
+
if ttl is not None:
|
60
|
+
await self.redis.setex(key, ttl, serialized)
|
61
|
+
else:
|
62
|
+
await self.redis.set(key, serialized)
|
63
|
+
return True
|
64
|
+
except Exception as e:
|
65
|
+
logger.error(f"Error setting cache key {key}: {e}")
|
66
|
+
return False
|
67
|
+
|
68
|
+
async def delete(self, key: str) -> bool:
|
69
|
+
"""
|
70
|
+
Delete key from Redis
|
71
|
+
|
72
|
+
Args:
|
73
|
+
key: Cache key to delete
|
74
|
+
|
75
|
+
Returns:
|
76
|
+
bool: True if key was deleted, False otherwise
|
77
|
+
"""
|
78
|
+
try:
|
79
|
+
return bool(await self.redis.delete(key))
|
80
|
+
except Exception as e:
|
81
|
+
logger.error(f"Error deleting cache key {key}: {e}")
|
82
|
+
return False
|
83
|
+
|
84
|
+
async def delete_pattern(self, pattern: str) -> int:
|
85
|
+
"""
|
86
|
+
Delete all keys matching pattern
|
87
|
+
|
88
|
+
Args:
|
89
|
+
pattern: Redis key pattern to match
|
90
|
+
|
91
|
+
Returns:
|
92
|
+
int: Number of keys deleted
|
93
|
+
"""
|
94
|
+
try:
|
95
|
+
keys = await self.redis.keys(pattern)
|
96
|
+
if keys:
|
97
|
+
return await self.redis.delete(*keys)
|
98
|
+
return 0
|
99
|
+
except Exception as e:
|
100
|
+
logger.error(f"Error deleting keys matching {pattern}: {e}")
|
101
|
+
return 0
|
102
|
+
|
103
|
+
async def exists(self, key: str) -> bool:
|
104
|
+
"""
|
105
|
+
Check if key exists
|
106
|
+
|
107
|
+
Args:
|
108
|
+
key: Cache key to check
|
109
|
+
|
110
|
+
Returns:
|
111
|
+
bool: True if key exists, False otherwise
|
112
|
+
"""
|
113
|
+
try:
|
114
|
+
return bool(await self.redis.exists(key))
|
115
|
+
except Exception as e:
|
116
|
+
logger.error(f"Error checking existence of key {key}: {e}")
|
117
|
+
return False
|
118
|
+
|
119
|
+
async def ttl(self, key: str) -> int:
|
120
|
+
"""
|
121
|
+
Get TTL of key in seconds
|
122
|
+
|
123
|
+
Args:
|
124
|
+
key: Cache key
|
125
|
+
|
126
|
+
Returns:
|
127
|
+
int: TTL in seconds, -2 if key doesn't exist, -1 if key has no TTL
|
128
|
+
"""
|
129
|
+
try:
|
130
|
+
return await self.redis.ttl(key)
|
131
|
+
except Exception as e:
|
132
|
+
logger.error(f"Error getting TTL for key {key}: {e}")
|
133
|
+
return -2
|
134
|
+
|
135
|
+
async def incr(self, key: str, amount: int = 1) -> Optional[int]:
|
136
|
+
"""
|
137
|
+
Increment value by amount
|
138
|
+
|
139
|
+
Args:
|
140
|
+
key: Cache key
|
141
|
+
amount: Amount to increment by
|
142
|
+
|
143
|
+
Returns:
|
144
|
+
int: New value after increment or None on error
|
145
|
+
"""
|
146
|
+
try:
|
147
|
+
return await self.redis.incrby(key, amount)
|
148
|
+
except Exception as e:
|
149
|
+
logger.error(f"Error incrementing key {key}: {e}")
|
150
|
+
return None
|
151
|
+
|
152
|
+
async def set_nx(self, key: str, value: Any, ttl: Optional[int] = None) -> bool:
|
153
|
+
"""
|
154
|
+
Set key only if it doesn't exist (SET NX operation)
|
155
|
+
|
156
|
+
Args:
|
157
|
+
key: Cache key
|
158
|
+
value: Value to set
|
159
|
+
ttl: Optional TTL in seconds
|
160
|
+
|
161
|
+
Returns:
|
162
|
+
bool: True if key was set, False otherwise
|
163
|
+
"""
|
164
|
+
try:
|
165
|
+
serialized = pickle.dumps(value)
|
166
|
+
if ttl is not None:
|
167
|
+
return await self.redis.set(key, serialized, nx=True, ex=ttl)
|
168
|
+
return await self.redis.set(key, serialized, nx=True)
|
169
|
+
except Exception as e:
|
170
|
+
logger.error(f"Error setting NX for key {key}: {e}")
|
171
|
+
return False
|
172
|
+
|
173
|
+
async def clear(self) -> bool:
|
174
|
+
"""
|
175
|
+
Clear all keys from the current database
|
176
|
+
|
177
|
+
Returns:
|
178
|
+
bool: True if successful, False otherwise
|
179
|
+
"""
|
180
|
+
try:
|
181
|
+
await self.redis.flushdb()
|
182
|
+
return True
|
183
|
+
except Exception as e:
|
184
|
+
logger.error(f"Error clearing cache: {e}")
|
185
|
+
return False
|
186
|
+
|
187
|
+
async def close(self) -> None:
|
188
|
+
"""Close Redis connection"""
|
189
|
+
await self.redis.close()
|
190
|
+
|
191
|
+
async def ping(self) -> bool:
|
192
|
+
"""
|
193
|
+
Check Redis connection
|
194
|
+
|
195
|
+
Returns:
|
196
|
+
bool: True if connection is alive, False otherwise
|
197
|
+
"""
|
198
|
+
try:
|
199
|
+
return await self.redis.ping()
|
200
|
+
except Exception:
|
201
|
+
return False
|