Repository: pypa/pipenv

Issue:
Pipenv install can't handle spaces in windows user folder
Hi there, I've poked my head in on this project to check it out, but I'm having some trouble on my work machine.

##### Describe your environment

1. Windows 10 Pro
1. Python version: Python 3.6.4
1. Pipenv version: pipenv, version 11.1.5
1. Happens in both Powershell and CMD, but not in bash (probably due to Linux Subsystem for Windows uses a different home directory path, that doesn't have spaces in the name)

##### Expected result

pipenv to create the environment

##### Actual result
```
PS D:\Projects\Django\feedback> pipenv --verbose install
Error: no such option: --verbose
PS D:\Projects\Django\feedback> pipenv install --verbose
Creating a Pipfile for this project…
Pipfile.lock not found, creating…
Locking [dev-packages] dependencies…

'C:\Users\First' is not recognized as an internal or external command,
operable program or batch file.
```

I've removed my actual name from the output, but to add here, my user folder path is 
"C:\Users\First Last\"

##### Steps to replicate

 1. cd D:\Projects\Django\feedback
 1. pipenv install --verbose



Discussion:
Possibly cache dir related, what if you run `set PIPENV_CACHE_DIR=D:\Projects\Django\feedback\__cache__` and then `mkdir D:\Projects\Django\feedback\__cache__` and then try?
@techalchemy we need to shellquote — i took out your patch because of an error i was getting locally — turns out it wasn't the cause, though.
forgot to put it back in
Okay, so setting PIPENV_VENV_IN_PROJECT to true did allow pipenv to install successfully, but now I get
```
PS D:\Projects\Django\feedback> pipenv shell
  File "<string>", line 1
    c:\users\First Last\appdata\local\programs\python\python36-32\python.exe -c "from pew.pew import prevent_path_errors; prevent_path_errors()"

SyntaxError: unexpected character after line continuation character
``` 

Give me a minute and I'll try @techalchemy's suggestion
@techalchemy Here's with the cache moved to the project directory
```
PS D:\Projects\Django\feedback> pipenv install --verbose
Creating a virtualenv for this project…
Using C:\Users\First Last\AppData\Local\Programs\Python\Python36-32\python.exe to create virtualenv…
Running virtualenv with interpreter C:\Users\First Last\AppData\Local\Programs\Python\Python36-32\python.exe
Using base prefix 'C:\\Users\\First Last\\AppData\\Local\\Programs\\Python\\Python36-32'
New python executable in C:\Users\FIRSTL~1\VIRTUA~1\FEEDBA~1\Scripts\python.exe
Installing setuptools, pip, wheel...done.

Virtualenv location: C:\Users\First Last\.virtualenvs\feedback-bBVyDX7t
Pipfile.lock not found, creating…
Locking [dev-packages] dependencies…

'C:\Users\First' is not recognized as an internal or external command,
operable program or batch file.
```

Resolving patch:
diff --git a/pipenv/core.py b/pipenv/core.py
--- a/pipenv/core.py
+++ b/pipenv/core.py
@@ -33,7 +33,7 @@
 from .project import Project
 from .utils import (
     convert_deps_from_pip, convert_deps_to_pip, is_required_version,
-    proper_case, pep423_name, split_file, merge_deps, venv_resolve_deps, shellquote, is_vcs,
+    proper_case, pep423_name, split_file, merge_deps, venv_resolve_deps, escape_grouped_arguments, is_vcs,
     python_version, find_windows_executable, is_file, prepare_pip_source_args,
     temp_environ, is_valid_url, download_file, get_requirement, need_update_check,
     touch_update_stamp, is_pinned, is_star, TemporaryDirectory
@@ -217,7 +217,7 @@ def ensure_latest_pip():
 
     # Ensure that pip is installed.
     try:
-        c = delegator.run('"{0}" install pip'.format(which_pip()))
+        c = delegator.run('{0} install pip'.format(escape_grouped_arguments(which_pip())))
 
         # Check if version is out of date.
         if 'however' in c.err:
@@ -226,7 +226,7 @@ def ensure_latest_pip():
 
             windows = '-m' if os.name == 'nt' else ''
 
-            c = delegator.run('"{0}" install {1} pip --upgrade'.format(which_pip(), windows), block=False)
+            c = delegator.run('{0} install {1} pip --upgrade'.format(escape_grouped_arguments(which_pip()), windows), block=False)
             click.echo(crayons.blue(c.out))
     except AttributeError:
         pass
@@ -1013,8 +1013,8 @@ def get_downloads_info(names_map, section):
         version = parse_download_fname(fname, name)
 
         # Get the hash of each file.
-        cmd = '"{0}" hash "{1}"'.format(
-            which_pip(),
+        cmd = '{0} hash "{1}"'.format(
+            escape_grouped_arguments(which_pip()),
             os.sep.join([project.download_location, fname])
         )
 
@@ -1102,7 +1102,7 @@ def do_lock(verbose=False, system=False, clear=False, pre=False, keep_outdated=F
     # Add refs for VCS installs.
     # TODO: be smarter about this.
     vcs_deps = convert_deps_to_pip(project.vcs_dev_packages, project, r=False)
-    pip_freeze = delegator.run('{0} freeze'.format(which_pip())).out
+    pip_freeze = delegator.run('{0} freeze'.format(escape_grouped_arguments(which_pip()))).out
 
     if vcs_deps:
         for line in pip_freeze.strip().split('\n'):
@@ -1262,7 +1262,7 @@ def do_purge(bare=False, downloads=False, allow_global=False, verbose=False):
         shutil.rmtree(project.download_location)
         return
 
-    freeze = delegator.run('"{0}" freeze'.format(which_pip(allow_global=allow_global))).out
+    freeze = delegator.run('{0} freeze'.format(escape_grouped_arguments(which_pip(allow_global=allow_global)))).out
 
     # Remove comments from the output, if any.
     installed = [line for line in freeze.splitlines() if not line.lstrip().startswith('#')]
@@ -1290,7 +1290,7 @@ def do_purge(bare=False, downloads=False, allow_global=False, verbose=False):
 
     if not bare:
         click.echo(u'Found {0} installed package(s), purging…'.format(len(actually_installed)))
-    command = '"{0}" uninstall {1} -y'.format(which_pip(allow_global=allow_global), ' '.join(actually_installed))
+    command = '{0} uninstall {1} -y'.format(escape_grouped_arguments(which_pip(allow_global=allow_global)), ' '.join(actually_installed))
 
     if verbose:
         click.echo('$ {0}'.format(command))
@@ -1416,7 +1416,7 @@ def pip_install(
 
         # Don't specify a source directory when using --system.
         if not allow_global and ('PIP_SRC' not in os.environ):
-            src = '--src {0}'.format(shellquote(project.virtualenv_src_location))
+            src = '--src {0}'.format(escape_grouped_arguments(project.virtualenv_src_location))
         else:
             src = ''
     else:
@@ -1454,7 +1454,7 @@ def pip_install(
         pre = '--pre' if pre else ''
 
         quoted_pip = which_pip(allow_global=allow_global)
-        quoted_pip = shellquote(quoted_pip)
+        quoted_pip = escape_grouped_arguments(quoted_pip)
         upgrade_strategy = '--upgrade-strategy=only-if-needed' if selective_upgrade else ''
 
         pip_command = '{0} install {4} {5} {6} {7} {3} {1} {2} --exists-action w'.format(
@@ -1481,8 +1481,8 @@ def pip_install(
 
 def pip_download(package_name):
     for source in project.sources:
-        cmd = '"{0}" download "{1}" -i {2} -d {3}'.format(
-            which_pip(),
+        cmd = '{0} download "{1}" -i {2} -d {3}'.format(
+            delegator.run(which_pip()),
             package_name,
             source['url'],
             project.download_location
@@ -1499,7 +1499,7 @@ def which_pip(allow_global=False):
         if 'VIRTUAL_ENV' in os.environ:
             return which('pip', location=os.environ['VIRTUAL_ENV'])
 
-        for p in ('pip', 'pip2', 'pip3'):
+        for p in ('pip', 'pip3', 'pip2'):
             where = system_which(p)
             if where:
                 return where
@@ -2226,7 +2226,7 @@ def do_check(three=None, python=False, system=False, unused=False, args=None):
         python = which('python')
 
     # Run the PEP 508 checker in the virtualenv.
-    c = delegator.run('"{0}" {1}'.format(python, shellquote(pep508checker.__file__.rstrip('cdo'))))
+    c = delegator.run('"{0}" {1}'.format(python, escape_grouped_arguments(pep508checker.__file__.rstrip('cdo'))))
     results = simplejson.loads(c.out)
 
     # Load the pipfile.
@@ -2267,7 +2267,7 @@ def do_check(three=None, python=False, system=False, unused=False, args=None):
     else:
         python = system_which('python')
 
-    c = delegator.run('"{0}" {1} check --json --key=1ab8d58f-5122e025-83674263-bc1e79e0'.format(python, shellquote(path)))
+    c = delegator.run('"{0}" {1} check --json --key=1ab8d58f-5122e025-83674263-bc1e79e0'.format(python, escape_grouped_arguments(path)))
     try:
         results = simplejson.loads(c.out)
     except ValueError:
@@ -2338,7 +2338,7 @@ def do_graph(bare=False, json=False, reverse=False):
 
     cmd = '"{0}" {1} {2}'.format(
         python_path,
-        shellquote(pipdeptree.__file__.rstrip('cdo')),
+        escape_grouped_arguments(pipdeptree.__file__.rstrip('cdo')),
         flag
     )
 
diff --git a/pipenv/utils.py b/pipenv/utils.py
--- a/pipenv/utils.py
+++ b/pipenv/utils.py
@@ -209,8 +209,11 @@ def allow_empty(text):
     return u"{v[0]}.{v[1]}.{v[2]}".format(v=parsed)
 
 
-def shellquote(s):
-    """Prepares a string for the shell (on Windows too!)"""
+def escape_grouped_arguments(s):
+    """Prepares a string for the shell (on Windows too!)
+
+    Only for use on grouped arguments (passed as a string to Popen)
+    """
     if s is None:
         return None
     # Additional escaping for windows paths
@@ -348,8 +351,8 @@ def venv_resolve_deps(deps, which, project, pre=False, verbose=False, clear=Fals
     from . import resolver
     import json
 
-    resolver = shellquote(resolver.__file__.rstrip('co'))
-    cmd = '{0} {1} {2} {3}'.format(which('python'), resolver, '--pre' if pre else '', '--verbose' if verbose else '')
+    resolver = escape_grouped_arguments(resolver.__file__.rstrip('co'))
+    cmd = '{0} {1} {2} {3}'.format(escape_grouped_arguments(which('python')), resolver, '--pre' if pre else '', '--verbose' if verbose else '')
     os.environ['PIPENV_PACKAGES'] = '\n'.join(deps)
 
     c = delegator.run(cmd, block=True)


Test patch:
diff --git a/tests/test_utils.py b/tests/test_utils.py
--- a/tests/test_utils.py
+++ b/tests/test_utils.py
@@ -185,7 +185,7 @@ def test_python_version_output_variants(self, mocked_delegator, version_output,
     def test_windows_shellquote(self):
         test_path = 'C:\Program Files\Python36\python.exe'
         expected_path = '"C:\\\\Program Files\\\\Python36\\\\python.exe"'
-        assert pipenv.utils.shellquote(test_path) == expected_path
+        assert pipenv.utils.escape_grouped_arguments(test_path) == expected_path
 
     @pytest.mark.utils
     def test_is_valid_url(self):