From 2affca1d4593a0fd90aab02fcd012f1bad69c864 Mon Sep 17 00:00:00 2001 From: Kale Franz Date: Wed, 27 Jul 2016 16:13:25 -0500 Subject: [PATCH] add conda config --validate --- conda/__init__.py | 20 +++++++++- conda/_vendor/auxlib/type_coercion.py | 19 +++++++--- conda/cli/main_config.py | 13 +++++++ conda/common/configuration.py | 54 +++++++++------------------ 4 files changed, 64 insertions(+), 42 deletions(-) diff --git a/conda/__init__.py b/conda/__init__.py index 2c17749972c..03eecb2e92f 100644 --- a/conda/__init__.py +++ b/conda/__init__.py @@ -8,6 +8,7 @@ import os import sys +from conda.compat import text_type from ._vendor.auxlib.packaging import get_version from .common.compat import with_metaclass @@ -43,7 +44,11 @@ def __init__(cls, name, bases, attr): @with_metaclass(CondaErrorType) class CondaError(Exception): def __init__(self, *args, **kwargs): - super(CondaError, self).__init__(*args, **kwargs) + msg = kwargs.pop('msg', None) + if msg: + super(CondaError, self).__init__(msg, *args, **kwargs) + else: + super(CondaError, self).__init__(*args, **kwargs) def __repr__(self): ret_str = ' '.join([str(arg) for arg in self.args if not isinstance(arg, bool)]) @@ -52,3 +57,16 @@ def __repr__(self): def __str__(self): ret_str = ' '.join([str(arg) for arg in self.args if not isinstance(arg, bool)]) return ret_str + + +class CondaMultiError(CondaError): + + def __init__(self, errors, *args, **kwargs): + self.errors = errors + super(CondaError, self).__init__(*args, **kwargs) + + def __repr__(self): + return '\n'.join(text_type(e)for e in self.errors) + '\n' + + def __str__(self): + return '\n'.join(text_type(e) for e in self.errors) + '\n' diff --git a/conda/_vendor/auxlib/type_coercion.py b/conda/_vendor/auxlib/type_coercion.py index b224009b9e8..9f91f05e0f1 100644 --- a/conda/_vendor/auxlib/type_coercion.py +++ b/conda/_vendor/auxlib/type_coercion.py @@ -1,9 +1,11 @@ """Collection of functions to coerce conversion of types with an intelligent guess.""" -from itertools import chain from collections import Mapping -from re import compile, IGNORECASE -from .compat import integer_types, string_types, text_type, isiterable, iteritems, NoneType +from itertools import chain +from re import IGNORECASE, compile + +from .compat import NoneType, integer_types, isiterable, iteritems, string_types, text_type from .decorators import memoize, memoizeproperty +from .exceptions import AuxlibError __all__ = ["boolify", "typify", "maybecall", "listify", "numberify"] @@ -19,6 +21,13 @@ NO_MATCH = object() +class TypeCoercionError(AuxlibError, ValueError): + + def __init__(self, value, msg, *args, **kwargs): + self.value = value + super(TypeCoercionError, self).__init__(msg, *args, **kwargs) + + class _Regex(object): @memoizeproperty @@ -109,7 +118,7 @@ def numberify(value): candidate = _REGEX.convert_number(value) if candidate is not NO_MATCH: return candidate - raise ValueError("Cannot convert {0} to a number.".format(value)) + raise TypeCoercionError(value, "Cannot convert {0} to a number.".format(value)) def boolify(value, nullable=False, return_string=False): @@ -155,7 +164,7 @@ def boolify(value, nullable=False, return_string=False): except ValueError: if isinstance(value, string_types) and return_string: return value - raise ValueError("The value {0} cannot be boolified.".format(repr(value))) + raise TypeCoercionError(value, "The value %r cannot be boolified." % value) def boolify_truthy_string_ok(value): diff --git a/conda/cli/main_config.py b/conda/cli/main_config.py index 3d5224412de..dab7ed6d1bf 100644 --- a/conda/cli/main_config.py +++ b/conda/cli/main_config.py @@ -152,6 +152,11 @@ def configure_parser(sub_parsers): action="store_true", help="Display all identified configuration sources.", ) + action.add_argument( + "--validate", + action="store_true", + help="Validate all configuration sources.", + ) action.add_argument( "--get", nargs='*', @@ -261,6 +266,14 @@ def execute_config(args, parser): 'update_dependencies', 'use_pip')) print(yaml_dump(d)) + return + + if args.validate: + from conda.base.context import context + context.validate_all() + return + + if args.system: rc_path = sys_rc_path diff --git a/conda/common/configuration.py b/conda/common/configuration.py index fb3fe22f124..4ab306c4d26 100644 --- a/conda/common/configuration.py +++ b/conda/common/configuration.py @@ -27,12 +27,12 @@ from .._vendor.toolz.functoolz import excepts from .._vendor.toolz.itertoolz import concat, concatv, unique -from .. import CondaError +from .. import CondaError, CondaMultiError from .._vendor.auxlib.collection import first, frozendict, last from .._vendor.auxlib.exceptions import (Raise, ThisShouldNeverHappenError, ValidationError as AuxlibValidationError, AuxlibError) from .._vendor.auxlib.path import expand -from .._vendor.auxlib.type_coercion import typify_data_structure +from .._vendor.auxlib.type_coercion import typify_data_structure, TypeCoercionError from ..base.constants import EMPTY_MAP from .compat import (isiterable, iteritems, itervalues, odict, primitive_types, text_type, with_metaclass) @@ -63,7 +63,7 @@ def __init__(self, parameter_name, parameter_value, source, msg=None, **kwargs): self.parameter_name = parameter_name self.parameter_value = parameter_value self.source = source - if msg is not None: + if msg is None: msg = ("Parameter %s = %r declared in %s is invalid." % (parameter_name, parameter_value, source)) super(ConfigurationError, self).__init__(msg, **kwargs) @@ -108,29 +108,9 @@ def __init__(self, parameter_name, parameter_value, source, custom_message): msg=msg) -class MultiValidationError(ConfigurationError): +class MultiValidationError(CondaMultiError, ConfigurationError): def __init__(self, errors, *args, **kwargs): - if len(errors) > 1: - msg = 'Multiple errors' - else: - msg = '' - - error_message = '' - if isinstance(errors, dict): - error_items = errors.items() - #error_descriptions = [ - # ''.join(['Error with key %r in %r: ' % (item[0], item[1][0]), - # ''.join([str(exception) for exception in item[1]])]) - # for item in error_items - # ] - - error_descriptions = ['Error with key %r in %r' % (item[0], item[1]) for item in error_items] - - error_message = '\n'.join(error_descriptions) + '\n' - elif isinstance(errors, tuple): - error_message = '\n'.join(str(exception) for exception in errors) - - super(MultiValidationError, self).__init__(msg, error_message, *args, **kwargs) + super(MultiValidationError, self).__init__(errors, *args, **kwargs) class ParameterFlag(Enum): @@ -413,11 +393,11 @@ def _get_all_matches(self, instance): matches = [] multikey_exceptions = [] for filepath, raw_parameters in iteritems(instance.raw_data): - for match, error in self._raw_parameters_from_single_source(raw_parameters): - if match is not None: - matches.append(match) - if error: - multikey_exceptions.append(error) + match, error = self._raw_parameters_from_single_source(raw_parameters) + if match is not None: + matches.append(match) + if error: + multikey_exceptions.append(error) return matches, multikey_exceptions @abstractmethod @@ -434,10 +414,10 @@ def __get__(self, instance, instance_type): matches, errors = self._get_all_matches(instance) try: result = typify_data_structure(self._merge(matches) if matches else self.default) - except AuxlibError as e: + except TypeCoercionError as e: if 'result' not in locals(): result = None - errors.append(e) + errors.append(CustomValidationError(self.name, e.value, "<>", text_type(e))) self.validate_and_raise(instance, result, errors) instance._cache[self.name] = result return result @@ -747,9 +727,10 @@ def check_source(self, source): if match is not None and not isinstance(match, dict): try: typed_value = typify_data_structure(match.value(parameter.__class__), - parameter._type) - except AuxlibError as e: - validation_errors.append(e) + parameter._element_type) + except TypeCoercionError as e: + validation_errors.append(CustomValidationError(match.key, e.value, match.source, text_type(e))) + else: validation_result = parameter.collect_errors(self, typed_value, match.source) if validation_result is not True: @@ -761,6 +742,7 @@ def check_source(self, source): return validation_errors def validate_all(self): - validation_errors = [self.check_source(source) for source in self.raw_data] + validation_errors = list(chain.from_iterable(self.check_source(source) + for source in self.raw_data)) if validation_errors: raise MultiValidationError(validation_errors)