Repository: conda/conda

Issue:
Jupyter install on 3.6 env crashes conda with infinite recursion
This is a fairly fresh py3.6 env. Error trace follows (I snipped the hundreds of redundant frames in the middle of the recursion for readability, all non-redundant info kept):

```
(py36) denali[~]> conda install jupyter
Fetching package metadata .........
Solving package specifications: .
An unexpected error has occurred.
Please consider posting the following information to the
conda GitHub issue tracker at:

    https://github.com/conda/conda/issues

Current conda install:

               platform : osx-64
          conda version : 4.3.4
       conda is private : False
      conda-env version : 4.3.4
    conda-build version : not installed
         python version : 3.5.2.final.0
       requests version : 2.12.4
       root environment : /Users/fperez/usr/conda  (writable)
    default environment : /Users/fperez/usr/conda/envs/py36
       envs directories : /Users/fperez/usr/conda/envs
          package cache : /Users/fperez/usr/conda/pkgs
           channel URLs : https://repo.continuum.io/pkgs/free/osx-64
                          https://repo.continuum.io/pkgs/free/noarch
                          https://repo.continuum.io/pkgs/r/osx-64
                          https://repo.continuum.io/pkgs/r/noarch
                          https://repo.continuum.io/pkgs/pro/osx-64
                          https://repo.continuum.io/pkgs/pro/noarch
            config file : /Users/fperez/.condarc
           offline mode : False
             user-agent : conda/4.3.4 requests/2.12.4 CPython/3.5.2 Darwin/15.6.0 OSX/10.11.6
                UID:GID : 33212:20665


`$ /Users/fperez/usr/conda/envs/py36/bin/conda install jupyter`


    Traceback (most recent call last):
      File "/Users/fperez/usr/conda/lib/python3.5/site-packages/conda/exceptions.py", line 617, in conda_exception_handler
        return_value = func(*args, **kwargs)
      File "/Users/fperez/usr/conda/lib/python3.5/site-packages/conda/cli/main.py", line 158, in _main
        exit_code = args.func(args, p)
      File "/Users/fperez/usr/conda/lib/python3.5/site-packages/conda/cli/main_install.py", line 80, in execute
        install(args, parser, 'install')
      File "/Users/fperez/usr/conda/lib/python3.5/site-packages/conda/cli/install.py", line 240, in install
        channel_priority_map=_channel_priority_map, is_update=isupdate)
      File "/Users/fperez/usr/conda/lib/python3.5/contextlib.py", line 77, in __exit__
        self.gen.throw(type, value, traceback)
      File "/Users/fperez/usr/conda/lib/python3.5/site-packages/conda/cli/common.py", line 591, in json_progress_bars
        yield
      File "/Users/fperez/usr/conda/lib/python3.5/site-packages/conda/cli/install.py", line 240, in install
        channel_priority_map=_channel_priority_map, is_update=isupdate)
      File "/Users/fperez/usr/conda/lib/python3.5/site-packages/conda/plan.py", line 516, in install_actions_list
        for dists_by_prefix in required_solves]
      File "/Users/fperez/usr/conda/lib/python3.5/site-packages/conda/plan.py", line 516, in <listcomp>
        for dists_by_prefix in required_solves]
      File "/Users/fperez/usr/conda/lib/python3.5/site-packages/conda/plan.py", line 662, in get_actions_for_dists
        pkgs = r.install(specs, installed, update_deps=update_deps)
      File "/Users/fperez/usr/conda/lib/python3.5/site-packages/conda/resolve.py", line 814, in install
        pkgs = self.solve(specs, returnall=returnall)
      File "/Users/fperez/usr/conda/lib/python3.5/site-packages/conda/resolve.py", line 868, in solve
        self.find_conflicts(specs)
      File "/Users/fperez/usr/conda/lib/python3.5/site-packages/conda/resolve.py", line 364, in find_conflicts
        ndeps = set(self.invalid_chains(ms, filter))
      File "/Users/fperez/usr/conda/lib/python3.5/site-packages/conda/resolve.py", line 273, in chains_
        for x in chains_(m2, names):
      File "/Users/fperez/usr/conda/lib/python3.5/site-packages/conda/resolve.py", line 273, in chains_
        for x in chains_(m2, names):
      File "/Users/fperez/usr/conda/lib/python3.5/site-packages/conda/resolve.py", line 273, in chains_

[ snip intermediate frames, all like these ones. Tail of the traceback follows: ]

      File "/Users/fperez/usr/conda/lib/python3.5/site-packages/conda/resolve.py", line 273, in chains_
        for x in chains_(m2, names):
      File "/Users/fperez/usr/conda/lib/python3.5/site-packages/conda/resolve.py", line 266, in chains_
        if self.valid(spec, filter) or spec.name in names:
      File "/Users/fperez/usr/conda/lib/python3.5/site-packages/conda/resolve.py", line 245, in valid
        result = v_(spec_or_dist)
      File "/Users/fperez/usr/conda/lib/python3.5/site-packages/conda/resolve.py", line 232, in v_
        return v_ms_(spec) if isinstance(spec, MatchSpec) else v_fkey_(spec)
      File "/Users/fperez/usr/conda/lib/python3.5/site-packages/conda/resolve.py", line 235, in v_ms_
        return ms.optional or any(v_fkey_(fkey) for fkey in self.find_matches(ms))
      File "/Users/fperez/usr/conda/lib/python3.5/site-packages/conda/resolve.py", line 235, in <genexpr>
        return ms.optional or any(v_fkey_(fkey) for fkey in self.find_matches(ms))
      File "/Users/fperez/usr/conda/lib/python3.5/site-packages/conda/resolve.py", line 239, in v_fkey_
        val = filter.get(dist)
      File "/Users/fperez/usr/conda/lib/python3.5/site-packages/conda/models/dist.py", line 243, in __hash__
        return hash(self.__key__())
      File "/Users/fperez/usr/conda/lib/python3.5/site-packages/conda/models/dist.py", line 224, in __key__
        return self.channel, self.dist_name, self.with_features_depends
      File "/Users/fperez/usr/conda/lib/python3.5/site-packages/conda/_vendor/auxlib/entity.py", line 414, in __get__
        if val is None and not self.nullable:
      File "/Users/fperez/usr/conda/lib/python3.5/site-packages/conda/_vendor/auxlib/entity.py", line 485, in nullable
        return self.is_nullable
    RecursionError: maximum recursion depth exceeded
```
Jupyter install on 3.6 env crashes conda with infinite recursion
This is a fairly fresh py3.6 env. Error trace follows (I snipped the hundreds of redundant frames in the middle of the recursion for readability, all non-redundant info kept):

