Repository: pypa/pipenv

Issue:
Failed to install from SVN with caret character ^ in the path.
### Issue description

For investigating of [this problem](https://github.com/pypa/pipenv/issues/3290) I created a public SVN repository. It contains a ^ character in the path: https://eu-subversion.assembla.com/svn/testuuu^testuuu-first-space/test_pyenv/trunk/test_package_1

I tried to install a package from it using pipenv:

pipenv install -e svn+https://eu-subversion.assembla.com/svn/testuuu^testuuu-first-space/test_pyenv/trunk/test_package_1#egg=test-package1

This fails because the pipenv misses the ^ character (see below).

### Expected result

The package should be installed. It works with pip, if I quote the path:
pip install -e "svn+https://eu-subversion.assembla.com/svn/testuuu^testuuu-first-space/test_pyenv/trunk/test_package_1#egg=test-package1"

Quoting does not help with pipenv.

### Actual result

```
c:\Python36\Scripts\pipenv --verbose install -e svn+https://eu-subversion.assemb
la.com/svn/testuuu^testuuu-first-space/test_pyenv/trunk/test_package_1#egg=test-package1
Installing -e svn+https://eu-subversion.assembla.com/svn/testuuutestuuu-first-space/test_pyenv/trunk/test_package_1#egg=test-package1…
[    ] Installing...Installing 'test-package1'
$ ['C:\\Users\\ivan\\.virtualenvs\\test_pipenv-4FKtD8uM\\Scripts\\pip.exe', 'install', '--src', 'C:\\Users\\ivan\\.virtualenvs\\test_pipenv-4FKtD8uM\\src', '--v
erbose', '--upgrade', '-e', 'svn+https://eu-subversion.assembla.com/svn/testuuutestuuu-first-space/test_pyenv/trunk/test_package_1#egg=test-package1', '-i', 'ht
tps://pypi.org/simple']
Adding test-package1 to Pipfile's [packages]…
Installation Succeeded
Pipfile.lock (c58142) out of date, updating to (6c5ddb)…
Locking [dev-packages] dependencies…
Locking [packages] dependencies…
[    ] Pinning VCS Packages...INFO:pipenv.patched.notpip._internal.vcs.subversion:Checking out https://eu-subversion.assembla.com/svn/testuuutestuuu-first-space
/test_pyenv/trunk/test_package_1 to c:\users\ivan\appdata\local\temp\requirementslibqlbhaoiy\test-package1
[=   ] Pinning VCS Packages...svn: E170013: Unable to connect to a repository at URL 'https://eu-subversion.assembla.com/svn/testuuutestuuu-first-space/test_pye
nv/trunk/test_package_1'
svn: E175009: The XML response contains invalid XML
svn: E130003: Malformed XML: no element found
Traceback (most recent call last):
  File "c:\Python36\Scripts\pipenv-script.py", line 11, in <module>
    load_entry_point('pipenv', 'console_scripts', 'pipenv')()
  File "c:\python36\scripts\src\pipenv\pipenv\vendor\click\core.py", line 764, in __call__
    return self.main(*args, **kwargs)
  File "c:\python36\scripts\src\pipenv\pipenv\vendor\click\core.py", line 717, in main
    rv = self.invoke(ctx)
  File "c:\python36\scripts\src\pipenv\pipenv\vendor\click\core.py", line 1137, in invoke
    return _process_result(sub_ctx.command.invoke(sub_ctx))
  File "c:\python36\scripts\src\pipenv\pipenv\vendor\click\core.py", line 956, in invoke
    return ctx.invoke(self.callback, **ctx.params)
  File "c:\python36\scripts\src\pipenv\pipenv\vendor\click\core.py", line 555, in invoke
    return callback(*args, **kwargs)
  File "c:\python36\scripts\src\pipenv\pipenv\vendor\click\decorators.py", line 64, in new_func
    return ctx.invoke(f, obj, *args, **kwargs)
  File "c:\python36\scripts\src\pipenv\pipenv\vendor\click\core.py", line 555, in invoke
    return callback(*args, **kwargs)
  File "c:\python36\scripts\src\pipenv\pipenv\vendor\click\decorators.py", line 17, in new_func
    return f(get_current_context(), *args, **kwargs)
  File "c:\python36\scripts\src\pipenv\pipenv\cli\command.py", line 250, in install
    editable_packages=state.installstate.editables,
  File "c:\python36\scripts\src\pipenv\pipenv\core.py", line 2005, in do_install
    skip_lock=skip_lock,
  File "c:\python36\scripts\src\pipenv\pipenv\core.py", line 1214, in do_init
    pypi_mirror=pypi_mirror,
  File "c:\python36\scripts\src\pipenv\pipenv\core.py", line 1061, in do_lock
    lockfile=lockfile
  File "c:\python36\scripts\src\pipenv\pipenv\utils.py", line 612, in venv_resolve_deps
    dev=dev,
  File "c:\python36\scripts\src\pipenv\pipenv\utils.py", line 1294, in get_vcs_deps
    with locked_repository(requirement) as repo:
  File "c:\python36\lib\contextlib.py", line 81, in __enter__
    return next(self.gen)
  File "c:\python36\scripts\src\pipenv\pipenv\utils.py", line 1430, in locked_repository
    with requirement.req.locked_vcs_repo(src_dir=src_dir) as repo:
  File "c:\python36\lib\contextlib.py", line 81, in __enter__
    return next(self.gen)
  File "c:\python36\scripts\src\pipenv\pipenv\vendor\requirementslib\models\requirements.py", line 840, in locked_vcs_repo
    vcsrepo = self.get_vcs_repo(src_dir=src_dir)
  File "c:\python36\scripts\src\pipenv\pipenv\vendor\requirementslib\models\requirements.py", line 802, in get_vcs_repo
    vcsrepo.obtain()
  File "c:\python36\scripts\src\pipenv\pipenv\vendor\requirementslib\models\vcs.py", line 37, in obtain
    self.repo_instance.obtain(self.checkout_directory)
  File "c:\python36\scripts\src\pipenv\pipenv\patched\notpip\_internal\vcs\__init__.py", line 332, in obtain
    self.fetch_new(dest, url, rev_options)
  File "c:\python36\scripts\src\pipenv\pipenv\patched\notpip\_internal\vcs\subversion.py", line 54, in fetch_new
    self.run_command(cmd_args)
  File "c:\python36\scripts\src\pipenv\pipenv\patched\notpip\_internal\vcs\__init__.py", line 456, in run_command
    spinner=spinner)
  File "c:\python36\scripts\src\pipenv\pipenv\patched\notpip\_internal\utils\misc.py", line 705, in call_subprocess
    % (command_desc, proc.returncode, cwd))
pipenv.patched.notpip._internal.exceptions.InstallationError: Command "svn checkout -q https://eu-subversion.assembla.com/svn/testuuutestuuu-first-space/test_py
env/trunk/test_package_1 C:\Users\ivan\AppData\Local\Temp\requirementslibqlbhaoiy\test-package1" failed with error code 1 in None
```


**The ^ charachter is missing in the path pipenv is trying to check out.**

### Steps to replicate
See actual result.

Provide the steps to replicate (which usually at least includes the commands and the Pipfile).

-------------------------------------------------------------------------------

<details><summary>$ pipenv --support</summary>

Pipenv version: `'2018.11.15.dev0'`

Pipenv location: `'c:\\python36\\scripts\\src\\pipenv\\pipenv'`

Python location: `'c:\\python36\\python.exe'`

Python installations found:

  - `5.2.0`: `C:\Anaconda3-5.2.0-64\python.exe`
  - `4.2.0`: `C:\Anaconda3-4.2.0-64\python.exe`
  - `4.1.1`: `C:\Anaconda3-4.1.1-64\python.exe`
  - `3.6.6`: `C:\Python36\python.exe`
  - `3.5`: `d:\Anaconda3\Scripts\python.exe`
  - `2.7`: `d:\Program Files\Anaconda2\Scripts\python.exe`

PEP 508 Information:

```
{'implementation_name': 'cpython',
 'implementation_version': '3.6.6',
 'os_name': 'nt',
 'platform_machine': 'AMD64',
 'platform_python_implementation': 'CPython',
 'platform_release': '7',
 'platform_system': 'Windows',
 'platform_version': '6.1.7601',
 'python_full_version': '3.6.6',
 'python_version': '3.6',
 'sys_platform': 'win32'}
```

System environment variables:

  - `ACML_FMA`
  - `ALLUSERSPROFILE`
  - `APPDATA`
  - `BOOST_INCLUDE_PATH`
  - `BOOST_LIB_PATH`
  - `CNTK_MKL`
  - `CNTK_MKL_PATH`
  - `COGNEX_CCF_PATH`
  - `COMMONPROGRAMFILES`
  - `COMMONPROGRAMFILES(X86)`
  - `COMMONPROGRAMW6432`
  - `COMPUTERNAME`
  - `COMSPEC`
  - `CUB_PATH`
  - `CUDA_PATH`
  - `CUDA_PATH_V7_5`
  - `CUDA_PATH_V8_0`
  - `CUDA_PATH_V9_0`
  - `CUDA_PATH_V9_1`
  - `CUDNN_PATH`
  - `FARHOME`
  - `FARLANG`
  - `FARLOCALPROFILE`
  - `FARPROFILE`
  - `FP_NO_HOST_CHECK`
  - `HALCONARCH`
  - `HALCONEXAMPLES`
  - `HALCONIMAGES`
  - `HALCONROOT`
  - `HOMEDRIVE`
  - `HOMEPATH`
  - `IMAGEMAGICK_BINARY`
  - `INTEL_DEV_REDIST`
  - `IVANROOT`
  - `IVAN_BACKUP`
  - `IVP_3D_CAMERA`
  - `KINECTSDK10_DIR`
  - `LANG`
  - `LOCALAPPDATA`
  - `LOGONSERVER`
  - `MIC_LD_LIBRARY_PATH`
  - `MOZ_PLUGIN_PATH`
  - `MSMPI_BIN`
  - `MSMPI_INC`
  - `MSMPI_LIB32`
  - `MSMPI_LIB64`
  - `NUMBER_OF_PROCESSORS`
  - `NVCUDASAMPLES7_5_ROOT`
  - `NVCUDASAMPLES8_0_ROOT`
  - `NVCUDASAMPLES9_0_ROOT`
  - `NVCUDASAMPLES9_1_ROOT`
  - `NVCUDASAMPLES_ROOT`
  - `NVTOOLSEXT_PATH`
  - `OPENCV_PATH_V31`
  - `OPENNI2_INCLUDE64`
  - `OPENNI2_LIB64`
  - `OPENNI2_REDIST64`
  - `OS`
  - `PATH`
  - `PATHEXT`
  - `PIPENV_ACTIVE`
  - `PIP_DISABLE_PIP_VERSION_CHECK`
  - `PIP_PYTHON_PATH`
  - `POSTGRESQL_DIR`
  - `PROCESSOR_ARCHITECTURE`
  - `PROCESSOR_IDENTIFIER`
  - `PROCESSOR_LEVEL`
  - `PROCESSOR_REVISION`
  - `PROGRAMDATA`
  - `PROGRAMFILES`
  - `PROGRAMFILES(X86)`
  - `PROGRAMW6432`
  - `PROMPT`
  - `PSMODULEPATH`
  - `PUBLIC`
  - `PUREGEV_ROOT`
  - `PYTHONDONTWRITEBYTECODE`
  - `QTMSBUILD`
  - `SESSIONNAME`
  - `SICK_EASYRANGER_ROOT`
  - `SYSTEMDRIVE`
  - `SYSTEMROOT`
  - `TEMP`
  - `TESSDATA_PREFIX`
  - `TMP`
  - `UR_BLOB_DIR`
  - `UR_LIB_DIR`
  - `UR_PYTHON3_DIR`
  - `UR_UEYE_DIR`
  - `USERDOMAIN`
  - `USERNAME`
  - `USERPROFILE`
  - `VBOX_MSI_INSTALL_PATH`
  - `VIRTUAL_ENV`
  - `VS110COMNTOOLS`
  - `VS120COMNTOOLS`
  - `VS140COMNTOOLS`
  - `VSSDK140INSTALL`
  - `WINDIR`
  - `ZLIB_PATH`
  - `PIP_SHIMS_BASE_MODULE`
  - `PYTHONFINDER_IGNORE_UNSUPPORTED`

Pipenvûspecific environment variables:

 - `PIPENV_ACTIVE`: `1`

Debugûspecific environment variables:

  - `PATH`: `C:\Users\ivan\.virtualenvs\test_pipenv-4FKtD8uM\Scripts;C:\Program Files (x86)\Common Files\Intel\Shared Libraries\redist\intel64\compiler;C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v9.0\bin;C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v9.0\libnvvp;C:\Program Files\ImageMagick-7.0.7-Q16;C:\Program Files (x86)\SICK\3D Cameras\icon\lib;C:\ProgramData\Oracle\Java\javapath;D:\Program Files\Microsoft MPI\Bin\;C:\Program Files\PostgreSQL\9.5\bin;C:\Windows\system32;C:\Windows;C:\Windows\System32\Wbem;C:\Windows\System32\WindowsPowerShell\v1.0\;C:\Program Files (x86)\NVIDIA Corporation\PhysX\Common;C:\Program Files\Intel\WiFi\bin\;C:\Program Files\Common Files\Intel\WirelessCommon\;C:\Program Files (x86)\Windows Kits\8.1\Windows Performance Toolkit\;C:\Program Files\Microsoft SQL Server\110\Tools\Binn\;C:\Program Files (x86)\Microsoft SDKs\TypeScript\1.0\;C:\Program Files\SlikSvn\bin;C:\Program Files\WinRAR;C:\Program Files (x86)\Araxis\Araxis Merge 2001 Professional;D:\tools;C:\Program Files (x86)\DATEV\SiPacompact;C:\Program Files (x86)\CMake\bin;d:\Program Files\MVTec\HALCON-13.0\bin\x64-win64;D:\Program Files (x86)\Graphviz2.38\bin;D:\Program Files\OpenNI2\Redist;C:\Program Files (x86)\Common Files\Pleora\eBUS SDK;C:\Program Files\Common Files\Pleora\eBUS SDK;C:\Program Files (x86)\SICK\EasyRanger\;C:\Program Files\Git\cmd;D:\tools\TortoiseSVN\bin;D:\lib\NVIDIA\cudnn-9.0-windows7-x64-v7\bin;D:\Program Files\MVTec\HALCON-13.0\bin\x64-win64;C:\Program Files\Intel\WiFi\bin\;C:\Program Files\Common Files\Intel\WirelessCommon\;C:\Users\ivan\AppData\Local\GitHubDesktop\bin;C:\Users\ivan\AppData\Local\Programs\Microsoft VS Code\bin`
  - `LANG`: `en_US`
  - `VIRTUAL_ENV`: `C:\Users\ivan\.virtualenvs\test_pipenv-4FKtD8uM`


---------------------------

Contents of `Pipfile` ('D:\\ivan\\dev\\sandbox\\test_pyenv\\test_pyenv_wf\\test_pipenv\\Pipfile'):

```toml
[[source]]
name = "pypi"
url = "https://pypi.org/simple"
verify_ssl = true

[dev-packages]

[packages]
test-package1 = {editable = true,svn = "https://eu-subversion.assembla.com/svn/testuuutestuuu-first-space/test_pyenv/trunk/test_package_1"}
rbose = {editable = true,version = "*"}

[requires]
python_version = "3.6"

```


Contents of `Pipfile.lock` ('D:\\ivan\\dev\\sandbox\\test_pyenv\\test_pyenv_wf\\test_pipenv\\Pipfile.lock'):

```json
{
    "_meta": {
        "hash": {
            "sha256": "b59210558139e4cbe83b9a066e242b2ae504a68377c334c9cfccd1d86c6c5ddb"
        },
        "pipfile-spec": 6,
        "requires": {
            "python_version": "3.6"
        },
        "sources": [
            {
                "name": "pypi",
                "url": "https://pypi.org/simple",
                "verify_ssl": true
            }
        ]
    },
    "default": {
        "numpy": {
            "hashes": [
                "sha256:0df89ca13c25eaa1621a3f09af4c8ba20da849692dcae184cb55e80952c453fb",
                "sha256:154c35f195fd3e1fad2569930ca51907057ae35e03938f89a8aedae91dd1b7c7",
                "sha256:18e84323cdb8de3325e741a7a8dd4a82db74fde363dce32b625324c7b32aa6d7",
                "sha256:1e8956c37fc138d65ded2d96ab3949bd49038cc6e8a4494b1515b0ba88c91565",
                "sha256:23557bdbca3ccbde3abaa12a6e82299bc92d2b9139011f8c16ca1bb8c75d1e95",
                "sha256:24fd645a5e5d224aa6e39d93e4a722fafa9160154f296fd5ef9580191c755053",
                "sha256:36e36b6868e4440760d4b9b44587ea1dc1f06532858d10abba98e851e154ca70",
                "sha256:3d734559db35aa3697dadcea492a423118c5c55d176da2f3be9c98d4803fc2a7",
                "sha256:416a2070acf3a2b5d586f9a6507bb97e33574df5bd7508ea970bbf4fc563fa52",
                "sha256:4a22dc3f5221a644dfe4a63bf990052cc674ef12a157b1056969079985c92816",
                "sha256:4d8d3e5aa6087490912c14a3c10fbdd380b40b421c13920ff468163bc50e016f",
                "sha256:4f41fd159fba1245e1958a99d349df49c616b133636e0cf668f169bce2aeac2d",
                "sha256:561ef098c50f91fbac2cc9305b68c915e9eb915a74d9038ecf8af274d748f76f",
                "sha256:56994e14b386b5c0a9b875a76d22d707b315fa037affc7819cda08b6d0489756",
                "sha256:73a1f2a529604c50c262179fcca59c87a05ff4614fe8a15c186934d84d09d9a5",
                "sha256:7da99445fd890206bfcc7419f79871ba8e73d9d9e6b82fe09980bc5bb4efc35f",
                "sha256:99d59e0bcadac4aa3280616591fb7bcd560e2218f5e31d5223a2e12a1425d495",
                "sha256:a4cc09489843c70b22e8373ca3dfa52b3fab778b57cf81462f1203b0852e95e3",
                "sha256:a61dc29cfca9831a03442a21d4b5fd77e3067beca4b5f81f1a89a04a71cf93fa",
                "sha256:b1853df739b32fa913cc59ad9137caa9cc3d97ff871e2bbd89c2a2a1d4a69451",
                "sha256:b1f44c335532c0581b77491b7715a871d0dd72e97487ac0f57337ccf3ab3469b",
                "sha256:b261e0cb0d6faa8fd6863af26d30351fd2ffdb15b82e51e81e96b9e9e2e7ba16",
                "sha256:c857ae5dba375ea26a6228f98c195fec0898a0fd91bcf0e8a0cae6d9faf3eca7",
                "sha256:cf5bb4a7d53a71bb6a0144d31df784a973b36d8687d615ef6a7e9b1809917a9b",
                "sha256:db9814ff0457b46f2e1d494c1efa4111ca089e08c8b983635ebffb9c1573361f",
                "sha256:df04f4bad8a359daa2ff74f8108ea051670cafbca533bb2636c58b16e962989e",
                "sha256:ecf81720934a0e18526177e645cbd6a8a21bb0ddc887ff9738de07a1df5c6b61",
                "sha256:edfa6fba9157e0e3be0f40168eb142511012683ac3dc82420bee4a3f3981b30e"
            ],
            "version": "==1.15.4"
        },
        "test-package1": {
            "editable": true,
            "ref": 7,
            "svn": "https://eu-subversion.assembla.com/svn/testuuu^testuuu-first-space/test_pyenv/trunk/test_package_1"
        }
    },
    "develop": {}
}

```
</details>



Discussion:
Note that on powershell `^` is an escape character and is not a legal path character. /cc @uranusjr
I'm working in the windows console. And pip works. So this is probably related to pipenv. 

I also tried to escape ^ with \ and `, that does not help. 
Note that the character was also missing from the path pip attempted to check out earlier in the command. I’m fairly confident the failure relates to with passing escape characters around, but then again I don’t have an svn server to test against so maybe you can find out if this works without the special characters?
That is to say yes, still a bug in pipenv because obviously if pip handles it when you run `pip install -e blah` and pipenv doesn’t we should fix it
Actually I created this repo as a testbed for another bug. Unfortunately I cannot create repos without ^ (this is a temporary new workaround in assembla.com as I was told). You can use this repo for tests, it is public. If you could suggest a workaround for ^, I would go straight  to the more important problem.
```
[pipenv.exceptions.InstallError]: ['Obtaining test-package1 from svn+https://eu-subversion.assembla.com/svn/testuuutestuuu-first-space/test_pyenv/trunk/test_package_1@8#egg=test-package1', '  Checking out https://eu-subversion.assembla.com/svn/testuuutestuuu-first-space/test_pyenv/trunk/test_package_1 (to revision 8) to c:\\users\\dan.ryan\\.virtualenvs\\tmp-c5yu54up\\src\\test-package1', "svn: E170013: Unable to connect to a repository at URL 'https://eu-subversion.assembla.com/svn/testuuutestuuu-first-space/test_pyenv/trunk/test_package_1'", "svn: E175003: The server at 'https://eu-subversio
n.assembla.com/svn/testuuutestuuu-first-space/test_pyenv/trunk/test_package_1' does not support the HTTP/DAV protocol"]
[pipenv.exceptions.InstallError]: ['Command "svn checkout -q -r 8 https://eu-subversion.assembla.com/svn/testuuutestuuu-first-space/test_pyenv/trunk/test_package_1 C:\\Users\\Dan.Ryan\\.virtualenvs\\tmp-c5yu54up\\src\\test-package1" failed with error code 1 in None']
```

This is the error I get, I simply ran the command you provided from the command line in windows.  It works fine with a normal SVN client on *nix but I installed tortoise on windows and it seems to be broken with this combo...
Well, the path is used to exit repository (as shown in the error log) does not exist, so maybe that caused the problem. Can you try pip?

pip install -e "svn+https://eu-subversion.assembla.com/svn/testuuu^testuuu-first-space/test_pyenv/trunk/test_package_1#egg=test-package1"
I think caret escape on Windows is by doubling it, i.e. `^^`. You shouldn’t need to do this, of course, so it’s still a bug.

Resolving patch:
diff --git a/pipenv/cmdparse.py b/pipenv/cmdparse.py
--- a/pipenv/cmdparse.py
+++ b/pipenv/cmdparse.py
@@ -70,14 +70,29 @@ def cmdify(self):
         Foul characters include:
 
         * Whitespaces.
+        * Carets (^). (pypa/pipenv#3307)
         * Parentheses in the command. (pypa/pipenv#3168)
 
+        Carets introduce a difficult situation since they are essentially
+        "lossy" when parsed. Consider this in cmd.exe::
+
+            > echo "foo^bar"
+            "foo^bar"
+            > echo foo^^bar
+            foo^bar
+
+        The two commands produce different results, but are both parsed by the
+        shell as `foo^bar`, and there's essentially no sensible way to tell
+        what was actually passed in. This implementation assumes the quoted
+        variation (the first) since it is easier to implement, and arguably
+        the more common case.
+
         The intended use of this function is to pre-process an argument list
         before passing it into ``subprocess.Popen(..., shell=True)``.
 
         See also: https://docs.python.org/3/library/subprocess.html#converting-argument-sequence
         """
         return " ".join(itertools.chain(
-            [_quote_if_contains(self.command, r'[\s()]')],
-            (_quote_if_contains(arg, r'\s') for arg in self.args),
+            [_quote_if_contains(self.command, r'[\s^()]')],
+            (_quote_if_contains(arg, r'[\s^]') for arg in self.args),
         ))


Test patch:
diff --git a/tests/unit/test_cmdparse.py b/tests/unit/test_cmdparse.py
--- a/tests/unit/test_cmdparse.py
+++ b/tests/unit/test_cmdparse.py
@@ -64,3 +64,12 @@ def test_cmdify_quote_if_paren_in_command():
         '-c',
         "print(123)",
     ]), script
+
+
+@pytest.mark.run
+@pytest.mark.script
+def test_cmdify_quote_if_carets():
+    """Ensure arguments are quoted if they contain carets.
+    """
+    script = Script('foo^bar', ['baz^rex'])
+    assert script.cmdify() == '"foo^bar" "baz^rex"', script