]> crepu.dev Git - config.git/blame - djavu-asus/elpy/rpc-venv/lib/python3.11/site-packages/setuptools/command/bdist_egg.py
Actualizado el Readme
[config.git] / djavu-asus / elpy / rpc-venv / lib / python3.11 / site-packages / setuptools / command / bdist_egg.py
CommitLineData
53e6db90
DC
1"""setuptools.command.bdist_egg
2
3Build .egg distributions"""
4
5from distutils.dir_util import remove_tree, mkpath
6from distutils import log
7from types import CodeType
8import sys
9import os
10import re
11import textwrap
12import marshal
13
14from pkg_resources import get_build_platform, Distribution
15from setuptools.extension import Library
16from setuptools import Command
17from .._path import ensure_directory
18
19from sysconfig import get_path, get_python_version
20
21
22def _get_purelib():
23 return get_path("purelib")
24
25
26def strip_module(filename):
27 if '.' in filename:
28 filename = os.path.splitext(filename)[0]
29 if filename.endswith('module'):
30 filename = filename[:-6]
31 return filename
32
33
34def sorted_walk(dir):
35 """Do os.walk in a reproducible way,
36 independent of indeterministic filesystem readdir order
37 """
38 for base, dirs, files in os.walk(dir):
39 dirs.sort()
40 files.sort()
41 yield base, dirs, files
42
43
44def write_stub(resource, pyfile):
45 _stub_template = textwrap.dedent("""
46 def __bootstrap__():
47 global __bootstrap__, __loader__, __file__
48 import sys, pkg_resources, importlib.util
49 __file__ = pkg_resources.resource_filename(__name__, %r)
50 __loader__ = None; del __bootstrap__, __loader__
51 spec = importlib.util.spec_from_file_location(__name__,__file__)
52 mod = importlib.util.module_from_spec(spec)
53 spec.loader.exec_module(mod)
54 __bootstrap__()
55 """).lstrip()
56 with open(pyfile, 'w') as f:
57 f.write(_stub_template % resource)
58
59
60class bdist_egg(Command):
61 description = "create an \"egg\" distribution"
62
63 user_options = [
64 ('bdist-dir=', 'b',
65 "temporary directory for creating the distribution"),
66 ('plat-name=', 'p', "platform name to embed in generated filenames "
67 "(default: %s)" % get_build_platform()),
68 ('exclude-source-files', None,
69 "remove all .py files from the generated egg"),
70 ('keep-temp', 'k',
71 "keep the pseudo-installation tree around after " +
72 "creating the distribution archive"),
73 ('dist-dir=', 'd',
74 "directory to put final built distributions in"),
75 ('skip-build', None,
76 "skip rebuilding everything (for testing/debugging)"),
77 ]
78
79 boolean_options = [
80 'keep-temp', 'skip-build', 'exclude-source-files'
81 ]
82
83 def initialize_options(self):
84 self.bdist_dir = None
85 self.plat_name = None
86 self.keep_temp = 0
87 self.dist_dir = None
88 self.skip_build = 0
89 self.egg_output = None
90 self.exclude_source_files = None
91
92 def finalize_options(self):
93 ei_cmd = self.ei_cmd = self.get_finalized_command("egg_info")
94 self.egg_info = ei_cmd.egg_info
95
96 if self.bdist_dir is None:
97 bdist_base = self.get_finalized_command('bdist').bdist_base
98 self.bdist_dir = os.path.join(bdist_base, 'egg')
99
100 if self.plat_name is None:
101 self.plat_name = get_build_platform()
102
103 self.set_undefined_options('bdist', ('dist_dir', 'dist_dir'))
104
105 if self.egg_output is None:
106
107 # Compute filename of the output egg
108 basename = Distribution(
109 None, None, ei_cmd.egg_name, ei_cmd.egg_version,
110 get_python_version(),
111 self.distribution.has_ext_modules() and self.plat_name
112 ).egg_name()
113
114 self.egg_output = os.path.join(self.dist_dir, basename + '.egg')
115
116 def do_install_data(self):
117 # Hack for packages that install data to install's --install-lib
118 self.get_finalized_command('install').install_lib = self.bdist_dir
119
120 site_packages = os.path.normcase(os.path.realpath(_get_purelib()))
121 old, self.distribution.data_files = self.distribution.data_files, []
122
123 for item in old:
124 if isinstance(item, tuple) and len(item) == 2:
125 if os.path.isabs(item[0]):
126 realpath = os.path.realpath(item[0])
127 normalized = os.path.normcase(realpath)
128 if normalized == site_packages or normalized.startswith(
129 site_packages + os.sep
130 ):
131 item = realpath[len(site_packages) + 1:], item[1]
132 # XXX else: raise ???
133 self.distribution.data_files.append(item)
134
135 try:
136 log.info("installing package data to %s", self.bdist_dir)
137 self.call_command('install_data', force=0, root=None)
138 finally:
139 self.distribution.data_files = old
140
141 def get_outputs(self):
142 return [self.egg_output]
143
144 def call_command(self, cmdname, **kw):
145 """Invoke reinitialized command `cmdname` with keyword args"""
146 for dirname in INSTALL_DIRECTORY_ATTRS:
147 kw.setdefault(dirname, self.bdist_dir)
148 kw.setdefault('skip_build', self.skip_build)
149 kw.setdefault('dry_run', self.dry_run)
150 cmd = self.reinitialize_command(cmdname, **kw)
151 self.run_command(cmdname)
152 return cmd
153
154 def run(self): # noqa: C901 # is too complex (14) # FIXME
155 # Generate metadata first
156 self.run_command("egg_info")
157 # We run install_lib before install_data, because some data hacks
158 # pull their data path from the install_lib command.
159 log.info("installing library code to %s", self.bdist_dir)
160 instcmd = self.get_finalized_command('install')
161 old_root = instcmd.root
162 instcmd.root = None
163 if self.distribution.has_c_libraries() and not self.skip_build:
164 self.run_command('build_clib')
165 cmd = self.call_command('install_lib', warn_dir=0)
166 instcmd.root = old_root
167
168 all_outputs, ext_outputs = self.get_ext_outputs()
169 self.stubs = []
170 to_compile = []
171 for (p, ext_name) in enumerate(ext_outputs):
172 filename, ext = os.path.splitext(ext_name)
173 pyfile = os.path.join(self.bdist_dir, strip_module(filename) +
174 '.py')
175 self.stubs.append(pyfile)
176 log.info("creating stub loader for %s", ext_name)
177 if not self.dry_run:
178 write_stub(os.path.basename(ext_name), pyfile)
179 to_compile.append(pyfile)
180 ext_outputs[p] = ext_name.replace(os.sep, '/')
181
182 if to_compile:
183 cmd.byte_compile(to_compile)
184 if self.distribution.data_files:
185 self.do_install_data()
186
187 # Make the EGG-INFO directory
188 archive_root = self.bdist_dir
189 egg_info = os.path.join(archive_root, 'EGG-INFO')
190 self.mkpath(egg_info)
191 if self.distribution.scripts:
192 script_dir = os.path.join(egg_info, 'scripts')
193 log.info("installing scripts to %s", script_dir)
194 self.call_command('install_scripts', install_dir=script_dir,
195 no_ep=1)
196
197 self.copy_metadata_to(egg_info)
198 native_libs = os.path.join(egg_info, "native_libs.txt")
199 if all_outputs:
200 log.info("writing %s", native_libs)
201 if not self.dry_run:
202 ensure_directory(native_libs)
203 libs_file = open(native_libs, 'wt')
204 libs_file.write('\n'.join(all_outputs))
205 libs_file.write('\n')
206 libs_file.close()
207 elif os.path.isfile(native_libs):
208 log.info("removing %s", native_libs)
209 if not self.dry_run:
210 os.unlink(native_libs)
211
212 write_safety_flag(
213 os.path.join(archive_root, 'EGG-INFO'), self.zip_safe()
214 )
215
216 if os.path.exists(os.path.join(self.egg_info, 'depends.txt')):
217 log.warn(
218 "WARNING: 'depends.txt' will not be used by setuptools 0.6!\n"
219 "Use the install_requires/extras_require setup() args instead."
220 )
221
222 if self.exclude_source_files:
223 self.zap_pyfiles()
224
225 # Make the archive
226 make_zipfile(self.egg_output, archive_root, verbose=self.verbose,
227 dry_run=self.dry_run, mode=self.gen_header())
228 if not self.keep_temp:
229 remove_tree(self.bdist_dir, dry_run=self.dry_run)
230
231 # Add to 'Distribution.dist_files' so that the "upload" command works
232 getattr(self.distribution, 'dist_files', []).append(
233 ('bdist_egg', get_python_version(), self.egg_output))
234
235 def zap_pyfiles(self):
236 log.info("Removing .py files from temporary directory")
237 for base, dirs, files in walk_egg(self.bdist_dir):
238 for name in files:
239 path = os.path.join(base, name)
240
241 if name.endswith('.py'):
242 log.debug("Deleting %s", path)
243 os.unlink(path)
244
245 if base.endswith('__pycache__'):
246 path_old = path
247
248 pattern = r'(?P<name>.+)\.(?P<magic>[^.]+)\.pyc'
249 m = re.match(pattern, name)
250 path_new = os.path.join(
251 base, os.pardir, m.group('name') + '.pyc')
252 log.info(
253 "Renaming file from [%s] to [%s]"
254 % (path_old, path_new))
255 try:
256 os.remove(path_new)
257 except OSError:
258 pass
259 os.rename(path_old, path_new)
260
261 def zip_safe(self):
262 safe = getattr(self.distribution, 'zip_safe', None)
263 if safe is not None:
264 return safe
265 log.warn("zip_safe flag not set; analyzing archive contents...")
266 return analyze_egg(self.bdist_dir, self.stubs)
267
268 def gen_header(self):
269 return 'w'
270
271 def copy_metadata_to(self, target_dir):
272 "Copy metadata (egg info) to the target_dir"
273 # normalize the path (so that a forward-slash in egg_info will
274 # match using startswith below)
275 norm_egg_info = os.path.normpath(self.egg_info)
276 prefix = os.path.join(norm_egg_info, '')
277 for path in self.ei_cmd.filelist.files:
278 if path.startswith(prefix):
279 target = os.path.join(target_dir, path[len(prefix):])
280 ensure_directory(target)
281 self.copy_file(path, target)
282
283 def get_ext_outputs(self):
284 """Get a list of relative paths to C extensions in the output distro"""
285
286 all_outputs = []
287 ext_outputs = []
288
289 paths = {self.bdist_dir: ''}
290 for base, dirs, files in sorted_walk(self.bdist_dir):
291 for filename in files:
292 if os.path.splitext(filename)[1].lower() in NATIVE_EXTENSIONS:
293 all_outputs.append(paths[base] + filename)
294 for filename in dirs:
295 paths[os.path.join(base, filename)] = (paths[base] +
296 filename + '/')
297
298 if self.distribution.has_ext_modules():
299 build_cmd = self.get_finalized_command('build_ext')
300 for ext in build_cmd.extensions:
301 if isinstance(ext, Library):
302 continue
303 fullname = build_cmd.get_ext_fullname(ext.name)
304 filename = build_cmd.get_ext_filename(fullname)
305 if not os.path.basename(filename).startswith('dl-'):
306 if os.path.exists(os.path.join(self.bdist_dir, filename)):
307 ext_outputs.append(filename)
308
309 return all_outputs, ext_outputs
310
311
312NATIVE_EXTENSIONS = dict.fromkeys('.dll .so .dylib .pyd'.split())
313
314
315def walk_egg(egg_dir):
316 """Walk an unpacked egg's contents, skipping the metadata directory"""
317 walker = sorted_walk(egg_dir)
318 base, dirs, files = next(walker)
319 if 'EGG-INFO' in dirs:
320 dirs.remove('EGG-INFO')
321 yield base, dirs, files
322 for bdf in walker:
323 yield bdf
324
325
326def analyze_egg(egg_dir, stubs):
327 # check for existing flag in EGG-INFO
328 for flag, fn in safety_flags.items():
329 if os.path.exists(os.path.join(egg_dir, 'EGG-INFO', fn)):
330 return flag
331 if not can_scan():
332 return False
333 safe = True
334 for base, dirs, files in walk_egg(egg_dir):
335 for name in files:
336 if name.endswith('.py') or name.endswith('.pyw'):
337 continue
338 elif name.endswith('.pyc') or name.endswith('.pyo'):
339 # always scan, even if we already know we're not safe
340 safe = scan_module(egg_dir, base, name, stubs) and safe
341 return safe
342
343
344def write_safety_flag(egg_dir, safe):
345 # Write or remove zip safety flag file(s)
346 for flag, fn in safety_flags.items():
347 fn = os.path.join(egg_dir, fn)
348 if os.path.exists(fn):
349 if safe is None or bool(safe) != flag:
350 os.unlink(fn)
351 elif safe is not None and bool(safe) == flag:
352 f = open(fn, 'wt')
353 f.write('\n')
354 f.close()
355
356
357safety_flags = {
358 True: 'zip-safe',
359 False: 'not-zip-safe',
360}
361
362
363def scan_module(egg_dir, base, name, stubs):
364 """Check whether module possibly uses unsafe-for-zipfile stuff"""
365
366 filename = os.path.join(base, name)
367 if filename[:-1] in stubs:
368 return True # Extension module
369 pkg = base[len(egg_dir) + 1:].replace(os.sep, '.')
370 module = pkg + (pkg and '.' or '') + os.path.splitext(name)[0]
371 if sys.version_info < (3, 7):
372 skip = 12 # skip magic & date & file size
373 else:
374 skip = 16 # skip magic & reserved? & date & file size
375 f = open(filename, 'rb')
376 f.read(skip)
377 code = marshal.load(f)
378 f.close()
379 safe = True
380 symbols = dict.fromkeys(iter_symbols(code))
381 for bad in ['__file__', '__path__']:
382 if bad in symbols:
383 log.warn("%s: module references %s", module, bad)
384 safe = False
385 if 'inspect' in symbols:
386 for bad in [
387 'getsource', 'getabsfile', 'getsourcefile', 'getfile'
388 'getsourcelines', 'findsource', 'getcomments', 'getframeinfo',
389 'getinnerframes', 'getouterframes', 'stack', 'trace'
390 ]:
391 if bad in symbols:
392 log.warn("%s: module MAY be using inspect.%s", module, bad)
393 safe = False
394 return safe
395
396
397def iter_symbols(code):
398 """Yield names and strings used by `code` and its nested code objects"""
399 for name in code.co_names:
400 yield name
401 for const in code.co_consts:
402 if isinstance(const, str):
403 yield const
404 elif isinstance(const, CodeType):
405 for name in iter_symbols(const):
406 yield name
407
408
409def can_scan():
410 if not sys.platform.startswith('java') and sys.platform != 'cli':
411 # CPython, PyPy, etc.
412 return True
413 log.warn("Unable to analyze compiled code on this platform.")
414 log.warn("Please ask the author to include a 'zip_safe'"
415 " setting (either True or False) in the package's setup.py")
416
417
418# Attribute names of options for commands that might need to be convinced to
419# install to the egg build directory
420
421INSTALL_DIRECTORY_ATTRS = [
422 'install_lib', 'install_dir', 'install_data', 'install_base'
423]
424
425
426def make_zipfile(zip_filename, base_dir, verbose=0, dry_run=0, compress=True,
427 mode='w'):
428 """Create a zip file from all the files under 'base_dir'. The output
429 zip file will be named 'base_dir' + ".zip". Uses either the "zipfile"
430 Python module (if available) or the InfoZIP "zip" utility (if installed
431 and found on the default search path). If neither tool is available,
432 raises DistutilsExecError. Returns the name of the output zip file.
433 """
434 import zipfile
435
436 mkpath(os.path.dirname(zip_filename), dry_run=dry_run)
437 log.info("creating '%s' and adding '%s' to it", zip_filename, base_dir)
438
439 def visit(z, dirname, names):
440 for name in names:
441 path = os.path.normpath(os.path.join(dirname, name))
442 if os.path.isfile(path):
443 p = path[len(base_dir) + 1:]
444 if not dry_run:
445 z.write(path, p)
446 log.debug("adding '%s'", p)
447
448 compression = zipfile.ZIP_DEFLATED if compress else zipfile.ZIP_STORED
449 if not dry_run:
450 z = zipfile.ZipFile(zip_filename, mode, compression=compression)
451 for dirname, dirs, files in sorted_walk(base_dir):
452 visit(z, dirname, files)
453 z.close()
454 else:
455 for dirname, dirs, files in sorted_walk(base_dir):
456 visit(None, dirname, files)
457 return zip_filename