```
(py36) denali[~]> conda install jupyter
Fetching package metadata .........
Solving package specifications: .
An unexpected error has occurred.
Please consider posting the following information to the
conda GitHub issue tracker at:

    https://github.com/conda/conda/issues

Current conda install:

               platform : osx-64
          conda version : 4.3.4
       conda is private : False
      conda-env version : 4.3.4
    conda-build version : not installed
         python version : 3.5.2.final.0
       requests version : 2.12.4
       root environment : /Users/fperez/usr/conda  (writable)
    default environment : /Users/fperez/usr/conda/envs/py36
       envs directories : /Users/fperez/usr/conda/envs
          package cache : /Users/fperez/usr/conda/pkgs
           channel URLs : https://repo.continuum.io/pkgs/free/osx-64
                          https://repo.continuum.io/pkgs/free/noarch
                          https://repo.continuum.io/pkgs/r/osx-64
                          https://repo.continuum.io/pkgs/r/noarch
                          https://repo.continuum.io/pkgs/pro/osx-64
                          https://repo.continuum.io/pkgs/pro/noarch
            config file : /Users/fperez/.condarc
           offline mode : False
             user-agent : conda/4.3.4 requests/2.12.4 CPython/3.5.2 Darwin/15.6.0 OSX/10.11.6
                UID:GID : 33212:20665


`$ /Users/fperez/usr/conda/envs/py36/bin/conda install jupyter`


    Traceback (most recent call last):
      File "/Users/fperez/usr/conda/lib/python3.5/site-packages/conda/exceptions.py", line 617, in conda_exception_handler
        return_value = func(*args, **kwargs)
      File "/Users/fperez/usr/conda/lib/python3.5/site-packages/conda/cli/main.py", line 158, in _main
        exit_code = args.func(args, p)
      File "/Users/fperez/usr/conda/lib/python3.5/site-packages/conda/cli/main_install.py", line 80, in execute
        install(args, parser, 'install')
      File "/Users/fperez/usr/conda/lib/python3.5/site-packages/conda/cli/install.py", line 240, in install
        channel_priority_map=_channel_priority_map, is_update=isupdate)
      File "/Users/fperez/usr/conda/lib/python3.5/contextlib.py", line 77, in __exit__
        self.gen.throw(type, value, traceback)
      File "/Users/fperez/usr/conda/lib/python3.5/site-packages/conda/cli/common.py", line 591, in json_progress_bars
        yield
      File "/Users/fperez/usr/conda/lib/python3.5/site-packages/conda/cli/install.py", line 240, in install
        channel_priority_map=_channel_priority_map, is_update=isupdate)
      File "/Users/fperez/usr/conda/lib/python3.5/site-packages/conda/plan.py", line 516, in install_actions_list
        for dists_by_prefix in required_solves]
      File "/Users/fperez/usr/conda/lib/python3.5/site-packages/conda/plan.py", line 516, in <listcomp>
        for dists_by_prefix in required_solves]
      File "/Users/fperez/usr/conda/lib/python3.5/site-packages/conda/plan.py", line 662, in get_actions_for_dists
        pkgs = r.install(specs, installed, update_deps=update_deps)
      File "/Users/fperez/usr/conda/lib/python3.5/site-packages/conda/resolve.py", line 814, in install
        pkgs = self.solve(specs, returnall=returnall)
      File "/Users/fperez/usr/conda/lib/python3.5/site-packages/conda/resolve.py", line 868, in solve
        self.find_conflicts(specs)
      File "/Users/fperez/usr/conda/lib/python3.5/site-packages/conda/resolve.py", line 364, in find_conflicts
        ndeps = set(self.invalid_chains(ms, filter))
      File "/Users/fperez/usr/conda/lib/python3.5/site-packages/conda/resolve.py", line 273, in chains_
        for x in chains_(m2, names):
      File "/Users/fperez/usr/conda/lib/python3.5/site-packages/conda/resolve.py", line 273, in chains_
        for x in chains_(m2, names):
      File "/Users/fperez/usr/conda/lib/python3.5/site-packages/conda/resolve.py", line 273, in chains_

[ snip intermediate frames, all like these ones. Tail of the traceback follows: ]

      File "/Users/fperez/usr/conda/lib/python3.5/site-packages/conda/resolve.py", line 273, in chains_
        for x in chains_(m2, names):
      File "/Users/fperez/usr/conda/lib/python3.5/site-packages/conda/resolve.py", line 266, in chains_
        if self.valid(spec, filter) or spec.name in names:
      File "/Users/fperez/usr/conda/lib/python3.5/site-packages/conda/resolve.py", line 245, in valid
        result = v_(spec_or_dist)
      File "/Users/fperez/usr/conda/lib/python3.5/site-packages/conda/resolve.py", line 232, in v_
        return v_ms_(spec) if isinstance(spec, MatchSpec) else v_fkey_(spec)
      File "/Users/fperez/usr/conda/lib/python3.5/site-packages/conda/resolve.py", line 235, in v_ms_
        return ms.optional or any(v_fkey_(fkey) for fkey in self.find_matches(ms))
      File "/Users/fperez/usr/conda/lib/python3.5/site-packages/conda/resolve.py", line 235, in <genexpr>
        return ms.optional or any(v_fkey_(fkey) for fkey in self.find_matches(ms))
      File "/Users/fperez/usr/conda/lib/python3.5/site-packages/conda/resolve.py", line 239, in v_fkey_
        val = filter.get(dist)
      File "/Users/fperez/usr/conda/lib/python3.5/site-packages/conda/models/dist.py", line 243, in __hash__
        return hash(self.__key__())
      File "/Users/fperez/usr/conda/lib/python3.5/site-packages/conda/models/dist.py", line 224, in __key__
        return self.channel, self.dist_name, self.with_features_depends
      File "/Users/fperez/usr/conda/lib/python3.5/site-packages/conda/_vendor/auxlib/entity.py", line 414, in __get__
        if val is None and not self.nullable:
      File "/Users/fperez/usr/conda/lib/python3.5/site-packages/conda/_vendor/auxlib/entity.py", line 485, in nullable
        return self.is_nullable
    RecursionError: maximum recursion depth exceeded
```


