Repository: pypa/pip

Issue:
On Windows, Pip does not suppport UNC paths.
When you specify a UNC path as a --find-links option, pip fails when specifying an UNC path:

```
cd f:\
pip install -f \\UNC\path --no-index ipython

Exception:
Traceback (most recent call last):
  File "F:\python27\lib\site-packages\pip\basecommand.py", line 122, in main
    status = self.run(options, args)
  File "F:\python27\lib\site-packages\pip\commands\install.py", line 278, in run
    requirement_set.prepare_files(finder, force_root_egg_info=self.bundle, bundle=self.bundle)
  File "F:\python27\lib\site-packages\pip\req.py", line 1197, in prepare_files
    do_download,
  File "F:\python27\lib\site-packages\pip\req.py", line 1364, in unpack_url
    unpack_file_url(link, location, download_dir)
  File "F:\python27\lib\site-packages\pip\download.py", line 640, in unpack_file_url
    unpack_file(from_path, location, content_type, link)
  File "F:\python27\lib\site-packages\pip\util.py", line 621, in unpack_file
    unzip_file(filename, location, flatten=not filename.endswith(('.pybundle', '.whl')))
  File "F:\python27\lib\site-packages\pip\util.py", line 489, in unzip_file
    zipfp = open(filename, 'rb')
IOError: [Errno 2] No such file or directory: 'f:\\UNC\path\\ipython-2.0.0-py27-none-any.whl'
```



Discussion:
+1 on this. This is a must have.


Resolving patch:
diff --git a/pip/download.py b/pip/download.py
--- a/pip/download.py
+++ b/pip/download.py
@@ -15,6 +15,7 @@
 import tempfile
 
 from pip._vendor.six.moves.urllib import parse as urllib_parse
+from pip._vendor.six.moves.urllib import request as urllib_request
 
 import pip
 
@@ -371,17 +372,15 @@ def url_to_path(url):
     """
     assert url.startswith('file:'), (
         "You can only turn file: urls into filenames (not %r)" % url)
-    path = url[len('file:'):].lstrip('/')
-    path = urllib_parse.unquote(path)
-    if _url_drive_re.match(path):
-        path = path[0] + ':' + path[2:]
-    else:
-        path = '/' + path
-    return path
 
+    _, netloc, path, _, _ = urllib_parse.urlsplit(url)
 
-_drive_re = re.compile('^([a-z]):', re.I)
-_url_drive_re = re.compile('^([a-z])[:|]', re.I)
+    # if we have a UNC path, prepend UNC share notation
+    if netloc:
+        netloc = '\\\\' + netloc
+
+    path = urllib_request.url2pathname(netloc + path)
+    return path
 
 
 def path_to_url(path):
@@ -390,12 +389,8 @@ def path_to_url(path):
     quoted path parts.
     """
     path = os.path.normpath(os.path.abspath(path))
-    drive, path = os.path.splitdrive(path)
-    filepath = path.split(os.path.sep)
-    url = '/'.join([urllib_parse.quote(part) for part in filepath])
-    if not drive:
-        url = url.lstrip('/')
-    return 'file:///' + drive + url
+    url = urllib_parse.urljoin('file:', urllib_request.pathname2url(path))
+    return url
 
 
 def is_archive_file(name):
diff --git a/pip/index.py b/pip/index.py
--- a/pip/index.py
+++ b/pip/index.py
@@ -1041,6 +1041,11 @@ class Link(object):
 
     def __init__(self, url, comes_from=None, internal=None, trusted=None,
                  _deprecated_regex=False):
+
+        # url can be a UNC windows share
+        if url != Inf and url.startswith('\\\\'):
+            url = path_to_url(url)
+
         self.url = url
         self.comes_from = comes_from
         self.internal = internal


Test patch:
diff --git a/tests/unit/test_download.py b/tests/unit/test_download.py
--- a/tests/unit/test_download.py
+++ b/tests/unit/test_download.py
@@ -135,15 +135,26 @@ def test_url_to_path_unix():
 
 @pytest.mark.skipif("sys.platform != 'win32'")
 def test_path_to_url_win():
-    assert path_to_url('c:/tmp/file') == 'file:///c:/tmp/file'
-    assert path_to_url('c:\\tmp\\file') == 'file:///c:/tmp/file'
+    assert path_to_url('c:/tmp/file') == 'file:///C:/tmp/file'
+    assert path_to_url('c:\\tmp\\file') == 'file:///C:/tmp/file'
+    assert path_to_url(r'\\unc\as\path') == 'file://unc/as/path'
     path = os.path.join(os.getcwd(), 'file')
     assert path_to_url('file') == 'file:' + urllib_request.pathname2url(path)
 
 
 @pytest.mark.skipif("sys.platform != 'win32'")
 def test_url_to_path_win():
-    assert url_to_path('file:///c:/tmp/file') == 'c:/tmp/file'
+    assert url_to_path('file:///c:/tmp/file') == 'C:\\tmp\\file'
+    assert url_to_path('file://unc/as/path') == r'\\unc\as\path'
+
+
+@pytest.mark.skipif("sys.platform != 'win32'")
+def test_url_to_path_path_to_url_symetry_win():
+    path = r'C:\tmp\file'
+    assert url_to_path(path_to_url(path)) == path
+
+    unc_path = r'\\unc\share\path'
+    assert url_to_path(path_to_url(unc_path)) == unc_path
 
 
 class Test_unpack_file_url(object):