Skip to content

Commit 7ca5a84

Browse files
authored
Vendor wheel 0.36.2 (#275)
* Vendor wheel 0.36.2 `wheel` does not provide a public API yet. In order not to be impacted by any internal refactoring/changes in the `wheel` package, the version of the `wheel` package is pinned by `auditwheel`. [This causes some nuisance for users of `auditwheel`](#274). This commit vendors `wheel` to avoid such nuisance and while waiting for a public API in `wheel`. * Omit coverage report for vendored packages * Exclude vendored packages from flake8 check * Fix flake8 issue * Exclude vendored packages from mypy check
1 parent 8d52879 commit 7ca5a84

File tree

15 files changed

+759
-5
lines changed

15 files changed

+759
-5
lines changed

.coveragerc

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,12 @@
1+
[run]
2+
omit=
3+
*/_vendor/*
4+
15
[paths]
26
source =
37
auditwheel/
48
/auditwheel_src/auditwheel
59

610
[report]
711
exclude_lines =
8-
raise NotImplementedError
12+
raise NotImplementedError

auditwheel/_vendor/__init__.py

Whitespace-only changes.
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
"wheel" copyright (c) 2012-2014 Daniel Holth <[email protected]> and
2+
contributors.
3+
4+
The MIT License
5+
6+
Permission is hereby granted, free of charge, to any person obtaining a
7+
copy of this software and associated documentation files (the "Software"),
8+
to deal in the Software without restriction, including without limitation
9+
the rights to use, copy, modify, merge, publish, distribute, sublicense,
10+
and/or sell copies of the Software, and to permit persons to whom the
11+
Software is furnished to do so, subject to the following conditions:
12+
13+
The above copyright notice and this permission notice shall be included
14+
in all copies or substantial portions of the Software.
15+
16+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
19+
THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
20+
OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
21+
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
22+
OTHER DEALINGS IN THE SOFTWARE.
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
__version__ = '0.36.2'
Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
"""
2+
Wheel command-line utility.
3+
"""
4+
5+
from __future__ import print_function
6+
7+
import argparse
8+
import os
9+
import sys
10+
11+
12+
def require_pkgresources(name):
13+
try:
14+
import pkg_resources # noqa: F401
15+
except ImportError:
16+
raise RuntimeError("'{0}' needs pkg_resources (part of setuptools).".format(name))
17+
18+
19+
class WheelError(Exception):
20+
pass
21+
22+
23+
def unpack_f(args):
24+
from .unpack import unpack
25+
unpack(args.wheelfile, args.dest)
26+
27+
28+
def pack_f(args):
29+
from .pack import pack
30+
pack(args.directory, args.dest_dir, args.build_number)
31+
32+
33+
def convert_f(args):
34+
from .convert import convert
35+
convert(args.files, args.dest_dir, args.verbose)
36+
37+
38+
def version_f(args):
39+
from .. import __version__
40+
print("wheel %s" % __version__)
41+
42+
43+
def parser():
44+
p = argparse.ArgumentParser()
45+
s = p.add_subparsers(help="commands")
46+
47+
unpack_parser = s.add_parser('unpack', help='Unpack wheel')
48+
unpack_parser.add_argument('--dest', '-d', help='Destination directory',
49+
default='.')
50+
unpack_parser.add_argument('wheelfile', help='Wheel file')
51+
unpack_parser.set_defaults(func=unpack_f)
52+
53+
repack_parser = s.add_parser('pack', help='Repack wheel')
54+
repack_parser.add_argument('directory', help='Root directory of the unpacked wheel')
55+
repack_parser.add_argument('--dest-dir', '-d', default=os.path.curdir,
56+
help="Directory to store the wheel (default %(default)s)")
57+
repack_parser.add_argument('--build-number', help="Build tag to use in the wheel name")
58+
repack_parser.set_defaults(func=pack_f)
59+
60+
convert_parser = s.add_parser('convert', help='Convert egg or wininst to wheel')
61+
convert_parser.add_argument('files', nargs='*', help='Files to convert')
62+
convert_parser.add_argument('--dest-dir', '-d', default=os.path.curdir,
63+
help="Directory to store wheels (default %(default)s)")
64+
convert_parser.add_argument('--verbose', '-v', action='store_true')
65+
convert_parser.set_defaults(func=convert_f)
66+
67+
version_parser = s.add_parser('version', help='Print version and exit')
68+
version_parser.set_defaults(func=version_f)
69+
70+
help_parser = s.add_parser('help', help='Show this help')
71+
help_parser.set_defaults(func=lambda args: p.print_help())
72+
73+
return p
74+
75+
76+
def main():
77+
p = parser()
78+
args = p.parse_args()
79+
if not hasattr(args, 'func'):
80+
p.print_help()
81+
else:
82+
try:
83+
args.func(args)
84+
return 0
85+
except WheelError as e:
86+
print(e, file=sys.stderr)
87+
88+
return 1
Lines changed: 269 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,269 @@
1+
import os.path
2+
import re
3+
import shutil
4+
import sys
5+
import tempfile
6+
import zipfile
7+
from distutils import dist
8+
from glob import iglob
9+
10+
from ..bdist_wheel import bdist_wheel
11+
from ..wheelfile import WheelFile
12+
from . import WheelError, require_pkgresources
13+
14+
egg_info_re = re.compile(r'''
15+
(?P<name>.+?)-(?P<ver>.+?)
16+
(-(?P<pyver>py\d\.\d+)
17+
(-(?P<arch>.+?))?
18+
)?.egg$''', re.VERBOSE)
19+
20+
21+
class _bdist_wheel_tag(bdist_wheel):
22+
# allow the client to override the default generated wheel tag
23+
# The default bdist_wheel implementation uses python and abi tags
24+
# of the running python process. This is not suitable for
25+
# generating/repackaging prebuild binaries.
26+
27+
full_tag_supplied = False
28+
full_tag = None # None or a (pytag, soabitag, plattag) triple
29+
30+
def get_tag(self):
31+
if self.full_tag_supplied and self.full_tag is not None:
32+
return self.full_tag
33+
else:
34+
return bdist_wheel.get_tag(self)
35+
36+
37+
def egg2wheel(egg_path, dest_dir):
38+
filename = os.path.basename(egg_path)
39+
match = egg_info_re.match(filename)
40+
if not match:
41+
raise WheelError('Invalid egg file name: {}'.format(filename))
42+
43+
egg_info = match.groupdict()
44+
dir = tempfile.mkdtemp(suffix="_e2w")
45+
if os.path.isfile(egg_path):
46+
# assume we have a bdist_egg otherwise
47+
with zipfile.ZipFile(egg_path) as egg:
48+
egg.extractall(dir)
49+
else:
50+
# support buildout-style installed eggs directories
51+
for pth in os.listdir(egg_path):
52+
src = os.path.join(egg_path, pth)
53+
if os.path.isfile(src):
54+
shutil.copy2(src, dir)
55+
else:
56+
shutil.copytree(src, os.path.join(dir, pth))
57+
58+
pyver = egg_info['pyver']
59+
if pyver:
60+
pyver = egg_info['pyver'] = pyver.replace('.', '')
61+
62+
arch = (egg_info['arch'] or 'any').replace('.', '_').replace('-', '_')
63+
64+
# assume all binary eggs are for CPython
65+
abi = 'cp' + pyver[2:] if arch != 'any' else 'none'
66+
67+
root_is_purelib = egg_info['arch'] is None
68+
if root_is_purelib:
69+
bw = bdist_wheel(dist.Distribution())
70+
else:
71+
bw = _bdist_wheel_tag(dist.Distribution())
72+
73+
bw.root_is_pure = root_is_purelib
74+
bw.python_tag = pyver
75+
bw.plat_name_supplied = True
76+
bw.plat_name = egg_info['arch'] or 'any'
77+
if not root_is_purelib:
78+
bw.full_tag_supplied = True
79+
bw.full_tag = (pyver, abi, arch)
80+
81+
dist_info_dir = os.path.join(dir, '{name}-{ver}.dist-info'.format(**egg_info))
82+
bw.egg2dist(os.path.join(dir, 'EGG-INFO'), dist_info_dir)
83+
bw.write_wheelfile(dist_info_dir, generator='egg2wheel')
84+
wheel_name = '{name}-{ver}-{pyver}-{}-{}.whl'.format(abi, arch, **egg_info)
85+
with WheelFile(os.path.join(dest_dir, wheel_name), 'w') as wf:
86+
wf.write_files(dir)
87+
88+
shutil.rmtree(dir)
89+
90+
91+
def parse_wininst_info(wininfo_name, egginfo_name):
92+
"""Extract metadata from filenames.
93+
94+
Extracts the 4 metadataitems needed (name, version, pyversion, arch) from
95+
the installer filename and the name of the egg-info directory embedded in
96+
the zipfile (if any).
97+
98+
The egginfo filename has the format::
99+
100+
name-ver(-pyver)(-arch).egg-info
101+
102+
The installer filename has the format::
103+
104+
name-ver.arch(-pyver).exe
105+
106+
Some things to note:
107+
108+
1. The installer filename is not definitive. An installer can be renamed
109+
and work perfectly well as an installer. So more reliable data should
110+
be used whenever possible.
111+
2. The egg-info data should be preferred for the name and version, because
112+
these come straight from the distutils metadata, and are mandatory.
113+
3. The pyver from the egg-info data should be ignored, as it is
114+
constructed from the version of Python used to build the installer,
115+
which is irrelevant - the installer filename is correct here (even to
116+
the point that when it's not there, any version is implied).
117+
4. The architecture must be taken from the installer filename, as it is
118+
not included in the egg-info data.
119+
5. Architecture-neutral installers still have an architecture because the
120+
installer format itself (being executable) is architecture-specific. We
121+
should therefore ignore the architecture if the content is pure-python.
122+
"""
123+
124+
egginfo = None
125+
if egginfo_name:
126+
egginfo = egg_info_re.search(egginfo_name)
127+
if not egginfo:
128+
raise ValueError("Egg info filename %s is not valid" % (egginfo_name,))
129+
130+
# Parse the wininst filename
131+
# 1. Distribution name (up to the first '-')
132+
w_name, sep, rest = wininfo_name.partition('-')
133+
if not sep:
134+
raise ValueError("Installer filename %s is not valid" % (wininfo_name,))
135+
136+
# Strip '.exe'
137+
rest = rest[:-4]
138+
# 2. Python version (from the last '-', must start with 'py')
139+
rest2, sep, w_pyver = rest.rpartition('-')
140+
if sep and w_pyver.startswith('py'):
141+
rest = rest2
142+
w_pyver = w_pyver.replace('.', '')
143+
else:
144+
# Not version specific - use py2.py3. While it is possible that
145+
# pure-Python code is not compatible with both Python 2 and 3, there
146+
# is no way of knowing from the wininst format, so we assume the best
147+
# here (the user can always manually rename the wheel to be more
148+
# restrictive if needed).
149+
w_pyver = 'py2.py3'
150+
# 3. Version and architecture
151+
w_ver, sep, w_arch = rest.rpartition('.')
152+
if not sep:
153+
raise ValueError("Installer filename %s is not valid" % (wininfo_name,))
154+
155+
if egginfo:
156+
w_name = egginfo.group('name')
157+
w_ver = egginfo.group('ver')
158+
159+
return {'name': w_name, 'ver': w_ver, 'arch': w_arch, 'pyver': w_pyver}
160+
161+
162+
def wininst2wheel(path, dest_dir):
163+
with zipfile.ZipFile(path) as bdw:
164+
# Search for egg-info in the archive
165+
egginfo_name = None
166+
for filename in bdw.namelist():
167+
if '.egg-info' in filename:
168+
egginfo_name = filename
169+
break
170+
171+
info = parse_wininst_info(os.path.basename(path), egginfo_name)
172+
173+
root_is_purelib = True
174+
for zipinfo in bdw.infolist():
175+
if zipinfo.filename.startswith('PLATLIB'):
176+
root_is_purelib = False
177+
break
178+
if root_is_purelib:
179+
paths = {'purelib': ''}
180+
else:
181+
paths = {'platlib': ''}
182+
183+
dist_info = "%(name)s-%(ver)s" % info
184+
datadir = "%s.data/" % dist_info
185+
186+
# rewrite paths to trick ZipFile into extracting an egg
187+
# XXX grab wininst .ini - between .exe, padding, and first zip file.
188+
members = []
189+
egginfo_name = ''
190+
for zipinfo in bdw.infolist():
191+
key, basename = zipinfo.filename.split('/', 1)
192+
key = key.lower()
193+
basepath = paths.get(key, None)
194+
if basepath is None:
195+
basepath = datadir + key.lower() + '/'
196+
oldname = zipinfo.filename
197+
newname = basepath + basename
198+
zipinfo.filename = newname
199+
del bdw.NameToInfo[oldname]
200+
bdw.NameToInfo[newname] = zipinfo
201+
# Collect member names, but omit '' (from an entry like "PLATLIB/"
202+
if newname:
203+
members.append(newname)
204+
# Remember egg-info name for the egg2dist call below
205+
if not egginfo_name:
206+
if newname.endswith('.egg-info'):
207+
egginfo_name = newname
208+
elif '.egg-info/' in newname:
209+
egginfo_name, sep, _ = newname.rpartition('/')
210+
dir = tempfile.mkdtemp(suffix="_b2w")
211+
bdw.extractall(dir, members)
212+
213+
# egg2wheel
214+
abi = 'none'
215+
pyver = info['pyver']
216+
arch = (info['arch'] or 'any').replace('.', '_').replace('-', '_')
217+
# Wininst installers always have arch even if they are not
218+
# architecture-specific (because the format itself is).
219+
# So, assume the content is architecture-neutral if root is purelib.
220+
if root_is_purelib:
221+
arch = 'any'
222+
# If the installer is architecture-specific, it's almost certainly also
223+
# CPython-specific.
224+
if arch != 'any':
225+
pyver = pyver.replace('py', 'cp')
226+
wheel_name = '-'.join((dist_info, pyver, abi, arch))
227+
if root_is_purelib:
228+
bw = bdist_wheel(dist.Distribution())
229+
else:
230+
bw = _bdist_wheel_tag(dist.Distribution())
231+
232+
bw.root_is_pure = root_is_purelib
233+
bw.python_tag = pyver
234+
bw.plat_name_supplied = True
235+
bw.plat_name = info['arch'] or 'any'
236+
237+
if not root_is_purelib:
238+
bw.full_tag_supplied = True
239+
bw.full_tag = (pyver, abi, arch)
240+
241+
dist_info_dir = os.path.join(dir, '%s.dist-info' % dist_info)
242+
bw.egg2dist(os.path.join(dir, egginfo_name), dist_info_dir)
243+
bw.write_wheelfile(dist_info_dir, generator='wininst2wheel')
244+
245+
wheel_path = os.path.join(dest_dir, wheel_name)
246+
with WheelFile(wheel_path, 'w') as wf:
247+
wf.write_files(dir)
248+
249+
shutil.rmtree(dir)
250+
251+
252+
def convert(files, dest_dir, verbose):
253+
# Only support wheel convert if pkg_resources is present
254+
require_pkgresources('wheel convert')
255+
256+
for pat in files:
257+
for installer in iglob(pat):
258+
if os.path.splitext(installer)[1] == '.egg':
259+
conv = egg2wheel
260+
else:
261+
conv = wininst2wheel
262+
263+
if verbose:
264+
print("{}... ".format(installer))
265+
sys.stdout.flush()
266+
267+
conv(installer, dest_dir)
268+
if verbose:
269+
print("OK")

0 commit comments

Comments
 (0)