mirror of
https://gitlab.com/allianceauth/allianceauth.git
synced 2025-07-15 07:20:17 +02:00
Merge branch 'fix-doc-redis-issue' into 'master'
Fix: Broken docs generation on readthedocs.org (2nd attempt) See merge request allianceauth/allianceauth!1425
This commit is contained in:
commit
2401f2299d
@ -1,27 +1,62 @@
|
|||||||
import datetime as dt
|
import datetime as dt
|
||||||
from typing import Optional, List
|
import logging
|
||||||
|
from typing import List, Optional
|
||||||
|
|
||||||
from redis import Redis
|
|
||||||
from pytz import utc
|
from pytz import utc
|
||||||
|
from redis import Redis, RedisError
|
||||||
|
|
||||||
from django.core.cache import cache
|
from django.core.cache import cache
|
||||||
|
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
class _RedisStub:
|
||||||
|
"""Stub of a Redis client.
|
||||||
|
|
||||||
|
It's purpose is to prevent EventSeries objects from trying to access Redis
|
||||||
|
when it is not available. e.g. when the Sphinx docs are rendered by readthedocs.org.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def delete(self, *args, **kwargs):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def incr(self, *args, **kwargs):
|
||||||
|
return 0
|
||||||
|
|
||||||
|
def zadd(self, *args, **kwargs):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def zcount(self, *args, **kwargs):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def zrangebyscore(self, *args, **kwargs):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
class EventSeries:
|
class EventSeries:
|
||||||
"""API for recording and analysing a series of events."""
|
"""API for recording and analyzing a series of events."""
|
||||||
|
|
||||||
_ROOT_KEY = "ALLIANCEAUTH_EVENT_SERIES"
|
_ROOT_KEY = "ALLIANCEAUTH_EVENT_SERIES"
|
||||||
|
|
||||||
def __init__(self, key_id: str, redis: Redis = None) -> None:
|
def __init__(self, key_id: str, redis: Redis = None) -> None:
|
||||||
self._redis = cache.get_master_client() if not redis else redis
|
self._redis = cache.get_master_client() if not redis else redis
|
||||||
if not isinstance(self._redis, Redis):
|
try:
|
||||||
raise TypeError(
|
if not self._redis.ping():
|
||||||
"This class requires a Redis client, but none was provided "
|
raise RuntimeError()
|
||||||
"and the default Django cache backend is not Redis either."
|
except (AttributeError, RedisError, RuntimeError):
|
||||||
|
logger.exception(
|
||||||
|
"Failed to establish a connection with Redis. "
|
||||||
|
"This EventSeries object is disabled.",
|
||||||
)
|
)
|
||||||
|
self._redis = _RedisStub()
|
||||||
self._key_id = str(key_id)
|
self._key_id = str(key_id)
|
||||||
self.clear()
|
self.clear()
|
||||||
|
|
||||||
|
@property
|
||||||
|
def is_disabled(self):
|
||||||
|
"""True when this object is disabled, e.g. Redis was not available at startup."""
|
||||||
|
return isinstance(self._redis, _RedisStub)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def _key_counter(self):
|
def _key_counter(self):
|
||||||
return f"{self._ROOT_KEY}_{self._key_id}_COUNTER"
|
return f"{self._ROOT_KEY}_{self._key_id}_COUNTER"
|
||||||
|
@ -1,13 +1,48 @@
|
|||||||
import datetime as dt
|
import datetime as dt
|
||||||
|
from unittest.mock import patch
|
||||||
|
|
||||||
from pytz import utc
|
from pytz import utc
|
||||||
|
from redis import RedisError
|
||||||
|
|
||||||
from django.test import TestCase
|
from django.test import TestCase
|
||||||
from django.utils.timezone import now
|
from django.utils.timezone import now
|
||||||
|
|
||||||
from allianceauth.authentication.task_statistics.event_series import EventSeries
|
from allianceauth.authentication.task_statistics.event_series import (
|
||||||
|
EventSeries,
|
||||||
|
_RedisStub,
|
||||||
|
)
|
||||||
|
|
||||||
|
MODULE_PATH = "allianceauth.authentication.task_statistics.event_series"
|
||||||
|
|
||||||
|
|
||||||
class TestEventSeries(TestCase):
|
class TestEventSeries(TestCase):
|
||||||
|
def test_should_abort_without_redis_client(self):
|
||||||
|
# when
|
||||||
|
with patch(MODULE_PATH + ".cache.get_master_client") as mock:
|
||||||
|
mock.return_value = None
|
||||||
|
events = EventSeries("dummy")
|
||||||
|
# then
|
||||||
|
self.assertTrue(events._redis, _RedisStub)
|
||||||
|
self.assertTrue(events.is_disabled)
|
||||||
|
|
||||||
|
def test_should_disable_itself_if_redis_not_available_1(self):
|
||||||
|
# when
|
||||||
|
with patch(MODULE_PATH + ".cache.get_master_client") as mock_get_master_client:
|
||||||
|
mock_get_master_client.return_value.ping.side_effect = RedisError
|
||||||
|
events = EventSeries("dummy")
|
||||||
|
# then
|
||||||
|
self.assertIsInstance(events._redis, _RedisStub)
|
||||||
|
self.assertTrue(events.is_disabled)
|
||||||
|
|
||||||
|
def test_should_disable_itself_if_redis_not_available_2(self):
|
||||||
|
# when
|
||||||
|
with patch(MODULE_PATH + ".cache.get_master_client") as mock_get_master_client:
|
||||||
|
mock_get_master_client.return_value.ping.return_value = False
|
||||||
|
events = EventSeries("dummy")
|
||||||
|
# then
|
||||||
|
self.assertIsInstance(events._redis, _RedisStub)
|
||||||
|
self.assertTrue(events.is_disabled)
|
||||||
|
|
||||||
def test_should_add_event(self):
|
def test_should_add_event(self):
|
||||||
# given
|
# given
|
||||||
events = EventSeries("dummy")
|
events = EventSeries("dummy")
|
||||||
|
Loading…
x
Reference in New Issue
Block a user