Repository: pypa/pip

Issue:
pip 9.0 cannot parse UNC paths.
* Pip version: 9.0
* Python version: 2.7.2
* Operating System: Windows 10

### Description:
I'm trying to install a wheel I've prepared on a UNC share. This works with pip 8.1.2 but not with pip 9.0

### What I've run:

With pip 8.1.2:
```
pip install \\<UNC path>\<package>.whl
[...]
Successfully installed <package>
```

With pip 9.0:
```
pip install \\<UNC path>\<package>.whl
Exception:
Traceback (most recent call last):
  File "d:\venv\pip9\lib\site-packages\pip\basecommand.py", line 215, in main
    status = self.run(options, args)
  File "d:\venv\pip9\lib\site-packages\pip\commands\install.py", line 335, in run
    wb.build(autobuilding=True)
  File "d:\venv\pip9\lib\site-packages\pip\wheel.py", line 749, in build
    self.requirement_set.prepare_files(self.finder)
  File "d:\venv\pip9\lib\site-packages\pip\req\req_set.py", line 380, in prepare_files
    ignore_dependencies=self.ignore_dependencies))
  File "d:\venv\pip9\lib\site-packages\pip\req\req_set.py", line 620, in _prepare_file
    session=self.session, hashes=hashes)
  File "d:\venv\pip9\lib\site-packages\pip\download.py", line 809, in unpack_url
    unpack_file_url(link, location, download_dir, hashes=hashes)
  File "d:\venv\pip9\lib\site-packages\pip\download.py", line 715, in unpack_file_url
    unpack_file(from_path, location, content_type, link)
  File "d:\venv\pip9\lib\site-packages\pip\utils\__init__.py", line 599, in unpack_file
    flatten=not filename.endswith('.whl')
  File "d:\venv\pip9\lib\site-packages\pip\utils\__init__.py", line 482, in unzip_file
    zipfp = open(filename, 'rb')
IOError: [Errno 2] No such file or directory: 'D:\\<UNC path>\\<package>.whl'
```


Discussion:
The UNC path syntax seems wrong - it should be `\\server\share\path\to\file` (double backslash at the start). Was that a typo in your report? If not, could you try the correct syntax and confirm if the problem occurs with that?

That was just due to missing code tags, thanks for pointing it out.

I don't know anything about UNC on Windows. @pfmoore is this something we should get fixed in a 9.0.1? 

@dstufft It's a regression from 8.1.2 (just tested it here). I've no immediate feel for how many people this could affect, but I'd be inclined to say it's worth fixing quickly. I'll do some quick checking to see if I can locate the source of the problem, but I don't have a feel yet for how easy a fix will be.

Okay added to 9.0.1 milestone, but I'm probably not personally going to be able to fix it since I don't really know much about UNC on Windows :(

OK. Bisect found the issue - introduced by 84c969669c0242c9e70137312b7d1001b4af26f8 (PR #3724). As a quick resolution, I recommend that we revert that commit. It looks like `urllib` might be doing funky things with UNC paths, and I don't really have the time to dig into that right now...

Seems like a reasonable idea to me, do you want to do that @pfmoore or want me to?

If you have the time, I'd appreciate it. My git skills are limited and I'd rather not risk breaking anything.


Resolving patch:
diff --git a/pip/index.py b/pip/index.py
--- a/pip/index.py
+++ b/pip/index.py
@@ -1036,16 +1036,6 @@ def is_artifact(self):
 
         return True
 
-    def normalize(self):
-        parsed = urllib_parse.urlsplit(self.url)
-        if parsed.scheme == 'file':
-            path = urllib_request.url2pathname(parsed.path)
-            path = normalize_path(path)
-            path = urllib_request.pathname2url(path)
-            self.url = urllib_parse.urlunsplit(
-                (parsed.scheme, parsed.netloc,
-                 path, parsed.query, parsed.fragment))
-
 
 FormatControl = namedtuple('FormatControl', 'no_binary only_binary')
 """This object has two fields, no_binary and only_binary.
diff --git a/pip/req/req_install.py b/pip/req/req_install.py
--- a/pip/req/req_install.py
+++ b/pip/req/req_install.py
@@ -212,8 +212,10 @@ def from_line(
 
         # it's a local file, dir, or url
         if link:
-            # Normalize URLs
-            link.normalize()
+            # Handle relative file URLs
+            if link.scheme == 'file' and re.search(r'\.\./', link.url):
+                link = Link(
+                    path_to_url(os.path.normpath(os.path.abspath(link.path))))
             # wheel file
             if link.is_wheel:
                 wheel = Wheel(link.filename)  # can raise InvalidWheelFilename


Test patch:
diff --git a/tests/data/src/singlemodule/subdir/.placeholder b/tests/data/src/singlemodule/subdir/.placeholder
deleted file mode 100644
diff --git a/tests/functional/test_install_reqs.py b/tests/functional/test_install_reqs.py
--- a/tests/functional/test_install_reqs.py
+++ b/tests/functional/test_install_reqs.py
@@ -337,29 +337,6 @@ def test_constrained_to_url_install_same_url(script, data):
     assert 'Running setup.py install for singlemodule' in result.stdout
 
 
-def test_constraints_constrain_to_local_dotdot(script, data):
-    to_install = data.src.join("singlemodule/subdir/..")
-    script.scratch_path.join("constraints.txt").write(
-        "file://%s#egg=singlemodule" % to_install
-    )
-    result = script.pip(
-        'install', '--no-index', '-f', data.find_links, '-c',
-        script.scratch_path / 'constraints.txt', to_install)
-    assert 'Running setup.py install for singlemodule' in result.stdout
-
-
-def test_constraints_constrain_to_local_extraslash(script, data):
-    to_install = data.src.join("singlemodule")
-    to_install = '//'.join(to_install.rsplit('/', 1))
-    script.scratch_path.join("constraints.txt").write(
-        "file://%s#egg=singlemodule" % to_install
-    )
-    result = script.pip(
-        'install', '--no-index', '-f', data.find_links, '-c',
-        script.scratch_path / 'constraints.txt', to_install)
-    assert 'Running setup.py install for singlemodule' in result.stdout
-
-
 @pytest.mark.network
 def test_double_install_spurious_hash_mismatch(script, tmpdir):
     """Make sure installing the same hashed sdist twice doesn't throw hash