Repository: conda/conda

Issue:
Conda mangles channels with hyphens
Update: This is not related to hyphens, only substrings.

Seen on versions: 4.3.21, 4.3.23 (maybe more versions)

Conda mangles channels that are a substring match when using default_channels and channels.  The channel name "substring-channels" gets mangled into "substring/-channels".  It looks like it happens when there is a substring that matches.  

```
default_channels:
   - http://test/conda/substring
   - http://test/conda/default-channels

channels:
   - http://test/conda/substring-channels
   - http://test/conda/not_substring-channels
   - defaults
```

Here is the output of conda info
```
Current conda install:

               platform : linux-64
          conda version : 4.3.23
       conda is private : False
      conda-env version : 4.3.23
    conda-build version : 2.1.0
         python version : 2.7.13.final.0
       requests version : 2.12.4
       root environment : /home/ec2-user/miniconda2  (writable)
    default environment : /home/ec2-user/miniconda2
       envs directories : /home/ec2-user/miniconda2/envs
                          /home/ec2-user/.conda/envs
          package cache : /home/ec2-user/miniconda2/pkgs
                          /home/ec2-user/.conda/pkgs
           channel URLs : http://test/conda/substring/-channels/linux-64   <<<<<<<<<<<<<<====
                          http://test/conda/substring/-channels/noarch   <<<<<<<<<<<<<<====
                          http://test/conda/not_substring-channels/linux-64
                          http://test/conda/not_substring-channels/noarch
                          http://test/conda/substring/linux-64
                          http://test/conda/substring/noarch
                          http://test/conda/default-channels/linux-64
                          http://test/conda/default-channels/noarch
            config file : /home/ec2-user/miniconda2/.condarc
             netrc file : None
           offline mode : False
             user-agent : conda/4.3.23 requests/2.12.4 CPython/2.7.13 Linux/4.4.11-23.53.amzn1.x86_64 / glibc/2.17    
                UID:GID : 500:500
```

Also `conda info --unsafe-channels` seems to give weird output.  It doesn't expand "defaults".


Discussion:
I've updated this to say that it is substrings between channels and default_channels that causes this bugs, not hyphens.  Here is a complete example.  Substrings from default_channels cause problems with "channels".

```
default_channels:
   - http://test/conda/substring  <<<<< PROBLEM
   - http://test/conda/default-channels
   - http://test/conda/sub            <<<<< PROBLEM
   - http://test/conda/sub2          <<<<< Not a problem
   - http://test/conda/sub2sub2  <<<<< not a problem

channels:
   - http://test/conda/substring-channels
   - http://test/conda/not_substring-channels
   - http://test/conda/subsub
   - defaults
```
It's this bit of logic:  https://github.com/conda/conda/blob/master/conda/models/channel.py#L397-L404

I don't know why this does this, but that splits off anything that is a substring and joins it as a separate path component. 
```
    # Step 4. custom_channels matches
    for name, channel in sorted(context.custom_channels.items(), reverse=True,
                                key=lambda x: len(x[0])):
        that_test_url = join_url(channel.location, channel.name)
        if test_url.startswith(that_test_url):
            subname = test_url.replace(that_test_url, '', 1).strip('/')
            return (channel.location, join_url(channel.name, subname), scheme,
                    channel.auth, channel.token)
```

Resolving patch:
diff --git a/conda/models/channel.py b/conda/models/channel.py
--- a/conda/models/channel.py
+++ b/conda/models/channel.py
@@ -95,14 +95,14 @@ def _read_channel_configuration(scheme, host, port, path):
     for name, channel in sorted(context.custom_channels.items(), reverse=True,
                                 key=lambda x: len(x[0])):
         that_test_url = join_url(channel.location, channel.name)
-        if test_url.startswith(that_test_url):
+        if tokenized_startswith(test_url.split('/'), that_test_url.split('/')):
             subname = test_url.replace(that_test_url, '', 1).strip('/')
             return (channel.location, join_url(channel.name, subname), scheme,
                     channel.auth, channel.token)
 
     # Step 5. channel_alias match
     ca = context.channel_alias
-    if ca.location and test_url.startswith(ca.location):
+    if ca.location and tokenized_startswith(test_url.split('/'), ca.location.split('/')):
         name = test_url.replace(ca.location, '', 1).strip('/') or None
         return ca.location, name, scheme, ca.auth, ca.token
 


