Created
August 31, 2020 06:34
-
-
Save gimntut/c1d61029e44461d7510f786557bb92c3 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
from typing import Any | |
from django.conf import settings | |
from django.utils.module_loading import import_string | |
def import_from_string(val, setting_name): | |
""" | |
Attempt to import a class from a string representation. | |
""" | |
try: | |
return import_string(val) | |
except ImportError as e: | |
msg = "Could not import '%s' for setting '%s'. %s: %s." % (val, setting_name, e.__class__.__name__, e) | |
raise ImportError(msg) | |
def perform_import(val, setting_name): | |
""" | |
If the given setting is a string import notation, | |
then perform the necessary import or imports. | |
""" | |
if val is None: | |
return None | |
elif isinstance(val, str): | |
return import_from_string(val, setting_name) | |
elif isinstance(val, (list, tuple)): | |
return [import_from_string(item, setting_name) for item in val] | |
return val | |
class SimpleAppSettings: | |
defaults = {} | |
transforms = {} | |
import_strings = [] | |
def __init__(self, setting_name, defaults=None, import_strings=None, transforms=None) -> None: | |
super().__init__() | |
self.defaults = defaults or self.defaults | |
self.user_settings = getattr(settings, setting_name, {}) | |
self.import_strings = import_strings or self.import_strings | |
self.transforms = transforms or self.transforms | |
self._cached_attrs = set() | |
def __getattr__(self, attr): | |
if attr not in self.defaults: | |
raise AttributeError("Invalid setting: '%s'" % attr) | |
try: | |
# Check if present in user settings | |
val = self.user_settings[attr] | |
except KeyError: | |
# Fall back to defaults | |
val = self.defaults[attr] | |
if attr in self.transforms: | |
val = self.transforms[attr](val) | |
# Coerce import strings into classes | |
if attr in self.import_strings: | |
val = perform_import(val, attr) | |
# Cache the result | |
self._cached_attrs.add(attr) | |
setattr(self, attr, val) | |
return val | |
def __dir__(self): | |
return [*self.defaults.keys()] | |
class AppSettings(SimpleAppSettings): | |
def __init__(self, setting_name, defaults=None, import_strings=None, transforms=None) -> None: | |
d = {k: v for k, v in self.__class__.__dict__.items() if not k.startswith('_')} | |
d.pop('defaults', None) | |
d.pop('transforms', None) | |
d.pop('import_strings', None) | |
d.update(defaults or {}) | |
super().__init__(setting_name, defaults=d, import_strings=import_strings, transforms=transforms) | |
def __getattribute__(self, name: str) -> Any: | |
if name != 'defaults' and name in self.defaults: | |
return self.__getattr__(name) | |
return object.__getattribute__(self, name) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment