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