1""" 2Easy Install 3------------ 4 5A tool for doing automatic download/extract/build of distutils-based Python 6packages. For detailed documentation, see the accompanying EasyInstall.txt 7file, or visit the `EasyInstall home page`__. 8 9__ https://setuptools.readthedocs.io/en/latest/easy_install.html 10 11""" 12 13from glob import glob 14from distutils.util import get_platform 15from distutils.util import convert_path, subst_vars 16from distutils.errors import ( 17 DistutilsArgError, DistutilsOptionError, 18 DistutilsError, DistutilsPlatformError, 19) 20from distutils.command.install import INSTALL_SCHEMES, SCHEME_KEYS 21from distutils import log, dir_util 22from distutils.command.build_scripts import first_line_re 23from distutils.spawn import find_executable 24import sys 25import os 26import zipimport 27import shutil 28import tempfile 29import zipfile 30import re 31import stat 32import random 33import textwrap 34import warnings 35import site 36import struct 37import contextlib 38import subprocess 39import shlex 40import io 41 42 43from sysconfig import get_config_vars, get_path 44 45from setuptools import SetuptoolsDeprecationWarning 46 47from setuptools.extern import six 48from setuptools.extern.six.moves import configparser, map 49 50from setuptools import Command 51from setuptools.sandbox import run_setup 52from setuptools.py27compat import rmtree_safe 53from setuptools.command import setopt 54from setuptools.archive_util import unpack_archive 55from setuptools.package_index import ( 56 PackageIndex, parse_requirement_arg, URL_SCHEME, 57) 58from setuptools.command import bdist_egg, egg_info 59from setuptools.wheel import Wheel 60from pkg_resources import ( 61 yield_lines, normalize_path, resource_string, ensure_directory, 62 get_distribution, find_distributions, Environment, Requirement, 63 Distribution, PathMetadata, EggMetadata, WorkingSet, DistributionNotFound, 64 VersionConflict, DEVELOP_DIST, 65) 66import pkg_resources 67 68__metaclass__ = type 69 70# Turn on PEP440Warnings 71warnings.filterwarnings("default", category=pkg_resources.PEP440Warning) 72 73__all__ = [ 74 'samefile', 'easy_install', 'PthDistributions', 'extract_wininst_cfg', 75 'main', 'get_exe_prefixes', 76] 77 78 79def is_64bit(): 80 return struct.calcsize("P") == 8 81 82 83def samefile(p1, p2): 84 """ 85 Determine if two paths reference the same file. 86 87 Augments os.path.samefile to work on Windows and 88 suppresses errors if the path doesn't exist. 89 """ 90 both_exist = os.path.exists(p1) and os.path.exists(p2) 91 use_samefile = hasattr(os.path, 'samefile') and both_exist 92 if use_samefile: 93 return os.path.samefile(p1, p2) 94 norm_p1 = os.path.normpath(os.path.normcase(p1)) 95 norm_p2 = os.path.normpath(os.path.normcase(p2)) 96 return norm_p1 == norm_p2 97 98 99if six.PY2: 100 101 def _to_bytes(s): 102 return s 103 104 def isascii(s): 105 try: 106 six.text_type(s, 'ascii') 107 return True 108 except UnicodeError: 109 return False 110else: 111 112 def _to_bytes(s): 113 return s.encode('utf8') 114 115 def isascii(s): 116 try: 117 s.encode('ascii') 118 return True 119 except UnicodeError: 120 return False 121 122 123def _one_liner(text): 124 return textwrap.dedent(text).strip().replace('\n', '; ') 125 126 127class easy_install(Command): 128 """Manage a download/build/install process""" 129 description = "Find/get/install Python packages" 130 command_consumes_arguments = True 131 132 user_options = [ 133 ('prefix=', None, "installation prefix"), 134 ("zip-ok", "z", "install package as a zipfile"), 135 ("multi-version", "m", "make apps have to require() a version"), 136 ("upgrade", "U", "force upgrade (searches PyPI for latest versions)"), 137 ("install-dir=", "d", "install package to DIR"), 138 ("script-dir=", "s", "install scripts to DIR"), 139 ("exclude-scripts", "x", "Don't install scripts"), 140 ("always-copy", "a", "Copy all needed packages to install dir"), 141 ("index-url=", "i", "base URL of Python Package Index"), 142 ("find-links=", "f", "additional URL(s) to search for packages"), 143 ("build-directory=", "b", 144 "download/extract/build in DIR; keep the results"), 145 ('optimize=', 'O', 146 "also compile with optimization: -O1 for \"python -O\", " 147 "-O2 for \"python -OO\", and -O0 to disable [default: -O0]"), 148 ('record=', None, 149 "filename in which to record list of installed files"), 150 ('always-unzip', 'Z', "don't install as a zipfile, no matter what"), 151 ('site-dirs=', 'S', "list of directories where .pth files work"), 152 ('editable', 'e', "Install specified packages in editable form"), 153 ('no-deps', 'N', "don't install dependencies"), 154 ('allow-hosts=', 'H', "pattern(s) that hostnames must match"), 155 ('local-snapshots-ok', 'l', 156 "allow building eggs from local checkouts"), 157 ('version', None, "print version information and exit"), 158 ('no-find-links', None, 159 "Don't load find-links defined in packages being installed"), 160 ('user', None, "install in user site-package '%s'" % site.USER_SITE) 161 ] 162 boolean_options = [ 163 'zip-ok', 'multi-version', 'exclude-scripts', 'upgrade', 'always-copy', 164 'editable', 165 'no-deps', 'local-snapshots-ok', 'version', 166 'user' 167 ] 168 169 negative_opt = {'always-unzip': 'zip-ok'} 170 create_index = PackageIndex 171 172 def initialize_options(self): 173 # the --user option seems to be an opt-in one, 174 # so the default should be False. 175 self.user = 0 176 self.zip_ok = self.local_snapshots_ok = None 177 self.install_dir = self.script_dir = self.exclude_scripts = None 178 self.index_url = None 179 self.find_links = None 180 self.build_directory = None 181 self.args = None 182 self.optimize = self.record = None 183 self.upgrade = self.always_copy = self.multi_version = None 184 self.editable = self.no_deps = self.allow_hosts = None 185 self.root = self.prefix = self.no_report = None 186 self.version = None 187 self.install_purelib = None # for pure module distributions 188 self.install_platlib = None # non-pure (dists w/ extensions) 189 self.install_headers = None # for C/C++ headers 190 self.install_lib = None # set to either purelib or platlib 191 self.install_scripts = None 192 self.install_data = None 193 self.install_base = None 194 self.install_platbase = None 195 if site.ENABLE_USER_SITE: 196 self.install_userbase = site.USER_BASE 197 self.install_usersite = site.USER_SITE 198 else: 199 self.install_userbase = None 200 self.install_usersite = None 201 self.no_find_links = None 202 203 # Options not specifiable via command line 204 self.package_index = None 205 self.pth_file = self.always_copy_from = None 206 self.site_dirs = None 207 self.installed_projects = {} 208 # Always read easy_install options, even if we are subclassed, or have 209 # an independent instance created. This ensures that defaults will 210 # always come from the standard configuration file(s)' "easy_install" 211 # section, even if this is a "develop" or "install" command, or some 212 # other embedding. 213 self._dry_run = None 214 self.verbose = self.distribution.verbose 215 self.distribution._set_command_options( 216 self, self.distribution.get_option_dict('easy_install') 217 ) 218 219 def delete_blockers(self, blockers): 220 extant_blockers = ( 221 filename for filename in blockers 222 if os.path.exists(filename) or os.path.islink(filename) 223 ) 224 list(map(self._delete_path, extant_blockers)) 225 226 def _delete_path(self, path): 227 log.info("Deleting %s", path) 228 if self.dry_run: 229 return 230 231 is_tree = os.path.isdir(path) and not os.path.islink(path) 232 remover = rmtree if is_tree else os.unlink 233 remover(path) 234 235 @staticmethod 236 def _render_version(): 237 """ 238 Render the Setuptools version and installation details, then exit. 239 """ 240 ver = '{}.{}'.format(*sys.version_info) 241 dist = get_distribution('setuptools') 242 tmpl = 'setuptools {dist.version} from {dist.location} (Python {ver})' 243 print(tmpl.format(**locals())) 244 raise SystemExit() 245 246 def finalize_options(self): 247 self.version and self._render_version() 248 249 py_version = sys.version.split()[0] 250 prefix, exec_prefix = get_config_vars('prefix', 'exec_prefix') 251 252 self.config_vars = { 253 'dist_name': self.distribution.get_name(), 254 'dist_version': self.distribution.get_version(), 255 'dist_fullname': self.distribution.get_fullname(), 256 'py_version': py_version, 257 'py_version_short': py_version[0:3], 258 'py_version_nodot': py_version[0] + py_version[2], 259 'sys_prefix': prefix, 260 'prefix': prefix, 261 'sys_exec_prefix': exec_prefix, 262 'exec_prefix': exec_prefix, 263 # Only python 3.2+ has abiflags 264 'abiflags': getattr(sys, 'abiflags', ''), 265 } 266 267 if site.ENABLE_USER_SITE: 268 self.config_vars['userbase'] = self.install_userbase 269 self.config_vars['usersite'] = self.install_usersite 270 271 elif self.user: 272 log.warn("WARNING: The user site-packages directory is disabled.") 273 274 self._fix_install_dir_for_user_site() 275 276 self.expand_basedirs() 277 self.expand_dirs() 278 279 self._expand( 280 'install_dir', 'script_dir', 'build_directory', 281 'site_dirs', 282 ) 283 # If a non-default installation directory was specified, default the 284 # script directory to match it. 285 if self.script_dir is None: 286 self.script_dir = self.install_dir 287 288 if self.no_find_links is None: 289 self.no_find_links = False 290 291 # Let install_dir get set by install_lib command, which in turn 292 # gets its info from the install command, and takes into account 293 # --prefix and --home and all that other crud. 294 self.set_undefined_options( 295 'install_lib', ('install_dir', 'install_dir') 296 ) 297 # Likewise, set default script_dir from 'install_scripts.install_dir' 298 self.set_undefined_options( 299 'install_scripts', ('install_dir', 'script_dir') 300 ) 301 302 if self.user and self.install_purelib: 303 self.install_dir = self.install_purelib 304 self.script_dir = self.install_scripts 305 # default --record from the install command 306 self.set_undefined_options('install', ('record', 'record')) 307 # Should this be moved to the if statement below? It's not used 308 # elsewhere 309 normpath = map(normalize_path, sys.path) 310 self.all_site_dirs = get_site_dirs() 311 if self.site_dirs is not None: 312 site_dirs = [ 313 os.path.expanduser(s.strip()) for s in 314 self.site_dirs.split(',') 315 ] 316 for d in site_dirs: 317 if not os.path.isdir(d): 318 log.warn("%s (in --site-dirs) does not exist", d) 319 elif normalize_path(d) not in normpath: 320 raise DistutilsOptionError( 321 d + " (in --site-dirs) is not on sys.path" 322 ) 323 else: 324 self.all_site_dirs.append(normalize_path(d)) 325 if not self.editable: 326 self.check_site_dir() 327 self.index_url = self.index_url or "https://pypi.org/simple/" 328 self.shadow_path = self.all_site_dirs[:] 329 for path_item in self.install_dir, normalize_path(self.script_dir): 330 if path_item not in self.shadow_path: 331 self.shadow_path.insert(0, path_item) 332 333 if self.allow_hosts is not None: 334 hosts = [s.strip() for s in self.allow_hosts.split(',')] 335 else: 336 hosts = ['*'] 337 if self.package_index is None: 338 self.package_index = self.create_index( 339 self.index_url, search_path=self.shadow_path, hosts=hosts, 340 ) 341 self.local_index = Environment(self.shadow_path + sys.path) 342 343 if self.find_links is not None: 344 if isinstance(self.find_links, six.string_types): 345 self.find_links = self.find_links.split() 346 else: 347 self.find_links = [] 348 if self.local_snapshots_ok: 349 self.package_index.scan_egg_links(self.shadow_path + sys.path) 350 if not self.no_find_links: 351 self.package_index.add_find_links(self.find_links) 352 self.set_undefined_options('install_lib', ('optimize', 'optimize')) 353 if not isinstance(self.optimize, int): 354 try: 355 self.optimize = int(self.optimize) 356 if not (0 <= self.optimize <= 2): 357 raise ValueError 358 except ValueError as e: 359 raise DistutilsOptionError( 360 "--optimize must be 0, 1, or 2" 361 ) from e 362 363 if self.editable and not self.build_directory: 364 raise DistutilsArgError( 365 "Must specify a build directory (-b) when using --editable" 366 ) 367 if not self.args: 368 raise DistutilsArgError( 369 "No urls, filenames, or requirements specified (see --help)") 370 371 self.outputs = [] 372 373 def _fix_install_dir_for_user_site(self): 374 """ 375 Fix the install_dir if "--user" was used. 376 """ 377 if not self.user or not site.ENABLE_USER_SITE: 378 return 379 380 self.create_home_path() 381 if self.install_userbase is None: 382 msg = "User base directory is not specified" 383 raise DistutilsPlatformError(msg) 384 self.install_base = self.install_platbase = self.install_userbase 385 scheme_name = os.name.replace('posix', 'unix') + '_user' 386 self.select_scheme(scheme_name) 387 388 def _expand_attrs(self, attrs): 389 for attr in attrs: 390 val = getattr(self, attr) 391 if val is not None: 392 if os.name == 'posix' or os.name == 'nt': 393 val = os.path.expanduser(val) 394 val = subst_vars(val, self.config_vars) 395 setattr(self, attr, val) 396 397 def expand_basedirs(self): 398 """Calls `os.path.expanduser` on install_base, install_platbase and 399 root.""" 400 self._expand_attrs(['install_base', 'install_platbase', 'root']) 401 402 def expand_dirs(self): 403 """Calls `os.path.expanduser` on install dirs.""" 404 dirs = [ 405 'install_purelib', 406 'install_platlib', 407 'install_lib', 408 'install_headers', 409 'install_scripts', 410 'install_data', 411 ] 412 self._expand_attrs(dirs) 413 414 def run(self, show_deprecation=True): 415 if show_deprecation: 416 self.announce( 417 "WARNING: The easy_install command is deprecated " 418 "and will be removed in a future version.", 419 log.WARN, 420 ) 421 if self.verbose != self.distribution.verbose: 422 log.set_verbosity(self.verbose) 423 try: 424 for spec in self.args: 425 self.easy_install(spec, not self.no_deps) 426 if self.record: 427 outputs = self.outputs 428 if self.root: # strip any package prefix 429 root_len = len(self.root) 430 for counter in range(len(outputs)): 431 outputs[counter] = outputs[counter][root_len:] 432 from distutils import file_util 433 434 self.execute( 435 file_util.write_file, (self.record, outputs), 436 "writing list of installed files to '%s'" % 437 self.record 438 ) 439 self.warn_deprecated_options() 440 finally: 441 log.set_verbosity(self.distribution.verbose) 442 443 def pseudo_tempname(self): 444 """Return a pseudo-tempname base in the install directory. 445 This code is intentionally naive; if a malicious party can write to 446 the target directory you're already in deep doodoo. 447 """ 448 try: 449 pid = os.getpid() 450 except Exception: 451 pid = random.randint(0, sys.maxsize) 452 return os.path.join(self.install_dir, "test-easy-install-%s" % pid) 453 454 def warn_deprecated_options(self): 455 pass 456 457 def check_site_dir(self): 458 """Verify that self.install_dir is .pth-capable dir, if needed""" 459 460 instdir = normalize_path(self.install_dir) 461 pth_file = os.path.join(instdir, 'easy-install.pth') 462 463 if not os.path.exists(instdir): 464 try: 465 os.makedirs(instdir) 466 except (OSError, IOError): 467 self.cant_write_to_target() 468 469 # Is it a configured, PYTHONPATH, implicit, or explicit site dir? 470 is_site_dir = instdir in self.all_site_dirs 471 472 if not is_site_dir and not self.multi_version: 473 # No? Then directly test whether it does .pth file processing 474 is_site_dir = self.check_pth_processing() 475 else: 476 # make sure we can write to target dir 477 testfile = self.pseudo_tempname() + '.write-test' 478 test_exists = os.path.exists(testfile) 479 try: 480 if test_exists: 481 os.unlink(testfile) 482 open(testfile, 'w').close() 483 os.unlink(testfile) 484 except (OSError, IOError): 485 self.cant_write_to_target() 486 487 if not is_site_dir and not self.multi_version: 488 # Can't install non-multi to non-site dir with easy_install 489 pythonpath = os.environ.get('PYTHONPATH', '') 490 log.warn(self.__no_default_msg, self.install_dir, pythonpath) 491 492 if is_site_dir: 493 if self.pth_file is None: 494 self.pth_file = PthDistributions(pth_file, self.all_site_dirs) 495 else: 496 self.pth_file = None 497 498 if self.multi_version and not os.path.exists(pth_file): 499 self.pth_file = None # don't create a .pth file 500 self.install_dir = instdir 501 502 __cant_write_msg = textwrap.dedent(""" 503 can't create or remove files in install directory 504 505 The following error occurred while trying to add or remove files in the 506 installation directory: 507 508 %s 509 510 The installation directory you specified (via --install-dir, --prefix, or 511 the distutils default setting) was: 512 513 %s 514 """).lstrip() # noqa 515 516 __not_exists_id = textwrap.dedent(""" 517 This directory does not currently exist. Please create it and try again, or 518 choose a different installation directory (using the -d or --install-dir 519 option). 520 """).lstrip() # noqa 521 522 __access_msg = textwrap.dedent(""" 523 Perhaps your account does not have write access to this directory? If the 524 installation directory is a system-owned directory, you may need to sign in 525 as the administrator or "root" account. If you do not have administrative 526 access to this machine, you may wish to choose a different installation 527 directory, preferably one that is listed in your PYTHONPATH environment 528 variable. 529 530 For information on other options, you may wish to consult the 531 documentation at: 532 533 https://setuptools.readthedocs.io/en/latest/easy_install.html 534 535 Please make the appropriate changes for your system and try again. 536 """).lstrip() # noqa 537 538 def cant_write_to_target(self): 539 msg = self.__cant_write_msg % (sys.exc_info()[1], self.install_dir,) 540 541 if not os.path.exists(self.install_dir): 542 msg += '\n' + self.__not_exists_id 543 else: 544 msg += '\n' + self.__access_msg 545 raise DistutilsError(msg) 546 547 def check_pth_processing(self): 548 """Empirically verify whether .pth files are supported in inst. dir""" 549 instdir = self.install_dir 550 log.info("Checking .pth file support in %s", instdir) 551 pth_file = self.pseudo_tempname() + ".pth" 552 ok_file = pth_file + '.ok' 553 ok_exists = os.path.exists(ok_file) 554 tmpl = _one_liner(""" 555 import os 556 f = open({ok_file!r}, 'w') 557 f.write('OK') 558 f.close() 559 """) + '\n' 560 try: 561 if ok_exists: 562 os.unlink(ok_file) 563 dirname = os.path.dirname(ok_file) 564 os.makedirs(dirname, exist_ok=True) 565 f = open(pth_file, 'w') 566 except (OSError, IOError): 567 self.cant_write_to_target() 568 else: 569 try: 570 f.write(tmpl.format(**locals())) 571 f.close() 572 f = None 573 executable = sys.executable 574 if os.name == 'nt': 575 dirname, basename = os.path.split(executable) 576 alt = os.path.join(dirname, 'pythonw.exe') 577 use_alt = ( 578 basename.lower() == 'python.exe' and 579 os.path.exists(alt) 580 ) 581 if use_alt: 582 # use pythonw.exe to avoid opening a console window 583 executable = alt 584 585 from distutils.spawn import spawn 586 587 spawn([executable, '-E', '-c', 'pass'], 0) 588 589 if os.path.exists(ok_file): 590 log.info( 591 "TEST PASSED: %s appears to support .pth files", 592 instdir 593 ) 594 return True 595 finally: 596 if f: 597 f.close() 598 if os.path.exists(ok_file): 599 os.unlink(ok_file) 600 if os.path.exists(pth_file): 601 os.unlink(pth_file) 602 if not self.multi_version: 603 log.warn("TEST FAILED: %s does NOT support .pth files", instdir) 604 return False 605 606 def install_egg_scripts(self, dist): 607 """Write all the scripts for `dist`, unless scripts are excluded""" 608 if not self.exclude_scripts and dist.metadata_isdir('scripts'): 609 for script_name in dist.metadata_listdir('scripts'): 610 if dist.metadata_isdir('scripts/' + script_name): 611 # The "script" is a directory, likely a Python 3 612 # __pycache__ directory, so skip it. 613 continue 614 self.install_script( 615 dist, script_name, 616 dist.get_metadata('scripts/' + script_name) 617 ) 618 self.install_wrapper_scripts(dist) 619 620 def add_output(self, path): 621 if os.path.isdir(path): 622 for base, dirs, files in os.walk(path): 623 for filename in files: 624 self.outputs.append(os.path.join(base, filename)) 625 else: 626 self.outputs.append(path) 627 628 def not_editable(self, spec): 629 if self.editable: 630 raise DistutilsArgError( 631 "Invalid argument %r: you can't use filenames or URLs " 632 "with --editable (except via the --find-links option)." 633 % (spec,) 634 ) 635 636 def check_editable(self, spec): 637 if not self.editable: 638 return 639 640 if os.path.exists(os.path.join(self.build_directory, spec.key)): 641 raise DistutilsArgError( 642 "%r already exists in %s; can't do a checkout there" % 643 (spec.key, self.build_directory) 644 ) 645 646 @contextlib.contextmanager 647 def _tmpdir(self): 648 tmpdir = tempfile.mkdtemp(prefix=u"easy_install-") 649 try: 650 # cast to str as workaround for #709 and #710 and #712 651 yield str(tmpdir) 652 finally: 653 os.path.exists(tmpdir) and rmtree(rmtree_safe(tmpdir)) 654 655 def easy_install(self, spec, deps=False): 656 with self._tmpdir() as tmpdir: 657 if not isinstance(spec, Requirement): 658 if URL_SCHEME(spec): 659 # It's a url, download it to tmpdir and process 660 self.not_editable(spec) 661 dl = self.package_index.download(spec, tmpdir) 662 return self.install_item(None, dl, tmpdir, deps, True) 663 664 elif os.path.exists(spec): 665 # Existing file or directory, just process it directly 666 self.not_editable(spec) 667 return self.install_item(None, spec, tmpdir, deps, True) 668 else: 669 spec = parse_requirement_arg(spec) 670 671 self.check_editable(spec) 672 dist = self.package_index.fetch_distribution( 673 spec, tmpdir, self.upgrade, self.editable, 674 not self.always_copy, self.local_index 675 ) 676 if dist is None: 677 msg = "Could not find suitable distribution for %r" % spec 678 if self.always_copy: 679 msg += " (--always-copy skips system and development eggs)" 680 raise DistutilsError(msg) 681 elif dist.precedence == DEVELOP_DIST: 682 # .egg-info dists don't need installing, just process deps 683 self.process_distribution(spec, dist, deps, "Using") 684 return dist 685 else: 686 return self.install_item(spec, dist.location, tmpdir, deps) 687 688 def install_item(self, spec, download, tmpdir, deps, install_needed=False): 689 690 # Installation is also needed if file in tmpdir or is not an egg 691 install_needed = install_needed or self.always_copy 692 install_needed = install_needed or os.path.dirname(download) == tmpdir 693 install_needed = install_needed or not download.endswith('.egg') 694 install_needed = install_needed or ( 695 self.always_copy_from is not None and 696 os.path.dirname(normalize_path(download)) == 697 normalize_path(self.always_copy_from) 698 ) 699 700 if spec and not install_needed: 701 # at this point, we know it's a local .egg, we just don't know if 702 # it's already installed. 703 for dist in self.local_index[spec.project_name]: 704 if dist.location == download: 705 break 706 else: 707 install_needed = True # it's not in the local index 708 709 log.info("Processing %s", os.path.basename(download)) 710 711 if install_needed: 712 dists = self.install_eggs(spec, download, tmpdir) 713 for dist in dists: 714 self.process_distribution(spec, dist, deps) 715 else: 716 dists = [self.egg_distribution(download)] 717 self.process_distribution(spec, dists[0], deps, "Using") 718 719 if spec is not None: 720 for dist in dists: 721 if dist in spec: 722 return dist 723 724 def select_scheme(self, name): 725 """Sets the install directories by applying the install schemes.""" 726 # it's the caller's problem if they supply a bad name! 727 scheme = INSTALL_SCHEMES[name] 728 for key in SCHEME_KEYS: 729 attrname = 'install_' + key 730 if getattr(self, attrname) is None: 731 setattr(self, attrname, scheme[key]) 732 733 def process_distribution(self, requirement, dist, deps=True, *info): 734 self.update_pth(dist) 735 self.package_index.add(dist) 736 if dist in self.local_index[dist.key]: 737 self.local_index.remove(dist) 738 self.local_index.add(dist) 739 self.install_egg_scripts(dist) 740 self.installed_projects[dist.key] = dist 741 log.info(self.installation_report(requirement, dist, *info)) 742 if (dist.has_metadata('dependency_links.txt') and 743 not self.no_find_links): 744 self.package_index.add_find_links( 745 dist.get_metadata_lines('dependency_links.txt') 746 ) 747 if not deps and not self.always_copy: 748 return 749 elif requirement is not None and dist.key != requirement.key: 750 log.warn("Skipping dependencies for %s", dist) 751 return # XXX this is not the distribution we were looking for 752 elif requirement is None or dist not in requirement: 753 # if we wound up with a different version, resolve what we've got 754 distreq = dist.as_requirement() 755 requirement = Requirement(str(distreq)) 756 log.info("Processing dependencies for %s", requirement) 757 try: 758 distros = WorkingSet([]).resolve( 759 [requirement], self.local_index, self.easy_install 760 ) 761 except DistributionNotFound as e: 762 raise DistutilsError(str(e)) from e 763 except VersionConflict as e: 764 raise DistutilsError(e.report()) from e 765 if self.always_copy or self.always_copy_from: 766 # Force all the relevant distros to be copied or activated 767 for dist in distros: 768 if dist.key not in self.installed_projects: 769 self.easy_install(dist.as_requirement()) 770 log.info("Finished processing dependencies for %s", requirement) 771 772 def should_unzip(self, dist): 773 if self.zip_ok is not None: 774 return not self.zip_ok 775 if dist.has_metadata('not-zip-safe'): 776 return True 777 if not dist.has_metadata('zip-safe'): 778 return True 779 return False 780 781 def maybe_move(self, spec, dist_filename, setup_base): 782 dst = os.path.join(self.build_directory, spec.key) 783 if os.path.exists(dst): 784 msg = ( 785 "%r already exists in %s; build directory %s will not be kept" 786 ) 787 log.warn(msg, spec.key, self.build_directory, setup_base) 788 return setup_base 789 if os.path.isdir(dist_filename): 790 setup_base = dist_filename 791 else: 792 if os.path.dirname(dist_filename) == setup_base: 793 os.unlink(dist_filename) # get it out of the tmp dir 794 contents = os.listdir(setup_base) 795 if len(contents) == 1: 796 dist_filename = os.path.join(setup_base, contents[0]) 797 if os.path.isdir(dist_filename): 798 # if the only thing there is a directory, move it instead 799 setup_base = dist_filename 800 ensure_directory(dst) 801 shutil.move(setup_base, dst) 802 return dst 803 804 def install_wrapper_scripts(self, dist): 805 if self.exclude_scripts: 806 return 807 for args in ScriptWriter.best().get_args(dist): 808 self.write_script(*args) 809 810 def install_script(self, dist, script_name, script_text, dev_path=None): 811 """Generate a legacy script wrapper and install it""" 812 spec = str(dist.as_requirement()) 813 is_script = is_python_script(script_text, script_name) 814 815 if is_script: 816 body = self._load_template(dev_path) % locals() 817 script_text = ScriptWriter.get_header(script_text) + body 818 self.write_script(script_name, _to_bytes(script_text), 'b') 819 820 @staticmethod 821 def _load_template(dev_path): 822 """ 823 There are a couple of template scripts in the package. This 824 function loads one of them and prepares it for use. 825 """ 826 # See https://github.com/pypa/setuptools/issues/134 for info 827 # on script file naming and downstream issues with SVR4 828 name = 'script.tmpl' 829 if dev_path: 830 name = name.replace('.tmpl', ' (dev).tmpl') 831 832 raw_bytes = resource_string('setuptools', name) 833 return raw_bytes.decode('utf-8') 834 835 def write_script(self, script_name, contents, mode="t", blockers=()): 836 """Write an executable file to the scripts directory""" 837 self.delete_blockers( # clean up old .py/.pyw w/o a script 838 [os.path.join(self.script_dir, x) for x in blockers] 839 ) 840 log.info("Installing %s script to %s", script_name, self.script_dir) 841 target = os.path.join(self.script_dir, script_name) 842 self.add_output(target) 843 844 if self.dry_run: 845 return 846 847 mask = current_umask() 848 ensure_directory(target) 849 if os.path.exists(target): 850 os.unlink(target) 851 with open(target, "w" + mode) as f: 852 f.write(contents) 853 chmod(target, 0o777 - mask) 854 855 def install_eggs(self, spec, dist_filename, tmpdir): 856 # .egg dirs or files are already built, so just return them 857 if dist_filename.lower().endswith('.egg'): 858 return [self.install_egg(dist_filename, tmpdir)] 859 elif dist_filename.lower().endswith('.exe'): 860 return [self.install_exe(dist_filename, tmpdir)] 861 elif dist_filename.lower().endswith('.whl'): 862 return [self.install_wheel(dist_filename, tmpdir)] 863 864 # Anything else, try to extract and build 865 setup_base = tmpdir 866 if os.path.isfile(dist_filename) and not dist_filename.endswith('.py'): 867 unpack_archive(dist_filename, tmpdir, self.unpack_progress) 868 elif os.path.isdir(dist_filename): 869 setup_base = os.path.abspath(dist_filename) 870 871 if (setup_base.startswith(tmpdir) # something we downloaded 872 and self.build_directory and spec is not None): 873 setup_base = self.maybe_move(spec, dist_filename, setup_base) 874 875 # Find the setup.py file 876 setup_script = os.path.join(setup_base, 'setup.py') 877 878 if not os.path.exists(setup_script): 879 setups = glob(os.path.join(setup_base, '*', 'setup.py')) 880 if not setups: 881 raise DistutilsError( 882 "Couldn't find a setup script in %s" % 883 os.path.abspath(dist_filename) 884 ) 885 if len(setups) > 1: 886 raise DistutilsError( 887 "Multiple setup scripts in %s" % 888 os.path.abspath(dist_filename) 889 ) 890 setup_script = setups[0] 891 892 # Now run it, and return the result 893 if self.editable: 894 log.info(self.report_editable(spec, setup_script)) 895 return [] 896 else: 897 return self.build_and_install(setup_script, setup_base) 898 899 def egg_distribution(self, egg_path): 900 if os.path.isdir(egg_path): 901 metadata = PathMetadata(egg_path, os.path.join(egg_path, 902 'EGG-INFO')) 903 else: 904 metadata = EggMetadata(zipimport.zipimporter(egg_path)) 905 return Distribution.from_filename(egg_path, metadata=metadata) 906 907 def install_egg(self, egg_path, tmpdir): 908 destination = os.path.join( 909 self.install_dir, 910 os.path.basename(egg_path), 911 ) 912 destination = os.path.abspath(destination) 913 if not self.dry_run: 914 ensure_directory(destination) 915 916 dist = self.egg_distribution(egg_path) 917 if not samefile(egg_path, destination): 918 if os.path.isdir(destination) and not os.path.islink(destination): 919 dir_util.remove_tree(destination, dry_run=self.dry_run) 920 elif os.path.exists(destination): 921 self.execute( 922 os.unlink, 923 (destination,), 924 "Removing " + destination, 925 ) 926 try: 927 new_dist_is_zipped = False 928 if os.path.isdir(egg_path): 929 if egg_path.startswith(tmpdir): 930 f, m = shutil.move, "Moving" 931 else: 932 f, m = shutil.copytree, "Copying" 933 elif self.should_unzip(dist): 934 self.mkpath(destination) 935 f, m = self.unpack_and_compile, "Extracting" 936 else: 937 new_dist_is_zipped = True 938 if egg_path.startswith(tmpdir): 939 f, m = shutil.move, "Moving" 940 else: 941 f, m = shutil.copy2, "Copying" 942 self.execute( 943 f, 944 (egg_path, destination), 945 (m + " %s to %s") % ( 946 os.path.basename(egg_path), 947 os.path.dirname(destination) 948 ), 949 ) 950 update_dist_caches( 951 destination, 952 fix_zipimporter_caches=new_dist_is_zipped, 953 ) 954 except Exception: 955 update_dist_caches(destination, fix_zipimporter_caches=False) 956 raise 957 958 self.add_output(destination) 959 return self.egg_distribution(destination) 960 961 def install_exe(self, dist_filename, tmpdir): 962 # See if it's valid, get data 963 cfg = extract_wininst_cfg(dist_filename) 964 if cfg is None: 965 raise DistutilsError( 966 "%s is not a valid distutils Windows .exe" % dist_filename 967 ) 968 # Create a dummy distribution object until we build the real distro 969 dist = Distribution( 970 None, 971 project_name=cfg.get('metadata', 'name'), 972 version=cfg.get('metadata', 'version'), platform=get_platform(), 973 ) 974 975 # Convert the .exe to an unpacked egg 976 egg_path = os.path.join(tmpdir, dist.egg_name() + '.egg') 977 dist.location = egg_path 978 egg_tmp = egg_path + '.tmp' 979 _egg_info = os.path.join(egg_tmp, 'EGG-INFO') 980 pkg_inf = os.path.join(_egg_info, 'PKG-INFO') 981 ensure_directory(pkg_inf) # make sure EGG-INFO dir exists 982 dist._provider = PathMetadata(egg_tmp, _egg_info) # XXX 983 self.exe_to_egg(dist_filename, egg_tmp) 984 985 # Write EGG-INFO/PKG-INFO 986 if not os.path.exists(pkg_inf): 987 f = open(pkg_inf, 'w') 988 f.write('Metadata-Version: 1.0\n') 989 for k, v in cfg.items('metadata'): 990 if k != 'target_version': 991 f.write('%s: %s\n' % (k.replace('_', '-').title(), v)) 992 f.close() 993 script_dir = os.path.join(_egg_info, 'scripts') 994 # delete entry-point scripts to avoid duping 995 self.delete_blockers([ 996 os.path.join(script_dir, args[0]) 997 for args in ScriptWriter.get_args(dist) 998 ]) 999 # Build .egg file from tmpdir 1000 bdist_egg.make_zipfile( 1001 egg_path, egg_tmp, verbose=self.verbose, dry_run=self.dry_run, 1002 ) 1003 # install the .egg 1004 return self.install_egg(egg_path, tmpdir) 1005 1006 def exe_to_egg(self, dist_filename, egg_tmp): 1007 """Extract a bdist_wininst to the directories an egg would use""" 1008 # Check for .pth file and set up prefix translations 1009 prefixes = get_exe_prefixes(dist_filename) 1010 to_compile = [] 1011 native_libs = [] 1012 top_level = {} 1013 1014 def process(src, dst): 1015 s = src.lower() 1016 for old, new in prefixes: 1017 if s.startswith(old): 1018 src = new + src[len(old):] 1019 parts = src.split('/') 1020 dst = os.path.join(egg_tmp, *parts) 1021 dl = dst.lower() 1022 if dl.endswith('.pyd') or dl.endswith('.dll'): 1023 parts[-1] = bdist_egg.strip_module(parts[-1]) 1024 top_level[os.path.splitext(parts[0])[0]] = 1 1025 native_libs.append(src) 1026 elif dl.endswith('.py') and old != 'SCRIPTS/': 1027 top_level[os.path.splitext(parts[0])[0]] = 1 1028 to_compile.append(dst) 1029 return dst 1030 if not src.endswith('.pth'): 1031 log.warn("WARNING: can't process %s", src) 1032 return None 1033 1034 # extract, tracking .pyd/.dll->native_libs and .py -> to_compile 1035 unpack_archive(dist_filename, egg_tmp, process) 1036 stubs = [] 1037 for res in native_libs: 1038 if res.lower().endswith('.pyd'): # create stubs for .pyd's 1039 parts = res.split('/') 1040 resource = parts[-1] 1041 parts[-1] = bdist_egg.strip_module(parts[-1]) + '.py' 1042 pyfile = os.path.join(egg_tmp, *parts) 1043 to_compile.append(pyfile) 1044 stubs.append(pyfile) 1045 bdist_egg.write_stub(resource, pyfile) 1046 self.byte_compile(to_compile) # compile .py's 1047 bdist_egg.write_safety_flag( 1048 os.path.join(egg_tmp, 'EGG-INFO'), 1049 bdist_egg.analyze_egg(egg_tmp, stubs)) # write zip-safety flag 1050 1051 for name in 'top_level', 'native_libs': 1052 if locals()[name]: 1053 txt = os.path.join(egg_tmp, 'EGG-INFO', name + '.txt') 1054 if not os.path.exists(txt): 1055 f = open(txt, 'w') 1056 f.write('\n'.join(locals()[name]) + '\n') 1057 f.close() 1058 1059 def install_wheel(self, wheel_path, tmpdir): 1060 wheel = Wheel(wheel_path) 1061 assert wheel.is_compatible() 1062 destination = os.path.join(self.install_dir, wheel.egg_name()) 1063 destination = os.path.abspath(destination) 1064 if not self.dry_run: 1065 ensure_directory(destination) 1066 if os.path.isdir(destination) and not os.path.islink(destination): 1067 dir_util.remove_tree(destination, dry_run=self.dry_run) 1068 elif os.path.exists(destination): 1069 self.execute( 1070 os.unlink, 1071 (destination,), 1072 "Removing " + destination, 1073 ) 1074 try: 1075 self.execute( 1076 wheel.install_as_egg, 1077 (destination,), 1078 ("Installing %s to %s") % ( 1079 os.path.basename(wheel_path), 1080 os.path.dirname(destination) 1081 ), 1082 ) 1083 finally: 1084 update_dist_caches(destination, fix_zipimporter_caches=False) 1085 self.add_output(destination) 1086 return self.egg_distribution(destination) 1087 1088 __mv_warning = textwrap.dedent(""" 1089 Because this distribution was installed --multi-version, before you can 1090 import modules from this package in an application, you will need to 1091 'import pkg_resources' and then use a 'require()' call similar to one of 1092 these examples, in order to select the desired version: 1093 1094 pkg_resources.require("%(name)s") # latest installed version 1095 pkg_resources.require("%(name)s==%(version)s") # this exact version 1096 pkg_resources.require("%(name)s>=%(version)s") # this version or higher 1097 """).lstrip() # noqa 1098 1099 __id_warning = textwrap.dedent(""" 1100 Note also that the installation directory must be on sys.path at runtime for 1101 this to work. (e.g. by being the application's script directory, by being on 1102 PYTHONPATH, or by being added to sys.path by your code.) 1103 """) # noqa 1104 1105 def installation_report(self, req, dist, what="Installed"): 1106 """Helpful installation message for display to package users""" 1107 msg = "\n%(what)s %(eggloc)s%(extras)s" 1108 if self.multi_version and not self.no_report: 1109 msg += '\n' + self.__mv_warning 1110 if self.install_dir not in map(normalize_path, sys.path): 1111 msg += '\n' + self.__id_warning 1112 1113 eggloc = dist.location 1114 name = dist.project_name 1115 version = dist.version 1116 extras = '' # TODO: self.report_extras(req, dist) 1117 return msg % locals() 1118 1119 __editable_msg = textwrap.dedent(""" 1120 Extracted editable version of %(spec)s to %(dirname)s 1121 1122 If it uses setuptools in its setup script, you can activate it in 1123 "development" mode by going to that directory and running:: 1124 1125 %(python)s setup.py develop 1126 1127 See the setuptools documentation for the "develop" command for more info. 1128 """).lstrip() # noqa 1129 1130 def report_editable(self, spec, setup_script): 1131 dirname = os.path.dirname(setup_script) 1132 python = sys.executable 1133 return '\n' + self.__editable_msg % locals() 1134 1135 def run_setup(self, setup_script, setup_base, args): 1136 sys.modules.setdefault('distutils.command.bdist_egg', bdist_egg) 1137 sys.modules.setdefault('distutils.command.egg_info', egg_info) 1138 1139 args = list(args) 1140 if self.verbose > 2: 1141 v = 'v' * (self.verbose - 1) 1142 args.insert(0, '-' + v) 1143 elif self.verbose < 2: 1144 args.insert(0, '-q') 1145 if self.dry_run: 1146 args.insert(0, '-n') 1147 log.info( 1148 "Running %s %s", setup_script[len(setup_base) + 1:], ' '.join(args) 1149 ) 1150 try: 1151 run_setup(setup_script, args) 1152 except SystemExit as v: 1153 raise DistutilsError( 1154 "Setup script exited with %s" % (v.args[0],) 1155 ) from v 1156 1157 def build_and_install(self, setup_script, setup_base): 1158 args = ['bdist_egg', '--dist-dir'] 1159 1160 dist_dir = tempfile.mkdtemp( 1161 prefix='egg-dist-tmp-', dir=os.path.dirname(setup_script) 1162 ) 1163 try: 1164 self._set_fetcher_options(os.path.dirname(setup_script)) 1165 args.append(dist_dir) 1166 1167 self.run_setup(setup_script, setup_base, args) 1168 all_eggs = Environment([dist_dir]) 1169 eggs = [] 1170 for key in all_eggs: 1171 for dist in all_eggs[key]: 1172 eggs.append(self.install_egg(dist.location, setup_base)) 1173 if not eggs and not self.dry_run: 1174 log.warn("No eggs found in %s (setup script problem?)", 1175 dist_dir) 1176 return eggs 1177 finally: 1178 rmtree(dist_dir) 1179 log.set_verbosity(self.verbose) # restore our log verbosity 1180 1181 def _set_fetcher_options(self, base): 1182 """ 1183 When easy_install is about to run bdist_egg on a source dist, that 1184 source dist might have 'setup_requires' directives, requiring 1185 additional fetching. Ensure the fetcher options given to easy_install 1186 are available to that command as well. 1187 """ 1188 # find the fetch options from easy_install and write them out 1189 # to the setup.cfg file. 1190 ei_opts = self.distribution.get_option_dict('easy_install').copy() 1191 fetch_directives = ( 1192 'find_links', 'site_dirs', 'index_url', 'optimize', 'allow_hosts', 1193 ) 1194 fetch_options = {} 1195 for key, val in ei_opts.items(): 1196 if key not in fetch_directives: 1197 continue 1198 fetch_options[key.replace('_', '-')] = val[1] 1199 # create a settings dictionary suitable for `edit_config` 1200 settings = dict(easy_install=fetch_options) 1201 cfg_filename = os.path.join(base, 'setup.cfg') 1202 setopt.edit_config(cfg_filename, settings) 1203 1204 def update_pth(self, dist): 1205 if self.pth_file is None: 1206 return 1207 1208 for d in self.pth_file[dist.key]: # drop old entries 1209 if self.multi_version or d.location != dist.location: 1210 log.info("Removing %s from easy-install.pth file", d) 1211 self.pth_file.remove(d) 1212 if d.location in self.shadow_path: 1213 self.shadow_path.remove(d.location) 1214 1215 if not self.multi_version: 1216 if dist.location in self.pth_file.paths: 1217 log.info( 1218 "%s is already the active version in easy-install.pth", 1219 dist, 1220 ) 1221 else: 1222 log.info("Adding %s to easy-install.pth file", dist) 1223 self.pth_file.add(dist) # add new entry 1224 if dist.location not in self.shadow_path: 1225 self.shadow_path.append(dist.location) 1226 1227 if not self.dry_run: 1228 1229 self.pth_file.save() 1230 1231 if dist.key == 'setuptools': 1232 # Ensure that setuptools itself never becomes unavailable! 1233 # XXX should this check for latest version? 1234 filename = os.path.join(self.install_dir, 'setuptools.pth') 1235 if os.path.islink(filename): 1236 os.unlink(filename) 1237 f = open(filename, 'wt') 1238 f.write(self.pth_file.make_relative(dist.location) + '\n') 1239 f.close() 1240 1241 def unpack_progress(self, src, dst): 1242 # Progress filter for unpacking 1243 log.debug("Unpacking %s to %s", src, dst) 1244 return dst # only unpack-and-compile skips files for dry run 1245 1246 def unpack_and_compile(self, egg_path, destination): 1247 to_compile = [] 1248 to_chmod = [] 1249 1250 def pf(src, dst): 1251 if dst.endswith('.py') and not src.startswith('EGG-INFO/'): 1252 to_compile.append(dst) 1253 elif dst.endswith('.dll') or dst.endswith('.so'): 1254 to_chmod.append(dst) 1255 self.unpack_progress(src, dst) 1256 return not self.dry_run and dst or None 1257 1258 unpack_archive(egg_path, destination, pf) 1259 self.byte_compile(to_compile) 1260 if not self.dry_run: 1261 for f in to_chmod: 1262 mode = ((os.stat(f)[stat.ST_MODE]) | 0o555) & 0o7755 1263 chmod(f, mode) 1264 1265 def byte_compile(self, to_compile): 1266 if sys.dont_write_bytecode: 1267 return 1268 1269 from distutils.util import byte_compile 1270 1271 try: 1272 # try to make the byte compile messages quieter 1273 log.set_verbosity(self.verbose - 1) 1274 1275 byte_compile(to_compile, optimize=0, force=1, dry_run=self.dry_run) 1276 if self.optimize: 1277 byte_compile( 1278 to_compile, optimize=self.optimize, force=1, 1279 dry_run=self.dry_run, 1280 ) 1281 finally: 1282 log.set_verbosity(self.verbose) # restore original verbosity 1283 1284 __no_default_msg = textwrap.dedent(""" 1285 bad install directory or PYTHONPATH 1286 1287 You are attempting to install a package to a directory that is not 1288 on PYTHONPATH and which Python does not read ".pth" files from. The 1289 installation directory you specified (via --install-dir, --prefix, or 1290 the distutils default setting) was: 1291 1292 %s 1293 1294 and your PYTHONPATH environment variable currently contains: 1295 1296 %r 1297 1298 Here are some of your options for correcting the problem: 1299 1300 * You can choose a different installation directory, i.e., one that is 1301 on PYTHONPATH or supports .pth files 1302 1303 * You can add the installation directory to the PYTHONPATH environment 1304 variable. (It must then also be on PYTHONPATH whenever you run 1305 Python and want to use the package(s) you are installing.) 1306 1307 * You can set up the installation directory to support ".pth" files by 1308 using one of the approaches described here: 1309 1310 https://setuptools.readthedocs.io/en/latest/easy_install.html#custom-installation-locations 1311 1312 1313 Please make the appropriate changes for your system and try again. 1314 """).strip() 1315 1316 def create_home_path(self): 1317 """Create directories under ~.""" 1318 if not self.user: 1319 return 1320 home = convert_path(os.path.expanduser("~")) 1321 for name, path in six.iteritems(self.config_vars): 1322 if path.startswith(home) and not os.path.isdir(path): 1323 self.debug_print("os.makedirs('%s', 0o700)" % path) 1324 os.makedirs(path, 0o700) 1325 1326 INSTALL_SCHEMES = dict( 1327 posix=dict( 1328 install_dir='$base/lib/python$py_version_short/site-packages', 1329 script_dir='$base/bin', 1330 ), 1331 ) 1332 1333 DEFAULT_SCHEME = dict( 1334 install_dir='$base/Lib/site-packages', 1335 script_dir='$base/Scripts', 1336 ) 1337 1338 def _expand(self, *attrs): 1339 config_vars = self.get_finalized_command('install').config_vars 1340 1341 if self.prefix: 1342 # Set default install_dir/scripts from --prefix 1343 config_vars = config_vars.copy() 1344 config_vars['base'] = self.prefix 1345 scheme = self.INSTALL_SCHEMES.get(os.name, self.DEFAULT_SCHEME) 1346 for attr, val in scheme.items(): 1347 if getattr(self, attr, None) is None: 1348 setattr(self, attr, val) 1349 1350 from distutils.util import subst_vars 1351 1352 for attr in attrs: 1353 val = getattr(self, attr) 1354 if val is not None: 1355 val = subst_vars(val, config_vars) 1356 if os.name == 'posix': 1357 val = os.path.expanduser(val) 1358 setattr(self, attr, val) 1359 1360 1361def _pythonpath(): 1362 items = os.environ.get('PYTHONPATH', '').split(os.pathsep) 1363 return filter(None, items) 1364 1365 1366def get_site_dirs(): 1367 """ 1368 Return a list of 'site' dirs 1369 """ 1370 1371 sitedirs = [] 1372 1373 # start with PYTHONPATH 1374 sitedirs.extend(_pythonpath()) 1375 1376 prefixes = [sys.prefix] 1377 if sys.exec_prefix != sys.prefix: 1378 prefixes.append(sys.exec_prefix) 1379 for prefix in prefixes: 1380 if prefix: 1381 if sys.platform in ('os2emx', 'riscos'): 1382 sitedirs.append(os.path.join(prefix, "Lib", "site-packages")) 1383 elif os.sep == '/': 1384 sitedirs.extend([ 1385 os.path.join( 1386 prefix, 1387 "lib", 1388 "python{}.{}".format(*sys.version_info), 1389 "site-packages", 1390 ), 1391 os.path.join(prefix, "lib", "site-python"), 1392 ]) 1393 else: 1394 sitedirs.extend([ 1395 prefix, 1396 os.path.join(prefix, "lib", "site-packages"), 1397 ]) 1398 if sys.platform == 'darwin': 1399 # for framework builds *only* we add the standard Apple 1400 # locations. Currently only per-user, but /Library and 1401 # /Network/Library could be added too 1402 if 'Python.framework' in prefix: 1403 home = os.environ.get('HOME') 1404 if home: 1405 home_sp = os.path.join( 1406 home, 1407 'Library', 1408 'Python', 1409 '{}.{}'.format(*sys.version_info), 1410 'site-packages', 1411 ) 1412 sitedirs.append(home_sp) 1413 lib_paths = get_path('purelib'), get_path('platlib') 1414 for site_lib in lib_paths: 1415 if site_lib not in sitedirs: 1416 sitedirs.append(site_lib) 1417 1418 if site.ENABLE_USER_SITE: 1419 sitedirs.append(site.USER_SITE) 1420 1421 try: 1422 sitedirs.extend(site.getsitepackages()) 1423 except AttributeError: 1424 pass 1425 1426 sitedirs = list(map(normalize_path, sitedirs)) 1427 1428 return sitedirs 1429 1430 1431def expand_paths(inputs): 1432 """Yield sys.path directories that might contain "old-style" packages""" 1433 1434 seen = {} 1435 1436 for dirname in inputs: 1437 dirname = normalize_path(dirname) 1438 if dirname in seen: 1439 continue 1440 1441 seen[dirname] = 1 1442 if not os.path.isdir(dirname): 1443 continue 1444 1445 files = os.listdir(dirname) 1446 yield dirname, files 1447 1448 for name in files: 1449 if not name.endswith('.pth'): 1450 # We only care about the .pth files 1451 continue 1452 if name in ('easy-install.pth', 'setuptools.pth'): 1453 # Ignore .pth files that we control 1454 continue 1455 1456 # Read the .pth file 1457 f = open(os.path.join(dirname, name)) 1458 lines = list(yield_lines(f)) 1459 f.close() 1460 1461 # Yield existing non-dupe, non-import directory lines from it 1462 for line in lines: 1463 if not line.startswith("import"): 1464 line = normalize_path(line.rstrip()) 1465 if line not in seen: 1466 seen[line] = 1 1467 if not os.path.isdir(line): 1468 continue 1469 yield line, os.listdir(line) 1470 1471 1472def extract_wininst_cfg(dist_filename): 1473 """Extract configuration data from a bdist_wininst .exe 1474 1475 Returns a configparser.RawConfigParser, or None 1476 """ 1477 f = open(dist_filename, 'rb') 1478 try: 1479 endrec = zipfile._EndRecData(f) 1480 if endrec is None: 1481 return None 1482 1483 prepended = (endrec[9] - endrec[5]) - endrec[6] 1484 if prepended < 12: # no wininst data here 1485 return None 1486 f.seek(prepended - 12) 1487 1488 tag, cfglen, bmlen = struct.unpack("<iii", f.read(12)) 1489 if tag not in (0x1234567A, 0x1234567B): 1490 return None # not a valid tag 1491 1492 f.seek(prepended - (12 + cfglen)) 1493 init = {'version': '', 'target_version': ''} 1494 cfg = configparser.RawConfigParser(init) 1495 try: 1496 part = f.read(cfglen) 1497 # Read up to the first null byte. 1498 config = part.split(b'\0', 1)[0] 1499 # Now the config is in bytes, but for RawConfigParser, it should 1500 # be text, so decode it. 1501 config = config.decode(sys.getfilesystemencoding()) 1502 cfg.readfp(six.StringIO(config)) 1503 except configparser.Error: 1504 return None 1505 if not cfg.has_section('metadata') or not cfg.has_section('Setup'): 1506 return None 1507 return cfg 1508 1509 finally: 1510 f.close() 1511 1512 1513def get_exe_prefixes(exe_filename): 1514 """Get exe->egg path translations for a given .exe file""" 1515 1516 prefixes = [ 1517 ('PURELIB/', ''), 1518 ('PLATLIB/pywin32_system32', ''), 1519 ('PLATLIB/', ''), 1520 ('SCRIPTS/', 'EGG-INFO/scripts/'), 1521 ('DATA/lib/site-packages', ''), 1522 ] 1523 z = zipfile.ZipFile(exe_filename) 1524 try: 1525 for info in z.infolist(): 1526 name = info.filename 1527 parts = name.split('/') 1528 if len(parts) == 3 and parts[2] == 'PKG-INFO': 1529 if parts[1].endswith('.egg-info'): 1530 prefixes.insert(0, ('/'.join(parts[:2]), 'EGG-INFO/')) 1531 break 1532 if len(parts) != 2 or not name.endswith('.pth'): 1533 continue 1534 if name.endswith('-nspkg.pth'): 1535 continue 1536 if parts[0].upper() in ('PURELIB', 'PLATLIB'): 1537 contents = z.read(name) 1538 if not six.PY2: 1539 contents = contents.decode() 1540 for pth in yield_lines(contents): 1541 pth = pth.strip().replace('\\', '/') 1542 if not pth.startswith('import'): 1543 prefixes.append((('%s/%s/' % (parts[0], pth)), '')) 1544 finally: 1545 z.close() 1546 prefixes = [(x.lower(), y) for x, y in prefixes] 1547 prefixes.sort() 1548 prefixes.reverse() 1549 return prefixes 1550 1551 1552class PthDistributions(Environment): 1553 """A .pth file with Distribution paths in it""" 1554 1555 dirty = False 1556 1557 def __init__(self, filename, sitedirs=()): 1558 self.filename = filename 1559 self.sitedirs = list(map(normalize_path, sitedirs)) 1560 self.basedir = normalize_path(os.path.dirname(self.filename)) 1561 self._load() 1562 Environment.__init__(self, [], None, None) 1563 for path in yield_lines(self.paths): 1564 list(map(self.add, find_distributions(path, True))) 1565 1566 def _load(self): 1567 self.paths = [] 1568 saw_import = False 1569 seen = dict.fromkeys(self.sitedirs) 1570 if os.path.isfile(self.filename): 1571 f = open(self.filename, 'rt') 1572 for line in f: 1573 if line.startswith('import'): 1574 saw_import = True 1575 continue 1576 path = line.rstrip() 1577 self.paths.append(path) 1578 if not path.strip() or path.strip().startswith('#'): 1579 continue 1580 # skip non-existent paths, in case somebody deleted a package 1581 # manually, and duplicate paths as well 1582 path = self.paths[-1] = normalize_path( 1583 os.path.join(self.basedir, path) 1584 ) 1585 if not os.path.exists(path) or path in seen: 1586 self.paths.pop() # skip it 1587 self.dirty = True # we cleaned up, so we're dirty now :) 1588 continue 1589 seen[path] = 1 1590 f.close() 1591 1592 if self.paths and not saw_import: 1593 self.dirty = True # ensure anything we touch has import wrappers 1594 while self.paths and not self.paths[-1].strip(): 1595 self.paths.pop() 1596 1597 def save(self): 1598 """Write changed .pth file back to disk""" 1599 if not self.dirty: 1600 return 1601 1602 rel_paths = list(map(self.make_relative, self.paths)) 1603 if rel_paths: 1604 log.debug("Saving %s", self.filename) 1605 lines = self._wrap_lines(rel_paths) 1606 data = '\n'.join(lines) + '\n' 1607 1608 if os.path.islink(self.filename): 1609 os.unlink(self.filename) 1610 with open(self.filename, 'wt') as f: 1611 f.write(data) 1612 1613 elif os.path.exists(self.filename): 1614 log.debug("Deleting empty %s", self.filename) 1615 os.unlink(self.filename) 1616 1617 self.dirty = False 1618 1619 @staticmethod 1620 def _wrap_lines(lines): 1621 return lines 1622 1623 def add(self, dist): 1624 """Add `dist` to the distribution map""" 1625 new_path = ( 1626 dist.location not in self.paths and ( 1627 dist.location not in self.sitedirs or 1628 # account for '.' being in PYTHONPATH 1629 dist.location == os.getcwd() 1630 ) 1631 ) 1632 if new_path: 1633 self.paths.append(dist.location) 1634 self.dirty = True 1635 Environment.add(self, dist) 1636 1637 def remove(self, dist): 1638 """Remove `dist` from the distribution map""" 1639 while dist.location in self.paths: 1640 self.paths.remove(dist.location) 1641 self.dirty = True 1642 Environment.remove(self, dist) 1643 1644 def make_relative(self, path): 1645 npath, last = os.path.split(normalize_path(path)) 1646 baselen = len(self.basedir) 1647 parts = [last] 1648 sep = os.altsep == '/' and '/' or os.sep 1649 while len(npath) >= baselen: 1650 if npath == self.basedir: 1651 parts.append(os.curdir) 1652 parts.reverse() 1653 return sep.join(parts) 1654 npath, last = os.path.split(npath) 1655 parts.append(last) 1656 else: 1657 return path 1658 1659 1660class RewritePthDistributions(PthDistributions): 1661 @classmethod 1662 def _wrap_lines(cls, lines): 1663 yield cls.prelude 1664 for line in lines: 1665 yield line 1666 yield cls.postlude 1667 1668 prelude = _one_liner(""" 1669 import sys 1670 sys.__plen = len(sys.path) 1671 """) 1672 postlude = _one_liner(""" 1673 import sys 1674 new = sys.path[sys.__plen:] 1675 del sys.path[sys.__plen:] 1676 p = getattr(sys, '__egginsert', 0) 1677 sys.path[p:p] = new 1678 sys.__egginsert = p + len(new) 1679 """) 1680 1681 1682if os.environ.get('SETUPTOOLS_SYS_PATH_TECHNIQUE', 'raw') == 'rewrite': 1683 PthDistributions = RewritePthDistributions 1684 1685 1686def _first_line_re(): 1687 """ 1688 Return a regular expression based on first_line_re suitable for matching 1689 strings. 1690 """ 1691 if isinstance(first_line_re.pattern, str): 1692 return first_line_re 1693 1694 # first_line_re in Python >=3.1.4 and >=3.2.1 is a bytes pattern. 1695 return re.compile(first_line_re.pattern.decode()) 1696 1697 1698def auto_chmod(func, arg, exc): 1699 if func in [os.unlink, os.remove] and os.name == 'nt': 1700 chmod(arg, stat.S_IWRITE) 1701 return func(arg) 1702 et, ev, _ = sys.exc_info() 1703 six.reraise(et, (ev[0], ev[1] + (" %s %s" % (func, arg)))) 1704 1705 1706def update_dist_caches(dist_path, fix_zipimporter_caches): 1707 """ 1708 Fix any globally cached `dist_path` related data 1709 1710 `dist_path` should be a path of a newly installed egg distribution (zipped 1711 or unzipped). 1712 1713 sys.path_importer_cache contains finder objects that have been cached when 1714 importing data from the original distribution. Any such finders need to be 1715 cleared since the replacement distribution might be packaged differently, 1716 e.g. a zipped egg distribution might get replaced with an unzipped egg 1717 folder or vice versa. Having the old finders cached may then cause Python 1718 to attempt loading modules from the replacement distribution using an 1719 incorrect loader. 1720 1721 zipimport.zipimporter objects are Python loaders charged with importing 1722 data packaged inside zip archives. If stale loaders referencing the 1723 original distribution, are left behind, they can fail to load modules from 1724 the replacement distribution. E.g. if an old zipimport.zipimporter instance 1725 is used to load data from a new zipped egg archive, it may cause the 1726 operation to attempt to locate the requested data in the wrong location - 1727 one indicated by the original distribution's zip archive directory 1728 information. Such an operation may then fail outright, e.g. report having 1729 read a 'bad local file header', or even worse, it may fail silently & 1730 return invalid data. 1731 1732 zipimport._zip_directory_cache contains cached zip archive directory 1733 information for all existing zipimport.zipimporter instances and all such 1734 instances connected to the same archive share the same cached directory 1735 information. 1736 1737 If asked, and the underlying Python implementation allows it, we can fix 1738 all existing zipimport.zipimporter instances instead of having to track 1739 them down and remove them one by one, by updating their shared cached zip 1740 archive directory information. This, of course, assumes that the 1741 replacement distribution is packaged as a zipped egg. 1742 1743 If not asked to fix existing zipimport.zipimporter instances, we still do 1744 our best to clear any remaining zipimport.zipimporter related cached data 1745 that might somehow later get used when attempting to load data from the new 1746 distribution and thus cause such load operations to fail. Note that when 1747 tracking down such remaining stale data, we can not catch every conceivable 1748 usage from here, and we clear only those that we know of and have found to 1749 cause problems if left alive. Any remaining caches should be updated by 1750 whomever is in charge of maintaining them, i.e. they should be ready to 1751 handle us replacing their zip archives with new distributions at runtime. 1752 1753 """ 1754 # There are several other known sources of stale zipimport.zipimporter 1755 # instances that we do not clear here, but might if ever given a reason to 1756 # do so: 1757 # * Global setuptools pkg_resources.working_set (a.k.a. 'master working 1758 # set') may contain distributions which may in turn contain their 1759 # zipimport.zipimporter loaders. 1760 # * Several zipimport.zipimporter loaders held by local variables further 1761 # up the function call stack when running the setuptools installation. 1762 # * Already loaded modules may have their __loader__ attribute set to the 1763 # exact loader instance used when importing them. Python 3.4 docs state 1764 # that this information is intended mostly for introspection and so is 1765 # not expected to cause us problems. 1766 normalized_path = normalize_path(dist_path) 1767 _uncache(normalized_path, sys.path_importer_cache) 1768 if fix_zipimporter_caches: 1769 _replace_zip_directory_cache_data(normalized_path) 1770 else: 1771 # Here, even though we do not want to fix existing and now stale 1772 # zipimporter cache information, we still want to remove it. Related to 1773 # Python's zip archive directory information cache, we clear each of 1774 # its stale entries in two phases: 1775 # 1. Clear the entry so attempting to access zip archive information 1776 # via any existing stale zipimport.zipimporter instances fails. 1777 # 2. Remove the entry from the cache so any newly constructed 1778 # zipimport.zipimporter instances do not end up using old stale 1779 # zip archive directory information. 1780 # This whole stale data removal step does not seem strictly necessary, 1781 # but has been left in because it was done before we started replacing 1782 # the zip archive directory information cache content if possible, and 1783 # there are no relevant unit tests that we can depend on to tell us if 1784 # this is really needed. 1785 _remove_and_clear_zip_directory_cache_data(normalized_path) 1786 1787 1788def _collect_zipimporter_cache_entries(normalized_path, cache): 1789 """ 1790 Return zipimporter cache entry keys related to a given normalized path. 1791 1792 Alternative path spellings (e.g. those using different character case or 1793 those using alternative path separators) related to the same path are 1794 included. Any sub-path entries are included as well, i.e. those 1795 corresponding to zip archives embedded in other zip archives. 1796 1797 """ 1798 result = [] 1799 prefix_len = len(normalized_path) 1800 for p in cache: 1801 np = normalize_path(p) 1802 if (np.startswith(normalized_path) and 1803 np[prefix_len:prefix_len + 1] in (os.sep, '')): 1804 result.append(p) 1805 return result 1806 1807 1808def _update_zipimporter_cache(normalized_path, cache, updater=None): 1809 """ 1810 Update zipimporter cache data for a given normalized path. 1811 1812 Any sub-path entries are processed as well, i.e. those corresponding to zip 1813 archives embedded in other zip archives. 1814 1815 Given updater is a callable taking a cache entry key and the original entry 1816 (after already removing the entry from the cache), and expected to update 1817 the entry and possibly return a new one to be inserted in its place. 1818 Returning None indicates that the entry should not be replaced with a new 1819 one. If no updater is given, the cache entries are simply removed without 1820 any additional processing, the same as if the updater simply returned None. 1821 1822 """ 1823 for p in _collect_zipimporter_cache_entries(normalized_path, cache): 1824 # N.B. pypy's custom zipimport._zip_directory_cache implementation does 1825 # not support the complete dict interface: 1826 # * Does not support item assignment, thus not allowing this function 1827 # to be used only for removing existing cache entries. 1828 # * Does not support the dict.pop() method, forcing us to use the 1829 # get/del patterns instead. For more detailed information see the 1830 # following links: 1831 # https://github.com/pypa/setuptools/issues/202#issuecomment-202913420 1832 # http://bit.ly/2h9itJX 1833 old_entry = cache[p] 1834 del cache[p] 1835 new_entry = updater and updater(p, old_entry) 1836 if new_entry is not None: 1837 cache[p] = new_entry 1838 1839 1840def _uncache(normalized_path, cache): 1841 _update_zipimporter_cache(normalized_path, cache) 1842 1843 1844def _remove_and_clear_zip_directory_cache_data(normalized_path): 1845 def clear_and_remove_cached_zip_archive_directory_data(path, old_entry): 1846 old_entry.clear() 1847 1848 _update_zipimporter_cache( 1849 normalized_path, zipimport._zip_directory_cache, 1850 updater=clear_and_remove_cached_zip_archive_directory_data) 1851 1852 1853# PyPy Python implementation does not allow directly writing to the 1854# zipimport._zip_directory_cache and so prevents us from attempting to correct 1855# its content. The best we can do there is clear the problematic cache content 1856# and have PyPy repopulate it as needed. The downside is that if there are any 1857# stale zipimport.zipimporter instances laying around, attempting to use them 1858# will fail due to not having its zip archive directory information available 1859# instead of being automatically corrected to use the new correct zip archive 1860# directory information. 1861if '__pypy__' in sys.builtin_module_names: 1862 _replace_zip_directory_cache_data = \ 1863 _remove_and_clear_zip_directory_cache_data 1864else: 1865 1866 def _replace_zip_directory_cache_data(normalized_path): 1867 def replace_cached_zip_archive_directory_data(path, old_entry): 1868 # N.B. In theory, we could load the zip directory information just 1869 # once for all updated path spellings, and then copy it locally and 1870 # update its contained path strings to contain the correct 1871 # spelling, but that seems like a way too invasive move (this cache 1872 # structure is not officially documented anywhere and could in 1873 # theory change with new Python releases) for no significant 1874 # benefit. 1875 old_entry.clear() 1876 zipimport.zipimporter(path) 1877 old_entry.update(zipimport._zip_directory_cache[path]) 1878 return old_entry 1879 1880 _update_zipimporter_cache( 1881 normalized_path, zipimport._zip_directory_cache, 1882 updater=replace_cached_zip_archive_directory_data) 1883 1884 1885def is_python(text, filename='<string>'): 1886 "Is this string a valid Python script?" 1887 try: 1888 compile(text, filename, 'exec') 1889 except (SyntaxError, TypeError): 1890 return False 1891 else: 1892 return True 1893 1894 1895def is_sh(executable): 1896 """Determine if the specified executable is a .sh (contains a #! line)""" 1897 try: 1898 with io.open(executable, encoding='latin-1') as fp: 1899 magic = fp.read(2) 1900 except (OSError, IOError): 1901 return executable 1902 return magic == '#!' 1903 1904 1905def nt_quote_arg(arg): 1906 """Quote a command line argument according to Windows parsing rules""" 1907 return subprocess.list2cmdline([arg]) 1908 1909 1910def is_python_script(script_text, filename): 1911 """Is this text, as a whole, a Python script? (as opposed to shell/bat/etc. 1912 """ 1913 if filename.endswith('.py') or filename.endswith('.pyw'): 1914 return True # extension says it's Python 1915 if is_python(script_text, filename): 1916 return True # it's syntactically valid Python 1917 if script_text.startswith('#!'): 1918 # It begins with a '#!' line, so check if 'python' is in it somewhere 1919 return 'python' in script_text.splitlines()[0].lower() 1920 1921 return False # Not any Python I can recognize 1922 1923 1924try: 1925 from os import chmod as _chmod 1926except ImportError: 1927 # Jython compatibility 1928 def _chmod(*args): 1929 pass 1930 1931 1932def chmod(path, mode): 1933 log.debug("changing mode of %s to %o", path, mode) 1934 try: 1935 _chmod(path, mode) 1936 except os.error as e: 1937 log.debug("chmod failed: %s", e) 1938 1939 1940class CommandSpec(list): 1941 """ 1942 A command spec for a #! header, specified as a list of arguments akin to 1943 those passed to Popen. 1944 """ 1945 1946 options = [] 1947 split_args = dict() 1948 1949 @classmethod 1950 def best(cls): 1951 """ 1952 Choose the best CommandSpec class based on environmental conditions. 1953 """ 1954 return cls 1955 1956 @classmethod 1957 def _sys_executable(cls): 1958 _default = os.path.normpath(sys.executable) 1959 return os.environ.get('__PYVENV_LAUNCHER__', _default) 1960 1961 @classmethod 1962 def from_param(cls, param): 1963 """ 1964 Construct a CommandSpec from a parameter to build_scripts, which may 1965 be None. 1966 """ 1967 if isinstance(param, cls): 1968 return param 1969 if isinstance(param, list): 1970 return cls(param) 1971 if param is None: 1972 return cls.from_environment() 1973 # otherwise, assume it's a string. 1974 return cls.from_string(param) 1975 1976 @classmethod 1977 def from_environment(cls): 1978 return cls([cls._sys_executable()]) 1979 1980 @classmethod 1981 def from_string(cls, string): 1982 """ 1983 Construct a command spec from a simple string representing a command 1984 line parseable by shlex.split. 1985 """ 1986 items = shlex.split(string, **cls.split_args) 1987 return cls(items) 1988 1989 def install_options(self, script_text): 1990 self.options = shlex.split(self._extract_options(script_text)) 1991 cmdline = subprocess.list2cmdline(self) 1992 if not isascii(cmdline): 1993 self.options[:0] = ['-x'] 1994 1995 @staticmethod 1996 def _extract_options(orig_script): 1997 """ 1998 Extract any options from the first line of the script. 1999 """ 2000 first = (orig_script + '\n').splitlines()[0] 2001 match = _first_line_re().match(first) 2002 options = match.group(1) or '' if match else '' 2003 return options.strip() 2004 2005 def as_header(self): 2006 return self._render(self + list(self.options)) 2007 2008 @staticmethod 2009 def _strip_quotes(item): 2010 _QUOTES = '"\'' 2011 for q in _QUOTES: 2012 if item.startswith(q) and item.endswith(q): 2013 return item[1:-1] 2014 return item 2015 2016 @staticmethod 2017 def _render(items): 2018 cmdline = subprocess.list2cmdline( 2019 CommandSpec._strip_quotes(item.strip()) for item in items) 2020 return '#!' + cmdline + '\n' 2021 2022 2023# For pbr compat; will be removed in a future version. 2024sys_executable = CommandSpec._sys_executable() 2025 2026 2027class WindowsCommandSpec(CommandSpec): 2028 split_args = dict(posix=False) 2029 2030 2031class ScriptWriter: 2032 """ 2033 Encapsulates behavior around writing entry point scripts for console and 2034 gui apps. 2035 """ 2036 2037 template = textwrap.dedent(r""" 2038 # EASY-INSTALL-ENTRY-SCRIPT: %(spec)r,%(group)r,%(name)r 2039 import re 2040 import sys 2041 2042 # for compatibility with easy_install; see #2198 2043 __requires__ = %(spec)r 2044 2045 try: 2046 from importlib.metadata import distribution 2047 except ImportError: 2048 try: 2049 from importlib_metadata import distribution 2050 except ImportError: 2051 from pkg_resources import load_entry_point 2052 2053 2054 def importlib_load_entry_point(spec, group, name): 2055 dist_name, _, _ = spec.partition('==') 2056 matches = ( 2057 entry_point 2058 for entry_point in distribution(dist_name).entry_points 2059 if entry_point.group == group and entry_point.name == name 2060 ) 2061 return next(matches).load() 2062 2063 2064 globals().setdefault('load_entry_point', importlib_load_entry_point) 2065 2066 2067 if __name__ == '__main__': 2068 sys.argv[0] = re.sub(r'(-script\.pyw?|\.exe)?$', '', sys.argv[0]) 2069 sys.exit(load_entry_point(%(spec)r, %(group)r, %(name)r)()) 2070 """).lstrip() 2071 2072 command_spec_class = CommandSpec 2073 2074 @classmethod 2075 def get_script_args(cls, dist, executable=None, wininst=False): 2076 # for backward compatibility 2077 warnings.warn("Use get_args", EasyInstallDeprecationWarning) 2078 writer = (WindowsScriptWriter if wininst else ScriptWriter).best() 2079 header = cls.get_script_header("", executable, wininst) 2080 return writer.get_args(dist, header) 2081 2082 @classmethod 2083 def get_script_header(cls, script_text, executable=None, wininst=False): 2084 # for backward compatibility 2085 warnings.warn( 2086 "Use get_header", EasyInstallDeprecationWarning, stacklevel=2) 2087 if wininst: 2088 executable = "python.exe" 2089 return cls.get_header(script_text, executable) 2090 2091 @classmethod 2092 def get_args(cls, dist, header=None): 2093 """ 2094 Yield write_script() argument tuples for a distribution's 2095 console_scripts and gui_scripts entry points. 2096 """ 2097 if header is None: 2098 header = cls.get_header() 2099 spec = str(dist.as_requirement()) 2100 for type_ in 'console', 'gui': 2101 group = type_ + '_scripts' 2102 for name, ep in dist.get_entry_map(group).items(): 2103 cls._ensure_safe_name(name) 2104 script_text = cls.template % locals() 2105 args = cls._get_script_args(type_, name, header, script_text) 2106 for res in args: 2107 yield res 2108 2109 @staticmethod 2110 def _ensure_safe_name(name): 2111 """ 2112 Prevent paths in *_scripts entry point names. 2113 """ 2114 has_path_sep = re.search(r'[\\/]', name) 2115 if has_path_sep: 2116 raise ValueError("Path separators not allowed in script names") 2117 2118 @classmethod 2119 def get_writer(cls, force_windows): 2120 # for backward compatibility 2121 warnings.warn("Use best", EasyInstallDeprecationWarning) 2122 return WindowsScriptWriter.best() if force_windows else cls.best() 2123 2124 @classmethod 2125 def best(cls): 2126 """ 2127 Select the best ScriptWriter for this environment. 2128 """ 2129 if sys.platform == 'win32' or (os.name == 'java' and os._name == 'nt'): 2130 return WindowsScriptWriter.best() 2131 else: 2132 return cls 2133 2134 @classmethod 2135 def _get_script_args(cls, type_, name, header, script_text): 2136 # Simply write the stub with no extension. 2137 yield (name, header + script_text) 2138 2139 @classmethod 2140 def get_header(cls, script_text="", executable=None): 2141 """Create a #! line, getting options (if any) from script_text""" 2142 cmd = cls.command_spec_class.best().from_param(executable) 2143 cmd.install_options(script_text) 2144 return cmd.as_header() 2145 2146 2147class WindowsScriptWriter(ScriptWriter): 2148 command_spec_class = WindowsCommandSpec 2149 2150 @classmethod 2151 def get_writer(cls): 2152 # for backward compatibility 2153 warnings.warn("Use best", EasyInstallDeprecationWarning) 2154 return cls.best() 2155 2156 @classmethod 2157 def best(cls): 2158 """ 2159 Select the best ScriptWriter suitable for Windows 2160 """ 2161 writer_lookup = dict( 2162 executable=WindowsExecutableLauncherWriter, 2163 natural=cls, 2164 ) 2165 # for compatibility, use the executable launcher by default 2166 launcher = os.environ.get('SETUPTOOLS_LAUNCHER', 'executable') 2167 return writer_lookup[launcher] 2168 2169 @classmethod 2170 def _get_script_args(cls, type_, name, header, script_text): 2171 "For Windows, add a .py extension" 2172 ext = dict(console='.pya', gui='.pyw')[type_] 2173 if ext not in os.environ['PATHEXT'].lower().split(';'): 2174 msg = ( 2175 "{ext} not listed in PATHEXT; scripts will not be " 2176 "recognized as executables." 2177 ).format(**locals()) 2178 warnings.warn(msg, UserWarning) 2179 old = ['.pya', '.py', '-script.py', '.pyc', '.pyo', '.pyw', '.exe'] 2180 old.remove(ext) 2181 header = cls._adjust_header(type_, header) 2182 blockers = [name + x for x in old] 2183 yield name + ext, header + script_text, 't', blockers 2184 2185 @classmethod 2186 def _adjust_header(cls, type_, orig_header): 2187 """ 2188 Make sure 'pythonw' is used for gui and and 'python' is used for 2189 console (regardless of what sys.executable is). 2190 """ 2191 pattern = 'pythonw.exe' 2192 repl = 'python.exe' 2193 if type_ == 'gui': 2194 pattern, repl = repl, pattern 2195 pattern_ob = re.compile(re.escape(pattern), re.IGNORECASE) 2196 new_header = pattern_ob.sub(string=orig_header, repl=repl) 2197 return new_header if cls._use_header(new_header) else orig_header 2198 2199 @staticmethod 2200 def _use_header(new_header): 2201 """ 2202 Should _adjust_header use the replaced header? 2203 2204 On non-windows systems, always use. On 2205 Windows systems, only use the replaced header if it resolves 2206 to an executable on the system. 2207 """ 2208 clean_header = new_header[2:-1].strip('"') 2209 return sys.platform != 'win32' or find_executable(clean_header) 2210 2211 2212class WindowsExecutableLauncherWriter(WindowsScriptWriter): 2213 @classmethod 2214 def _get_script_args(cls, type_, name, header, script_text): 2215 """ 2216 For Windows, add a .py extension and an .exe launcher 2217 """ 2218 if type_ == 'gui': 2219 launcher_type = 'gui' 2220 ext = '-script.pyw' 2221 old = ['.pyw'] 2222 else: 2223 launcher_type = 'cli' 2224 ext = '-script.py' 2225 old = ['.py', '.pyc', '.pyo'] 2226 hdr = cls._adjust_header(type_, header) 2227 blockers = [name + x for x in old] 2228 yield (name + ext, hdr + script_text, 't', blockers) 2229 yield ( 2230 name + '.exe', get_win_launcher(launcher_type), 2231 'b' # write in binary mode 2232 ) 2233 if not is_64bit(): 2234 # install a manifest for the launcher to prevent Windows 2235 # from detecting it as an installer (which it will for 2236 # launchers like easy_install.exe). Consider only 2237 # adding a manifest for launchers detected as installers. 2238 # See Distribute #143 for details. 2239 m_name = name + '.exe.manifest' 2240 yield (m_name, load_launcher_manifest(name), 't') 2241 2242 2243# for backward-compatibility 2244get_script_args = ScriptWriter.get_script_args 2245get_script_header = ScriptWriter.get_script_header 2246 2247 2248def get_win_launcher(type): 2249 """ 2250 Load the Windows launcher (executable) suitable for launching a script. 2251 2252 `type` should be either 'cli' or 'gui' 2253 2254 Returns the executable as a byte string. 2255 """ 2256 launcher_fn = '%s.exe' % type 2257 if is_64bit(): 2258 launcher_fn = launcher_fn.replace(".", "-64.") 2259 else: 2260 launcher_fn = launcher_fn.replace(".", "-32.") 2261 return resource_string('setuptools', launcher_fn) 2262 2263 2264def load_launcher_manifest(name): 2265 manifest = pkg_resources.resource_string(__name__, 'launcher manifest.xml') 2266 if six.PY2: 2267 return manifest % vars() 2268 else: 2269 return manifest.decode('utf-8') % vars() 2270 2271 2272def rmtree(path, ignore_errors=False, onerror=auto_chmod): 2273 return shutil.rmtree(path, ignore_errors, onerror) 2274 2275 2276def current_umask(): 2277 tmp = os.umask(0o022) 2278 os.umask(tmp) 2279 return tmp 2280 2281 2282def bootstrap(): 2283 # This function is called when setuptools*.egg is run using /bin/sh 2284 import setuptools 2285 2286 argv0 = os.path.dirname(setuptools.__path__[0]) 2287 sys.argv[0] = argv0 2288 sys.argv.append(argv0) 2289 main() 2290 2291 2292def main(argv=None, **kw): 2293 from setuptools import setup 2294 from setuptools.dist import Distribution 2295 2296 class DistributionWithoutHelpCommands(Distribution): 2297 common_usage = "" 2298 2299 def _show_help(self, *args, **kw): 2300 with _patch_usage(): 2301 Distribution._show_help(self, *args, **kw) 2302 2303 if argv is None: 2304 argv = sys.argv[1:] 2305 2306 with _patch_usage(): 2307 setup( 2308 script_args=['-q', 'easy_install', '-v'] + argv, 2309 script_name=sys.argv[0] or 'easy_install', 2310 distclass=DistributionWithoutHelpCommands, 2311 **kw 2312 ) 2313 2314 2315@contextlib.contextmanager 2316def _patch_usage(): 2317 import distutils.core 2318 USAGE = textwrap.dedent(""" 2319 usage: %(script)s [options] requirement_or_url ... 2320 or: %(script)s --help 2321 """).lstrip() 2322 2323 def gen_usage(script_name): 2324 return USAGE % dict( 2325 script=os.path.basename(script_name), 2326 ) 2327 2328 saved = distutils.core.gen_usage 2329 distutils.core.gen_usage = gen_usage 2330 try: 2331 yield 2332 finally: 2333 distutils.core.gen_usage = saved 2334 2335 2336class EasyInstallDeprecationWarning(SetuptoolsDeprecationWarning): 2337 """ 2338 Warning for EasyInstall deprecations, bypassing suppression. 2339 """ 2340