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