Repository: pypa/pip

Issue:
pip wheel of editable requirements creates a .zip file in the wheel directory
**Environment**

* pip version: master branch, today
* Python version: 3.8 (probably irrelevant)
* OS: linux (probably irrelevant)

**Description**

Consider the following `requirements.txt`:

```
-e git+https://github.com/PyPA/packaging@master#egg=packaging
```

When we run `pip wheel -r requirements.txt --wheel-dir=wheelhouse`, we now obtain `packaging-20.5.dev0.zip` in the `wheelhouse` directory, in addition to the expected whl files.

**Expected behavior**

Only wheels should be generated to the wheel-dir directory.

**Analysis**

This issue probably originates in https://github.com/pypa/pip/pull/8843 around [here](https://github.com/pypa/pip/commit/6887b0795b3567ac2a85578e5509dd8cfcc2b284#diff-5225c8e359020adb25dfc8c7a505950fd649c6c5775789c6f6517f7913f94542L588-L589) although I'm not entirely sure yet.




Discussion:
Tentatively adding to 20.3 milestone as it looks like a regression.
Probably off-topic, but it feels weird to me that `wheel` accepts an editable requirement; I would expect it to error, or convert the requirement to non-editable with a warning.
@uranusjr see #3291 for why it exists (in a nutshell, creating [installation bundles](https://pip.pypa.io/en/stable/user_guide/#installation-bundles) from requirement files).

Resolving patch:
diff --git a/src/pip/_internal/commands/download.py b/src/pip/_internal/commands/download.py
--- a/src/pip/_internal/commands/download.py
+++ b/src/pip/_internal/commands/download.py
@@ -132,7 +132,7 @@ def run(self, options, args):
 
         downloaded = []  # type: List[str]
         for req in requirement_set.requirements.values():
-            if not req.editable and req.satisfied_by is None:
+            if req.satisfied_by is None:
                 assert req.name is not None
                 preparer.save_linked_requirement(req)
                 downloaded.append(req.name)
diff --git a/src/pip/_internal/operations/prepare.py b/src/pip/_internal/operations/prepare.py
--- a/src/pip/_internal/operations/prepare.py
+++ b/src/pip/_internal/operations/prepare.py
@@ -530,7 +530,7 @@ def save_linked_requirement(self, req):
         assert self.download_dir is not None
         assert req.link is not None
         link = req.link
-        if link.is_vcs:
+        if link.is_vcs or (link.is_existing_dir() and req.editable):
             # Make a .zip of the source_dir we already created.
             req.archive(self.download_dir)
             return
@@ -576,7 +576,6 @@ def prepare_editable_requirement(
                 req, self.req_tracker, self.finder, self.build_isolation,
             )
 
-            req.archive(self.download_dir)
             req.check_if_exists(self.use_user_site)
 
         return dist


Test patch:
diff --git a/tests/functional/test_download.py b/tests/functional/test_download.py
--- a/tests/functional/test_download.py
+++ b/tests/functional/test_download.py
@@ -849,3 +849,19 @@ def test_download_http_url_bad_hash(
     assert len(requests) == 1
     assert requests[0]['PATH_INFO'] == '/simple-1.0.tar.gz'
     assert requests[0]['HTTP_ACCEPT_ENCODING'] == 'identity'
+
+
+def test_download_editable(script, data, tmpdir):
+    """
+    Test 'pip download' of editables in requirement file.
+    """
+    editable_path = str(data.src / 'simplewheel-1.0').replace(os.path.sep, "/")
+    requirements_path = tmpdir / "requirements.txt"
+    requirements_path.write_text("-e " + editable_path + "\n")
+    download_dir = tmpdir / "download_dir"
+    script.pip(
+        'download', '--no-deps', '-r', str(requirements_path), '-d', str(download_dir)
+    )
+    downloads = os.listdir(download_dir)
+    assert len(downloads) == 1
+    assert downloads[0].endswith(".zip")
diff --git a/tests/functional/test_wheel.py b/tests/functional/test_wheel.py
--- a/tests/functional/test_wheel.py
+++ b/tests/functional/test_wheel.py
@@ -169,6 +169,22 @@ def test_pip_wheel_builds_editable(script, data):
     result.did_create(wheel_file_path)
 
 
+def test_pip_wheel_builds_editable_does_not_create_zip(script, data, tmpdir):
+    """
+    Test 'pip wheel' of editables does not create zip files
+    (regression test for issue #9122)
+    """
+    wheel_dir = tmpdir / "wheel_dir"
+    wheel_dir.mkdir()
+    editable_path = os.path.join(data.src, 'simplewheel-1.0')
+    script.pip(
+        'wheel', '--no-deps', '-e', editable_path, '-w', wheel_dir
+    )
+    wheels = os.listdir(wheel_dir)
+    assert len(wheels) == 1
+    assert wheels[0].endswith(".whl")
+
+
 def test_pip_wheel_fail(script, data):
     """
     Test 'pip wheel' failure.