Discussion:
@fperez Thanks for the report.

@mcg1969 I know the stack trace ends in auxlib/entity, but it looks like the recursion is happening at 

    File "/Users/fperez/usr/conda/lib/python3.5/site-packages/conda/resolve.py", line 273, in chains_
        for x in chains_(m2, names):

when the stack blows.

Is there anything obvious that you see? If not, I'll dig in.
Actually, I believe I hit that recursion error in my supersedes branch. Take a look at the second commit there.
(Which of course I can do myself when I get to my desk.)
Cool.  We should pull that recursion fix out and aim it at 4.3.x.
@fperez Thanks for the report.

@mcg1969 I know the stack trace ends in auxlib/entity, but it looks like the recursion is happening at 

    File "/Users/fperez/usr/conda/lib/python3.5/site-packages/conda/resolve.py", line 273, in chains_
        for x in chains_(m2, names):

when the stack blows.

Is there anything obvious that you see? If not, I'll dig in.
Actually, I believe I hit that recursion error in my supersedes branch. Take a look at the second commit there.
(Which of course I can do myself when I get to my desk.)
Cool.  We should pull that recursion fix out and aim it at 4.3.x.

Resolving patch:
diff --git a/conda/resolve.py b/conda/resolve.py
--- a/conda/resolve.py
+++ b/conda/resolve.py
@@ -263,7 +263,10 @@ def invalid_chains(self, spec, filter):
             A generator of tuples, empty if the MatchSpec is valid.
         """
         def chains_(spec, names):
-            if self.valid(spec, filter) or spec.name in names:
+            if spec.name in names:
+                return
+            names.add(spec.name)
+            if self.valid(spec, filter):
                 return
             dists = self.find_matches(spec) if isinstance(spec, MatchSpec) else [Dist(spec)]
             found = False


Test patch: