Source code for duck.settings.settings
"""
Provides access to application settings.
"""
import os
import sys
import warnings
from functools import lru_cache
from typing import List, Tuple
from duck.exceptions.all import SettingsError
from duck.utils.importer import import_module_once
# Set default settings if not provided
os.environ.setdefault("DUCK_SETTINGS_MODULE", "web.settings")
# Retrieve settings module from the environment
SETTINGS_MODULE = os.environ.get("DUCK_SETTINGS_MODULE")
# Deprecated settings
DEPRECATED_SETTINGS: List[Tuple[str, str]] = [
(
"FORCE_HTTPS",
"Setting 'FORCE_HTTPS' is deprecated, please use "
"'HTTPS_REDIRECT' instead.",
),
(
"FORCE_HTTPS_BIND_PORT",
"Setting 'FORCE_HTTPS_BIND_PORT' is deprecated, please use "
"'HTTPS_REDIRECT_BIND_PORT' instead.",
),
]
[docs]
def warn_deprecated_settings(settings: "Settings"):
"""
Warn about deprecated settings currently in use.
Args:
settings:
Loaded settings object to inspect.
"""
for setting_name, message in DEPRECATED_SETTINGS:
if setting_name in settings:
warnings.warn(message, DeprecationWarning)
[docs]
class Settings(dict):
"""
A class for managing **Duck** settings.
"""
source = None
[docs]
def reload(self):
"""
Re-execute the settings module and update this dict in-place.
"""
import importlib
mod_str = os.environ.get("DUCK_SETTINGS_MODULE")
mod = sys.modules.get(mod_str, None)
mods = []
if mod:
importlib.reload_module(mod)
mods.append(mod)
else:
mod = import_module_once(mod_str)
mods.append(import_module_once("duck.etc.settings"))
mods.append(mod)
# Apply settings inplace
self.source = mod
for mod in mods:
for var in dir(mod):
if var.isupper():
self[var] = getattr(mod, var)
# Warn deprecated settings
warn_deprecated_settings(self)
[docs]
def __repr__(self):
return (
"<" + f"{self.__class__.__name__} "
f"source={repr(self.source)}".replace('<', "[").replace('>', "]") + ">"
)
[docs]
@lru_cache
def settings_to_dict(settings_module: str) -> Settings:
"""
Convert a settings module into a Settings object.
"""
settings_mod = import_module_once(settings_module)
# Initialize and update settings
settings = Settings({})
settings.source = settings_mod
for var in dir(settings_mod):
if var.isupper():
settings[var] = getattr(settings_mod, var)
# Finally, return settings.
return settings
[docs]
def get_combined_settings() -> Settings:
"""
Combine default and user settings into a single settings object.
Returns:
Settings:
Combined settings object.
Raises:
SettingsError:
Raised if the user settings module cannot be loaded.
"""
default_settings = settings_to_dict("duck.etc.settings")
try:
user_settings = settings_to_dict(SETTINGS_MODULE)
except Exception as e:
raise SettingsError(
"Error loading Duck settings module, ensure "
"environment variable DUCK_SETTINGS_MODULE "
f"is set correctly: {e}."
) from e
# Override defaults with user settings
default_settings.update(user_settings)
# Create and update Settings object.
settings = Settings(default_settings)
settings.source = user_settings.source
# Warn deprecated settings
warn_deprecated_settings(settings)
# Return final settings.
return settings
# Set and load important settings, objects, etc.
SETTINGS: Settings = get_combined_settings()
# Set Django specific configurations
if not SETTINGS_MODULE.startswith("web") and SETTINGS['DJANGO_SETTINGS_MODULE'].startswith('web.backend.django.duckapp.duckapp.settings'):
# Duck settings module is external yet the Django settings module is default.
try:
# Try to resolve the settings
import_module_once(SETTINGS['DJANGO_SETTINGS_MODULE'])
except ImportError:
# We need to fix the Django settings module
SETTINGS['DJANGO_SETTINGS_MODULE'] = SETTINGS_MODULE.rsplit('.', 1)[0] + ".backend.django.duckapp.duckapp.settings"
# Set django settings module
os.environ.setdefault('DJANGO_SETTINGS_MODULE', SETTINGS['DJANGO_SETTINGS_MODULE'])
if (
os.getenv("DUCK_USE_DJANGO", None) == "true"
or "-dj" in sys.argv
or "--use-django" in sys.argv
):
SETTINGS["USE_DJANGO"] = True