Test patch:
diff --git a/tests/models/test_channel.py b/tests/models/test_channel.py
--- a/tests/models/test_channel.py
+++ b/tests/models/test_channel.py
@@ -887,3 +887,35 @@ def test_regression_against_unknown_none(self):
         assert channel.base_url is None
         assert channel.url() == defaults.url()
         assert channel.urls() == defaults.urls()
+
+
+class OtherChannelParsingTests(TestCase):
+
+    @classmethod
+    def setUpClass(cls):
+        string = dals("""
+        default_channels:
+           - http://test/conda/anaconda
+        channels:
+           - http://test/conda/anaconda-cluster
+        """)
+        reset_context()
+        rd = odict(testdata=YamlRawParameter.make_raw_parameters('testdata', yaml_load(string)))
+        context._set_raw_data(rd)
+        Channel._reset_state()
+
+        cls.platform = context.subdir
+
+    @classmethod
+    def tearDownClass(cls):
+        reset_context()
+
+    def test_channels_with_dashes(self):
+        # regression test for #5763
+        assert context.channels == ('http://test/conda/anaconda-cluster',)
+        channel_urls = prioritize_channels(context.channels)
+        assert channel_urls == odict((
+            ('http://test/conda/anaconda-cluster/%s' % context.subdir, ('http://test/conda/anaconda-cluster', 0)),
+            ('http://test/conda/anaconda-cluster/noarch', ('http://test/conda/anaconda-cluster', 0)),
+        ))
+
diff --git a/tests/test_activate.py b/tests/test_activate.py
--- a/tests/test_activate.py
+++ b/tests/test_activate.py
@@ -191,7 +191,7 @@ def test_activate_bad_env_keeps_existing_good_env(shell):
 
 @pytest.mark.installed
 def test_activate_deactivate(shell):
-    if shell == "bash.exe" and datetime.now() < datetime(2017, 8, 1):
+    if shell == "bash.exe" and datetime.now() < datetime(2017, 9, 1):
         pytest.xfail("fix this soon")
     shell_vars = _format_vars(shell)
     with TemporaryDirectory(prefix=ENVS_PREFIX, dir=dirname(__file__)) as envs:
@@ -208,7 +208,7 @@ def test_activate_deactivate(shell):
 
 @pytest.mark.installed
 def test_activate_root_simple(shell):
-    if shell == "bash.exe" and datetime.now() < datetime(2017, 8, 1):
+    if shell == "bash.exe" and datetime.now() < datetime(2017, 9, 1):
         pytest.xfail("fix this soon")
     shell_vars = _format_vars(shell)
     with TemporaryDirectory(prefix=ENVS_PREFIX, dir=dirname(__file__)) as envs:
diff --git a/tests/test_create.py b/tests/test_create.py
--- a/tests/test_create.py
+++ b/tests/test_create.py
@@ -674,7 +674,7 @@ def test_update_deps_flag_absent(self):
             assert package_is_installed(prefix, 'itsdangerous-0.23')
             assert package_is_installed(prefix, 'flask')
 
-    @pytest.mark.xfail(datetime.now() < datetime(2017, 8, 1), reason="#5263", strict=True)
+    @pytest.mark.xfail(datetime.now() < datetime(2017, 9, 1), reason="#5263", strict=True)
     def test_update_deps_flag_present(self):
         with make_temp_env("python=2 itsdangerous=0.23") as prefix:
             assert package_is_installed(prefix, 'python-2')
diff --git a/tests/test_export.py b/tests/test_export.py
--- a/tests/test_export.py
+++ b/tests/test_export.py
@@ -31,7 +31,7 @@ def test_basic(self):
             output2, error= run_command(Commands.LIST, prefix2, "-e")
             self.assertEqual(output, output2)
 
-    @pytest.mark.xfail(datetime.now() < datetime(2017, 8, 1), reason="Bring back `conda list --export` #3445", strict=True)
+    @pytest.mark.xfail(datetime.now() < datetime(2017, 9, 1), reason="Bring back `conda list --export` #3445", strict=True)
     def test_multi_channel_export(self):
         """
             When try to import from txt