diff --git a/src/appsettings/settings.py b/src/appsettings/settings.py index 592bd47..5316499 100644 --- a/src/appsettings/settings.py +++ b/src/appsettings/settings.py @@ -12,6 +12,13 @@ from django.conf import settings from django.core.exceptions import ValidationError +from django.core.validators import ( + MaxLengthValidator, MaxValueValidator, MinLengthValidator, + MinValueValidator) + +from .validators import ( + DictKeysTypeValidator, DictValuesTypeValidator, TypeValidator, + ValuesTypeValidator) # Type checkers =============================================================== @@ -32,6 +39,7 @@ def __init__(self, base_type=None): Args: base_type (type): the type to check against value's type. """ + warnings.warn("Checkers are deprecated in favor of validators.", DeprecationWarning) self.base_type = base_type def __call__(self, name, value): @@ -581,8 +589,10 @@ def transform(self, value): class BooleanSetting(Setting): """Boolean setting.""" + default_validators = (TypeValidator(bool), ) + def __init__(self, name='', default=True, required=False, - prefix='', call_default=True, transform_default=False): + prefix='', call_default=True, transform_default=False, validators=()): """ Initialization method. @@ -594,19 +604,21 @@ def __init__(self, name='', default=True, required=False, the setting's prefix (overrides ``AppSettings.Meta`` prefix). call_default (bool): whether to call the default (if callable). transform_default (bool): whether to transform the default value. + validators (list of callables): list of additional validators to use. """ super(BooleanSetting, self).__init__( name=name, default=default, required=required, prefix=prefix, - call_default=call_default, transform_default=transform_default, - checker=BooleanTypeChecker()) + call_default=call_default, transform_default=transform_default, validators=validators) class IntegerSetting(Setting): """Integer setting.""" + default_validators = (TypeValidator(int), ) + def __init__(self, name='', default=0, required=False, prefix='', call_default=True, transform_default=False, - **checker_kwargs): + validators=(), minimum=None, maximum=None): """ Initialization method. @@ -618,20 +630,25 @@ def __init__(self, name='', default=0, required=False, the setting's prefix (overrides ``AppSettings.Meta`` prefix). call_default (bool): whether to call the default (if callable). transform_default (bool): whether to transform the default value. - **checker_kwargs: keyword arguments passed to the checker. + validators (list of callables): list of additional validators to use. + minimum (int): a minimum value (included). + maximum (int): a maximum value (included). """ super(IntegerSetting, self).__init__( name=name, default=default, required=required, prefix=prefix, call_default=call_default, transform_default=transform_default, - checker=IntegerTypeChecker(**checker_kwargs)) + validators=validators) + if minimum is not None: + self.validators.append(MinValueValidator(minimum)) + if maximum is not None: + self.validators.append(MaxValueValidator(maximum)) -class PositiveIntegerSetting(Setting): +class PositiveIntegerSetting(IntegerSetting): """Positive integer setting.""" def __init__(self, name='', default=0, required=False, - prefix='', call_default=True, transform_default=False, - **checker_kwargs): + prefix='', call_default=True, transform_default=False, validators=(), maximum=None): """ Initialization method. @@ -643,20 +660,23 @@ def __init__(self, name='', default=0, required=False, the setting's prefix (overrides ``AppSettings.Meta`` prefix). call_default (bool): whether to call the default (if callable). transform_default (bool): whether to transform the default value. - **checker_kwargs: keyword arguments passed to the checker. + validators (list of callables): list of additional validators to use. + maximum (int): a maximum value (included). """ super(PositiveIntegerSetting, self).__init__( name=name, default=default, required=required, prefix=prefix, - call_default=call_default, transform_default=transform_default, - checker=IntegerTypeChecker(minimum=0, **checker_kwargs)) + call_default=call_default, transform_default=transform_default, validators=validators, + minimum=0, maximum=maximum) -class FloatSetting(Setting): +class FloatSetting(IntegerSetting): """Float setting.""" + default_validators = (TypeValidator(float), ) + def __init__(self, name='', default=0.0, required=False, prefix='', call_default=True, transform_default=False, - **checker_kwargs): + validators=(), minimum=None, maximum=None): """ Initialization method. @@ -668,20 +688,21 @@ def __init__(self, name='', default=0.0, required=False, the setting's prefix (overrides ``AppSettings.Meta`` prefix). call_default (bool): whether to call the default (if callable). transform_default (bool): whether to transform the default value. - **checker_kwargs: keyword arguments passed to the checker. + validators (list of callables): list of additional validators to use. + minimum (int): a minimum value (included). + maximum (int): a maximum value (included). """ super(FloatSetting, self).__init__( name=name, default=default, required=required, prefix=prefix, call_default=call_default, transform_default=transform_default, - checker=FloatTypeChecker(**checker_kwargs)) + validators=validators, minimum=minimum, maximum=maximum) -class PositiveFloatSetting(Setting): +class PositiveFloatSetting(FloatSetting): """Positive float setting.""" def __init__(self, name='', default=0.0, required=False, - prefix='', call_default=True, transform_default=False, - **checker_kwargs): + prefix='', call_default=True, transform_default=False, validators=(), maximum=None): """ Initialization method. @@ -693,12 +714,13 @@ def __init__(self, name='', default=0.0, required=False, the setting's prefix (overrides ``AppSettings.Meta`` prefix). call_default (bool): whether to call the default (if callable). transform_default (bool): whether to transform the default value. - **checker_kwargs: keyword arguments passed to the checker. + validators (list of callables): list of additional validators to use. + maximum (int): a maximum value (included). """ super(PositiveFloatSetting, self).__init__( name=name, default=default, required=required, prefix=prefix, - call_default=call_default, transform_default=transform_default, - checker=FloatTypeChecker(minimum=0.0, **checker_kwargs)) + call_default=call_default, transform_default=transform_default, validators=validators, + minimum=0, maximum=maximum) # Iterable settings ----------------------------------------------------------- @@ -706,8 +728,8 @@ class IterableSetting(Setting): """Iterable setting.""" def __init__(self, name='', default=None, required=False, - prefix='', call_default=True, transform_default=False, - **checker_kwargs): + prefix='', call_default=True, transform_default=False, validators=(), + item_type=None, min_length=None, max_length=None, empty=None): """ Initialization method. @@ -719,21 +741,35 @@ def __init__(self, name='', default=None, required=False, the setting's prefix (overrides ``AppSettings.Meta`` prefix). call_default (bool): whether to call the default (if callable). transform_default (bool): whether to transform the default value. - **checker_kwargs: keyword arguments passed to the checker. + validators (list of callables): list of additional validators to use. + item_type (type): the type of the items inside the iterable. + min_length (int): minimum length of the iterable (included). + max_length (int): maximum length of the iterable (included). + empty (bool): whether empty iterable is allowed. Deprecated in favor of min_length. """ super(IterableSetting, self).__init__( name=name, default=default, required=required, prefix=prefix, - call_default=call_default, transform_default=transform_default, - checker=IterableTypeChecker( - iter_type=object, **checker_kwargs)) + call_default=call_default, transform_default=transform_default, validators=validators) + if item_type is not None: + self.validators.append(ValuesTypeValidator(item_type)) + if empty is not None: + warnings.warn("Empty argument is deprecated, use min_length instead.", DeprecationWarning) + if not empty: + min_length = 1 + if min_length is not None: + self.validators.append(MinLengthValidator(min_length)) + if max_length is not None: + self.validators.append(MaxLengthValidator(max_length)) class StringSetting(Setting): """String setting.""" + default_validators = (TypeValidator(str), ) + def __init__(self, name='', default='', required=False, - prefix='', call_default=True, transform_default=False, - **checker_kwargs): + prefix='', call_default=True, transform_default=False, validators=(), + min_length=None, max_length=None, empty=True): """ Initialization method. @@ -745,20 +781,30 @@ def __init__(self, name='', default='', required=False, the setting's prefix (overrides ``AppSettings.Meta`` prefix). call_default (bool): whether to call the default (if callable). transform_default (bool): whether to transform the default value. - **checker_kwargs: keyword arguments passed to the checker. + validators (list of callables): list of additional validators to use. + min_length (int): minimum length of the iterable (included). + max_length (int): maximum length of the iterable (included). + empty (bool): whether empty iterable is allowed. Deprecated in favor of min_length. """ super(StringSetting, self).__init__( name=name, default=default, required=required, prefix=prefix, - call_default=call_default, transform_default=transform_default, - checker=StringTypeChecker(**checker_kwargs)) - - -class ListSetting(Setting): + call_default=call_default, transform_default=transform_default, validators=validators) + if empty is not None: + warnings.warn("Empty argument is deprecated, use min_length instead.", DeprecationWarning) + if not empty: + min_length = 1 + if min_length is not None: + self.validators.append(MinLengthValidator(min_length)) + if max_length is not None: + self.validators.append(MaxLengthValidator(max_length)) + + +class ListSetting(IterableSetting): """List setting.""" - def __init__(self, name='', default=list, required=False, - prefix='', call_default=True, transform_default=False, - **checker_kwargs): + default_validators = (TypeValidator(list), ) + + def __init__(self, name='', default=list, *args, **kwargs): """ Initialization method. @@ -770,20 +816,21 @@ def __init__(self, name='', default=list, required=False, the setting's prefix (overrides ``AppSettings.Meta`` prefix). call_default (bool): whether to call the default (if callable). transform_default (bool): whether to transform the default value. - **checker_kwargs: keyword arguments passed to the checker. + validators (list of callables): list of additional validators to use. + item_type (type): the type of the items inside the iterable. + min_length (int): minimum length of the iterable (included). + max_length (int): maximum length of the iterable (included). + empty (bool): whether empty iterable is allowed. Deprecated in favor of min_length. """ - super(ListSetting, self).__init__( - name=name, default=default, required=required, prefix=prefix, - call_default=call_default, transform_default=transform_default, - checker=ListTypeChecker(**checker_kwargs)) + super(ListSetting, self).__init__(name=name, default=default, *args, **kwargs) -class SetSetting(Setting): +class SetSetting(IterableSetting): """Set setting.""" - def __init__(self, name='', default=set, required=False, - prefix='', call_default=True, transform_default=False, - **checker_kwargs): + default_validators = (TypeValidator(set), ) + + def __init__(self, name='', default=set, *args, **kwargs): """ Initialization method. @@ -795,20 +842,21 @@ def __init__(self, name='', default=set, required=False, the setting's prefix (overrides ``AppSettings.Meta`` prefix). call_default (bool): whether to call the default (if callable). transform_default (bool): whether to transform the default value. - **checker_kwargs: keyword arguments passed to the checker. + validators (list of callables): list of additional validators to use. + item_type (type): the type of the items inside the iterable. + min_length (int): minimum length of the iterable (included). + max_length (int): maximum length of the iterable (included). + empty (bool): whether empty iterable is allowed. Deprecated in favor of min_length. """ - super(SetSetting, self).__init__( - name=name, default=default, required=required, prefix=prefix, - call_default=call_default, transform_default=transform_default, - checker=SetTypeChecker(**checker_kwargs)) + super(SetSetting, self).__init__(name=name, default=default, *args, **kwargs) -class TupleSetting(Setting): +class TupleSetting(IterableSetting): """Tuple setting.""" - def __init__(self, name='', default=tuple, required=False, - prefix='', call_default=True, transform_default=False, - **checker_kwargs): + default_validators = (TypeValidator(tuple), ) + + def __init__(self, name='', default=tuple, *args, **kwargs): """ Initialization method. @@ -820,21 +868,24 @@ def __init__(self, name='', default=tuple, required=False, the setting's prefix (overrides ``AppSettings.Meta`` prefix). call_default (bool): whether to call the default (if callable). transform_default (bool): whether to transform the default value. - **checker_kwargs: keyword arguments passed to the checker. + validators (list of callables): list of additional validators to use. + item_type (type): the type of the items inside the iterable. + min_length (int): minimum length of the iterable (included). + max_length (int): maximum length of the iterable (included). + empty (bool): whether empty iterable is allowed. Deprecated in favor of min_length. """ - super(TupleSetting, self).__init__( - name=name, default=default, required=required, prefix=prefix, - call_default=call_default, transform_default=transform_default, - checker=SetTypeChecker(**checker_kwargs)) + super(TupleSetting, self).__init__(name=name, default=default, *args, **kwargs) # Dict settings --------------------------------------------------------------- class DictSetting(Setting): """Dict setting.""" + default_validators = (TypeValidator(dict), ) + def __init__(self, name='', default=dict, required=False, - prefix='', call_default=True, transform_default=False, - **checker_kwargs): + prefix='', call_default=True, transform_default=False, validators=(), + key_type=None, value_type=None, min_length=None, max_length=None, empty=None): """ Initialization method. @@ -846,12 +897,27 @@ def __init__(self, name='', default=dict, required=False, the setting's prefix (overrides ``AppSettings.Meta`` prefix). call_default (bool): whether to call the default (if callable). transform_default (bool): whether to transform the default value. - **checker_kwargs: keyword arguments passed to the checker. + validators (list of callables): list of additional validators to use. + key_type: the type of the dict keys. + value_type (type): the type of dict values. + min_length (int): Noop. Deprecated. + max_length (int): Noop. Deprecated. + empty (bool): whether empty iterable is allowed. Deprecated in favor of MinLengthValidator. """ super(DictSetting, self).__init__( name=name, default=default, required=required, prefix=prefix, - call_default=call_default, transform_default=transform_default, - checker=DictTypeChecker(**checker_kwargs)) + call_default=call_default, transform_default=transform_default, validators=validators) + if key_type is not None: + self.validators.append(DictKeysTypeValidator(key_type)) + if value_type is not None: + self.validators.append(DictValuesTypeValidator(value_type)) + if empty is not None: + warnings.warn("Empty argument is deprecated, use MinLengthValidator instead.", DeprecationWarning) + self.validators.append(MinLengthValidator(1)) + if min_length is not None: + warnings.warn("Argument min_length does nothing and is deprecated.", DeprecationWarning) + if max_length is not None: + warnings.warn("Argument max_length does nothing and is deprecated.", DeprecationWarning) # Complex settings ------------------------------------------------------------ @@ -862,9 +928,11 @@ class ObjectSetting(Setting): This setting allows to return an object given its Python path (a.b.c). """ + default_validators = (TypeValidator(str), ) + def __init__(self, name='', default=None, required=False, - prefix='', call_default=False, transform_default=False, - **checker_kwargs): + prefix='', call_default=True, transform_default=False, validators=(), + min_length=None, max_length=None, empty=True): """ Initialization method. @@ -876,12 +944,20 @@ def __init__(self, name='', default=None, required=False, the setting's prefix (overrides ``AppSettings.Meta`` prefix). call_default (bool): whether to call the default (if callable). transform_default (bool): whether to transform the default value. - **checker_kwargs: keyword arguments passed to the checker. + validators (list of callables): list of additional validators to use. + min_length (int): Noop. Deprecated. + max_length (int): Noop. Deprecated. + empty (bool): Noop. Deprecated. """ super(ObjectSetting, self).__init__( name=name, default=default, required=required, prefix=prefix, - call_default=call_default, transform_default=transform_default, - checker=ObjectTypeChecker(**checker_kwargs)) + call_default=call_default, transform_default=transform_default, validators=validators) + if min_length is not None: + warnings.warn("Argument min_length does nothing and is deprecated.", DeprecationWarning) + if max_length is not None: + warnings.warn("Argument max_length does nothing and is deprecated.", DeprecationWarning) + if empty is not None: + warnings.warn("Argument empty does nothing and is deprecated.", DeprecationWarning) def transform(self, path): """ @@ -926,9 +1002,7 @@ def transform(self, path): class NestedSetting(DictSetting): """Nested setting.""" - def __init__(self, settings, name='', default=dict, - required=False, prefix='', call_default=True, - transform_default=False, **checker_kwargs): + def __init__(self, settings, *args, **kwargs): """ Initialization method. @@ -941,12 +1015,14 @@ def __init__(self, settings, name='', default=dict, the setting's prefix (overrides ``AppSettings.Meta`` prefix). call_default (bool): whether to call the default (if callable). transform_default (bool): whether to transform the default value. - **checker_kwargs: keyword arguments passed to the checker. + validators (list of callables): list of additional validators to use. + key_type: the type of the dict keys. + value_type (type): the type of dict values. + min_length (int): minimum length of the iterable (included). + max_length (int): maximum length of the iterable (included). + empty (bool): whether empty iterable is allowed. Deprecated in favor of min_length. """ - super(NestedSetting, self).__init__( - name=name, default=default, required=required, prefix=prefix, - call_default=call_default, transform_default=transform_default, - key_type=str, **checker_kwargs) + super(NestedSetting, self).__init__(*args, **kwargs) for subname, subsetting in settings.items(): if subsetting.name == '': subsetting.name = subname @@ -983,11 +1059,12 @@ def check(self): AttributeError: if the setting is missing and required. ValueError: (or other Exception) if the raw value is invalid. """ - try: - value = self.raw_value - except (AttributeError, KeyError) as err: - self._reraise_if_required(err) - else: - self.checker(self.full_name, value) - for subsetting in self.settings.values(): + super(NestedSetting, self).check() + errors = [] + for subsetting in self.settings.values(): + try: subsetting.check() + except ValidationError as error: + errors.extend(error.messages) + if errors: + raise ValidationError(errors)