v4.2.0 #207

Merged
profitroll merged 20 commits from dev into main 2025-05-18 18:38:49 +03:00
8 changed files with 53 additions and 23 deletions

View File

@@ -16,6 +16,7 @@ There are different sub-packages available:
* pyrogram - Telegram bots with Pyrogram's fork "Pyrofork"
* pycord - Discord bots with Pycord
* speed - Performance improvements
* cache - Support for Redis and Memcached
* dev - Dependencies for package development purposes
You can freely choose any sub-package you want, as well as add multiple (comma-separated) or none at all.

View File

@@ -1,2 +1,2 @@
aiofiles>=23.0.0
typing-extensions~=4.12.2
typing-extensions~=4.13.0

View File

@@ -1,2 +1,2 @@
pymemcache~=4.0.0
redis~=5.2.1
redis~=6.1.0

View File

@@ -2,11 +2,11 @@ black==25.1.0
build==1.2.2.post1
isort==5.13.2
mypy==1.15.0
pylint==3.3.4
pytest-asyncio==0.25.3
pytest-cov==6.0.0
pytest==8.3.4
tox==4.24.0
pylint==3.3.7
pytest-asyncio==0.26.0
pytest-cov==6.1.1
pytest==8.3.5
tox==4.26.0
twine==6.1.0
types-aiofiles==24.1.0.20241221
types-ujson==5.10.0.20240515
types-aiofiles==24.1.0.20250516
types-ujson==5.10.0.20250326

View File

@@ -1,4 +1,4 @@
__version__ = "4.1.0"
__version__ = "4.2.0"
__license__ = "GPL3"
__author__ = "Profitroll"

View File

@@ -1,6 +1,6 @@
import logging
from logging import Logger
from typing import Dict, Any
from typing import Dict, Any, Optional
from pymemcache import Client
@@ -13,21 +13,27 @@ logger: Logger = logging.getLogger(__name__)
class CacheMemcached(Cache):
client: Client
def __init__(self, client: Client):
self.client = client
def __init__(self, client: Client, prefix: Optional[str] = None):
self.client: Client = client
self.prefix: str | None = prefix
logger.info("Initialized Memcached for caching")
@classmethod
def from_config(cls, engine_config: Dict[str, Any]) -> "CacheMemcached":
def from_config(cls, engine_config: Dict[str, Any], prefix: Optional[str] = None) -> "CacheMemcached":
if "uri" not in engine_config:
raise KeyError(
"Cache configuration is invalid. Please check if all keys are set (engine: memcached)"
)
return cls(Client(engine_config["uri"], default_noreply=True))
return cls(Client(engine_config["uri"], default_noreply=True), prefix=prefix)
def _get_prefixed_key(self, key: str) -> str:
return key if self.prefix is None else f"{self.prefix}_{key}"
def get_json(self, key: str) -> Any | None:
key = self._get_prefixed_key(key)
try:
result: Any | None = self.client.get(key, None)
@@ -43,6 +49,8 @@ class CacheMemcached(Cache):
return None if result is None else _string_to_json(result)
def get_string(self, key: str) -> str | None:
key = self._get_prefixed_key(key)
try:
result: str | None = self.client.get(key, None)
@@ -62,6 +70,8 @@ class CacheMemcached(Cache):
raise NotImplementedError()
def set_json(self, key: str, value: Any) -> None:
key = self._get_prefixed_key(key)
try:
self.client.set(key, _json_to_string(value))
logger.debug("Set json cache key '%s'", key)
@@ -70,6 +80,8 @@ class CacheMemcached(Cache):
return None
def set_string(self, key: str, value: str) -> None:
key = self._get_prefixed_key(key)
try:
self.client.set(key, value)
logger.debug("Set string cache key '%s'", key)
@@ -82,6 +94,8 @@ class CacheMemcached(Cache):
raise NotImplementedError()
def delete(self, key: str) -> None:
key = self._get_prefixed_key(key)
try:
self.client.delete(key)
logger.debug("Deleted cache key '%s'", key)

View File

@@ -1,6 +1,6 @@
import logging
from logging import Logger
from typing import Dict, Any
from typing import Dict, Any, Optional
from redis import Redis
@@ -13,21 +13,27 @@ logger: Logger = logging.getLogger(__name__)
class CacheRedis(Cache):
client: Redis
def __init__(self, client: Redis):
self.client = client
def __init__(self, client: Redis, prefix: Optional[str] = None):
self.client: Redis = client
self.prefix: str | None = prefix
logger.info("Initialized Redis for caching")
@classmethod
def from_config(cls, engine_config: Dict[str, Any]) -> Any:
def from_config(cls, engine_config: Dict[str, Any], prefix: Optional[str] = None) -> Any:
if "uri" not in engine_config:
raise KeyError(
"Cache configuration is invalid. Please check if all keys are set (engine: memcached)"
)
return cls(Redis.from_url(engine_config["uri"]))
return cls(Redis.from_url(engine_config["uri"]), prefix=prefix)
def _get_prefixed_key(self, key: str) -> str:
return key if self.prefix is None else f"{self.prefix}_{key}"
def get_json(self, key: str) -> Any | None:
key = self._get_prefixed_key(key)
try:
result: Any | None = self.client.get(key)
@@ -43,6 +49,8 @@ class CacheRedis(Cache):
return None if result is None else _string_to_json(result)
def get_string(self, key: str) -> str | None:
key = self._get_prefixed_key(key)
try:
result: str | None = self.client.get(key)
@@ -62,6 +70,8 @@ class CacheRedis(Cache):
raise NotImplementedError()
def set_json(self, key: str, value: Any) -> None:
key = self._get_prefixed_key(key)
try:
self.client.set(key, _json_to_string(value))
logger.debug("Set json cache key '%s'", key)
@@ -70,6 +80,8 @@ class CacheRedis(Cache):
return None
def set_string(self, key: str, value: str) -> None:
key = self._get_prefixed_key(key)
try:
self.client.set(key, value)
logger.debug("Set string cache key '%s'", key)
@@ -82,6 +94,8 @@ class CacheRedis(Cache):
raise NotImplementedError()
def delete(self, key: str) -> None:
key = self._get_prefixed_key(key)
try:
self.client.delete(key)
logger.debug("Deleted cache key '%s'", key)

View File

@@ -1,4 +1,4 @@
from typing import Dict, Any, Literal
from typing import Dict, Any, Literal, Optional
from ..classes import CacheMemcached, CacheRedis
@@ -6,6 +6,7 @@ from ..classes import CacheMemcached, CacheRedis
def create_cache_client(
config: Dict[str, Any],
engine: Literal["memcached", "redis"] | None = None,
prefix: Optional[str] = None,
) -> CacheMemcached | CacheRedis:
if engine not in ["memcached", "redis"] or engine is None:
raise KeyError(f"Incorrect cache engine provided. Expected 'memcached' or 'redis', got '{engine}'")
@@ -17,8 +18,8 @@ def create_cache_client(
match engine:
case "memcached":
return CacheMemcached.from_config(config["cache"][engine])
return CacheMemcached.from_config(config["cache"][engine], prefix=prefix)
case "redis":
return CacheRedis.from_config(config["cache"][engine])
return CacheRedis.from_config(config["cache"][engine], prefix=prefix)
case _:
raise KeyError(f"Cache implementation for the engine '{engine}' is not present.")