]> jfr.im git - dlqueue.git/blob - venv/lib/python3.11/site-packages/setuptools/wheel.py
init: venv aand flask
[dlqueue.git] / venv / lib / python3.11 / site-packages / setuptools / wheel.py
1 """Wheels support."""
2
3 import email
4 import itertools
5 import functools
6 import os
7 import posixpath
8 import re
9 import zipfile
10 import contextlib
11
12 from distutils.util import get_platform
13
14 import setuptools
15 from setuptools.extern.packaging.version import Version as parse_version
16 from setuptools.extern.packaging.tags import sys_tags
17 from setuptools.extern.packaging.utils import canonicalize_name
18 from setuptools.command.egg_info import write_requirements, _egg_basename
19 from setuptools.archive_util import _unpack_zipfile_obj
20
21
22 WHEEL_NAME = re.compile(
23 r"""^(?P<project_name>.+?)-(?P<version>\d.*?)
24 ((-(?P<build>\d.*?))?-(?P<py_version>.+?)-(?P<abi>.+?)-(?P<platform>.+?)
25 )\.whl$""",
26 re.VERBOSE,
27 ).match
28
29 NAMESPACE_PACKAGE_INIT = "__import__('pkg_resources').declare_namespace(__name__)\n"
30
31
32 @functools.lru_cache(maxsize=None)
33 def _get_supported_tags():
34 # We calculate the supported tags only once, otherwise calling
35 # this method on thousands of wheels takes seconds instead of
36 # milliseconds.
37 return {(t.interpreter, t.abi, t.platform) for t in sys_tags()}
38
39
40 def unpack(src_dir, dst_dir):
41 '''Move everything under `src_dir` to `dst_dir`, and delete the former.'''
42 for dirpath, dirnames, filenames in os.walk(src_dir):
43 subdir = os.path.relpath(dirpath, src_dir)
44 for f in filenames:
45 src = os.path.join(dirpath, f)
46 dst = os.path.join(dst_dir, subdir, f)
47 os.renames(src, dst)
48 for n, d in reversed(list(enumerate(dirnames))):
49 src = os.path.join(dirpath, d)
50 dst = os.path.join(dst_dir, subdir, d)
51 if not os.path.exists(dst):
52 # Directory does not exist in destination,
53 # rename it and prune it from os.walk list.
54 os.renames(src, dst)
55 del dirnames[n]
56 # Cleanup.
57 for dirpath, dirnames, filenames in os.walk(src_dir, topdown=True):
58 assert not filenames
59 os.rmdir(dirpath)
60
61
62 @contextlib.contextmanager
63 def disable_info_traces():
64 """
65 Temporarily disable info traces.
66 """
67 from distutils import log
68
69 saved = log.set_threshold(log.WARN)
70 try:
71 yield
72 finally:
73 log.set_threshold(saved)
74
75
76 class Wheel:
77 def __init__(self, filename):
78 match = WHEEL_NAME(os.path.basename(filename))
79 if match is None:
80 raise ValueError('invalid wheel name: %r' % filename)
81 self.filename = filename
82 for k, v in match.groupdict().items():
83 setattr(self, k, v)
84
85 def tags(self):
86 '''List tags (py_version, abi, platform) supported by this wheel.'''
87 return itertools.product(
88 self.py_version.split('.'),
89 self.abi.split('.'),
90 self.platform.split('.'),
91 )
92
93 def is_compatible(self):
94 '''Is the wheel compatible with the current platform?'''
95 return next((True for t in self.tags() if t in _get_supported_tags()), False)
96
97 def egg_name(self):
98 return (
99 _egg_basename(
100 self.project_name,
101 self.version,
102 platform=(None if self.platform == 'any' else get_platform()),
103 )
104 + ".egg"
105 )
106
107 def get_dist_info(self, zf):
108 # find the correct name of the .dist-info dir in the wheel file
109 for member in zf.namelist():
110 dirname = posixpath.dirname(member)
111 if dirname.endswith('.dist-info') and canonicalize_name(dirname).startswith(
112 canonicalize_name(self.project_name)
113 ):
114 return dirname
115 raise ValueError("unsupported wheel format. .dist-info not found")
116
117 def install_as_egg(self, destination_eggdir):
118 '''Install wheel as an egg directory.'''
119 with zipfile.ZipFile(self.filename) as zf:
120 self._install_as_egg(destination_eggdir, zf)
121
122 def _install_as_egg(self, destination_eggdir, zf):
123 dist_basename = '%s-%s' % (self.project_name, self.version)
124 dist_info = self.get_dist_info(zf)
125 dist_data = '%s.data' % dist_basename
126 egg_info = os.path.join(destination_eggdir, 'EGG-INFO')
127
128 self._convert_metadata(zf, destination_eggdir, dist_info, egg_info)
129 self._move_data_entries(destination_eggdir, dist_data)
130 self._fix_namespace_packages(egg_info, destination_eggdir)
131
132 @staticmethod
133 def _convert_metadata(zf, destination_eggdir, dist_info, egg_info):
134 import pkg_resources
135
136 def get_metadata(name):
137 with zf.open(posixpath.join(dist_info, name)) as fp:
138 value = fp.read().decode('utf-8')
139 return email.parser.Parser().parsestr(value)
140
141 wheel_metadata = get_metadata('WHEEL')
142 # Check wheel format version is supported.
143 wheel_version = parse_version(wheel_metadata.get('Wheel-Version'))
144 wheel_v1 = parse_version('1.0') <= wheel_version < parse_version('2.0dev0')
145 if not wheel_v1:
146 raise ValueError('unsupported wheel format version: %s' % wheel_version)
147 # Extract to target directory.
148 _unpack_zipfile_obj(zf, destination_eggdir)
149 # Convert metadata.
150 dist_info = os.path.join(destination_eggdir, dist_info)
151 dist = pkg_resources.Distribution.from_location(
152 destination_eggdir,
153 dist_info,
154 metadata=pkg_resources.PathMetadata(destination_eggdir, dist_info),
155 )
156
157 # Note: Evaluate and strip markers now,
158 # as it's difficult to convert back from the syntax:
159 # foobar; "linux" in sys_platform and extra == 'test'
160 def raw_req(req):
161 req.marker = None
162 return str(req)
163
164 install_requires = list(map(raw_req, dist.requires()))
165 extras_require = {
166 extra: [
167 req
168 for req in map(raw_req, dist.requires((extra,)))
169 if req not in install_requires
170 ]
171 for extra in dist.extras
172 }
173 os.rename(dist_info, egg_info)
174 os.rename(
175 os.path.join(egg_info, 'METADATA'),
176 os.path.join(egg_info, 'PKG-INFO'),
177 )
178 setup_dist = setuptools.Distribution(
179 attrs=dict(
180 install_requires=install_requires,
181 extras_require=extras_require,
182 ),
183 )
184 with disable_info_traces():
185 write_requirements(
186 setup_dist.get_command_obj('egg_info'),
187 None,
188 os.path.join(egg_info, 'requires.txt'),
189 )
190
191 @staticmethod
192 def _move_data_entries(destination_eggdir, dist_data):
193 """Move data entries to their correct location."""
194 dist_data = os.path.join(destination_eggdir, dist_data)
195 dist_data_scripts = os.path.join(dist_data, 'scripts')
196 if os.path.exists(dist_data_scripts):
197 egg_info_scripts = os.path.join(destination_eggdir, 'EGG-INFO', 'scripts')
198 os.mkdir(egg_info_scripts)
199 for entry in os.listdir(dist_data_scripts):
200 # Remove bytecode, as it's not properly handled
201 # during easy_install scripts install phase.
202 if entry.endswith('.pyc'):
203 os.unlink(os.path.join(dist_data_scripts, entry))
204 else:
205 os.rename(
206 os.path.join(dist_data_scripts, entry),
207 os.path.join(egg_info_scripts, entry),
208 )
209 os.rmdir(dist_data_scripts)
210 for subdir in filter(
211 os.path.exists,
212 (
213 os.path.join(dist_data, d)
214 for d in ('data', 'headers', 'purelib', 'platlib')
215 ),
216 ):
217 unpack(subdir, destination_eggdir)
218 if os.path.exists(dist_data):
219 os.rmdir(dist_data)
220
221 @staticmethod
222 def _fix_namespace_packages(egg_info, destination_eggdir):
223 namespace_packages = os.path.join(egg_info, 'namespace_packages.txt')
224 if os.path.exists(namespace_packages):
225 with open(namespace_packages) as fp:
226 namespace_packages = fp.read().split()
227 for mod in namespace_packages:
228 mod_dir = os.path.join(destination_eggdir, *mod.split('.'))
229 mod_init = os.path.join(mod_dir, '__init__.py')
230 if not os.path.exists(mod_dir):
231 os.mkdir(mod_dir)
232 if not os.path.exists(mod_init):
233 with open(mod_init, 'w') as fp:
234 fp.write(NAMESPACE_PACKAGE_INIT)