|
1 |
| -from typing import Dict, Union, Mapping, TypeVar, Callable, Optional, Sequence |
| 1 | +""" |
| 2 | +Internal API facade over Django settings. |
2 | 3 |
|
3 |
| -from django.conf import settings |
| 4 | +This provides a typed interface over Django settings as well as handling default |
| 5 | +values for settings not set by users and supporting the addition of overrides in |
| 6 | +some management commands. |
| 7 | +""" |
| 8 | + |
| 9 | +from typing import Any, Dict, List, Union, Callable, Optional, Sequence |
| 10 | + |
| 11 | +from typing_extensions import Protocol |
| 12 | + |
| 13 | +from django.conf import settings as django_settings |
4 | 14 |
|
5 | 15 | from . import constants
|
6 | 16 | from .types import Logger, QueueName
|
7 | 17 |
|
8 |
| -T = TypeVar('T') |
9 | 18 |
|
| 19 | +class LongNameAdapter: |
| 20 | + def __init__(self, target: Any) -> None: |
| 21 | + self.target = target |
| 22 | + |
| 23 | + def __getattr__(self, name: str) -> Any: |
| 24 | + return getattr(self.target, f'{constants.SETTING_NAME_PREFIX}{name}') |
| 25 | + |
| 26 | + |
| 27 | +class Settings(Protocol): |
| 28 | + WORKERS: Dict[QueueName, int] |
| 29 | + BACKEND: str |
| 30 | + LOGGER_FACTORY: Union[str, Callable[[str], Logger]] |
| 31 | + |
| 32 | + # Allow per-queue overrides of the backend. |
| 33 | + BACKEND_OVERRIDES: Dict[QueueName, str] |
| 34 | + MIDDLEWARE: Sequence[str] |
| 35 | + |
| 36 | + # Apps to ignore when looking for tasks. Apps must be specified as the dotted |
| 37 | + # name used in `INSTALLED_APPS`. This is expected to be useful when you need to |
| 38 | + # have a file called `tasks.py` within an app, but don't want |
| 39 | + # django-lightweight-queue to import that file. |
| 40 | + # Note: this _doesn't_ prevent tasks being registered from these apps. |
| 41 | + IGNORE_APPS: Sequence[str] |
| 42 | + |
| 43 | + REDIS_HOST: str |
| 44 | + REDIS_PORT: int |
| 45 | + REDIS_PASSWORD: Optional[str] |
| 46 | + REDIS_DATABASE: int |
| 47 | + REDIS_PREFIX: str |
| 48 | + |
| 49 | + ENABLE_PROMETHEUS: bool |
| 50 | + # Workers will export metrics on this port, and ports following it |
| 51 | + PROMETHEUS_START_PORT: int |
| 52 | + |
| 53 | + ATOMIC_JOBS: bool |
| 54 | + |
| 55 | + |
| 56 | +class LayeredSettings(Settings, Protocol): |
| 57 | + def add_layer(self, layer: Settings) -> None: |
| 58 | + ... |
| 59 | + |
| 60 | + |
| 61 | +class Defaults(Settings): |
| 62 | + WORKERS: Dict[QueueName, int] = {} |
| 63 | + BACKEND = 'django_lightweight_queue.backends.synchronous.SynchronousBackend' |
| 64 | + LOGGER_FACTORY = 'logging.getLogger' |
| 65 | + |
| 66 | + BACKEND_OVERRIDES: Dict[QueueName, str] = {} |
| 67 | + MIDDLEWARE = ('django_lightweight_queue.middleware.logging.LoggingMiddleware',) |
| 68 | + |
| 69 | + IGNORE_APPS: Sequence[str] = () |
| 70 | + |
| 71 | + REDIS_HOST = '127.0.0.1' |
| 72 | + REDIS_PORT = 6379 |
| 73 | + REDIS_PASSWORD = None |
| 74 | + REDIS_DATABASE = 0 |
| 75 | + REDIS_PREFIX = "" |
10 | 76 |
|
11 |
| -def setting(suffix: str, default: T) -> T: |
12 |
| - attr_name = '{}{}'.format(constants.SETTING_NAME_PREFIX, suffix) |
13 |
| - return getattr(settings, attr_name, default) |
| 77 | + ENABLE_PROMETHEUS = False |
14 | 78 |
|
| 79 | + PROMETHEUS_START_PORT = 9300 |
15 | 80 |
|
16 |
| -WORKERS = setting('WORKERS', {}) # type: Dict[QueueName, int] |
17 |
| -BACKEND = setting( |
18 |
| - 'BACKEND', |
19 |
| - 'django_lightweight_queue.backends.synchronous.SynchronousBackend', |
20 |
| -) # type: str |
| 81 | + ATOMIC_JOBS = True |
21 | 82 |
|
22 |
| -LOGGER_FACTORY = setting( |
23 |
| - 'LOGGER_FACTORY', |
24 |
| - 'logging.getLogger', |
25 |
| -) # type: Union[str, Callable[[str], Logger]] |
26 | 83 |
|
27 |
| -# Allow per-queue overrides of the backend. |
28 |
| -BACKEND_OVERRIDES = setting('BACKEND_OVERRIDES', {}) # type: Mapping[QueueName, str] |
| 84 | +class AppSettings: |
| 85 | + def __init__(self, layers: List[Settings]) -> None: |
| 86 | + self._layers = layers |
29 | 87 |
|
30 |
| -MIDDLEWARE = setting('MIDDLEWARE', ( |
31 |
| - 'django_lightweight_queue.middleware.logging.LoggingMiddleware', |
32 |
| - 'django_lightweight_queue.middleware.transaction.TransactionMiddleware', |
33 |
| -)) # type: Sequence[str] |
| 88 | + def add_layer(self, layer: Settings) -> None: |
| 89 | + self._layers.append(layer) |
34 | 90 |
|
35 |
| -# Apps to ignore when looking for tasks. Apps must be specified as the dotted |
36 |
| -# name used in `INSTALLED_APPS`. This is expected to be useful when you need to |
37 |
| -# have a file called `tasks.py` within an app, but don't want |
38 |
| -# django-lightweight-queue to import that file. |
39 |
| -# Note: this _doesn't_ prevent tasks being registered from these apps. |
40 |
| -IGNORE_APPS = setting('IGNORE_APPS', ()) # type: Sequence[str] |
| 91 | + def __getattr__(self, name: str) -> Any: |
| 92 | + # reverse so that later layers override earlier ones |
| 93 | + for layer in reversed(self._layers): |
| 94 | + if hasattr(layer, name): |
| 95 | + return getattr(layer, name) |
41 | 96 |
|
42 |
| -# Backend-specific settings |
43 |
| -REDIS_HOST = setting('REDIS_HOST', '127.0.0.1') # type: str |
44 |
| -REDIS_PORT = setting('REDIS_PORT', 6379) # type: int |
45 |
| -REDIS_PASSWORD = setting('REDIS_PASSWORD', None) # type: Optional[str] |
46 |
| -REDIS_DATABASE = setting('REDIS_DATABASE', 0) # type: int |
47 |
| -REDIS_PREFIX = setting('REDIS_PREFIX', '') # type: str |
| 97 | + raise AttributeError(f"Sorry, '{name}' is not a valid setting.") |
48 | 98 |
|
49 |
| -ENABLE_PROMETHEUS = setting('ENABLE_PROMETHEUS', False) # type: bool |
50 |
| -# Workers will export metrics on this port, and ports following it |
51 |
| -PROMETHEUS_START_PORT = setting('PROMETHEUS_START_PORT', 9300) # type: int |
52 | 99 |
|
53 |
| -ATOMIC_JOBS = setting('ATOMIC_JOBS', True) # type: bool |
| 100 | +app_settings: LayeredSettings = AppSettings(layers=[ |
| 101 | + Defaults(), |
| 102 | + LongNameAdapter(django_settings), |
| 103 | +]) |
0 commit comments