aboutsummaryrefslogtreecommitdiff
path: root/setuptools/wheel.py
diff options
context:
space:
mode:
Diffstat (limited to 'setuptools/wheel.py')
-rw-r--r--setuptools/wheel.py244
1 files changed, 147 insertions, 97 deletions
diff --git a/setuptools/wheel.py b/setuptools/wheel.py
index 37dfa53..9819e8b 100644
--- a/setuptools/wheel.py
+++ b/setuptools/wheel.py
@@ -1,16 +1,19 @@
-'''Wheels support.'''
+"""Wheels support."""
from distutils.util import get_platform
+from distutils import log
import email
import itertools
import os
+import posixpath
import re
import zipfile
-from pkg_resources import Distribution, PathMetadata, parse_version
-from setuptools.extern.six import PY3
-from setuptools import Distribution as SetuptoolsDistribution
-from setuptools import pep425tags
+import pkg_resources
+import setuptools
+from pkg_resources import parse_version
+from setuptools.extern.packaging.tags import sys_tags
+from setuptools.extern.packaging.utils import canonicalize_name
from setuptools.command.egg_info import write_requirements
@@ -18,14 +21,10 @@ WHEEL_NAME = re.compile(
r"""^(?P<project_name>.+?)-(?P<version>\d.*?)
((-(?P<build>\d.*?))?-(?P<py_version>.+?)-(?P<abi>.+?)-(?P<platform>.+?)
)\.whl$""",
-re.VERBOSE).match
+ re.VERBOSE).match
-NAMESPACE_PACKAGE_INIT = '''\
-try:
- __import__('pkg_resources').declare_namespace(__name__)
-except ImportError:
- __path__ = __import__('pkgutil').extend_path(__path__, __name__)
-'''
+NAMESPACE_PACKAGE_INIT = \
+ "__import__('pkg_resources').declare_namespace(__name__)\n"
def unpack(src_dir, dst_dir):
@@ -50,7 +49,7 @@ def unpack(src_dir, dst_dir):
os.rmdir(dirpath)
-class Wheel(object):
+class Wheel:
def __init__(self, filename):
match = WHEEL_NAME(os.path.basename(filename))
@@ -62,102 +61,153 @@ class Wheel(object):
def tags(self):
'''List tags (py_version, abi, platform) supported by this wheel.'''
- return itertools.product(self.py_version.split('.'),
- self.abi.split('.'),
- self.platform.split('.'))
+ return itertools.product(
+ self.py_version.split('.'),
+ self.abi.split('.'),
+ self.platform.split('.'),
+ )
def is_compatible(self):
'''Is the wheel is compatible with the current platform?'''
- supported_tags = pep425tags.get_supported()
+ supported_tags = set(
+ (t.interpreter, t.abi, t.platform) for t in sys_tags())
return next((True for t in self.tags() if t in supported_tags), False)
def egg_name(self):
- return Distribution(
+ return pkg_resources.Distribution(
project_name=self.project_name, version=self.version,
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_data = '%s.data' % dist_basename
- def get_metadata(name):
- with zf.open('%s/%s' % (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')
- dist_metadata = get_metadata('METADATA')
- # Check wheel format version is supported.
- wheel_version = parse_version(wheel_metadata.get('Wheel-Version'))
- if not parse_version('1.0') <= wheel_version < parse_version('2.0dev0'):
- raise ValueError('unsupported wheel format version: %s' % wheel_version)
- # Extract to target directory.
- os.mkdir(destination_eggdir)
- zf.extractall(destination_eggdir)
- # Convert metadata.
- dist_info = os.path.join(destination_eggdir, dist_info)
- dist = Distribution.from_location(
- destination_eggdir, dist_info,
- metadata=PathMetadata(destination_eggdir, dist_info)
- )
- # Note: we need to evaluate and strip markers now,
- # as we can't easily convert back from the syntax:
- # foobar; "linux" in sys_platform and extra == 'test'
- def raw_req(req):
- req.marker = None
- return str(req)
- install_requires = list(sorted(map(raw_req, dist.requires())))
- extras_require = {
- extra: list(sorted(
- req
- for req in map(raw_req, dist.requires((extra,)))
- if req not in install_requires
- ))
- for extra in dist.extras
- }
- egg_info = os.path.join(destination_eggdir, 'EGG-INFO')
- os.rename(dist_info, egg_info)
- os.rename(os.path.join(egg_info, 'METADATA'),
- os.path.join(egg_info, 'PKG-INFO'))
- setup_dist = SetuptoolsDistribution(attrs=dict(
+ self._install_as_egg(destination_eggdir, zf)
+
+ def _install_as_egg(self, destination_eggdir, zf):
+ dist_basename = '%s-%s' % (self.project_name, self.version)
+ dist_info = self.get_dist_info(zf)
+ dist_data = '%s.data' % dist_basename
+ egg_info = os.path.join(destination_eggdir, 'EGG-INFO')
+
+ self._convert_metadata(zf, destination_eggdir, dist_info, egg_info)
+ self._move_data_entries(destination_eggdir, dist_data)
+ self._fix_namespace_packages(egg_info, destination_eggdir)
+
+ @staticmethod
+ def _convert_metadata(zf, destination_eggdir, dist_info, egg_info):
+ def get_metadata(name):
+ with zf.open(posixpath.join(dist_info, name)) as fp:
+ value = fp.read().decode('utf-8')
+ return email.parser.Parser().parsestr(value)
+
+ wheel_metadata = get_metadata('WHEEL')
+ # Check wheel format version is supported.
+ wheel_version = parse_version(wheel_metadata.get('Wheel-Version'))
+ wheel_v1 = (
+ parse_version('1.0') <= wheel_version < parse_version('2.0dev0')
+ )
+ if not wheel_v1:
+ raise ValueError(
+ 'unsupported wheel format version: %s' % wheel_version)
+ # Extract to target directory.
+ os.mkdir(destination_eggdir)
+ zf.extractall(destination_eggdir)
+ # Convert metadata.
+ dist_info = os.path.join(destination_eggdir, dist_info)
+ dist = pkg_resources.Distribution.from_location(
+ destination_eggdir, dist_info,
+ metadata=pkg_resources.PathMetadata(destination_eggdir, dist_info),
+ )
+
+ # Note: Evaluate and strip markers now,
+ # as it's difficult to convert back from the syntax:
+ # foobar; "linux" in sys_platform and extra == 'test'
+ def raw_req(req):
+ req.marker = None
+ return str(req)
+ install_requires = list(map(raw_req, dist.requires()))
+ extras_require = {
+ extra: [
+ req
+ for req in map(raw_req, dist.requires((extra,)))
+ if req not in install_requires
+ ]
+ for extra in dist.extras
+ }
+ os.rename(dist_info, egg_info)
+ os.rename(
+ os.path.join(egg_info, 'METADATA'),
+ os.path.join(egg_info, 'PKG-INFO'),
+ )
+ setup_dist = setuptools.Distribution(
+ attrs=dict(
install_requires=install_requires,
extras_require=extras_require,
- ))
- write_requirements(setup_dist.get_command_obj('egg_info'),
- None, os.path.join(egg_info, 'requires.txt'))
- # Move data entries to their correct location.
- dist_data = os.path.join(destination_eggdir, dist_data)
- dist_data_scripts = os.path.join(dist_data, 'scripts')
- if os.path.exists(dist_data_scripts):
- egg_info_scripts = os.path.join(destination_eggdir,
- 'EGG-INFO', 'scripts')
- os.mkdir(egg_info_scripts)
- for entry in os.listdir(dist_data_scripts):
- # Remove bytecode, as it's not properly handled
- # during easy_install scripts install phase.
- if entry.endswith('.pyc'):
- os.unlink(os.path.join(dist_data_scripts, entry))
- else:
- os.rename(os.path.join(dist_data_scripts, entry),
- os.path.join(egg_info_scripts, entry))
- os.rmdir(dist_data_scripts)
- for subdir in filter(os.path.exists, (
- os.path.join(dist_data, d)
- for d in ('data', 'headers', 'purelib', 'platlib')
- )):
- unpack(subdir, destination_eggdir)
- if os.path.exists(dist_data):
- os.rmdir(dist_data)
- # Fix namespace packages.
- namespace_packages = os.path.join(egg_info, 'namespace_packages.txt')
- if os.path.exists(namespace_packages):
- with open(namespace_packages) as fp:
- namespace_packages = fp.read().split()
- for mod in namespace_packages:
- mod_dir = os.path.join(destination_eggdir, *mod.split('.'))
- mod_init = os.path.join(mod_dir, '__init__.py')
- if os.path.exists(mod_dir) and not os.path.exists(mod_init):
- with open(mod_init, 'w') as fp:
- fp.write(NAMESPACE_PACKAGE_INIT)
+ ),
+ )
+ # Temporarily disable info traces.
+ log_threshold = log._global_log.threshold
+ log.set_threshold(log.WARN)
+ try:
+ write_requirements(
+ setup_dist.get_command_obj('egg_info'),
+ None,
+ os.path.join(egg_info, 'requires.txt'),
+ )
+ finally:
+ log.set_threshold(log_threshold)
+
+ @staticmethod
+ def _move_data_entries(destination_eggdir, dist_data):
+ """Move data entries to their correct location."""
+ dist_data = os.path.join(destination_eggdir, dist_data)
+ dist_data_scripts = os.path.join(dist_data, 'scripts')
+ if os.path.exists(dist_data_scripts):
+ egg_info_scripts = os.path.join(
+ destination_eggdir, 'EGG-INFO', 'scripts')
+ os.mkdir(egg_info_scripts)
+ for entry in os.listdir(dist_data_scripts):
+ # Remove bytecode, as it's not properly handled
+ # during easy_install scripts install phase.
+ if entry.endswith('.pyc'):
+ os.unlink(os.path.join(dist_data_scripts, entry))
+ else:
+ os.rename(
+ os.path.join(dist_data_scripts, entry),
+ os.path.join(egg_info_scripts, entry),
+ )
+ os.rmdir(dist_data_scripts)
+ for subdir in filter(os.path.exists, (
+ os.path.join(dist_data, d)
+ for d in ('data', 'headers', 'purelib', 'platlib')
+ )):
+ unpack(subdir, destination_eggdir)
+ if os.path.exists(dist_data):
+ os.rmdir(dist_data)
+
+ @staticmethod
+ def _fix_namespace_packages(egg_info, destination_eggdir):
+ namespace_packages = os.path.join(
+ egg_info, 'namespace_packages.txt')
+ if os.path.exists(namespace_packages):
+ with open(namespace_packages) as fp:
+ namespace_packages = fp.read().split()
+ for mod in namespace_packages:
+ mod_dir = os.path.join(destination_eggdir, *mod.split('.'))
+ mod_init = os.path.join(mod_dir, '__init__.py')
+ if not os.path.exists(mod_dir):
+ os.mkdir(mod_dir)
+ if not os.path.exists(mod_init):
+ with open(mod_init, 'w') as fp:
+ fp.write(NAMESPACE_PACKAGE_INIT)