Repository: pypa/setuptools
Pull request #1360 (2018-05-15 08:10:42)

Use canonicalize_name to look for .dist-info in wheel files
wheel files contain a directory that ends with ".dist-info", but its full name may not exactly match the name of the package as written in the wheel filename. e.g. on Windows, the wheel file name may use different letter cases. Using canonicalize_name on both the package name from the wheel filename and the directories in the zip to find a match fixes the problem.
See issue #1350

The general logic was taken from the pip code: https://github.com/pypa/pip/blob/0ae0109901f63b7f133229a9fee41332dd7366b4/src/pip/_internal/wheel.py#L270

wiggin15: I updated the PR with the new test.

Patch:
diff --git a/changelog.d/1360.change.rst b/changelog.d/1360.change.rst
new file mode 100644
index 000000000..36f40483b
--- /dev/null
+++ b/changelog.d/1360.change.rst
@@ -0,0 +1,1 @@
+Fixed issue with a mismatch between the name of the package and the name of the .dist-info file in wheel files
diff --git a/netlify.toml b/netlify.toml
deleted file mode 100644
index ec21e7be9..000000000
--- a/netlify.toml
+++ /dev/null
@@ -1,5 +0,0 @@
-# Configuration for pull request documentation previews via Netlify
-
-[build]
-    publish = "docs/build/html"
-    command = "pip install tox && tox -e docs"
diff --git a/setuptools/wheel.py b/setuptools/wheel.py
index 37dfa5310..4a33b2032 100644
--- a/setuptools/wheel.py
+++ b/setuptools/wheel.py
@@ -4,10 +4,12 @@ from distutils.util import get_platform
 import email
 import itertools
 import os
+import posixpath
 import re
 import zipfile
 
 from pkg_resources import Distribution, PathMetadata, parse_version
+from setuptools.extern.packaging.utils import canonicalize_name
 from setuptools.extern.six import PY3
 from setuptools import Distribution as SetuptoolsDistribution
 from setuptools import pep425tags
@@ -77,14 +79,24 @@ class Wheel(object):
             platform=(None if self.platform == 'any' else get_platform()),
         ).egg_name() + '.egg'
 
+    def get_dist_info(self, zf):
+        # find the correct name of the .dist-info dir in the wheel file
+        for member in zf.namelist():
+            dirname = posixpath.dirname(member)
+            if (dirname.endswith('.dist-info') and
+                    canonicalize_name(dirname).startswith(
+                        canonicalize_name(self.project_name))):
+                return dirname
+        raise ValueError("unsupported wheel format. .dist-info not found")
+
     def install_as_egg(self, destination_eggdir):
         '''Install wheel as an egg directory.'''
         with zipfile.ZipFile(self.filename) as zf:
             dist_basename = '%s-%s' % (self.project_name, self.version)
-            dist_info = '%s.dist-info' % dist_basename
+            dist_info = self.get_dist_info(zf)
             dist_data = '%s.data' % dist_basename
             def get_metadata(name):
-                with zf.open('%s/%s' % (dist_info, name)) as fp:
+                with zf.open(posixpath.join(dist_info, name)) as fp:
                     value = fp.read().decode('utf-8') if PY3 else fp.read()
                     return email.parser.Parser().parsestr(value)
             wheel_metadata = get_metadata('WHEEL')


Test patch:
diff --git a/setuptools/tests/test_wheel.py b/setuptools/tests/test_wheel.py
index 150ac4c1b..cf6508686 100644
--- a/setuptools/tests/test_wheel.py
+++ b/setuptools/tests/test_wheel.py
@@ -9,12 +9,15 @@ import contextlib
 import glob
 import inspect
 import os
+import shutil
 import subprocess
 import sys
+import zipfile
 
 import pytest
 
 from pkg_resources import Distribution, PathMetadata, PY_MAJOR
+from setuptools.extern.packaging.utils import canonicalize_name
 from setuptools.wheel import Wheel
 
 from .contexts import tempdir
@@ -506,3 +509,33 @@ def test_wheel_install(params):
         _check_wheel_install(filename, install_dir,
                              install_tree, project_name,
                              version, requires_txt)
+
+
+def test_wheel_install_pep_503():
+    project_name = 'Foo_Bar'    # PEP 503 canonicalized name is "foo-bar"
+    version = '1.0'
+    with build_wheel(
+        name=project_name,
+        version=version,
+    ) as filename, tempdir() as install_dir:
+        new_filename = filename.replace(project_name,
+                                        canonicalize_name(project_name))
+        shutil.move(filename, new_filename)
+        _check_wheel_install(new_filename, install_dir, None,
+                             canonicalize_name(project_name),
+                             version, None)
+
+
+def test_wheel_no_dist_dir():
+    project_name = 'nodistinfo'
+    version = '1.0'
+    wheel_name = '{0}-{1}-py2.py3-none-any.whl'.format(project_name, version)
+    with tempdir() as source_dir:
+        wheel_path = os.path.join(source_dir, wheel_name)
+        # create an empty zip file
+        zipfile.ZipFile(wheel_path, 'w').close()
+        with tempdir() as install_dir:
+            with pytest.raises(ValueError):
+                _check_wheel_install(wheel_path, install_dir, None,
+                                     project_name,
+                                     version, None)