from functools import cached_property
from pathlib import Path
from typing import Any
import yaml
from django.conf import settings
[docs]
class Settings:
"""
This class provide settings configuration with default values.
Configuration keys are declared and document under subclass of this
one as UPPERCASED attributes. This allows to document them.
User can still import the class in ``settings.py`` in order to
reuse already existing values.
Example ``myapp.conf``
.. code-block:: python
from ox.utils import conf
__all__ = ("MyAppSettings", "app_settings")
class MyAppSettings(conf.Settings):
ALLOWED_TAGS = ["a", "b", "br"]
\""" Tags allowed for this application. \
"""
SOME_VALUE = 123
\""" Other documented setting. \"""
# this is the actual instance used to retrieve settings
app_settings = MyAppSettings("MYAPP")
In project ``settings.py``:
.. code-block:: python
from myapp.settings import MyAppSettings
MYAPP = {
"ALLOWED_TAGS": [*MyAppSettings.ALLOWED_TAGS, "p"],
}
Usage:
.. code-block:: python
from myapp.conf import app_settings
# Just print it out...
print(app_settings.ALLOWED_TAGS, app_settings.SOME_VALUE)
"""
def __init__(self, key, source=settings):
self._overrides = getattr(source, key, None)
def __getattribute__(self, name):
if name[0] != "_" and name.isupper():
overrides = object.__getattribute__(self, "_overrides")
if overrides and name in overrides:
return overrides[name]
return object.__getattribute__(self, name)
[docs]
class SettingsEditor:
"""Helper class to work with dynaconf settings files."""
def __init__(self, name, dir=None):
self.name = name
self.dir = Path(dir or settings.OX["SETTINGS_DIR"])
[docs]
@classmethod
def from_app(cls, app, *args, **kwargs):
"""Return SettingsEditor for the provided file."""
name = f"{app.label}.yaml"
return cls(name, *args, **kwargs)
@cached_property
def path(self):
""" """
return self.dir / self.name
def _ensure(self):
"""Ensure directory exists."""
self.dir.mkdir(parents=True, exist_ok=True)
[docs]
def read(self) -> dict[str, Any] | None:
"""Read data from settings."""
if not self.path.exists():
return None
with open(self.path, "r") as stream:
return yaml.load(stream, Loader=yaml.Loader)
[docs]
def write(self, data):
"""Write data to settings file."""
self._ensure()
with open(self.path, "w") as stream:
yaml.dump(data, stream)