Skip to content

Commit 4cc6adf

Browse files
committed
1 parent e272cf2 commit 4cc6adf

File tree

1 file changed

+182
-0
lines changed

1 file changed

+182
-0
lines changed
Lines changed: 182 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,182 @@
1+
2+
import argparse
3+
import logging
4+
import os
5+
import sys
6+
7+
from itertools import product
8+
from os.path import (abspath, basename, dirname, isfile, join as pjoin, splitext)
9+
10+
try:
11+
from wheel.install import WheelFile
12+
except ImportError: # As of Wheel 0.32.0
13+
from wheel.wheelfile import WheelFile
14+
from wheel.pkginfo import read_pkg_info, write_pkg_info
15+
from wheeltools.tools import unique_by_index
16+
from wheeltools.wheeltools import InWheelCtx
17+
18+
logger = logging.getLogger(splitext(basename(__file__))[0])
19+
20+
21+
def _get_wheelinfo_name(wheelfile):
22+
# Work round wheel API compatibility
23+
try:
24+
return wheelfile.wheelinfo_name
25+
except AttributeError:
26+
return wheelfile.dist_info_path + "/WHEEL"
27+
28+
29+
def _to_generic_pyver(pyver_tags):
30+
"""Convert from CPython implementation to generic python version tags
31+
32+
Convert each CPython version tag to the equivalent generic tag.
33+
34+
For example::
35+
36+
cp35 -> py3
37+
cp27 -> py2
38+
39+
See https://www.python.org/dev/peps/pep-0425/#python-tag
40+
"""
41+
return ['py%s' % tag[2] if tag.startswith('cp') else tag for tag in pyver_tags]
42+
43+
44+
def _convert_to_generic_platform_wheel(wheel_ctx):
45+
"""Switch to generic python tags and remove ABI tags from a wheel
46+
47+
Convert implementation specific python tags to their generic equivalent and
48+
remove all ABI tags from wheel_ctx's filename and ``WHEEL`` file.
49+
50+
Parameters
51+
----------
52+
wheel_ctx : InWheelCtx
53+
An open wheel context
54+
"""
55+
56+
abi_tags = ['none']
57+
58+
wf = WheelFile(wheel_ctx.in_wheel)
59+
info_fname = _get_wheelinfo_name(wf)
60+
info = read_pkg_info(info_fname)
61+
62+
# Check what tags we have
63+
if wheel_ctx.out_wheel is not None:
64+
out_dir = dirname(wheel_ctx.out_wheel)
65+
wheel_fname = basename(wheel_ctx.out_wheel)
66+
else:
67+
out_dir = '.'
68+
wheel_fname = basename(wheel_ctx.in_wheel)
69+
70+
# Update wheel filename
71+
fparts = wf.parsed_filename.groupdict()
72+
original_platform_tags = fparts['plat'].split('.')
73+
74+
original_abi_tags = fparts['abi'].split('.')
75+
logger.debug('Previous ABI tags: %s', ', '.join(original_abi_tags))
76+
if abi_tags != original_abi_tags:
77+
logger.debug('New ABI tags ....: %s', ', '.join(abi_tags))
78+
fparts['abi'] = '.'.join(abi_tags)
79+
else:
80+
logger.debug('No ABI tags change needed.')
81+
82+
original_pyver_tags = fparts['pyver'].split('.')
83+
logger.debug('Previous pyver tags: %s', ', '.join(original_pyver_tags))
84+
pyver_tags = _to_generic_pyver(original_pyver_tags)
85+
if pyver_tags != original_pyver_tags:
86+
logger.debug('New pyver tags ....: %s', ', '.join(pyver_tags))
87+
fparts['pyver'] = '.'.join(pyver_tags)
88+
else:
89+
logger.debug('No pyver change needed.')
90+
91+
_, ext = splitext(wheel_fname)
92+
fparts['ext'] = ext
93+
out_wheel_fname = "{namever}-{pyver}-{abi}-{plat}{ext}".format(**fparts)
94+
95+
logger.info('Previous filename: %s', wheel_fname)
96+
if out_wheel_fname != wheel_fname:
97+
logger.info('New filename ....: %s', out_wheel_fname)
98+
else:
99+
logger.info('No filename change needed.')
100+
101+
out_wheel = pjoin(out_dir, out_wheel_fname)
102+
103+
# Update wheel tags
104+
in_info_tags = [tag for name, tag in info.items() if name == 'Tag']
105+
logger.info('Previous WHEEL info tags: %s', ', '.join(in_info_tags))
106+
107+
# Python version, C-API version combinations
108+
pyc_apis = []
109+
for tag in in_info_tags:
110+
py_ver = '.'.join(_to_generic_pyver(tag.split('-')[0].split('.')))
111+
abi = 'none'
112+
pyc_apis.append('-'.join([py_ver, abi]))
113+
# unique Python version, C-API version combinations
114+
pyc_apis = unique_by_index(pyc_apis)
115+
116+
# Set tags for each Python version, C-API combination
117+
updated_tags = ['-'.join(tup) for tup in product(pyc_apis, original_platform_tags)]
118+
119+
if updated_tags != in_info_tags:
120+
del info['Tag']
121+
for tag in updated_tags:
122+
info.add_header('Tag', tag)
123+
124+
logger.info('New WHEEL info tags ....: %s', ', '.join(info.get_all('Tag')))
125+
write_pkg_info(info_fname, info)
126+
else:
127+
logger.info('No WHEEL info change needed.')
128+
return out_wheel
129+
130+
131+
def convert_to_generic_platform_wheel(wheel_path, out_dir='./dist/', remove_original=False, verbose=0):
132+
logging.disable(logging.NOTSET)
133+
if verbose >= 1:
134+
logging.basicConfig(level=logging.DEBUG)
135+
else:
136+
logging.basicConfig(level=logging.INFO)
137+
138+
wheel_fname = basename(wheel_path)
139+
out_dir = abspath(out_dir)
140+
141+
with InWheelCtx(wheel_path) as ctx:
142+
ctx.out_wheel = pjoin(out_dir, wheel_fname)
143+
ctx.out_wheel = _convert_to_generic_platform_wheel(ctx)
144+
145+
if remove_original:
146+
logger.info('Removed original wheel %s' % wheel_path)
147+
os.remove(wheel_path)
148+
149+
150+
def main():
151+
p = argparse.ArgumentParser(description='Convert wheel to be independent of python implementation and ABI')
152+
p.set_defaults(prog=basename(sys.argv[0]))
153+
p.add_argument("-v",
154+
"--verbose",
155+
action='count',
156+
dest='verbose',
157+
default=0,
158+
help='Give more output. Option is additive')
159+
160+
p.add_argument('WHEEL_FILE', help='Path to wheel file.')
161+
p.add_argument('-w',
162+
'--wheel-dir',
163+
dest='WHEEL_DIR',
164+
type=abspath,
165+
help='Directory to store updated wheels (default: "dist/")',
166+
default='dist/')
167+
p.add_argument("-r",
168+
"--remove-original",
169+
dest='remove_original',
170+
action='store_true',
171+
help='Remove original wheel')
172+
173+
args = p.parse_args()
174+
175+
if not isfile(args.WHEEL_FILE):
176+
p.error('cannot access %s. No such file' % args.WHEEL_FILE)
177+
178+
convert_to_generic_platform_wheel(args.WHEEL_FILE, args.WHEEL_DIR, args.remove_original, args.verbose)
179+
180+
181+
if __name__ == '__main__':
182+
main()

0 commit comments

Comments
 (0)