Repository: pypa/pipenv
Issue: Non-editable install from vcs does not work

I would like to install a package from Github in non-editable mode, mainly to not pull in dependencies. When I set `editable = false` in the `Pipfile`, and run `pipenv install`, the package is installed editable anyway, and dependencies are pulled in.

My pipenv version is 11.10.1

------------

##### Expected result

Using Pipfile:

```
[[source]]
url = "https://pypi.python.org/simple"
verify_ssl = true
name = "pypi"

[packages]
slicerator = {git = "https://github.com/soft-matter/slicerator.git", editable = false, ref = "master"}

[requires]
python_version = "2.7"
```

I expect a non editable install of `slicerator`, without dependencies.

##### Actual result

```
 ROUND 1                           
Current constraints:
  slicerator from git+https://github.com/soft-matter/slicerator.git@master#egg=slicerator

Finding the best candidates:
  found candidate -e git+https://github.com/soft-matter/slicerator.git@master#egg=slicerator (constraint was <any>)

Finding secondary dependencies:

New dependencies found in this round:
  adding [u'six', '', '[]']
Removed dependencies in this round:
Unsafe dependencies in this round:
------------------------------------------------------------
Result of round 1: not stable

                          ROUND 2                           
Current constraints:
  six
  slicerator from git+https://github.com/soft-matter/slicerator.git@master#egg=slicerator

Finding the best candidates:
  found candidate six==1.11.0 (constraint was <any>)
  found candidate -e git+https://github.com/soft-matter/slicerator.git@master#egg=slicerator (constraint was <any>)

Finding secondary dependencies:
  six==1.11.0               requires -
------------------------------------------------------------
Result of round 2: stable, done

Updated Pipfile.lock (89400d)!
```

Resulting lockfile:

```
{
    "_meta": {
        "hash": {
            "sha256": "c7173951bea190534637732bfe1da99d65f79ac699656354358940dd1289400d"
        },
        "pipfile-spec": 6,
        "requires": {
            "python_version": "2.7"
        },
        "sources": [
            {
                "name": "pypi",
                "url": "https://pypi.python.org/simple",
                "verify_ssl": true
            }
        ]
    },
    "default": {
        "six": {
            "hashes": [
                "sha256:70e8a77beed4562e7f14fe23a786b54f6296e34344c23bc42f07b15018ff98e9",
                "sha256:832dc0e10feb1aa2c68dcc57dbb658f1c7e65b9b61af69048abc87a2db00a0eb"
            ],
            "version": "==1.11.0"
        },
        "slicerator": {
            "editable": false,
            "git": "https://github.com/soft-matter/slicerator.git",
            "ref": "master"
        }
    },
    "develop": {}
}
```

##### Steps to replicate

Use the Pipfile above, then `pipenv install`.

This issue is part of #2068 I raised earlier.


Changed files:
pipenv/utils.py, tests/unit/test_utils.py

diff --git a/pipenv/utils.py b/pipenv/utils.py
index e5a1cb87..a63089ed 100644
--- a/pipenv/utils.py
+++ b/pipenv/utils.py
@@ -564,6 +564,10 @@ def multi_split(s, split):
 
 def convert_deps_from_pip(dep):
     """"Converts a pip-formatted dependency to a Pipfile-formatted one."""
+    try:
+        from collections.abc import Mapping
+    except ImportError:
+        from collections import Mapping
     dependency = {}
     req = get_requirement(dep)
     extras = {'extras': req.extras}
@@ -657,6 +661,7 @@ def convert_deps_to_pip(deps, project=None, r=True, include_index=False):
     for dep in deps.keys():
         # Default (e.g. '>1.10').
         extra = deps[dep] if isinstance(deps[dep], six.string_types) else ''
+        editable = False
         extras = ''
         version = ''
         index = ''
@@ -712,6 +717,8 @@ def convert_deps_to_pip(deps, project=None, r=True, include_index=False):
         vcs = maybe_vcs[0] if maybe_vcs else None
         if not any(key in deps[dep] for key in ['path', 'vcs', 'file']):
             extra += extras
+        if isinstance(deps[dep], Mapping):
+            editable = bool(deps[dep].get('editable', False))
         # Support for files.
         if 'file' in deps[dep]:
             dep_file = deps[dep]['file']
@@ -719,18 +726,12 @@ def convert_deps_to_pip(deps, project=None, r=True, include_index=False):
                 dep_file += '#egg={0}'.format(dep)
             extra = '{0}{1}'.format(dep_file, extras).strip()
             # Flag the file as editable if it is a local relative path
-            if 'editable' in deps[dep]:
-                dep = '-e '
-            else:
-                dep = ''
+            dep = '-e ' if editable else ''
         # Support for paths.
         elif 'path' in deps[dep]:
             extra = '{1}{0}'.format(extras, deps[dep]['path']).strip()
             # Flag the file as editable if it is a local relative path
-            if 'editable' in deps[dep]:
-                dep = '-e '
-            else:
-                dep = ''
+            dep = '-e ' if editable else ''
         if vcs:
             extra = '{0}+{1}'.format(vcs, deps[dep][vcs])
             # Support for @refs.
@@ -741,11 +742,7 @@ def convert_deps_to_pip(deps, project=None, r=True, include_index=False):
             if 'subdirectory' in deps[dep]:
                 extra += '&subdirectory={0}'.format(deps[dep]['subdirectory'])
             # Support for editable.
-            if 'editable' in deps[dep]:
-                # Support for --egg.
-                dep = '-e '
-            else:
-                dep = ''
+            dep = '-e ' if editable else ''
 
         s = '{0}{1}{2}{3}{4} {5}'.format(
             dep, extra, version, specs, hash, index
diff --git a/tests/unit/test_utils.py b/tests/unit/test_utils.py
index 1eedcccc..f314b7e9 100644
--- a/tests/unit/test_utils.py
+++ b/tests/unit/test_utils.py
@@ -2,7 +2,7 @@
 import os
 import pytest
 from mock import patch, Mock
-
+from first import first
 import pipenv.utils
 
 
@@ -56,6 +56,7 @@ DEP_PIP_PAIRS = [
         {'requests': {
             'git': 'https://github.com/requests/requests.git',
             'ref': 'master', 'extras': ['security'],
+            'editable': False
         }},
         'git+https://github.com/requests/requests.git@master#egg=requests[security]',
     ),
@@ -108,6 +109,10 @@ def test_convert_deps_to_pip_unicode():
 @pytest.mark.utils
 @pytest.mark.parametrize('expected, requirement', DEP_PIP_PAIRS)
 def test_convert_from_pip(expected, requirement):
+    # We don't build requirements back up with the editable key, so lets drop it out
+    package = first(expected.keys())
+    if hasattr(expected[package], 'keys') and expected[package].get('editable') is False:
+        del expected[package]['editable']
     assert pipenv.utils.convert_deps_from_pip(requirement) == expected