Repository: conda/conda

Issue:
Error when installing r-essential on windows 7
When I try to install the r-essentials, I get the following error:

 $ C:\ProgramData\Anaconda3\Scripts\conda-script.py install -c r r-essentials=1.5.2 

Traceback (most recent call last):
 File "C:\ProgramData\Anaconda3\lib\site-packages\conda\exceptions.py", line 617, in conda_exception_handler
 return_value = func(*args, **kwargs)
 File "C:\ProgramData\Anaconda3\lib\site-packages\conda\cli\main.py", line 137, in _main
 exit_code = args.func(args, p)
 File "C:\ProgramData\Anaconda3\lib\site-packages\conda\cli\main_install.py", line 80, in execute
 install(args, parser, 'install')
 File "C:\ProgramData\Anaconda3\lib\site-packages\conda\cli\install.py", line 118, in install
 context.validate_configuration()
 File "C:\ProgramData\Anaconda3\lib\site-packages\conda\common\configuration.py", line 830, in validate_configuration
 raise_errors(tuple(chain.from_iterable((errors, post_errors))))
 File "C:\ProgramData\Anaconda3\lib\site-packages\conda\common\configuration.py", line 828, in 
 for name in self.parameter_names)
 File "C:\ProgramData\Anaconda3\lib\site-packages\conda\common\configuration.py", line 821, in _collect_validation_error
 func(*args, **kwargs)
 File "C:\ProgramData\Anaconda3\lib\site-packages\conda\common\configuration.py", line 446, in get
 result = typify_data_structure(self._merge(matches) if matches else self.default,
 File "C:\ProgramData\Anaconda3\lib\site-packages\conda\common\configuration.py", line 662, in _merge
 for match in relevant_matches)
 File "C:\ProgramData\Anaconda3\lib\site-packages\conda\common\configuration.py", line 662, in 
 for match in relevant_matches)
 File "C:\ProgramData\Anaconda3\lib\site-packages\conda\common\compat.py", line 72, in iteritems
 return iter(d.items(**kw))
 AttributeError: 'NoneType' object has no attribute 'items'


Current conda install:

platform : win-64
 conda version : 4.3.8
 conda is private : False
 conda-env version : 4.3.8
 conda-build version : not installed
 python version : 3.6.0.final.0
 requests version : 2.12.4
 root environment : C:\ProgramData\Anaconda3 (writable)
 default environment : C:\ProgramData\Anaconda3
 envs directories : C:\ProgramData\Anaconda3\envs
 package cache : C:\ProgramData\Anaconda3\pkgs
 channel URLs : https://conda.anaconda.org/r/win-64
                         https://conda.anaconda.org/r/noarch
                         https://repo.continuum.io/pkgs/free/win-64
                         https://repo.continuum.io/pkgs/free/noarch
                         https://repo.continuum.io/pkgs/r/win-64
                         https://repo.continuum.io/pkgs/r/noarch
                         https://repo.continuum.io/pkgs/pro/win-64
                         https://repo.continuum.io/pkgs/pro/noarch
                         https://repo.continuum.io/pkgs/msys2/win-64
                         https://repo.continuum.io/pkgs/msys2/noarch
 config file : C:\Users\xxx.condarc
 offline mode : False
 user-agent : conda/4.3.8 requests/2.12.4 CPython/3.6.0 Windows/7 Windows/6.1.7601

If somebody could help me with this, that would be great.
Thanks



Discussion:
Latest conda version is 4.3.13.  `conda update conda`
Thanks.
I had an error in the .condarc file. 
It seems, that the above error occurs when another error message is going to be created.
Oh yeah.  Syntax errors in yaml configuration files don't go through conda's normal error handler because it hasn't been initialized yet.

Can you give me what you actually had as contents for the bad condarc file?  I'll write a regression test for it...

Resolving patch:
diff --git a/conda/common/configuration.py b/conda/common/configuration.py
--- a/conda/common/configuration.py
+++ b/conda/common/configuration.py
@@ -17,7 +17,7 @@
 from __future__ import absolute_import, division, print_function, unicode_literals
 
 from abc import ABCMeta, abstractmethod
-from collections import Mapping, defaultdict
+from collections import Mapping, Sequence, defaultdict
 from glob import glob
 from itertools import chain
 from logging import getLogger
@@ -35,7 +35,7 @@
 from .._vendor.auxlib.collection import AttrDict, first, frozendict, last, make_immutable
 from .._vendor.auxlib.exceptions import ThisShouldNeverHappenError
 from .._vendor.auxlib.path import expand
-from .._vendor.auxlib.type_coercion import TypeCoercionError, typify_data_structure
+from .._vendor.auxlib.type_coercion import TypeCoercionError, typify, typify_data_structure
 
 try:
     from cytoolz.dicttoolz import merge
@@ -561,7 +561,6 @@ def __init__(self, element_type, default=(), aliases=(), validation=None,
 
     def collect_errors(self, instance, value, source="<<merged>>"):
         errors = super(SequenceParameter, self).collect_errors(instance, value)
-
         element_type = self._element_type
         for idx, element in enumerate(value):
             if not isinstance(element, element_type):
@@ -616,9 +615,10 @@ def repr_raw(self, raw_parameter):
     def _get_all_matches(self, instance):
         # this is necessary to handle argparse `action="append"`, which can't be set to a
         #   default value of NULL
-        matches, multikey_exceptions = super(SequenceParameter, self)._get_all_matches(instance)
+        # it also config settings like `channels: ~`
+        matches, exceptions = super(SequenceParameter, self)._get_all_matches(instance)
         matches = tuple(m for m in matches if m._raw_value is not None)
-        return matches, multikey_exceptions
+        return matches, exceptions
 
 
 class MapParameter(Parameter):
@@ -647,6 +647,7 @@ def collect_errors(self, instance, value, source="<<merged>>"):
             errors.extend(InvalidElementTypeError(self.name, val, source, type(val),
                                                   element_type, key)
                           for key, val in iteritems(value) if not isinstance(val, element_type))
+
         return errors
 
     def _merge(self, matches):
@@ -676,6 +677,12 @@ def repr_raw(self, raw_parameter):
                                          self._str_format_flag(valueflag)))
         return '\n'.join(lines)
 
+    def _get_all_matches(self, instance):
+        # it also config settings like `proxy_servers: ~`
+        matches, exceptions = super(MapParameter, self)._get_all_matches(instance)
+        matches = tuple(m for m in matches if m._raw_value is not None)
+        return matches, exceptions
+
 
 class ConfigurationType(type):
     """metaclass for Configuration"""


Test patch:
diff --git a/tests/common/test_configuration.py b/tests/common/test_configuration.py
--- a/tests/common/test_configuration.py
+++ b/tests/common/test_configuration.py
@@ -7,7 +7,8 @@
 from conda.common.compat import odict, string_types
 from conda.common.configuration import (Configuration, MapParameter, ParameterFlag,
                                         PrimitiveParameter, SequenceParameter, YamlRawParameter,
-                                        load_file_configs, MultiValidationError, InvalidTypeError)
+                                        load_file_configs, MultiValidationError, InvalidTypeError,
+                                        CustomValidationError)
 from conda.common.yaml import yaml_load
 from conda.common.configuration import ValidationError
 from os import environ, mkdir
@@ -125,6 +126,8 @@
           an_int: 2
           a_float: 1.2
           a_complex: 1+2j
+        proxy_servers:
+        channels:
     """),
 
 }
@@ -389,7 +392,15 @@ def test_validate_all(self):
         config.validate_configuration()
 
         config = SampleConfiguration()._set_raw_data(load_from_string_data('bad_boolean_map'))
-        raises(ValidationError, config.validate_configuration)
+        try:
+            config.validate_configuration()
+        except ValidationError as e:
+            # the `proxy_servers: ~` part of 'bad_boolean_map' is a regression test for #4757
+            #   in the future, the below should probably be a MultiValidationError
+            #   with TypeValidationError for 'proxy_servers' and 'channels'
+            assert isinstance(e, CustomValidationError)
+        else:
+            assert False
 
     def test_cross_parameter_validation(self):
         pass
@@ -411,3 +422,9 @@ def test_config_resets(self):
         with env_var("MYAPP_CHANGEPS1", "false"):
             config.__init__(app_name=appname)
             assert config.changeps1 is False
+
+    def test_empty_map_parameter(self):
+
+        config = SampleConfiguration()._set_raw_data(load_from_string_data('bad_boolean_map'))
+        config.check_source('bad_boolean_map')
+