comparison lib/python3.8/site-packages/wheel/cli/convert.py @ 0:9e54283cc701 draft

"planemo upload commit d12c32a45bcd441307e632fca6d9af7d60289d44"
author guerler
date Mon, 27 Jul 2020 03:47:31 -0400
parents
children
comparison
equal deleted inserted replaced
-1:000000000000 0:9e54283cc701
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")