Source code for quade.settings

# This module is inspired by and indebted to Hypothesis' _settings module.
# https://github.com/HypothesisWorks/hypothesis-python/blob/master/src/hypothesis/_settings.py
from collections import Iterable
from inspect import cleandoc

from attr import attrib, attrs
from django.conf import settings
from django.core.exceptions import ImproperlyConfigured
from django.utils import six
from six import with_metaclass


__all__ = [
    'AllEnvs',
    'DebugEnvs',
    'Settings',
    'is_superuser',
]


@attrs
class Setting(object):
    """A descriptor that prevents deletion and provides a docstring."""
    name = attrib()
    default = attrib()
    default_description = attrib(default='')
    description = attrib(default='')
    validator = attrib(default=None)

    def __get__(self, obj, objtype=None):
        if obj is None:  # pragma: no cover
            return self
        else:
            try:
                return obj.__dict__[self.name]
            except KeyError:  # pragma: no cover
                raise AttributeError(self.name)

    def __set__(self, obj, value):
        obj.__dict__[self.name] = value

    def __delete__(self, obj):
        raise AttributeError('Cannot delete attribute {}'.format(self.name))

    @property
    def __doc__(self):
        return "{}\n\nDefault: {}".format(
            cleandoc(self.description),
            self.default_description or '``{}``'.format(self.default)
        )


[docs]class AllEnvs(object): """Enable Quade unconditionally.""" @staticmethod def func(_): return True
[docs]class DebugEnvs(object): """Enable Quade if the environment has DEBUG = True.""" @staticmethod def func(s): return s.DEBUG
[docs]def is_superuser(view): """Allow access to the view if the requesting user is a superuser.""" return view.request.user.is_superuser
all_settings = {} def validate_allowed_envs(val): if any([val in [AllEnvs, DebugEnvs], callable(val), isinstance(val, Iterable)]): return val else: raise TypeError def validate_access_test_func(val): if callable(val): return val else: raise TypeError def validate_boolean(val): if isinstance(val, bool): return val else: raise TypeError class SettingsMeta(type): def __new__(cls, name, bases, dct): obj = super(SettingsMeta, cls).__new__(cls, name, bases, dct) obj.define_setting( name='fixtures_file', default='quade.fixtures', description="""A dotted path to the location of your fixtures.""", ) obj.define_setting( name='allowed_envs', default=DebugEnvs, default_description=""":class:`.DebugEnvs`""", description="""Controls which environments Quade can run on. Allowed values are: - :class:`.DebugEnvs` - :class:`.AllEnvs` - a string -- your ``django.conf.settings.ENV`` is compared exactly to this string - an iterable -- your ``django.conf.settings.ENV`` is checked exactly for membership in the iterable - any callable that accepts one argument, the Django settings object If Quade is not enabled, fixtures will not be loaded, but views will be available to users with appropriate access, application models and tables can be accessed, etc. """, validator=validate_allowed_envs, ) obj.define_setting( name='access_test_func', default=is_superuser, default_description=""":func:`.is_superuser`""", description="""A callable that restricts access to Quade's views. The callable should accept one argument (a Django view class).""", validator=validate_access_test_func ) obj.define_setting( name='use_celery', default=False, description="""Whether to use Celery to set up a Record. Enabling this is useful if your fixtures take a long time to run. See :doc:`celery` for additional details. """, validator=validate_boolean, ) return obj
[docs]class Settings(with_metaclass(SettingsMeta)): _WHITELISTED_PROPERTIES = ['_construction_complete'] def __init__(self, **kwargs): self._construction_complete = False for setting in all_settings.values(): val = kwargs.pop(setting.name, setting.default) if setting.validator: try: val = setting.validator(val) except: raise ImproperlyConfigured( "{} is not a valid value for Quade setting {}".format(val, setting.name) ) setattr(self, setting.name, val) if kwargs: suffix = '' if len(kwargs) == 1 else 's' raise AttributeError("No such setting{}: '{}'".format(suffix, "', '".join(kwargs.keys()))) self._construction_complete = True @classmethod def define_setting(cls, name, **kwargs): s = Setting(name, **kwargs) all_settings[name] = s setattr(cls, name, s) @property def allowed(self): """Based on the settings, determine if scenarios are allowed to run on this environment.""" if self.allowed_envs in [AllEnvs, DebugEnvs]: func = self.allowed_envs.func elif callable(self.allowed_envs): func = self.allowed_envs elif isinstance(self.allowed_envs, six.string_types): def func(s): return s.ENV == self.allowed_envs elif isinstance(self.allowed_envs, Iterable): def func(s): return s.ENV in self.allowed_envs if func(settings): return True else: return False def __setattr__(self, key, value): if key in self._WHITELISTED_PROPERTIES: return super(Settings, self).__setattr__(key, value) elif key in all_settings: if self._construction_complete: raise RuntimeError('Quade settings may not be modified during runtime.') else: return super(Settings, self).__setattr__(key, value) else: # pragma: no cover raise AttributeError('No such setting {}'.format(key))