Repository: pypa/pipenv

Issue:
Manually adding an editable package would cause locking to fail
Locking requires every sdist to run `python setup.py egg_info` beforehand to produce a `*.egg-info` directory. `pipenv install -e .` does this, but if you write your TOML manually, you will get this error.

##### Steps to replicate

```python
# setup.py
from setuptools import setup
setup(name='hello', version='1.0')
```

```toml
# Pipfile
[packages]
hello = { path = ".", editable = true }
```

```bash
$ ls -a
Pipfile setup.py
$ pipenv --three
(snipped)
$ pipenv lock
(will produce the exception below)
```

##### Expected result

Locking finishes successfully with my editable package in the lock file.

##### Actual result

```
  File "pipenv/patched/piptools/resolver.py", line 278, in _iter_dependencies
    for dependency in self.repository.get_dependencies(ireq):
  File "pipenv/patched/piptools/repositories/pypi.py", line 246, in get_dependencies
    legacy_results = self.get_legacy_dependencies(ireq)
  File "pipenv/patched/piptools/repositories/pypi.py", line 264, in get_legacy_dependencies
    dist = ireq.get_dist()
  File "pipenv/patched/notpip/_internal/req/req_install.py", line 966, in get_dist
    egg_info = self.egg_info_path('').rstrip(os.path.sep)
  File "pipenv/patched/notpip/_internal/req/req_install.py", line 562, in egg_info_path
    'No files/directories in %s (from %s)' % (base, filename)
pipenv.patched.notpip._internal.exceptions.InstallationError: No files/directories in hello (from )
```

------

The following will produce the expected result:

```
pipenv run python setup.py egg_info
pipenv lock
```

so essentially we just need to run `egg_info` for local packages to ensure they are initialised correctly. I think.


Discussion:

Resolving patch:
diff --git a/pipenv/patched/piptools/_compat/__init__.py b/pipenv/patched/piptools/_compat/__init__.py
--- a/pipenv/patched/piptools/_compat/__init__.py
+++ b/pipenv/patched/piptools/_compat/__init__.py
@@ -28,4 +28,5 @@
     get_installed_distributions,
     PyPI,
     SafeFileCache,
+    InstallationError,
 )
diff --git a/pipenv/patched/piptools/_compat/pip_compat.py b/pipenv/patched/piptools/_compat/pip_compat.py
--- a/pipenv/patched/piptools/_compat/pip_compat.py
+++ b/pipenv/patched/piptools/_compat/pip_compat.py
@@ -39,3 +39,4 @@ def do_import(module_path, subimport=None, old_path=None, vendored_name=None):
 get_installed_distributions = do_import('utils.misc', 'get_installed_distributions', old_path='utils', vendored_name='notpip')
 PyPI = do_import('models.index', 'PyPI', vendored_name='notpip')
 SafeFileCache = do_import('download', 'SafeFileCache', vendored_name='notpip')
+InstallationError = do_import('exceptions', 'InstallationError', vendored_name='notpip')
diff --git a/pipenv/patched/piptools/repositories/pypi.py b/pipenv/patched/piptools/repositories/pypi.py
--- a/pipenv/patched/piptools/repositories/pypi.py
+++ b/pipenv/patched/piptools/repositories/pypi.py
@@ -19,6 +19,7 @@
     PyPI,
     InstallRequirement,
     SafeFileCache,
+    InstallationError,
 )
 
 from notpip._vendor.packaging.requirements import InvalidRequirement, Requirement
@@ -257,24 +258,6 @@ def get_legacy_dependencies(self, ireq):
         if not (ireq.editable or is_pinned_requirement(ireq)):
             raise TypeError('Expected pinned or editable InstallRequirement, got {}'.format(ireq))
 
-        # Collect setup_requires info from local eggs.
-        setup_requires = {}
-        if ireq.editable:
-            try:
-                dist = ireq.get_dist()
-                if dist.has_metadata('requires.txt'):
-                    setup_requires = self.finder.get_extras_links(
-                        dist.get_metadata_lines('requires.txt')
-                    )
-                # HACK: Sometimes the InstallRequirement doesn't properly get
-                # these values set on it during the resolution process. It's
-                # difficult to pin down what is going wrong. This fixes things.
-                ireq.version = dist.version
-                ireq.project_name = dist.project_name
-                ireq.req = dist.as_requirement()
-            except (TypeError, ValueError):
-                pass
-
         if ireq not in self._dependencies_cache:
             if ireq.editable and (ireq.source_dir and os.path.exists(ireq.source_dir)):
                 # No download_dir for locally available editable requirements.
@@ -338,6 +321,26 @@ def get_legacy_dependencies(self, ireq):
                 )
                 self.resolver.resolve(reqset)
                 result = reqset.requirements.values()
+
+            # Collect setup_requires info from local eggs.
+            # Do this after we call the preparer on these reqs to make sure their
+            # egg info has been created
+            setup_requires = {}
+            if ireq.editable:
+                try:
+                    dist = ireq.get_dist()
+                    if dist.has_metadata('requires.txt'):
+                        setup_requires = self.finder.get_extras_links(
+                            dist.get_metadata_lines('requires.txt')
+                        )
+                    # HACK: Sometimes the InstallRequirement doesn't properly get
+                    # these values set on it during the resolution process. It's
+                    # difficult to pin down what is going wrong. This fixes things.
+                    ireq.version = dist.version
+                    ireq.project_name = dist.project_name
+                    ireq.req = dist.as_requirement()
+                except (TypeError, ValueError):
+                    pass
             # Convert setup_requires dict into a somewhat usable form.
             if setup_requires:
                 for section in setup_requires:


Test patch: