Source code for duck.settings.settings

"""
Provides access to application settings.
"""
import os
import sys

from functools import lru_cache

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")
SETTINGS_MODULE = os.environ.get("DUCK_SETTINGS_MODULE")


[docs] class Settings(dict): """ A class for managing **Duck** settings. This class extends the built-in `dict` to store settings in a dictionary-like format. It also provides a custom representation of the settings for debugging and logging. **Notes:** - These settings can be altered at runtime once imported but the `SETTINGS` should be imported and edited at top level before importing anything related to **Duck**. **Example:** ```py from duck.settings import SETTINGS # Edit settings inplace .e.g., SETTINGS['ENABLE_HTTPS'] = True from duck.app import App app = App() if __name__ == '__main__': app.run() ``` """ source = None """ The source of settings e.g., `web.settings`. """
[docs] def reload(self): """ Re-execute the settings module and update this dict in-place. Notes: The settings module will be the latest one set in `DUCK_SETTINGS_MODULE`. """ 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(): # is a valid setting variable self[var] = getattr(mod, var)
[docs] def __repr__(self): # Provide a more detailed and readable string representation of the Settings object return ( "<" + f"{self.__class__.__name__} " f"source={repr(self.source)}".replace('<', "[").replace('>', "]") + ">" )
[docs] @lru_cache def settings_to_dict(settings_module: str) -> Settings: """ Converts a settings module to a dictionary. Args: settings_module (str): The path to the settings module. Returns: Settings: Settings object derived from a dictionary containing the settings. Raises: ImportError: If the settings module cannot be imported. """ settings_mod = import_module_once(settings_module) settings = Settings({}) settings.source = settings_mod for var in dir(settings_mod): if var.isupper(): # is a valid setting variable settings[var] = getattr(settings_mod, var) return settings
[docs] def get_combined_settings() -> Settings: """ Combines default and user settings into a single dictionary. Reads the default settings from `duck.etc.settings` and attempts to read user settings from a `settings` module in the current directory. Returns: Settings: Settings object derived from a dictionary containing the settings. Raises: SettingsError: If there's an error loading user settings. """ default_settings = settings_to_dict("duck.etc.settings") try: user_settings = settings_to_dict(SETTINGS_MODULE) except Exception as e: raise SettingsError( f"Error loading Duck settings module, ensure environment variable DUCK_SETTINGS_MODULE is set correctly: {e}." ) from e # Update the default settings with custom ones. default_settings.update(user_settings) settings = Settings(default_settings) settings.source = user_settings.source 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