Repository: pypa/setuptools

Issue:
tests_require=['pytest', 'pytest-cov'] breaks setuptools.setup
Originally reported by: **slashfoo (Bitbucket: [slashfoo](http://bitbucket.org/slashfoo), GitHub: [slashfoo](http://github.com/slashfoo))**

---

```
(test)[20:24:14] [jamiel@moeka:masterskel/] $ python setup.py coverage
This command (coverage) requires the installation of pytest-cov
running coverage
Searching for pytest-cov
Reading https://pypi.python.org/simple/pytest-cov/
Best match: pytest-cov 1.6
Downloading https://pypi.python.org/packages/source/p/pytest-cov/pytest-cov-1.6.tar.gz#md5=6da54d74bde9d200de45068ba2ea637a
Processing pytest-cov-1.6.tar.gz
Writing /tmp/easy_install-xvMxzZ/pytest-cov-1.6/setup.cfg
Running pytest-cov-1.6/setup.py -q bdist_egg --dist-dir /tmp/easy_install-xvMxzZ/pytest-cov-1.6/egg-dist-tmp-BGJIES

Installed /home/jamiel/dev/masterskel/pytest_cov-1.6-py2.7.egg
Searching for pytest
Best match: pytest cov-1.6
Downloading https://pypi.python.org/packages/source/p/pytest-cov/pytest-cov-1.6.tar.gz#md5=6da54d74bde9d200de45068ba2ea637a
Processing pytest-cov-1.6.tar.gz
Writing /tmp/easy_install-EweVbo/pytest-cov-1.6/setup.cfg
Running pytest-cov-1.6/setup.py -q bdist_egg --dist-dir /tmp/easy_install-EweVbo/pytest-cov-1.6/egg-dist-tmp-zxCrHa

Installed /home/jamiel/dev/masterskel/pytest_cov-1.6-py2.7.egg
Traceback (most recent call last):
  File "setup.py", line 203, in <module>
    zip_safe=False,
  File "/usr/lib/python2.7/distutils/core.py", line 151, in setup
    dist.run_commands()
  File "/usr/lib/python2.7/distutils/dist.py", line 953, in run_commands
    self.run_command(cmd)
  File "/usr/lib/python2.7/distutils/dist.py", line 972, in run_command
    cmd_obj.run()
  File "/home/jamiel/.virtualenvs/test/local/lib/python2.7/site-packages/setuptools/command/test.py", line 130, in run
    self.distribution.fetch_build_eggs(self.distribution.tests_require)
  File "/home/jamiel/.virtualenvs/test/local/lib/python2.7/site-packages/setuptools/dist.py", line 264, in fetch_build_eggs
    replace_conflicting=True
  File "/home/jamiel/.virtualenvs/test/local/lib/python2.7/site-packages/pkg_resources.py", line 588, in resolve
    raise DistributionNotFound(req)
pkg_resources.DistributionNotFound: pytest
```

since 'pytest-cov' depends on 'pytest', making `tests_require=['pytest-cov']` fixes the issue, same if it is `tests_require=['pytest-cov', 'pytest']`.

Setting a breakpoint on `${HOME}/.virtualenvs/slashpyenv/lib/python3.4/site-packages/pkg_resources.py:620` (replace for your path to pkg_sources.py) shows:

```
dist = best[req.key] = env.best_match(req, ws, installer)
```

after running on for `req = Requirement.parse('pytest-cov')`, when I run `!env.best_match(Requirement.parse('pytest'), ws, installer)` it returns the best match to be 'pytest-cov 1.6' and exits.

_note_: In the report you see 3.4 in the outputs ect... but this is also present in 2.7.

---
- Bitbucket: https://bitbucket.org/pypa/setuptools/issue/196



Discussion:
_Original comment by_ **jaraco (Bitbucket: [jaraco](http://bitbucket.org/jaraco), GitHub: [jaraco](http://github.com/jaraco))**:

---

Issue #370 was marked as a duplicate of this issue.

_Original comment by_ **jaraco (Bitbucket: [jaraco](http://bitbucket.org/jaraco), GitHub: [jaraco](http://github.com/jaraco))**:

---

It looks like pytest-cov is being matched as the "cov" version of pytest, and so is fulfilling the requirement of "pytest". I seem to recall somewhere that setuptools discourages the use of dashes in package names, probably for this reason.

I don't yet know what to recommend here other than to follow the indicated workaround.

_Original comment by_ **jkbr (Bitbucket: [jkbr](http://bitbucket.org/jkbr), GitHub: [jkbr](http://github.com/jkbr))**:

---

Got the same problem with

```
#!python

tests_require = [
   'pytest',
   'pytest-httpbin',

]
```

Reversing the order fixed it.

_Original comment by_ **pbn (Bitbucket: [pbn](http://bitbucket.org/pbn), GitHub: [pbn](http://github.com/pbn))**:

---

I am seeing the same problem here with:
<pre>

```
#!python

install_require = [
    'pytest',
    'pytest-instafail',
]
```

Hello everyone, who can say when this bug will be resolved?
I don't think it will ever be fixed. Setup requires and easy install are deprecated in favor of PEP 518 in pip and tools like tox and pip. I recommend avoiding easy install and setup requires as much as possible, use the workaround where necesaary.
> I recommend avoiding easy install and setup requires as much as possible, use the workaround where necesaary.

What about `tests_require`? Is there a PEP 518 equivalent yet?
Just encountered this issue today while looking at another bug. It looks like it's caused by reusing the `easy_install` command instead of creating a new one every time:
```diff
diff --git a/setuptools/dist.py b/setuptools/dist.py
index fa0b5eb4..6cd04d6e 100644
--- a/setuptools/dist.py
+++ b/setuptools/dist.py
@@ -482,36 +482,30 @@ class Distribution(Distribution_parse_config_files, _Distribution):
 
     def fetch_build_egg(self, req):
         """Fetch an egg needed for building"""
-
-        try:
-            cmd = self._egg_fetcher
-            cmd.package_index.to_scan = []
-        except AttributeError:
-            from setuptools.command.easy_install import easy_install
-            dist = self.__class__({'script_args': ['easy_install']})
-            dist.parse_config_files()
-            opts = dist.get_option_dict('easy_install')
-            keep = (
-                'find_links', 'site_dirs', 'index_url', 'optimize',
-                'site_dirs', 'allow_hosts'
-            )
-            for key in list(opts):
-                if key not in keep:
-                    del opts[key]  # don't use any other settings
-            if self.dependency_links:
-                links = self.dependency_links[:]
-                if 'find_links' in opts:
-                    links = opts['find_links'][1].split() + links
-                opts['find_links'] = ('setup', links)
-            install_dir = self.get_egg_cache_dir()
-            cmd = easy_install(
-                dist, args=["x"], install_dir=install_dir,
-                exclude_scripts=True,
-                always_copy=False, build_directory=None, editable=False,
-                upgrade=False, multi_version=True, no_report=True, user=False
-            )
-            cmd.ensure_finalized()
-            self._egg_fetcher = cmd
+        from setuptools.command.easy_install import easy_install
+        dist = self.__class__({'script_args': ['easy_install']})
+        dist.parse_config_files()
+        opts = dist.get_option_dict('easy_install')
+        keep = (
+            'find_links', 'site_dirs', 'index_url', 'optimize',
+            'site_dirs', 'allow_hosts'
+        )
+        for key in list(opts):
+            if key not in keep:
+                del opts[key]  # don't use any other settings
+        if self.dependency_links:
+            links = self.dependency_links[:]
+            if 'find_links' in opts:
+                links = opts['find_links'][1].split() + links
+            opts['find_links'] = ('setup', links)
+        install_dir = self.get_egg_cache_dir()
+        cmd = easy_install(
+            dist, args=["x"], install_dir=install_dir,
+            exclude_scripts=True,
+            always_copy=False, build_directory=None, editable=False,
+            upgrade=False, multi_version=True, no_report=True, user=False
+        )
+        cmd.ensure_finalized()
         return cmd.easy_install(req)
 
     def _set_global_opts_from_features(self):
```
Working on adding a test before making a PR.

Resolving patch:
diff --git a/setuptools/dist.py b/setuptools/dist.py
--- a/setuptools/dist.py
+++ b/setuptools/dist.py
@@ -495,36 +495,30 @@ def get_egg_cache_dir(self):
 
     def fetch_build_egg(self, req):
         """Fetch an egg needed for building"""
-
-        try:
-            cmd = self._egg_fetcher
-            cmd.package_index.to_scan = []
-        except AttributeError:
-            from setuptools.command.easy_install import easy_install
-            dist = self.__class__({'script_args': ['easy_install']})
-            dist.parse_config_files()
-            opts = dist.get_option_dict('easy_install')
-            keep = (
-                'find_links', 'site_dirs', 'index_url', 'optimize',
-                'site_dirs', 'allow_hosts'
-            )
-            for key in list(opts):
-                if key not in keep:
-                    del opts[key]  # don't use any other settings
-            if self.dependency_links:
-                links = self.dependency_links[:]
-                if 'find_links' in opts:
-                    links = opts['find_links'][1].split() + links
-                opts['find_links'] = ('setup', links)
-            install_dir = self.get_egg_cache_dir()
-            cmd = easy_install(
-                dist, args=["x"], install_dir=install_dir,
-                exclude_scripts=True,
-                always_copy=False, build_directory=None, editable=False,
-                upgrade=False, multi_version=True, no_report=True, user=False
-            )
-            cmd.ensure_finalized()
-            self._egg_fetcher = cmd
+        from setuptools.command.easy_install import easy_install
+        dist = self.__class__({'script_args': ['easy_install']})
+        dist.parse_config_files()
+        opts = dist.get_option_dict('easy_install')
+        keep = (
+            'find_links', 'site_dirs', 'index_url', 'optimize',
+            'site_dirs', 'allow_hosts'
+        )
+        for key in list(opts):
+            if key not in keep:
+                del opts[key]  # don't use any other settings
+        if self.dependency_links:
+            links = self.dependency_links[:]
+            if 'find_links' in opts:
+                links = opts['find_links'][1].split() + links
+            opts['find_links'] = ('setup', links)
+        install_dir = self.get_egg_cache_dir()
+        cmd = easy_install(
+            dist, args=["x"], install_dir=install_dir,
+            exclude_scripts=True,
+            always_copy=False, build_directory=None, editable=False,
+            upgrade=False, multi_version=True, no_report=True, user=False
+        )
+        cmd.ensure_finalized()
         return cmd.easy_install(req)
 
     def _set_global_opts_from_features(self):


Test patch:
diff --git a/setuptools/tests/test_dist.py b/setuptools/tests/test_dist.py
new file mode 100644
--- /dev/null
+++ b/setuptools/tests/test_dist.py
@@ -0,0 +1,46 @@
+from setuptools import Distribution
+from setuptools.extern.six.moves.urllib.request import pathname2url
+from setuptools.extern.six.moves.urllib_parse import urljoin
+
+from .textwrap import DALS
+from .test_easy_install import make_nspkg_sdist
+
+
+def test_dist_fetch_build_egg(tmpdir):
+    """
+    Check multiple calls to `Distribution.fetch_build_egg` work as expected.
+    """
+    index = tmpdir.mkdir('index')
+    index_url = urljoin('file://', pathname2url(str(index)))
+    def sdist_with_index(distname, version):
+        dist_dir = index.mkdir(distname)
+        dist_sdist = '%s-%s.tar.gz' % (distname, version)
+        make_nspkg_sdist(str(dist_dir.join(dist_sdist)), distname, version)
+        with dist_dir.join('index.html').open('w') as fp:
+            fp.write(DALS(
+                '''
+                <!DOCTYPE html><html><body>
+                <a href="{dist_sdist}" rel="internal">{dist_sdist}</a><br/>
+                </body></html>
+                '''
+            ).format(dist_sdist=dist_sdist))
+    sdist_with_index('barbazquux', '3.2.0')
+    sdist_with_index('barbazquux-runner', '2.11.1')
+    with tmpdir.join('setup.cfg').open('w') as fp:
+        fp.write(DALS(
+            '''
+            [easy_install]
+            index_url = {index_url}
+            '''
+        ).format(index_url=index_url))
+    reqs = '''
+    barbazquux-runner
+    barbazquux
+    '''.split()
+    with tmpdir.as_cwd():
+        dist = Distribution()
+        resolved_dists = [
+            dist.fetch_build_egg(r)
+            for r in reqs
+        ]
+    assert [dist.key for dist in resolved_dists if dist] == reqs