Skip to content

Commit

Permalink
add conda config --validate
Browse files Browse the repository at this point in the history
  • Loading branch information
kalefranz committed Jul 28, 2016
1 parent af0dfca commit 2affca1
Show file tree
Hide file tree
Showing 4 changed files with 64 additions and 42 deletions.
20 changes: 19 additions & 1 deletion conda/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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)])
Expand All @@ -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'
19 changes: 14 additions & 5 deletions conda/_vendor/auxlib/type_coercion.py
Original file line number Diff line number Diff line change
@@ -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"]

Expand All @@ -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
Expand Down Expand Up @@ -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):
Expand Down Expand Up @@ -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):
Expand Down
13 changes: 13 additions & 0 deletions conda/cli/main_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -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='*',
Expand Down Expand Up @@ -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
Expand Down
54 changes: 18 additions & 36 deletions conda/common/configuration.py
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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):
Expand Down Expand Up @@ -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
Expand All @@ -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, "<<merged>>", text_type(e)))
self.validate_and_raise(instance, result, errors)
instance._cache[self.name] = result
return result
Expand Down Expand Up @@ -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:
Expand All @@ -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)

0 comments on commit 2affca1

Please sign in to comment.