1#!/usr/bin/env python 2"""Create a "virtual" Python installation 3""" 4 5# If you change the version here, change it in setup.py 6# and docs/conf.py as well. 7__version__ = "1.9.1" # following best practices 8virtualenv_version = __version__ # legacy, again 9 10import base64 11import sys 12import os 13import codecs 14import optparse 15import re 16import shutil 17import logging 18import tempfile 19import zlib 20import errno 21import glob 22import distutils.sysconfig 23from distutils.util import strtobool 24import struct 25import subprocess 26 27if sys.version_info < (2, 5): 28 print('ERROR: %s' % sys.exc_info()[1]) 29 print('ERROR: this script requires Python 2.5 or greater.') 30 sys.exit(101) 31 32try: 33 set 34except NameError: 35 from sets import Set as set 36try: 37 basestring 38except NameError: 39 basestring = str 40 41try: 42 import ConfigParser 43except ImportError: 44 import configparser as ConfigParser 45 46join = os.path.join 47py_version = 'python%s.%s' % (sys.version_info[0], sys.version_info[1]) 48 49is_jython = sys.platform.startswith('java') 50is_pypy = hasattr(sys, 'pypy_version_info') 51is_win = (sys.platform == 'win32') 52is_cygwin = (sys.platform == 'cygwin') 53is_darwin = (sys.platform == 'darwin') 54abiflags = getattr(sys, 'abiflags', '') 55 56user_dir = os.path.expanduser('~') 57if is_win: 58 default_storage_dir = os.path.join(user_dir, 'virtualenv') 59else: 60 default_storage_dir = os.path.join(user_dir, '.virtualenv') 61default_config_file = os.path.join(default_storage_dir, 'virtualenv.ini') 62 63if is_pypy: 64 expected_exe = 'pypy' 65elif is_jython: 66 expected_exe = 'jython' 67else: 68 expected_exe = 'python' 69 70 71REQUIRED_MODULES = ['os', 'posix', 'posixpath', 'nt', 'ntpath', 'genericpath', 72 'fnmatch', 'locale', 'encodings', 'codecs', 73 'stat', 'UserDict', 'readline', 'copy_reg', 'types', 74 're', 'sre', 'sre_parse', 'sre_constants', 'sre_compile', 75 'zlib'] 76 77REQUIRED_FILES = ['lib-dynload', 'config'] 78 79majver, minver = sys.version_info[:2] 80if majver == 2: 81 if minver >= 6: 82 REQUIRED_MODULES.extend(['warnings', 'linecache', '_abcoll', 'abc']) 83 if minver >= 7: 84 REQUIRED_MODULES.extend(['_weakrefset']) 85 if minver <= 3: 86 REQUIRED_MODULES.extend(['sets', '__future__']) 87elif majver == 3: 88 # Some extra modules are needed for Python 3, but different ones 89 # for different versions. 90 REQUIRED_MODULES.extend(['_abcoll', 'warnings', 'linecache', 'abc', 'io', 91 '_weakrefset', 'copyreg', 'tempfile', 'random', 92 '__future__', 'collections', 'keyword', 'tarfile', 93 'shutil', 'struct', 'copy', 'tokenize', 'token', 94 'functools', 'heapq', 'bisect', 'weakref', 95 'reprlib']) 96 if minver >= 2: 97 REQUIRED_FILES[-1] = 'config-%s' % majver 98 if minver == 3: 99 import sysconfig 100 platdir = sysconfig.get_config_var('PLATDIR') 101 REQUIRED_FILES.append(platdir) 102 # The whole list of 3.3 modules is reproduced below - the current 103 # uncommented ones are required for 3.3 as of now, but more may be 104 # added as 3.3 development continues. 105 REQUIRED_MODULES.extend([ 106 #"aifc", 107 #"antigravity", 108 #"argparse", 109 #"ast", 110 #"asynchat", 111 #"asyncore", 112 "base64", 113 #"bdb", 114 #"binhex", 115 #"bisect", 116 #"calendar", 117 #"cgi", 118 #"cgitb", 119 #"chunk", 120 #"cmd", 121 #"codeop", 122 #"code", 123 #"colorsys", 124 #"_compat_pickle", 125 #"compileall", 126 #"concurrent", 127 #"configparser", 128 #"contextlib", 129 #"cProfile", 130 #"crypt", 131 #"csv", 132 #"ctypes", 133 #"curses", 134 #"datetime", 135 #"dbm", 136 #"decimal", 137 #"difflib", 138 #"dis", 139 #"doctest", 140 #"dummy_threading", 141 "_dummy_thread", 142 #"email", 143 #"filecmp", 144 #"fileinput", 145 #"formatter", 146 #"fractions", 147 #"ftplib", 148 #"functools", 149 #"getopt", 150 #"getpass", 151 #"gettext", 152 #"glob", 153 #"gzip", 154 "hashlib", 155 #"heapq", 156 "hmac", 157 #"html", 158 #"http", 159 #"idlelib", 160 #"imaplib", 161 #"imghdr", 162 "imp", 163 "importlib", 164 #"inspect", 165 #"json", 166 #"lib2to3", 167 #"logging", 168 #"macpath", 169 #"macurl2path", 170 #"mailbox", 171 #"mailcap", 172 #"_markupbase", 173 #"mimetypes", 174 #"modulefinder", 175 #"multiprocessing", 176 #"netrc", 177 #"nntplib", 178 #"nturl2path", 179 #"numbers", 180 #"opcode", 181 #"optparse", 182 #"os2emxpath", 183 #"pdb", 184 #"pickle", 185 #"pickletools", 186 #"pipes", 187 #"pkgutil", 188 #"platform", 189 #"plat-linux2", 190 #"plistlib", 191 #"poplib", 192 #"pprint", 193 #"profile", 194 #"pstats", 195 #"pty", 196 #"pyclbr", 197 #"py_compile", 198 #"pydoc_data", 199 #"pydoc", 200 #"_pyio", 201 #"queue", 202 #"quopri", 203 #"reprlib", 204 "rlcompleter", 205 #"runpy", 206 #"sched", 207 #"shelve", 208 #"shlex", 209 #"smtpd", 210 #"smtplib", 211 #"sndhdr", 212 #"socket", 213 #"socketserver", 214 #"sqlite3", 215 #"ssl", 216 #"stringprep", 217 #"string", 218 #"_strptime", 219 #"subprocess", 220 #"sunau", 221 #"symbol", 222 #"symtable", 223 #"sysconfig", 224 #"tabnanny", 225 #"telnetlib", 226 #"test", 227 #"textwrap", 228 #"this", 229 #"_threading_local", 230 #"threading", 231 #"timeit", 232 #"tkinter", 233 #"tokenize", 234 #"token", 235 #"traceback", 236 #"trace", 237 #"tty", 238 #"turtledemo", 239 #"turtle", 240 #"unittest", 241 #"urllib", 242 #"uuid", 243 #"uu", 244 #"wave", 245 #"weakref", 246 #"webbrowser", 247 #"wsgiref", 248 #"xdrlib", 249 #"xml", 250 #"xmlrpc", 251 #"zipfile", 252 ]) 253 254if is_pypy: 255 # these are needed to correctly display the exceptions that may happen 256 # during the bootstrap 257 REQUIRED_MODULES.extend(['traceback', 'linecache']) 258 259class Logger(object): 260 261 """ 262 Logging object for use in command-line script. Allows ranges of 263 levels, to avoid some redundancy of displayed information. 264 """ 265 266 DEBUG = logging.DEBUG 267 INFO = logging.INFO 268 NOTIFY = (logging.INFO+logging.WARN)/2 269 WARN = WARNING = logging.WARN 270 ERROR = logging.ERROR 271 FATAL = logging.FATAL 272 273 LEVELS = [DEBUG, INFO, NOTIFY, WARN, ERROR, FATAL] 274 275 def __init__(self, consumers): 276 self.consumers = consumers 277 self.indent = 0 278 self.in_progress = None 279 self.in_progress_hanging = False 280 281 def debug(self, msg, *args, **kw): 282 self.log(self.DEBUG, msg, *args, **kw) 283 def info(self, msg, *args, **kw): 284 self.log(self.INFO, msg, *args, **kw) 285 def notify(self, msg, *args, **kw): 286 self.log(self.NOTIFY, msg, *args, **kw) 287 def warn(self, msg, *args, **kw): 288 self.log(self.WARN, msg, *args, **kw) 289 def error(self, msg, *args, **kw): 290 self.log(self.ERROR, msg, *args, **kw) 291 def fatal(self, msg, *args, **kw): 292 self.log(self.FATAL, msg, *args, **kw) 293 def log(self, level, msg, *args, **kw): 294 if args: 295 if kw: 296 raise TypeError( 297 "You may give positional or keyword arguments, not both") 298 args = args or kw 299 rendered = None 300 for consumer_level, consumer in self.consumers: 301 if self.level_matches(level, consumer_level): 302 if (self.in_progress_hanging 303 and consumer in (sys.stdout, sys.stderr)): 304 self.in_progress_hanging = False 305 sys.stdout.write('\n') 306 sys.stdout.flush() 307 if rendered is None: 308 if args: 309 rendered = msg % args 310 else: 311 rendered = msg 312 rendered = ' '*self.indent + rendered 313 if hasattr(consumer, 'write'): 314 consumer.write(rendered+'\n') 315 else: 316 consumer(rendered) 317 318 def start_progress(self, msg): 319 assert not self.in_progress, ( 320 "Tried to start_progress(%r) while in_progress %r" 321 % (msg, self.in_progress)) 322 if self.level_matches(self.NOTIFY, self._stdout_level()): 323 sys.stdout.write(msg) 324 sys.stdout.flush() 325 self.in_progress_hanging = True 326 else: 327 self.in_progress_hanging = False 328 self.in_progress = msg 329 330 def end_progress(self, msg='done.'): 331 assert self.in_progress, ( 332 "Tried to end_progress without start_progress") 333 if self.stdout_level_matches(self.NOTIFY): 334 if not self.in_progress_hanging: 335 # Some message has been printed out since start_progress 336 sys.stdout.write('...' + self.in_progress + msg + '\n') 337 sys.stdout.flush() 338 else: 339 sys.stdout.write(msg + '\n') 340 sys.stdout.flush() 341 self.in_progress = None 342 self.in_progress_hanging = False 343 344 def show_progress(self): 345 """If we are in a progress scope, and no log messages have been 346 shown, write out another '.'""" 347 if self.in_progress_hanging: 348 sys.stdout.write('.') 349 sys.stdout.flush() 350 351 def stdout_level_matches(self, level): 352 """Returns true if a message at this level will go to stdout""" 353 return self.level_matches(level, self._stdout_level()) 354 355 def _stdout_level(self): 356 """Returns the level that stdout runs at""" 357 for level, consumer in self.consumers: 358 if consumer is sys.stdout: 359 return level 360 return self.FATAL 361 362 def level_matches(self, level, consumer_level): 363 """ 364 >>> l = Logger([]) 365 >>> l.level_matches(3, 4) 366 False 367 >>> l.level_matches(3, 2) 368 True 369 >>> l.level_matches(slice(None, 3), 3) 370 False 371 >>> l.level_matches(slice(None, 3), 2) 372 True 373 >>> l.level_matches(slice(1, 3), 1) 374 True 375 >>> l.level_matches(slice(2, 3), 1) 376 False 377 """ 378 if isinstance(level, slice): 379 start, stop = level.start, level.stop 380 if start is not None and start > consumer_level: 381 return False 382 if stop is not None and stop <= consumer_level: 383 return False 384 return True 385 else: 386 return level >= consumer_level 387 388 #@classmethod 389 def level_for_integer(cls, level): 390 levels = cls.LEVELS 391 if level < 0: 392 return levels[0] 393 if level >= len(levels): 394 return levels[-1] 395 return levels[level] 396 397 level_for_integer = classmethod(level_for_integer) 398 399# create a silent logger just to prevent this from being undefined 400# will be overridden with requested verbosity main() is called. 401logger = Logger([(Logger.LEVELS[-1], sys.stdout)]) 402 403def mkdir(path): 404 if not os.path.exists(path): 405 logger.info('Creating %s', path) 406 os.makedirs(path) 407 else: 408 logger.info('Directory %s already exists', path) 409 410def copyfileordir(src, dest): 411 if os.path.isdir(src): 412 shutil.copytree(src, dest, True) 413 else: 414 shutil.copy2(src, dest) 415 416def copyfile(src, dest, symlink=True): 417 if not os.path.exists(src): 418 # Some bad symlink in the src 419 logger.warn('Cannot find file %s (bad symlink)', src) 420 return 421 if os.path.exists(dest): 422 logger.debug('File %s already exists', dest) 423 return 424 if not os.path.exists(os.path.dirname(dest)): 425 logger.info('Creating parent directories for %s' % os.path.dirname(dest)) 426 os.makedirs(os.path.dirname(dest)) 427 if not os.path.islink(src): 428 srcpath = os.path.abspath(src) 429 else: 430 srcpath = os.readlink(src) 431 if symlink and hasattr(os, 'symlink') and not is_win: 432 logger.info('Symlinking %s', dest) 433 try: 434 os.symlink(srcpath, dest) 435 except (OSError, NotImplementedError): 436 logger.info('Symlinking failed, copying to %s', dest) 437 copyfileordir(src, dest) 438 else: 439 logger.info('Copying to %s', dest) 440 copyfileordir(src, dest) 441 442def writefile(dest, content, overwrite=True): 443 if not os.path.exists(dest): 444 logger.info('Writing %s', dest) 445 f = open(dest, 'wb') 446 f.write(content.encode('utf-8')) 447 f.close() 448 return 449 else: 450 f = open(dest, 'rb') 451 c = f.read() 452 f.close() 453 if c != content.encode("utf-8"): 454 if not overwrite: 455 logger.notify('File %s exists with different content; not overwriting', dest) 456 return 457 logger.notify('Overwriting %s with new content', dest) 458 f = open(dest, 'wb') 459 f.write(content.encode('utf-8')) 460 f.close() 461 else: 462 logger.info('Content %s already in place', dest) 463 464def rmtree(dir): 465 if os.path.exists(dir): 466 logger.notify('Deleting tree %s', dir) 467 shutil.rmtree(dir) 468 else: 469 logger.info('Do not need to delete %s; already gone', dir) 470 471def make_exe(fn): 472 if hasattr(os, 'chmod'): 473 oldmode = os.stat(fn).st_mode & 0xFFF # 0o7777 474 newmode = (oldmode | 0x16D) & 0xFFF # 0o555, 0o7777 475 os.chmod(fn, newmode) 476 logger.info('Changed mode of %s to %s', fn, oct(newmode)) 477 478def _find_file(filename, dirs): 479 for dir in reversed(dirs): 480 files = glob.glob(os.path.join(dir, filename)) 481 if files and os.path.isfile(files[0]): 482 return True, files[0] 483 return False, filename 484 485def _install_req(py_executable, unzip=False, distribute=False, 486 search_dirs=None, never_download=False): 487 488 if search_dirs is None: 489 search_dirs = file_search_dirs() 490 491 if not distribute: 492 egg_path = 'setuptools-*-py%s.egg' % sys.version[:3] 493 found, egg_path = _find_file(egg_path, search_dirs) 494 project_name = 'setuptools' 495 bootstrap_script = EZ_SETUP_PY 496 tgz_path = None 497 else: 498 # Look for a distribute egg (these are not distributed by default, 499 # but can be made available by the user) 500 egg_path = 'distribute-*-py%s.egg' % sys.version[:3] 501 found, egg_path = _find_file(egg_path, search_dirs) 502 project_name = 'distribute' 503 if found: 504 tgz_path = None 505 bootstrap_script = DISTRIBUTE_FROM_EGG_PY 506 else: 507 # Fall back to sdist 508 # NB: egg_path is not None iff tgz_path is None 509 # iff bootstrap_script is a generic setup script accepting 510 # the standard arguments. 511 egg_path = None 512 tgz_path = 'distribute-*.tar.gz' 513 found, tgz_path = _find_file(tgz_path, search_dirs) 514 bootstrap_script = DISTRIBUTE_SETUP_PY 515 516 if is_jython and os._name == 'nt': 517 # Jython's .bat sys.executable can't handle a command line 518 # argument with newlines 519 fd, ez_setup = tempfile.mkstemp('.py') 520 os.write(fd, bootstrap_script) 521 os.close(fd) 522 cmd = [py_executable, ez_setup] 523 else: 524 cmd = [py_executable, '-c', bootstrap_script] 525 if unzip and egg_path: 526 cmd.append('--always-unzip') 527 env = {} 528 remove_from_env = ['__PYVENV_LAUNCHER__'] 529 if logger.stdout_level_matches(logger.DEBUG) and egg_path: 530 cmd.append('-v') 531 532 old_chdir = os.getcwd() 533 if egg_path is not None and os.path.exists(egg_path): 534 logger.info('Using existing %s egg: %s' % (project_name, egg_path)) 535 cmd.append(egg_path) 536 if os.environ.get('PYTHONPATH'): 537 env['PYTHONPATH'] = egg_path + os.path.pathsep + os.environ['PYTHONPATH'] 538 else: 539 env['PYTHONPATH'] = egg_path 540 elif tgz_path is not None and os.path.exists(tgz_path): 541 # Found a tgz source dist, let's chdir 542 logger.info('Using existing %s egg: %s' % (project_name, tgz_path)) 543 os.chdir(os.path.dirname(tgz_path)) 544 # in this case, we want to be sure that PYTHONPATH is unset (not 545 # just empty, really unset), else CPython tries to import the 546 # site.py that it's in virtualenv_support 547 remove_from_env.append('PYTHONPATH') 548 elif never_download: 549 logger.fatal("Can't find any local distributions of %s to install " 550 "and --never-download is set. Either re-run virtualenv " 551 "without the --never-download option, or place a %s " 552 "distribution (%s) in one of these " 553 "locations: %r" % (project_name, project_name, 554 egg_path or tgz_path, 555 search_dirs)) 556 sys.exit(1) 557 elif egg_path: 558 logger.info('No %s egg found; downloading' % project_name) 559 cmd.extend(['--always-copy', '-U', project_name]) 560 else: 561 logger.info('No %s tgz found; downloading' % project_name) 562 logger.start_progress('Installing %s...' % project_name) 563 logger.indent += 2 564 cwd = None 565 if project_name == 'distribute': 566 env['DONT_PATCH_SETUPTOOLS'] = 'true' 567 568 def _filter_ez_setup(line): 569 return filter_ez_setup(line, project_name) 570 571 if not os.access(os.getcwd(), os.W_OK): 572 cwd = tempfile.mkdtemp() 573 if tgz_path is not None and os.path.exists(tgz_path): 574 # the current working dir is hostile, let's copy the 575 # tarball to a temp dir 576 target = os.path.join(cwd, os.path.split(tgz_path)[-1]) 577 shutil.copy(tgz_path, target) 578 try: 579 call_subprocess(cmd, show_stdout=False, 580 filter_stdout=_filter_ez_setup, 581 extra_env=env, 582 remove_from_env=remove_from_env, 583 cwd=cwd) 584 finally: 585 logger.indent -= 2 586 logger.end_progress() 587 if cwd is not None: 588 shutil.rmtree(cwd) 589 if os.getcwd() != old_chdir: 590 os.chdir(old_chdir) 591 if is_jython and os._name == 'nt': 592 os.remove(ez_setup) 593 594def file_search_dirs(): 595 here = os.path.dirname(os.path.abspath(__file__)) 596 dirs = ['.', here, 597 join(here, 'virtualenv_support')] 598 if os.path.splitext(os.path.dirname(__file__))[0] != 'virtualenv': 599 # Probably some boot script; just in case virtualenv is installed... 600 try: 601 import virtualenv 602 except ImportError: 603 pass 604 else: 605 dirs.append(os.path.join(os.path.dirname(virtualenv.__file__), 'virtualenv_support')) 606 return [d for d in dirs if os.path.isdir(d)] 607 608def install_setuptools(py_executable, unzip=False, 609 search_dirs=None, never_download=False): 610 _install_req(py_executable, unzip, 611 search_dirs=search_dirs, never_download=never_download) 612 613def install_distribute(py_executable, unzip=False, 614 search_dirs=None, never_download=False): 615 _install_req(py_executable, unzip, distribute=True, 616 search_dirs=search_dirs, never_download=never_download) 617 618_pip_re = re.compile(r'^pip-.*(zip|tar.gz|tar.bz2|tgz|tbz)$', re.I) 619def install_pip(py_executable, search_dirs=None, never_download=False): 620 if search_dirs is None: 621 search_dirs = file_search_dirs() 622 623 filenames = [] 624 for dir in search_dirs: 625 filenames.extend([join(dir, fn) for fn in os.listdir(dir) 626 if _pip_re.search(fn)]) 627 filenames = [(os.path.basename(filename).lower(), i, filename) for i, filename in enumerate(filenames)] 628 filenames.sort() 629 filenames = [filename for basename, i, filename in filenames] 630 if not filenames: 631 filename = 'pip' 632 else: 633 filename = filenames[-1] 634 easy_install_script = 'easy_install' 635 if is_win: 636 easy_install_script = 'easy_install-script.py' 637 # There's two subtle issues here when invoking easy_install. 638 # 1. On unix-like systems the easy_install script can *only* be executed 639 # directly if its full filesystem path is no longer than 78 characters. 640 # 2. A work around to [1] is to use the `python path/to/easy_install foo` 641 # pattern, but that breaks if the path contains non-ASCII characters, as 642 # you can't put the file encoding declaration before the shebang line. 643 # The solution is to use Python's -x flag to skip the first line of the 644 # script (and any ASCII decoding errors that may have occurred in that line) 645 cmd = [py_executable, '-x', join(os.path.dirname(py_executable), easy_install_script), filename] 646 # jython and pypy don't yet support -x 647 if is_jython or is_pypy: 648 cmd.remove('-x') 649 if filename == 'pip': 650 if never_download: 651 logger.fatal("Can't find any local distributions of pip to install " 652 "and --never-download is set. Either re-run virtualenv " 653 "without the --never-download option, or place a pip " 654 "source distribution (zip/tar.gz/tar.bz2) in one of these " 655 "locations: %r" % search_dirs) 656 sys.exit(1) 657 logger.info('Installing pip from network...') 658 else: 659 logger.info('Installing existing %s distribution: %s' % ( 660 os.path.basename(filename), filename)) 661 logger.start_progress('Installing pip...') 662 logger.indent += 2 663 def _filter_setup(line): 664 return filter_ez_setup(line, 'pip') 665 try: 666 call_subprocess(cmd, show_stdout=False, 667 filter_stdout=_filter_setup) 668 finally: 669 logger.indent -= 2 670 logger.end_progress() 671 672def filter_ez_setup(line, project_name='setuptools'): 673 if not line.strip(): 674 return Logger.DEBUG 675 if project_name == 'distribute': 676 for prefix in ('Extracting', 'Now working', 'Installing', 'Before', 677 'Scanning', 'Setuptools', 'Egg', 'Already', 678 'running', 'writing', 'reading', 'installing', 679 'creating', 'copying', 'byte-compiling', 'removing', 680 'Processing'): 681 if line.startswith(prefix): 682 return Logger.DEBUG 683 return Logger.DEBUG 684 for prefix in ['Reading ', 'Best match', 'Processing setuptools', 685 'Copying setuptools', 'Adding setuptools', 686 'Installing ', 'Installed ']: 687 if line.startswith(prefix): 688 return Logger.DEBUG 689 return Logger.INFO 690 691 692class UpdatingDefaultsHelpFormatter(optparse.IndentedHelpFormatter): 693 """ 694 Custom help formatter for use in ConfigOptionParser that updates 695 the defaults before expanding them, allowing them to show up correctly 696 in the help listing 697 """ 698 def expand_default(self, option): 699 if self.parser is not None: 700 self.parser.update_defaults(self.parser.defaults) 701 return optparse.IndentedHelpFormatter.expand_default(self, option) 702 703 704class ConfigOptionParser(optparse.OptionParser): 705 """ 706 Custom option parser which updates its defaults by by checking the 707 configuration files and environmental variables 708 """ 709 def __init__(self, *args, **kwargs): 710 self.config = ConfigParser.RawConfigParser() 711 self.files = self.get_config_files() 712 self.config.read(self.files) 713 optparse.OptionParser.__init__(self, *args, **kwargs) 714 715 def get_config_files(self): 716 config_file = os.environ.get('VIRTUALENV_CONFIG_FILE', False) 717 if config_file and os.path.exists(config_file): 718 return [config_file] 719 return [default_config_file] 720 721 def update_defaults(self, defaults): 722 """ 723 Updates the given defaults with values from the config files and 724 the environ. Does a little special handling for certain types of 725 options (lists). 726 """ 727 # Then go and look for the other sources of configuration: 728 config = {} 729 # 1. config files 730 config.update(dict(self.get_config_section('virtualenv'))) 731 # 2. environmental variables 732 config.update(dict(self.get_environ_vars())) 733 # Then set the options with those values 734 for key, val in config.items(): 735 key = key.replace('_', '-') 736 if not key.startswith('--'): 737 key = '--%s' % key # only prefer long opts 738 option = self.get_option(key) 739 if option is not None: 740 # ignore empty values 741 if not val: 742 continue 743 # handle multiline configs 744 if option.action == 'append': 745 val = val.split() 746 else: 747 option.nargs = 1 748 if option.action == 'store_false': 749 val = not strtobool(val) 750 elif option.action in ('store_true', 'count'): 751 val = strtobool(val) 752 try: 753 val = option.convert_value(key, val) 754 except optparse.OptionValueError: 755 e = sys.exc_info()[1] 756 print("An error occured during configuration: %s" % e) 757 sys.exit(3) 758 defaults[option.dest] = val 759 return defaults 760 761 def get_config_section(self, name): 762 """ 763 Get a section of a configuration 764 """ 765 if self.config.has_section(name): 766 return self.config.items(name) 767 return [] 768 769 def get_environ_vars(self, prefix='VIRTUALENV_'): 770 """ 771 Returns a generator with all environmental vars with prefix VIRTUALENV 772 """ 773 for key, val in os.environ.items(): 774 if key.startswith(prefix): 775 yield (key.replace(prefix, '').lower(), val) 776 777 def get_default_values(self): 778 """ 779 Overridding to make updating the defaults after instantiation of 780 the option parser possible, update_defaults() does the dirty work. 781 """ 782 if not self.process_default_values: 783 # Old, pre-Optik 1.5 behaviour. 784 return optparse.Values(self.defaults) 785 786 defaults = self.update_defaults(self.defaults.copy()) # ours 787 for option in self._get_all_options(): 788 default = defaults.get(option.dest) 789 if isinstance(default, basestring): 790 opt_str = option.get_opt_string() 791 defaults[option.dest] = option.check_value(opt_str, default) 792 return optparse.Values(defaults) 793 794 795def main(): 796 parser = ConfigOptionParser( 797 version=virtualenv_version, 798 usage="%prog [OPTIONS] DEST_DIR", 799 formatter=UpdatingDefaultsHelpFormatter()) 800 801 parser.add_option( 802 '-v', '--verbose', 803 action='count', 804 dest='verbose', 805 default=0, 806 help="Increase verbosity") 807 808 parser.add_option( 809 '-q', '--quiet', 810 action='count', 811 dest='quiet', 812 default=0, 813 help='Decrease verbosity') 814 815 parser.add_option( 816 '-p', '--python', 817 dest='python', 818 metavar='PYTHON_EXE', 819 help='The Python interpreter to use, e.g., --python=python2.5 will use the python2.5 ' 820 'interpreter to create the new environment. The default is the interpreter that ' 821 'virtualenv was installed with (%s)' % sys.executable) 822 823 parser.add_option( 824 '--clear', 825 dest='clear', 826 action='store_true', 827 help="Clear out the non-root install and start from scratch") 828 829 parser.set_defaults(system_site_packages=False) 830 parser.add_option( 831 '--no-site-packages', 832 dest='system_site_packages', 833 action='store_false', 834 help="Don't give access to the global site-packages dir to the " 835 "virtual environment (default)") 836 837 parser.add_option( 838 '--system-site-packages', 839 dest='system_site_packages', 840 action='store_true', 841 help="Give access to the global site-packages dir to the " 842 "virtual environment") 843 844 parser.add_option( 845 '--unzip-setuptools', 846 dest='unzip_setuptools', 847 action='store_true', 848 help="Unzip Setuptools or Distribute when installing it") 849 850 parser.add_option( 851 '--relocatable', 852 dest='relocatable', 853 action='store_true', 854 help='Make an EXISTING virtualenv environment relocatable. ' 855 'This fixes up scripts and makes all .pth files relative') 856 857 parser.add_option( 858 '--distribute', '--use-distribute', # the second option is for legacy reasons here. Hi Kenneth! 859 dest='use_distribute', 860 action='store_true', 861 help='Use Distribute instead of Setuptools. Set environ variable ' 862 'VIRTUALENV_DISTRIBUTE to make it the default ') 863 864 parser.add_option( 865 '--no-setuptools', 866 dest='no_setuptools', 867 action='store_true', 868 help='Do not install distribute/setuptools (or pip) ' 869 'in the new virtualenv.') 870 871 parser.add_option( 872 '--no-pip', 873 dest='no_pip', 874 action='store_true', 875 help='Do not install pip in the new virtualenv.') 876 877 parser.add_option( 878 '--setuptools', 879 dest='use_distribute', 880 action='store_false', 881 help='Use Setuptools instead of Distribute. Set environ variable ' 882 'VIRTUALENV_SETUPTOOLS to make it the default ') 883 884 # Set this to True to use distribute by default, even in Python 2. 885 parser.set_defaults(use_distribute=False) 886 887 default_search_dirs = file_search_dirs() 888 parser.add_option( 889 '--extra-search-dir', 890 dest="search_dirs", 891 action="append", 892 default=default_search_dirs, 893 help="Directory to look for setuptools/distribute/pip distributions in. " 894 "You can add any number of additional --extra-search-dir paths.") 895 896 parser.add_option( 897 '--never-download', 898 dest="never_download", 899 action="store_true", 900 help="Never download anything from the network. Instead, virtualenv will fail " 901 "if local distributions of setuptools/distribute/pip are not present.") 902 903 parser.add_option( 904 '--prompt', 905 dest='prompt', 906 help='Provides an alternative prompt prefix for this environment') 907 908 if 'extend_parser' in globals(): 909 extend_parser(parser) 910 911 options, args = parser.parse_args() 912 913 global logger 914 915 if 'adjust_options' in globals(): 916 adjust_options(options, args) 917 918 verbosity = options.verbose - options.quiet 919 logger = Logger([(Logger.level_for_integer(2 - verbosity), sys.stdout)]) 920 921 if options.python and not os.environ.get('VIRTUALENV_INTERPRETER_RUNNING'): 922 env = os.environ.copy() 923 interpreter = resolve_interpreter(options.python) 924 if interpreter == sys.executable: 925 logger.warn('Already using interpreter %s' % interpreter) 926 else: 927 logger.notify('Running virtualenv with interpreter %s' % interpreter) 928 env['VIRTUALENV_INTERPRETER_RUNNING'] = 'true' 929 file = __file__ 930 if file.endswith('.pyc'): 931 file = file[:-1] 932 popen = subprocess.Popen([interpreter, file] + sys.argv[1:], env=env) 933 raise SystemExit(popen.wait()) 934 935 # Force --distribute on Python 3, since setuptools is not available. 936 if majver > 2: 937 options.use_distribute = True 938 939 if os.environ.get('PYTHONDONTWRITEBYTECODE') and not options.use_distribute: 940 print( 941 "The PYTHONDONTWRITEBYTECODE environment variable is " 942 "not compatible with setuptools. Either use --distribute " 943 "or unset PYTHONDONTWRITEBYTECODE.") 944 sys.exit(2) 945 if not args: 946 print('You must provide a DEST_DIR') 947 parser.print_help() 948 sys.exit(2) 949 if len(args) > 1: 950 print('There must be only one argument: DEST_DIR (you gave %s)' % ( 951 ' '.join(args))) 952 parser.print_help() 953 sys.exit(2) 954 955 home_dir = args[0] 956 957 if os.environ.get('WORKING_ENV'): 958 logger.fatal('ERROR: you cannot run virtualenv while in a workingenv') 959 logger.fatal('Please deactivate your workingenv, then re-run this script') 960 sys.exit(3) 961 962 if 'PYTHONHOME' in os.environ: 963 logger.warn('PYTHONHOME is set. You *must* activate the virtualenv before using it') 964 del os.environ['PYTHONHOME'] 965 966 if options.relocatable: 967 make_environment_relocatable(home_dir) 968 return 969 970 create_environment(home_dir, 971 site_packages=options.system_site_packages, 972 clear=options.clear, 973 unzip_setuptools=options.unzip_setuptools, 974 use_distribute=options.use_distribute, 975 prompt=options.prompt, 976 search_dirs=options.search_dirs, 977 never_download=options.never_download, 978 no_setuptools=options.no_setuptools, 979 no_pip=options.no_pip) 980 if 'after_install' in globals(): 981 after_install(options, home_dir) 982 983def call_subprocess(cmd, show_stdout=True, 984 filter_stdout=None, cwd=None, 985 raise_on_returncode=True, extra_env=None, 986 remove_from_env=None): 987 cmd_parts = [] 988 for part in cmd: 989 if len(part) > 45: 990 part = part[:20]+"..."+part[-20:] 991 if ' ' in part or '\n' in part or '"' in part or "'" in part: 992 part = '"%s"' % part.replace('"', '\\"') 993 if hasattr(part, 'decode'): 994 try: 995 part = part.decode(sys.getdefaultencoding()) 996 except UnicodeDecodeError: 997 part = part.decode(sys.getfilesystemencoding()) 998 cmd_parts.append(part) 999 cmd_desc = ' '.join(cmd_parts) 1000 if show_stdout: 1001 stdout = None 1002 else: 1003 stdout = subprocess.PIPE 1004 logger.debug("Running command %s" % cmd_desc) 1005 if extra_env or remove_from_env: 1006 env = os.environ.copy() 1007 if extra_env: 1008 env.update(extra_env) 1009 if remove_from_env: 1010 for varname in remove_from_env: 1011 env.pop(varname, None) 1012 else: 1013 env = None 1014 try: 1015 proc = subprocess.Popen( 1016 cmd, stderr=subprocess.STDOUT, stdin=None, stdout=stdout, 1017 cwd=cwd, env=env) 1018 except Exception: 1019 e = sys.exc_info()[1] 1020 logger.fatal( 1021 "Error %s while executing command %s" % (e, cmd_desc)) 1022 raise 1023 all_output = [] 1024 if stdout is not None: 1025 stdout = proc.stdout 1026 encoding = sys.getdefaultencoding() 1027 fs_encoding = sys.getfilesystemencoding() 1028 while 1: 1029 line = stdout.readline() 1030 try: 1031 line = line.decode(encoding) 1032 except UnicodeDecodeError: 1033 line = line.decode(fs_encoding) 1034 if not line: 1035 break 1036 line = line.rstrip() 1037 all_output.append(line) 1038 if filter_stdout: 1039 level = filter_stdout(line) 1040 if isinstance(level, tuple): 1041 level, line = level 1042 logger.log(level, line) 1043 if not logger.stdout_level_matches(level): 1044 logger.show_progress() 1045 else: 1046 logger.info(line) 1047 else: 1048 proc.communicate() 1049 proc.wait() 1050 if proc.returncode: 1051 if raise_on_returncode: 1052 if all_output: 1053 logger.notify('Complete output from command %s:' % cmd_desc) 1054 logger.notify('\n'.join(all_output) + '\n----------------------------------------') 1055 raise OSError( 1056 "Command %s failed with error code %s" 1057 % (cmd_desc, proc.returncode)) 1058 else: 1059 logger.warn( 1060 "Command %s had error code %s" 1061 % (cmd_desc, proc.returncode)) 1062 1063 1064def create_environment(home_dir, site_packages=False, clear=False, 1065 unzip_setuptools=False, use_distribute=False, 1066 prompt=None, search_dirs=None, never_download=False, 1067 no_setuptools=False, no_pip=False): 1068 """ 1069 Creates a new environment in ``home_dir``. 1070 1071 If ``site_packages`` is true, then the global ``site-packages/`` 1072 directory will be on the path. 1073 1074 If ``clear`` is true (default False) then the environment will 1075 first be cleared. 1076 """ 1077 home_dir, lib_dir, inc_dir, bin_dir = path_locations(home_dir) 1078 1079 py_executable = os.path.abspath(install_python( 1080 home_dir, lib_dir, inc_dir, bin_dir, 1081 site_packages=site_packages, clear=clear)) 1082 1083 install_distutils(home_dir) 1084 1085 if not no_setuptools: 1086 if use_distribute: 1087 install_distribute(py_executable, unzip=unzip_setuptools, 1088 search_dirs=search_dirs, never_download=never_download) 1089 else: 1090 install_setuptools(py_executable, unzip=unzip_setuptools, 1091 search_dirs=search_dirs, never_download=never_download) 1092 1093 if not no_pip: 1094 install_pip(py_executable, search_dirs=search_dirs, never_download=never_download) 1095 1096 install_activate(home_dir, bin_dir, prompt) 1097 1098def is_executable_file(fpath): 1099 return os.path.isfile(fpath) and os.access(fpath, os.X_OK) 1100 1101def path_locations(home_dir): 1102 """Return the path locations for the environment (where libraries are, 1103 where scripts go, etc)""" 1104 # XXX: We'd use distutils.sysconfig.get_python_inc/lib but its 1105 # prefix arg is broken: http://bugs.python.org/issue3386 1106 if is_win: 1107 # Windows has lots of problems with executables with spaces in 1108 # the name; this function will remove them (using the ~1 1109 # format): 1110 mkdir(home_dir) 1111 if ' ' in home_dir: 1112 import ctypes 1113 GetShortPathName = ctypes.windll.kernel32.GetShortPathNameW 1114 size = max(len(home_dir)+1, 256) 1115 buf = ctypes.create_unicode_buffer(size) 1116 try: 1117 u = unicode 1118 except NameError: 1119 u = str 1120 ret = GetShortPathName(u(home_dir), buf, size) 1121 if not ret: 1122 print('Error: the path "%s" has a space in it' % home_dir) 1123 print('We could not determine the short pathname for it.') 1124 print('Exiting.') 1125 sys.exit(3) 1126 home_dir = str(buf.value) 1127 lib_dir = join(home_dir, 'Lib') 1128 inc_dir = join(home_dir, 'Include') 1129 bin_dir = join(home_dir, 'Scripts') 1130 if is_jython: 1131 lib_dir = join(home_dir, 'Lib') 1132 inc_dir = join(home_dir, 'Include') 1133 bin_dir = join(home_dir, 'bin') 1134 elif is_pypy: 1135 lib_dir = home_dir 1136 inc_dir = join(home_dir, 'include') 1137 bin_dir = join(home_dir, 'bin') 1138 elif not is_win: 1139 lib_dir = join(home_dir, 'lib', py_version) 1140 multiarch_exec = '/usr/bin/multiarch-platform' 1141 if is_executable_file(multiarch_exec): 1142 # In Mageia (2) and Mandriva distros the include dir must be like: 1143 # virtualenv/include/multiarch-x86_64-linux/python2.7 1144 # instead of being virtualenv/include/python2.7 1145 p = subprocess.Popen(multiarch_exec, stdout=subprocess.PIPE, stderr=subprocess.PIPE) 1146 stdout, stderr = p.communicate() 1147 # stdout.strip is needed to remove newline character 1148 inc_dir = join(home_dir, 'include', stdout.strip(), py_version + abiflags) 1149 else: 1150 inc_dir = join(home_dir, 'include', py_version + abiflags) 1151 bin_dir = join(home_dir, 'bin') 1152 return home_dir, lib_dir, inc_dir, bin_dir 1153 1154 1155def change_prefix(filename, dst_prefix): 1156 prefixes = [sys.prefix] 1157 1158 if is_darwin: 1159 prefixes.extend(( 1160 os.path.join("/Library/Python", sys.version[:3], "site-packages"), 1161 os.path.join(sys.prefix, "Extras", "lib", "python"), 1162 os.path.join("~", "Library", "Python", sys.version[:3], "site-packages"), 1163 # Python 2.6 no-frameworks 1164 os.path.join("~", ".local", "lib","python", sys.version[:3], "site-packages"), 1165 # System Python 2.7 on OSX Mountain Lion 1166 os.path.join("~", "Library", "Python", sys.version[:3], "lib", "python", "site-packages"))) 1167 1168 if hasattr(sys, 'real_prefix'): 1169 prefixes.append(sys.real_prefix) 1170 if hasattr(sys, 'base_prefix'): 1171 prefixes.append(sys.base_prefix) 1172 prefixes = list(map(os.path.expanduser, prefixes)) 1173 prefixes = list(map(os.path.abspath, prefixes)) 1174 # Check longer prefixes first so we don't split in the middle of a filename 1175 prefixes = sorted(prefixes, key=len, reverse=True) 1176 filename = os.path.abspath(filename) 1177 for src_prefix in prefixes: 1178 if filename.startswith(src_prefix): 1179 _, relpath = filename.split(src_prefix, 1) 1180 if src_prefix != os.sep: # sys.prefix == "/" 1181 assert relpath[0] == os.sep 1182 relpath = relpath[1:] 1183 return join(dst_prefix, relpath) 1184 assert False, "Filename %s does not start with any of these prefixes: %s" % \ 1185 (filename, prefixes) 1186 1187def copy_required_modules(dst_prefix): 1188 import imp 1189 # If we are running under -p, we need to remove the current 1190 # directory from sys.path temporarily here, so that we 1191 # definitely get the modules from the site directory of 1192 # the interpreter we are running under, not the one 1193 # virtualenv.py is installed under (which might lead to py2/py3 1194 # incompatibility issues) 1195 _prev_sys_path = sys.path 1196 if os.environ.get('VIRTUALENV_INTERPRETER_RUNNING'): 1197 sys.path = sys.path[1:] 1198 try: 1199 for modname in REQUIRED_MODULES: 1200 if modname in sys.builtin_module_names: 1201 logger.info("Ignoring built-in bootstrap module: %s" % modname) 1202 continue 1203 try: 1204 f, filename, _ = imp.find_module(modname) 1205 except ImportError: 1206 logger.info("Cannot import bootstrap module: %s" % modname) 1207 else: 1208 if f is not None: 1209 f.close() 1210 # special-case custom readline.so on OS X, but not for pypy: 1211 if modname == 'readline' and sys.platform == 'darwin' and not ( 1212 is_pypy or filename.endswith(join('lib-dynload', 'readline.so'))): 1213 dst_filename = join(dst_prefix, 'lib', 'python%s' % sys.version[:3], 'readline.so') 1214 else: 1215 dst_filename = change_prefix(filename, dst_prefix) 1216 copyfile(filename, dst_filename) 1217 if filename.endswith('.pyc'): 1218 pyfile = filename[:-1] 1219 if os.path.exists(pyfile): 1220 copyfile(pyfile, dst_filename[:-1]) 1221 finally: 1222 sys.path = _prev_sys_path 1223 1224 1225def subst_path(prefix_path, prefix, home_dir): 1226 prefix_path = os.path.normpath(prefix_path) 1227 prefix = os.path.normpath(prefix) 1228 home_dir = os.path.normpath(home_dir) 1229 if not prefix_path.startswith(prefix): 1230 logger.warn('Path not in prefix %r %r', prefix_path, prefix) 1231 return 1232 return prefix_path.replace(prefix, home_dir, 1) 1233 1234 1235def install_python(home_dir, lib_dir, inc_dir, bin_dir, site_packages, clear): 1236 """Install just the base environment, no distutils patches etc""" 1237 if sys.executable.startswith(bin_dir): 1238 print('Please use the *system* python to run this script') 1239 return 1240 1241 if clear: 1242 rmtree(lib_dir) 1243 ## FIXME: why not delete it? 1244 ## Maybe it should delete everything with #!/path/to/venv/python in it 1245 logger.notify('Not deleting %s', bin_dir) 1246 1247 if hasattr(sys, 'real_prefix'): 1248 logger.notify('Using real prefix %r' % sys.real_prefix) 1249 prefix = sys.real_prefix 1250 elif hasattr(sys, 'base_prefix'): 1251 logger.notify('Using base prefix %r' % sys.base_prefix) 1252 prefix = sys.base_prefix 1253 else: 1254 prefix = sys.prefix 1255 mkdir(lib_dir) 1256 fix_lib64(lib_dir) 1257 stdlib_dirs = [os.path.dirname(os.__file__)] 1258 if is_win: 1259 stdlib_dirs.append(join(os.path.dirname(stdlib_dirs[0]), 'DLLs')) 1260 elif is_darwin: 1261 stdlib_dirs.append(join(stdlib_dirs[0], 'site-packages')) 1262 if hasattr(os, 'symlink'): 1263 logger.info('Symlinking Python bootstrap modules') 1264 else: 1265 logger.info('Copying Python bootstrap modules') 1266 logger.indent += 2 1267 try: 1268 # copy required files... 1269 for stdlib_dir in stdlib_dirs: 1270 if not os.path.isdir(stdlib_dir): 1271 continue 1272 for fn in os.listdir(stdlib_dir): 1273 bn = os.path.splitext(fn)[0] 1274 if fn != 'site-packages' and bn in REQUIRED_FILES: 1275 copyfile(join(stdlib_dir, fn), join(lib_dir, fn)) 1276 # ...and modules 1277 copy_required_modules(home_dir) 1278 finally: 1279 logger.indent -= 2 1280 mkdir(join(lib_dir, 'site-packages')) 1281 import site 1282 site_filename = site.__file__ 1283 if site_filename.endswith('.pyc'): 1284 site_filename = site_filename[:-1] 1285 elif site_filename.endswith('$py.class'): 1286 site_filename = site_filename.replace('$py.class', '.py') 1287 site_filename_dst = change_prefix(site_filename, home_dir) 1288 site_dir = os.path.dirname(site_filename_dst) 1289 writefile(site_filename_dst, SITE_PY) 1290 writefile(join(site_dir, 'orig-prefix.txt'), prefix) 1291 site_packages_filename = join(site_dir, 'no-global-site-packages.txt') 1292 if not site_packages: 1293 writefile(site_packages_filename, '') 1294 1295 if is_pypy or is_win: 1296 stdinc_dir = join(prefix, 'include') 1297 else: 1298 stdinc_dir = join(prefix, 'include', py_version + abiflags) 1299 if os.path.exists(stdinc_dir): 1300 copyfile(stdinc_dir, inc_dir) 1301 else: 1302 logger.debug('No include dir %s' % stdinc_dir) 1303 1304 platinc_dir = distutils.sysconfig.get_python_inc(plat_specific=1) 1305 if platinc_dir != stdinc_dir: 1306 platinc_dest = distutils.sysconfig.get_python_inc( 1307 plat_specific=1, prefix=home_dir) 1308 if platinc_dir == platinc_dest: 1309 # Do platinc_dest manually due to a CPython bug; 1310 # not http://bugs.python.org/issue3386 but a close cousin 1311 platinc_dest = subst_path(platinc_dir, prefix, home_dir) 1312 if platinc_dest: 1313 # PyPy's stdinc_dir and prefix are relative to the original binary 1314 # (traversing virtualenvs), whereas the platinc_dir is relative to 1315 # the inner virtualenv and ignores the prefix argument. 1316 # This seems more evolved than designed. 1317 copyfile(platinc_dir, platinc_dest) 1318 1319 # pypy never uses exec_prefix, just ignore it 1320 if sys.exec_prefix != prefix and not is_pypy: 1321 if is_win: 1322 exec_dir = join(sys.exec_prefix, 'lib') 1323 elif is_jython: 1324 exec_dir = join(sys.exec_prefix, 'Lib') 1325 else: 1326 exec_dir = join(sys.exec_prefix, 'lib', py_version) 1327 for fn in os.listdir(exec_dir): 1328 copyfile(join(exec_dir, fn), join(lib_dir, fn)) 1329 1330 if is_jython: 1331 # Jython has either jython-dev.jar and javalib/ dir, or just 1332 # jython.jar 1333 for name in 'jython-dev.jar', 'javalib', 'jython.jar': 1334 src = join(prefix, name) 1335 if os.path.exists(src): 1336 copyfile(src, join(home_dir, name)) 1337 # XXX: registry should always exist after Jython 2.5rc1 1338 src = join(prefix, 'registry') 1339 if os.path.exists(src): 1340 copyfile(src, join(home_dir, 'registry'), symlink=False) 1341 copyfile(join(prefix, 'cachedir'), join(home_dir, 'cachedir'), 1342 symlink=False) 1343 1344 mkdir(bin_dir) 1345 py_executable = join(bin_dir, os.path.basename(sys.executable)) 1346 if 'Python.framework' in prefix: 1347 # OS X framework builds cause validation to break 1348 # https://github.com/pypa/virtualenv/issues/322 1349 if os.environ.get('__PYVENV_LAUNCHER__'): 1350 os.unsetenv('__PYVENV_LAUNCHER__') 1351 if re.search(r'/Python(?:-32|-64)*$', py_executable): 1352 # The name of the python executable is not quite what 1353 # we want, rename it. 1354 py_executable = os.path.join( 1355 os.path.dirname(py_executable), 'python') 1356 1357 logger.notify('New %s executable in %s', expected_exe, py_executable) 1358 pcbuild_dir = os.path.dirname(sys.executable) 1359 pyd_pth = os.path.join(lib_dir, 'site-packages', 'virtualenv_builddir_pyd.pth') 1360 if is_win and os.path.exists(os.path.join(pcbuild_dir, 'build.bat')): 1361 logger.notify('Detected python running from build directory %s', pcbuild_dir) 1362 logger.notify('Writing .pth file linking to build directory for *.pyd files') 1363 writefile(pyd_pth, pcbuild_dir) 1364 else: 1365 pcbuild_dir = None 1366 if os.path.exists(pyd_pth): 1367 logger.info('Deleting %s (not Windows env or not build directory python)' % pyd_pth) 1368 os.unlink(pyd_pth) 1369 1370 if sys.executable != py_executable: 1371 ## FIXME: could I just hard link? 1372 executable = sys.executable 1373 shutil.copyfile(executable, py_executable) 1374 make_exe(py_executable) 1375 if is_win or is_cygwin: 1376 pythonw = os.path.join(os.path.dirname(sys.executable), 'pythonw.exe') 1377 if os.path.exists(pythonw): 1378 logger.info('Also created pythonw.exe') 1379 shutil.copyfile(pythonw, os.path.join(os.path.dirname(py_executable), 'pythonw.exe')) 1380 python_d = os.path.join(os.path.dirname(sys.executable), 'python_d.exe') 1381 python_d_dest = os.path.join(os.path.dirname(py_executable), 'python_d.exe') 1382 if os.path.exists(python_d): 1383 logger.info('Also created python_d.exe') 1384 shutil.copyfile(python_d, python_d_dest) 1385 elif os.path.exists(python_d_dest): 1386 logger.info('Removed python_d.exe as it is no longer at the source') 1387 os.unlink(python_d_dest) 1388 # we need to copy the DLL to enforce that windows will load the correct one. 1389 # may not exist if we are cygwin. 1390 py_executable_dll = 'python%s%s.dll' % ( 1391 sys.version_info[0], sys.version_info[1]) 1392 py_executable_dll_d = 'python%s%s_d.dll' % ( 1393 sys.version_info[0], sys.version_info[1]) 1394 pythondll = os.path.join(os.path.dirname(sys.executable), py_executable_dll) 1395 pythondll_d = os.path.join(os.path.dirname(sys.executable), py_executable_dll_d) 1396 pythondll_d_dest = os.path.join(os.path.dirname(py_executable), py_executable_dll_d) 1397 if os.path.exists(pythondll): 1398 logger.info('Also created %s' % py_executable_dll) 1399 shutil.copyfile(pythondll, os.path.join(os.path.dirname(py_executable), py_executable_dll)) 1400 if os.path.exists(pythondll_d): 1401 logger.info('Also created %s' % py_executable_dll_d) 1402 shutil.copyfile(pythondll_d, pythondll_d_dest) 1403 elif os.path.exists(pythondll_d_dest): 1404 logger.info('Removed %s as the source does not exist' % pythondll_d_dest) 1405 os.unlink(pythondll_d_dest) 1406 if is_pypy: 1407 # make a symlink python --> pypy-c 1408 python_executable = os.path.join(os.path.dirname(py_executable), 'python') 1409 if sys.platform in ('win32', 'cygwin'): 1410 python_executable += '.exe' 1411 logger.info('Also created executable %s' % python_executable) 1412 copyfile(py_executable, python_executable) 1413 1414 if is_win: 1415 for name in 'libexpat.dll', 'libpypy.dll', 'libpypy-c.dll', 'libeay32.dll', 'ssleay32.dll', 'sqlite.dll': 1416 src = join(prefix, name) 1417 if os.path.exists(src): 1418 copyfile(src, join(bin_dir, name)) 1419 1420 if os.path.splitext(os.path.basename(py_executable))[0] != expected_exe: 1421 secondary_exe = os.path.join(os.path.dirname(py_executable), 1422 expected_exe) 1423 py_executable_ext = os.path.splitext(py_executable)[1] 1424 if py_executable_ext == '.exe': 1425 # python2.4 gives an extension of '.4' :P 1426 secondary_exe += py_executable_ext 1427 if os.path.exists(secondary_exe): 1428 logger.warn('Not overwriting existing %s script %s (you must use %s)' 1429 % (expected_exe, secondary_exe, py_executable)) 1430 else: 1431 logger.notify('Also creating executable in %s' % secondary_exe) 1432 shutil.copyfile(sys.executable, secondary_exe) 1433 make_exe(secondary_exe) 1434 1435 if '.framework' in prefix: 1436 if 'Python.framework' in prefix: 1437 logger.debug('MacOSX Python framework detected') 1438 # Make sure we use the the embedded interpreter inside 1439 # the framework, even if sys.executable points to 1440 # the stub executable in ${sys.prefix}/bin 1441 # See http://groups.google.com/group/python-virtualenv/ 1442 # browse_thread/thread/17cab2f85da75951 1443 original_python = os.path.join( 1444 prefix, 'Resources/Python.app/Contents/MacOS/Python') 1445 if 'EPD' in prefix: 1446 logger.debug('EPD framework detected') 1447 original_python = os.path.join(prefix, 'bin/python') 1448 shutil.copy(original_python, py_executable) 1449 1450 # Copy the framework's dylib into the virtual 1451 # environment 1452 virtual_lib = os.path.join(home_dir, '.Python') 1453 1454 if os.path.exists(virtual_lib): 1455 os.unlink(virtual_lib) 1456 copyfile( 1457 os.path.join(prefix, 'Python'), 1458 virtual_lib) 1459 1460 # And then change the install_name of the copied python executable 1461 try: 1462 mach_o_change(py_executable, 1463 os.path.join(prefix, 'Python'), 1464 '@executable_path/../.Python') 1465 except: 1466 e = sys.exc_info()[1] 1467 logger.warn("Could not call mach_o_change: %s. " 1468 "Trying to call install_name_tool instead." % e) 1469 try: 1470 call_subprocess( 1471 ["install_name_tool", "-change", 1472 os.path.join(prefix, 'Python'), 1473 '@executable_path/../.Python', 1474 py_executable]) 1475 except: 1476 logger.fatal("Could not call install_name_tool -- you must " 1477 "have Apple's development tools installed") 1478 raise 1479 1480 if not is_win: 1481 # Ensure that 'python', 'pythonX' and 'pythonX.Y' all exist 1482 py_exe_version_major = 'python%s' % sys.version_info[0] 1483 py_exe_version_major_minor = 'python%s.%s' % ( 1484 sys.version_info[0], sys.version_info[1]) 1485 py_exe_no_version = 'python' 1486 required_symlinks = [ py_exe_no_version, py_exe_version_major, 1487 py_exe_version_major_minor ] 1488 1489 py_executable_base = os.path.basename(py_executable) 1490 1491 if py_executable_base in required_symlinks: 1492 # Don't try to symlink to yourself. 1493 required_symlinks.remove(py_executable_base) 1494 1495 for pth in required_symlinks: 1496 full_pth = join(bin_dir, pth) 1497 if os.path.exists(full_pth): 1498 os.unlink(full_pth) 1499 os.symlink(py_executable_base, full_pth) 1500 1501 if is_win and ' ' in py_executable: 1502 # There's a bug with subprocess on Windows when using a first 1503 # argument that has a space in it. Instead we have to quote 1504 # the value: 1505 py_executable = '"%s"' % py_executable 1506 # NOTE: keep this check as one line, cmd.exe doesn't cope with line breaks 1507 cmd = [py_executable, '-c', 'import sys;out=sys.stdout;' 1508 'getattr(out, "buffer", out).write(sys.prefix.encode("utf-8"))'] 1509 logger.info('Testing executable with %s %s "%s"' % tuple(cmd)) 1510 try: 1511 proc = subprocess.Popen(cmd, 1512 stdout=subprocess.PIPE) 1513 proc_stdout, proc_stderr = proc.communicate() 1514 except OSError: 1515 e = sys.exc_info()[1] 1516 if e.errno == errno.EACCES: 1517 logger.fatal('ERROR: The executable %s could not be run: %s' % (py_executable, e)) 1518 sys.exit(100) 1519 else: 1520 raise e 1521 1522 proc_stdout = proc_stdout.strip().decode("utf-8") 1523 proc_stdout = os.path.normcase(os.path.abspath(proc_stdout)) 1524 norm_home_dir = os.path.normcase(os.path.abspath(home_dir)) 1525 if hasattr(norm_home_dir, 'decode'): 1526 norm_home_dir = norm_home_dir.decode(sys.getfilesystemencoding()) 1527 if proc_stdout != norm_home_dir: 1528 logger.fatal( 1529 'ERROR: The executable %s is not functioning' % py_executable) 1530 logger.fatal( 1531 'ERROR: It thinks sys.prefix is %r (should be %r)' 1532 % (proc_stdout, norm_home_dir)) 1533 logger.fatal( 1534 'ERROR: virtualenv is not compatible with this system or executable') 1535 if is_win: 1536 logger.fatal( 1537 'Note: some Windows users have reported this error when they ' 1538 'installed Python for "Only this user" or have multiple ' 1539 'versions of Python installed. Copying the appropriate ' 1540 'PythonXX.dll to the virtualenv Scripts/ directory may fix ' 1541 'this problem.') 1542 sys.exit(100) 1543 else: 1544 logger.info('Got sys.prefix result: %r' % proc_stdout) 1545 1546 pydistutils = os.path.expanduser('~/.pydistutils.cfg') 1547 if os.path.exists(pydistutils): 1548 logger.notify('Please make sure you remove any previous custom paths from ' 1549 'your %s file.' % pydistutils) 1550 ## FIXME: really this should be calculated earlier 1551 1552 fix_local_scheme(home_dir) 1553 1554 if site_packages: 1555 if os.path.exists(site_packages_filename): 1556 logger.info('Deleting %s' % site_packages_filename) 1557 os.unlink(site_packages_filename) 1558 1559 return py_executable 1560 1561 1562def install_activate(home_dir, bin_dir, prompt=None): 1563 home_dir = os.path.abspath(home_dir) 1564 if is_win or is_jython and os._name == 'nt': 1565 files = { 1566 'activate.bat': ACTIVATE_BAT, 1567 'deactivate.bat': DEACTIVATE_BAT, 1568 'activate.ps1': ACTIVATE_PS, 1569 } 1570 1571 # MSYS needs paths of the form /c/path/to/file 1572 drive, tail = os.path.splitdrive(home_dir.replace(os.sep, '/')) 1573 home_dir_msys = (drive and "/%s%s" or "%s%s") % (drive[:1], tail) 1574 1575 # Run-time conditional enables (basic) Cygwin compatibility 1576 home_dir_sh = ("""$(if [ "$OSTYPE" "==" "cygwin" ]; then cygpath -u '%s'; else echo '%s'; fi;)""" % 1577 (home_dir, home_dir_msys)) 1578 files['activate'] = ACTIVATE_SH.replace('__VIRTUAL_ENV__', home_dir_sh) 1579 1580 else: 1581 files = {'activate': ACTIVATE_SH} 1582 1583 # suppling activate.fish in addition to, not instead of, the 1584 # bash script support. 1585 files['activate.fish'] = ACTIVATE_FISH 1586 1587 # same for csh/tcsh support... 1588 files['activate.csh'] = ACTIVATE_CSH 1589 1590 files['activate_this.py'] = ACTIVATE_THIS 1591 if hasattr(home_dir, 'decode'): 1592 home_dir = home_dir.decode(sys.getfilesystemencoding()) 1593 vname = os.path.basename(home_dir) 1594 for name, content in files.items(): 1595 content = content.replace('__VIRTUAL_PROMPT__', prompt or '') 1596 content = content.replace('__VIRTUAL_WINPROMPT__', prompt or '(%s)' % vname) 1597 content = content.replace('__VIRTUAL_ENV__', home_dir) 1598 content = content.replace('__VIRTUAL_NAME__', vname) 1599 content = content.replace('__BIN_NAME__', os.path.basename(bin_dir)) 1600 writefile(os.path.join(bin_dir, name), content) 1601 1602def install_distutils(home_dir): 1603 distutils_path = change_prefix(distutils.__path__[0], home_dir) 1604 mkdir(distutils_path) 1605 ## FIXME: maybe this prefix setting should only be put in place if 1606 ## there's a local distutils.cfg with a prefix setting? 1607 home_dir = os.path.abspath(home_dir) 1608 ## FIXME: this is breaking things, removing for now: 1609 #distutils_cfg = DISTUTILS_CFG + "\n[install]\nprefix=%s\n" % home_dir 1610 writefile(os.path.join(distutils_path, '__init__.py'), DISTUTILS_INIT) 1611 writefile(os.path.join(distutils_path, 'distutils.cfg'), DISTUTILS_CFG, overwrite=False) 1612 1613def fix_local_scheme(home_dir): 1614 """ 1615 Platforms that use the "posix_local" install scheme (like Ubuntu with 1616 Python 2.7) need to be given an additional "local" location, sigh. 1617 """ 1618 try: 1619 import sysconfig 1620 except ImportError: 1621 pass 1622 else: 1623 if sysconfig._get_default_scheme() == 'posix_local': 1624 local_path = os.path.join(home_dir, 'local') 1625 if not os.path.exists(local_path): 1626 os.mkdir(local_path) 1627 for subdir_name in os.listdir(home_dir): 1628 if subdir_name == 'local': 1629 continue 1630 os.symlink(os.path.abspath(os.path.join(home_dir, subdir_name)), \ 1631 os.path.join(local_path, subdir_name)) 1632 1633def fix_lib64(lib_dir): 1634 """ 1635 Some platforms (particularly Gentoo on x64) put things in lib64/pythonX.Y 1636 instead of lib/pythonX.Y. If this is such a platform we'll just create a 1637 symlink so lib64 points to lib 1638 """ 1639 if [p for p in distutils.sysconfig.get_config_vars().values() 1640 if isinstance(p, basestring) and 'lib64' in p]: 1641 logger.debug('This system uses lib64; symlinking lib64 to lib') 1642 assert os.path.basename(lib_dir) == 'python%s' % sys.version[:3], ( 1643 "Unexpected python lib dir: %r" % lib_dir) 1644 lib_parent = os.path.dirname(lib_dir) 1645 top_level = os.path.dirname(lib_parent) 1646 lib_dir = os.path.join(top_level, 'lib') 1647 lib64_link = os.path.join(top_level, 'lib64') 1648 assert os.path.basename(lib_parent) == 'lib', ( 1649 "Unexpected parent dir: %r" % lib_parent) 1650 if os.path.lexists(lib64_link): 1651 return 1652 os.symlink('lib', lib64_link) 1653 1654def resolve_interpreter(exe): 1655 """ 1656 If the executable given isn't an absolute path, search $PATH for the interpreter 1657 """ 1658 if os.path.abspath(exe) != exe: 1659 paths = os.environ.get('PATH', '').split(os.pathsep) 1660 for path in paths: 1661 if os.path.exists(os.path.join(path, exe)): 1662 exe = os.path.join(path, exe) 1663 break 1664 if not os.path.exists(exe): 1665 logger.fatal('The executable %s (from --python=%s) does not exist' % (exe, exe)) 1666 raise SystemExit(3) 1667 if not is_executable(exe): 1668 logger.fatal('The executable %s (from --python=%s) is not executable' % (exe, exe)) 1669 raise SystemExit(3) 1670 return exe 1671 1672def is_executable(exe): 1673 """Checks a file is executable""" 1674 return os.access(exe, os.X_OK) 1675 1676############################################################ 1677## Relocating the environment: 1678 1679def make_environment_relocatable(home_dir): 1680 """ 1681 Makes the already-existing environment use relative paths, and takes out 1682 the #!-based environment selection in scripts. 1683 """ 1684 home_dir, lib_dir, inc_dir, bin_dir = path_locations(home_dir) 1685 activate_this = os.path.join(bin_dir, 'activate_this.py') 1686 if not os.path.exists(activate_this): 1687 logger.fatal( 1688 'The environment doesn\'t have a file %s -- please re-run virtualenv ' 1689 'on this environment to update it' % activate_this) 1690 fixup_scripts(home_dir) 1691 fixup_pth_and_egg_link(home_dir) 1692 ## FIXME: need to fix up distutils.cfg 1693 1694OK_ABS_SCRIPTS = ['python', 'python%s' % sys.version[:3], 1695 'activate', 'activate.bat', 'activate_this.py'] 1696 1697def fixup_scripts(home_dir): 1698 # This is what we expect at the top of scripts: 1699 shebang = '#!%s/bin/python' % os.path.normcase(os.path.abspath(home_dir)) 1700 # This is what we'll put: 1701 new_shebang = '#!/usr/bin/env python%s' % sys.version[:3] 1702 if is_win: 1703 bin_suffix = 'Scripts' 1704 else: 1705 bin_suffix = 'bin' 1706 bin_dir = os.path.join(home_dir, bin_suffix) 1707 home_dir, lib_dir, inc_dir, bin_dir = path_locations(home_dir) 1708 for filename in os.listdir(bin_dir): 1709 filename = os.path.join(bin_dir, filename) 1710 if not os.path.isfile(filename): 1711 # ignore subdirs, e.g. .svn ones. 1712 continue 1713 f = open(filename, 'rb') 1714 try: 1715 try: 1716 lines = f.read().decode('utf-8').splitlines() 1717 except UnicodeDecodeError: 1718 # This is probably a binary program instead 1719 # of a script, so just ignore it. 1720 continue 1721 finally: 1722 f.close() 1723 if not lines: 1724 logger.warn('Script %s is an empty file' % filename) 1725 continue 1726 if not lines[0].strip().startswith(shebang): 1727 if os.path.basename(filename) in OK_ABS_SCRIPTS: 1728 logger.debug('Cannot make script %s relative' % filename) 1729 elif lines[0].strip() == new_shebang: 1730 logger.info('Script %s has already been made relative' % filename) 1731 else: 1732 logger.warn('Script %s cannot be made relative (it\'s not a normal script that starts with %s)' 1733 % (filename, shebang)) 1734 continue 1735 logger.notify('Making script %s relative' % filename) 1736 script = relative_script([new_shebang] + lines[1:]) 1737 f = open(filename, 'wb') 1738 f.write('\n'.join(script).encode('utf-8')) 1739 f.close() 1740 1741def relative_script(lines): 1742 "Return a script that'll work in a relocatable environment." 1743 activate = "import os; activate_this=os.path.join(os.path.dirname(os.path.realpath(__file__)), 'activate_this.py'); execfile(activate_this, dict(__file__=activate_this)); del os, activate_this" 1744 # Find the last future statement in the script. If we insert the activation 1745 # line before a future statement, Python will raise a SyntaxError. 1746 activate_at = None 1747 for idx, line in reversed(list(enumerate(lines))): 1748 if line.split()[:3] == ['from', '__future__', 'import']: 1749 activate_at = idx + 1 1750 break 1751 if activate_at is None: 1752 # Activate after the shebang. 1753 activate_at = 1 1754 return lines[:activate_at] + ['', activate, ''] + lines[activate_at:] 1755 1756def fixup_pth_and_egg_link(home_dir, sys_path=None): 1757 """Makes .pth and .egg-link files use relative paths""" 1758 home_dir = os.path.normcase(os.path.abspath(home_dir)) 1759 if sys_path is None: 1760 sys_path = sys.path 1761 for path in sys_path: 1762 if not path: 1763 path = '.' 1764 if not os.path.isdir(path): 1765 continue 1766 path = os.path.normcase(os.path.abspath(path)) 1767 if not path.startswith(home_dir): 1768 logger.debug('Skipping system (non-environment) directory %s' % path) 1769 continue 1770 for filename in os.listdir(path): 1771 filename = os.path.join(path, filename) 1772 if filename.endswith('.pth'): 1773 if not os.access(filename, os.W_OK): 1774 logger.warn('Cannot write .pth file %s, skipping' % filename) 1775 else: 1776 fixup_pth_file(filename) 1777 if filename.endswith('.egg-link'): 1778 if not os.access(filename, os.W_OK): 1779 logger.warn('Cannot write .egg-link file %s, skipping' % filename) 1780 else: 1781 fixup_egg_link(filename) 1782 1783def fixup_pth_file(filename): 1784 lines = [] 1785 prev_lines = [] 1786 f = open(filename) 1787 prev_lines = f.readlines() 1788 f.close() 1789 for line in prev_lines: 1790 line = line.strip() 1791 if (not line or line.startswith('#') or line.startswith('import ') 1792 or os.path.abspath(line) != line): 1793 lines.append(line) 1794 else: 1795 new_value = make_relative_path(filename, line) 1796 if line != new_value: 1797 logger.debug('Rewriting path %s as %s (in %s)' % (line, new_value, filename)) 1798 lines.append(new_value) 1799 if lines == prev_lines: 1800 logger.info('No changes to .pth file %s' % filename) 1801 return 1802 logger.notify('Making paths in .pth file %s relative' % filename) 1803 f = open(filename, 'w') 1804 f.write('\n'.join(lines) + '\n') 1805 f.close() 1806 1807def fixup_egg_link(filename): 1808 f = open(filename) 1809 link = f.readline().strip() 1810 f.close() 1811 if os.path.abspath(link) != link: 1812 logger.debug('Link in %s already relative' % filename) 1813 return 1814 new_link = make_relative_path(filename, link) 1815 logger.notify('Rewriting link %s in %s as %s' % (link, filename, new_link)) 1816 f = open(filename, 'w') 1817 f.write(new_link) 1818 f.close() 1819 1820def make_relative_path(source, dest, dest_is_directory=True): 1821 """ 1822 Make a filename relative, where the filename is dest, and it is 1823 being referred to from the filename source. 1824 1825 >>> make_relative_path('/usr/share/something/a-file.pth', 1826 ... '/usr/share/another-place/src/Directory') 1827 '../another-place/src/Directory' 1828 >>> make_relative_path('/usr/share/something/a-file.pth', 1829 ... '/home/user/src/Directory') 1830 '../../../home/user/src/Directory' 1831 >>> make_relative_path('/usr/share/a-file.pth', '/usr/share/') 1832 './' 1833 """ 1834 source = os.path.dirname(source) 1835 if not dest_is_directory: 1836 dest_filename = os.path.basename(dest) 1837 dest = os.path.dirname(dest) 1838 dest = os.path.normpath(os.path.abspath(dest)) 1839 source = os.path.normpath(os.path.abspath(source)) 1840 dest_parts = dest.strip(os.path.sep).split(os.path.sep) 1841 source_parts = source.strip(os.path.sep).split(os.path.sep) 1842 while dest_parts and source_parts and dest_parts[0] == source_parts[0]: 1843 dest_parts.pop(0) 1844 source_parts.pop(0) 1845 full_parts = ['..']*len(source_parts) + dest_parts 1846 if not dest_is_directory: 1847 full_parts.append(dest_filename) 1848 if not full_parts: 1849 # Special case for the current directory (otherwise it'd be '') 1850 return './' 1851 return os.path.sep.join(full_parts) 1852 1853 1854 1855############################################################ 1856## Bootstrap script creation: 1857 1858def create_bootstrap_script(extra_text, python_version=''): 1859 """ 1860 Creates a bootstrap script, which is like this script but with 1861 extend_parser, adjust_options, and after_install hooks. 1862 1863 This returns a string that (written to disk of course) can be used 1864 as a bootstrap script with your own customizations. The script 1865 will be the standard virtualenv.py script, with your extra text 1866 added (your extra text should be Python code). 1867 1868 If you include these functions, they will be called: 1869 1870 ``extend_parser(optparse_parser)``: 1871 You can add or remove options from the parser here. 1872 1873 ``adjust_options(options, args)``: 1874 You can change options here, or change the args (if you accept 1875 different kinds of arguments, be sure you modify ``args`` so it is 1876 only ``[DEST_DIR]``). 1877 1878 ``after_install(options, home_dir)``: 1879 1880 After everything is installed, this function is called. This 1881 is probably the function you are most likely to use. An 1882 example would be:: 1883 1884 def after_install(options, home_dir): 1885 subprocess.call([join(home_dir, 'bin', 'easy_install'), 1886 'MyPackage']) 1887 subprocess.call([join(home_dir, 'bin', 'my-package-script'), 1888 'setup', home_dir]) 1889 1890 This example immediately installs a package, and runs a setup 1891 script from that package. 1892 1893 If you provide something like ``python_version='2.5'`` then the 1894 script will start with ``#!/usr/bin/env python2.5`` instead of 1895 ``#!/usr/bin/env python``. You can use this when the script must 1896 be run with a particular Python version. 1897 """ 1898 filename = __file__ 1899 if filename.endswith('.pyc'): 1900 filename = filename[:-1] 1901 f = codecs.open(filename, 'r', encoding='utf-8') 1902 content = f.read() 1903 f.close() 1904 py_exe = 'python%s' % python_version 1905 content = (('#!/usr/bin/env %s\n' % py_exe) 1906 + '## WARNING: This file is generated\n' 1907 + content) 1908 return content.replace('##EXT' 'END##', extra_text) 1909 1910##EXTEND## 1911 1912def convert(s): 1913 b = base64.b64decode(s.encode('ascii')) 1914 return zlib.decompress(b).decode('utf-8') 1915 1916##file site.py 1917SITE_PY = convert(""" 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 2058""") 2059 2060##file ez_setup.py 2061EZ_SETUP_PY = convert(""" 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 2128qL7+C1AkCcw= 2129""") 2130 2131##file distribute_from_egg.py 2132DISTRIBUTE_FROM_EGG_PY = convert(""" 2133eJw9j8tqAzEMRfcG/4MgmxQyptkGusonZBmGoGTUGYFfWPKE6dfXTkM3gqt7rh47OKP3NMF3SQFW 2134LlrRU1zhybpAxoKBlIqcrNnBdRjQP3GTocYfzmNrrCPQPN9iwzpxSQfQhWBi0cL3qtRtYIG/4Mv0 2135KApY5hooqrOGQ05FQTaxptF9Fnx16Rq0XofjaE1XGXVxHIWK7j8P8EY/rHndLqQ1a0pe3COFgHFy 2136hLLdWkDbi/DeEpCjNb3u/zccT2Ob8gtnwVyI 2137""") 2138 2139##file distribute_setup.py 2140DISTRIBUTE_SETUP_PY = convert(""" 2141eJztPGtz2ziS3/UrcHK5SOUkxs7MzV25TlOVmTizrs0mKdvZ/ZC4aIiEJI75GpC0ov311403SEp2 2142LrMfruq8O7ZENBqNfncDzMm/1ft2W5WT6XT6S1W1TctpTdIM/marrmUkK5uW5jltMwCaXK3JvurI 2143jpYtaSvSNYw0rO3qtqryBmBxlJOaJg90w4JGDkb1fk5+75oWAJK8Sxlpt1kzWWc5oocvgIQWDFbl 2144LGkrvie7rN2SrJ0TWqaEpqmYgAsibFvVpFrLlTT+i4vJhMDPmleFQ30sxklW1BVvkdrYUivg/Ufh 2145bLBDzv7ogCxCSVOzJFtnCXlkvAFmIA126hw/A1Ra7cq8oumkyDiv+JxUXHCJloTmLeMlBZ5qILvj 2146uVg0Aai0Ik1FVnvSdHWd77NyM8FN07rmVc0znF7VKAzBj/v7/g7u76PJ5BbZJfibiIURIyO8g88N 2147biXhWS22p6QrqKw3nKauPCNUioliXtXoT822a7PcfNubgTYrmP68LgvaJlszxIoa6THfKXe/wo5q 2148yhs2mRgB4hqNllxebSaTlu8vrJCbDJVTDn+6ubyOb65uLyfsa8JgZ1fi+SVKQE4xEGRJ3lclc7Dp 2149fXQr4HDCmkZqUsrWJJa2ESdFGr6gfNPM5BT8wa+ALIT9R+wrS7qWrnI2n5F/F0MGjgM7eemgjxJg 2150eCiwkeWSnE0OEn0CdgCyAcmBkFOyBiFJgsir6Ic/lcgT8kdXtaBr+LgrWNkC69ewfAmqasHgEWKq 2151wRsAMQWSHwDMD68Cu6QmCxEy3ObMH1N4Avgf2D6MD4cdtgXT02YakFMEHMApmP6Q2vRnS4FgHXxQ 2152KzZ3felUTdTUFIwyhE8f43+8vrqdkx7TyAtXZm8u377+9O42/vvl9c3Vh/ew3vQs+in64cepGfp0 2153/Q4fb9u2vnj5st7XWSRFFVV881L5yOZlA34sYS/Tl9ZtvZxObi5vP328/fDh3U389vVfL9/0FkrO 2154z6cTF+jjX3+Lr96//YDj0+mXyd9YS1Pa0sXfpbe6IOfR2eQ9uNkLx8InZvS0mdx0RUHBKshX+Jn8 2155pSrYogYKxffJ6w4o5+7nBStolssn77KElY0CfcOkfxF48QEQBBI8tKPJZCLUWLmiEFzDCv7OtW+K 2156ke3LcDbTRsG+QoxKhLaKcCDhxWBb1OBSgQfa30TFQ4qfwbPjOPiRaEd5GQaXFgkoxWkTzNVkCVjl 2157abxLARHow4a1yS5VGIzbEFBgzFuYE7pTBRQVREgnF1U1K/W2LEys9qH27E2OkrxqGIYja6GbShGL 2158mzaBwwCAg5FbB6Jq2m6j3wFeETbHhzmol0Pr57O72XAjEosdsAx7X+3IruIPLsc0tEOlEhqGrSGO 2159KzNI3hhlD2aufymr1vNogY7wsFygkMPHF65y9DyMXe8GdBgyB1huBy6N7HgFH9OOa9Vxc5vIoaOH 2160hTEBzdAzkwJcOFgFoavqkfUnoXJmbVJBGNWu+5UHoPyNfLjOSlh9TJ+k+lncMuRGvGg5Y0bblOGs 2161ugzA2WYTwn9zYuynrWIE+3+z+T9gNkKGIv6WBKQ4gugXA+HYDsJaQUh5W04dMqPFH/h7hfEG1UY8 2162WuA3+MUdRH+Kksr9Sb3XusdZ0+Wtr1pAiARWTkDLAwyqaRsxbGngNIOc+uqDSJbC4Neqy1MxS/BR 2163Wutmg9apbCSFLamkO1T5+9yk4fGKNkxv23mcspzu1arI6L6SKPjABu7FabOo96dpBP9Hzo6mNvBz 2164SiwVmGaoLxAD1xVo2MjD87vZ89mjjAYINntxSoQD+z9Ea+/nAJes1j3hjgSgyCKRfPDAjLfh2ZxY 2165+at83C/UnKpkpctUnTLEoiBYCsOR8u4VRWrHy17S1uPA0kncRrkhd7BEA+j4CBOW5/8xB+HEa/rA 2166lre8Y8b3FlQ4gKaDSnIn0nmho3TVVDmaMfJiYpdwNA1A8G/ocm9Hm1hyiaGvDeqHTQwmJfLIRqTV 2167yN+iSrucNVjafTG7CSxX+oBDP+19cUTjrecDSOXc0oa2LQ89QDCUOHWi/mhZgLMVB8frAjHkl+x9 2168EOUcbDVlIA4VWmamjM7f4y0OM89jRqT6CuHUsuTn5RTqMrXebISw/j58jCqV/7Uq13mWtP7iDPRE 21691jOJ8CfhDDxKX3SuXg25j9MhFEIWFO04FN/hAGJ6K3y72FjqtkmcdlL48/IUiqisEaKmj1BCiOrq 2170Szkd4sPuT0LLoMVEShk7YN5tsbMhWkKqkwGfeFdifInIx5yBgEbx6W4HJUXFkdQE00JN6DrjTTsH 21714wQ0o9MDQLzXTocsPjn7CqIR+C/llzL8teMcVsn3EjE55TNA7kUAFmEWi5nFUJml0LI2fOWPsbwZ 2172sRDQQdIzOsfCP/c8xR1OwdgselHVw6EC+1vs4VlR5JDNjOq1yXZg1fdV+7bqyvS7zfZJMsdIHKRC 2173xxxWnHBGW9b3VzFuTligybJExDoSqL83bImfkdilQpZyxFCkv7FtSWOvIrSa5icYX14lol4SrVnF 2174+ayV3caSFkxmjfeK9nvICkVytsIW6iPNMw+7Nr2yK1aMg0lTYcvGLQhc2LIUWbFo45jeKaiBmMLI 2175vcePe4KNlxCcRLLVq7MylZET+8qUBC+DWUTuJU/ucUWvOAAHwzjTWaSp5PQqLI3kHgUHzXS1B9EV 2176TqoyFf3ZmmKsX7E1+htsxSZtR3PbJRb7a7HUaiMthn9JzuCFIyHUjkMlvhKBiGFrXvXIeY5118Qx 2177x9Fw6aB4NTa33fwzRnXAfpSXH0dYp23+iR5QSV824rmXrqIgIRhqLDIFpI8MWHogC9egKsHkCaKD 2178fal+r2OuvdRZop1dIM9fP1YZanWNppsacmySM4jqpn4x1iOcfDOd45Z8ny2JUlwKB8Mn5JrR9KUI 2179rgQjDORnQDpZgck9zPFUYIdKiOFQ+hbQ5KTiHNyFsL4eMtit0GptLxmez7RMwGsV1j/YKcQMgSeg 2180DzTtJVWSjYJoyaw5me5W0wGQygsQmR0bOE0lCVhrJMcAAnQN34MH/CPxDhZ14W07V0gY9pILS1Ay 21811tUgOOwG3Neq+hquuzJBd6a8oBh2x0XTd05evHjYzY5kxvJIwtYoarq2jDfatdzI58eS5j4s5s1Q 2182ao8lzEjtY1bJBtag+e/+1LRpBgP9lSJcByQ9fG4WeQYOAwuYDs+r8XRIlC9YKD0jtbET3lIAeHZO 21833593WIZKebRGeKJ/Up3VMkO6jzNoVASjad04pKv1rt5qTRdkxegdQjSEOTgM8AFla4P+P0R0o8lD 2184Vwt/sZa5NSvlliC265C01k4AMc1UhAAXCg4vVmgBYu16kLVnncCm4YSlJsmy7gS8HyLZa66OtMNe 2185+xBuI1axw6qJnfURobFKiPQESDQxasTCTdiNeXsFC9wFY2FUOTzN0/EkcT3moYTSTxzxwHqu23FG 2186jNfCM3LNt1FpfreAFHFHhKRpGXBNUlCynY76+BQieBB9ePcmOm3wDA/PhyP8NWgrXyM6GTgxaxLt 2187TLlDjVH1l7Fwxq/h2KgiXz+0tBbVIyTiYHSx2/EP65wmbAtmxHSXvJchZA32OYdgPvGfygeIsd5h 2188AuR0ahPO3MMKusaaxvNsmOnq+xFOE3qcFKBaHbdH6m+Ic+dut+cF9iMXWHj0A4lefOCHV6AnDy5b 21891n7pZTlg+6+iOnDvELjr9hgw6SnB36pHVAGWM3kAXXUtZtPolHZ0b01WV1D9TNBhzpxIy1HE9+Sp 21905jt8sEFCGR4QHXuw0pq8yDSYJN2smjEnI6ezqqeu+DmIGZYXYAe07+HmxKdmVJVOAPOO5KwNGoJq 2191b3x6n59GzRS/UdNCtz047zUW1eEB3rvAjw73NIZj8lAw3llfv4etQHp1tOtqBliGucKYVoJPlocC 2192wFZNrOLEgRZ9cGNvNaVOAyLo7cR354c8Td+5H4Izrp6uIVE3J+JIgOKKEwARxNzfMT1xYySW+VgI 2193AQY8kAOPXhRARVytfg/Nceos0o30GopNqOhkZHyqgeH5NkX4t8zxXK5LLyjlSJ32lBseEbfmju5Z 2194DF2QYNX+UTAJjE4FqvDZZzKy2LQbVaHcsSN1JNRYPwgLfPG0Ljx0NWIuafsGt9cjZeABNS+HLnDU 219590jwI56n78N/RfnLQD6Y5edOJlcx/tIkWSqlvywfM16VaGy9vN4turEc3kJ5R2rGi6xp9M04WUaf 2196Ygf0IatroGl6ZBtD+lRuN+rEBcDhPE+KqzWJ3WFxOXoSwYSgnxf12NluHalaDqrHT6WpHhlOI7Cv 2197M0/v7ykz7/m7Z7mTycyvWUwEttnliYprEA6TB9TqDL+N1QoHbUVm85e//bZASWI8A6nKz99gK9kg 2198Gz8a9A8FqOcGeaunTqA/ULgA8cWD4Zv/6CgrZk94mSc5d8yi/zTTcljhlVBKW8arKDVoL8yIdqwJ 2199r4PQ+ots1x6MrSNnkAqz6EnHNWfr7Guoo44NdCbiijCljl8p3zxe9PyRTcbVZUYN+Fl/gJCdsq9O 2200DIda6/zizmR1YniuLz2ysisYp/I6pNsjQlB5nVjmf4sFh93KGyFyG/1yAbYBOCJYlbcN9tNRj5cY 22011CSekQZUW9VKOGJmnWdtGOA6y2D2edE7h3SYoBnoLqZw9Q/DJFVYqEoqRg+Xc1BOeYfzZ8mf8V6Z 2202R27zWUAid4d0fiutlkpgb9cwHohTFHs5WR2LYsd6tDc1toqZPWIdUisH6tpX+JuEisNT54xVX08d 2203M+CD1wCO9eJOyI4FYFUJkDCSdDj5Nqikc8MprZhkSsNYgYHdPQoetn3E1x2ajF+8qDtYyIbhhpxw 2204hJkyTN41EWaR/hm3j/FaHnRjehKJy+u96okzEepxfCnctq+zXqpzu6/ZgF/YjHXOyl5/vPpXEmyp 2205s0VqfxlQT1813Xtu7osgbskk2wbjgjohKWuZuk+I8RzvIJigiHqb9jNsc/647JMX6aG+drsvqDhF 2206mVwadF03a0ZWUbwQpynSN6J6Ct+YfRXE1rx6zFKWyndVsrWCd9+KaZzWSKquIhZze5qjG61uPeSH 2207kjHKxqWgsAFD532CAZE8BBq7hDv0bfJ+PtCyherocAXlZWZgo1KOjXuRUW1pZBMRK1MVRMR9uQOb 2208KhfynqMVnkcHWvvhLt+oVPVkRRrgGPO3I00f5yrsYZIOJVEjpBzPqRSJ4aGUFHXO75Z8Q1p6MC89 22090lvv8cafN+yuu7phzizRrMXBuvSQ4pDb8f4l64vWLwi+V55DeiEmFTUQyZxDgZx2ZbK1mZ190g+e 221012rE2zhGO1mWinfIJIToSeiXjCRUndWkoPwBbzJUhIrjZ2onrLqNKp6K9BzfaQkWiX8RHhIJvFaU 2211s4VqTSzYV/GaGSTQi4KWEMPT4M4geXUICWdJxTWkes9HJJwXP9xhwiIpAFcyNvDKCaV6+OzO9EGw 2212Xegms5/9N2vuILnS0yYah7jzNPrSlBGJcxG8YflanhgspxHU+QXDuxjNEqOVPepSl9fF2bqCkAe3 22134l4FBxFKeeHXRF7b0ne39f7sHRH09vjKX7UrsZIvqhRfDpSRBc84BIDbk7CHoBpJBuotOn2gSGkT 2214kXvcQGDu2uCbeoB0zQQhg6vrQKjiAHyEyWpHAfp4mQTTXBBR4JuX4v4N8FOQLFqfGg+eLSj7gOi0 22152pMNaxWucOZfSlGJX1LVe/c7VH1QW6h7lpKh8gq/BlCMt5cxXQ6APtyZjEOLZZBp6AGM+vl6Yuoc 2216WEl4WohVCsQr09Ww6vz3PN6JJsyjR90RauiaoVRZ76aEhYxoDeVuGqo1fCep6VoKbkX46ygg3tHD 2217XtGPP/6XTIuSrAD5ifoMCDz7z7MzJ/vL15GSvUYqtd+kK9cM3QEjDbLfpdm1b7eZSf6bhK/m5EeH 2218RWhkOJ/xEDCczxHPq9loXZIUtYCJsCUhASN7LtfnGyINJeZxAC6pD8dOXQaIHth+qTUwwhsUoL9I 2219c4AEBDNMxAU2eSNbMwiSQnF5BnAZEzZmi7or5IFZYp95Pa1zxj0ixfnnaBNFS9xn0OA6gpBysgXi 2220rIwV3tkQsBPnqs8ATLawsyOAuvnqmOz/4iqxVFGcnAP3cyi4z4fFtrio3Svkx65+CGRxutqEoIRT 22215VvwlUW8RMZ670G5L4aF6k1pGwLE31/MSyL2bVfwpoF6uVbHLGK6NZV+e8gUY6o89r2js7L0aooZ 2222iooIK35Nn+elDhjjT4cytKnsHui71g35qF8L/glDNOSjjPeuZ8lL8Tf7pmXFJcbWcydpcgjXTk03 2223KLymggtomrVgWpLZPS5/xBEZS+WhE0Sakjkdp8YDF4jELUb1Lnj0QUAJNFy5AgkU0TSNJQ5b72qC 22248WJr0y4Dl9nwkIo7PcugabH114IrEJBr2uWqPLd3Z7csr5c6PUIbF8wWL5wruZPwGOtnwXOo1Rfz 2225FnjX0ZDt3YAMMJNp6SPly+mn63dTS6KmfPTur6Rf/3MDmNTgjVgRmNXN1speCxxXbLUDJai5ztzU 2226jlyh60S2Av6onMMYFcUu6qYEjqeuGmnxCw0qKDjGAzedrUZdHft3CoTPvqTNXkFpldL/TsLSV1PZ 2227/zn6ipR/wVrbr/fUM4zhy8vHvBF4rExcM8RaLRbtwDhGPsSxepHeZMCCOzDhfwBqDMd7 2228""") 2229 2230##file activate.sh 2231ACTIVATE_SH = convert(""" 2232eJytVVFvokAQfudXTLEPtTlLeo9tvMSmJpq02hSvl7u2wRUG2QR2DSxSe7n/frOACEVNLlceRHa+ 2233nfl25pvZDswCnoDPQ4QoTRQsENIEPci4CsBMZBq7CAsuLOYqvmYKTTj3YxnBgiXBudGBjUzBZUJI 2234BXEqgCvweIyuCjeG4eF2F5x14bcB9KQiQQWrjSddI1/oQIx6SYYeoFjzWIoIhYI1izlbhJjkKO7D 2235M/QEmKfO9O7WeRo/zr4P7pyHwWxkwitcgwpQ5Ej96OX+PmiFwLeVjFUOrNYKaq1Nud3nR2n8nI2m 2236k9H0friPTGVsUdptaxGrTEfpNVFEskxpXtUkkCkl1UNF9cgLBkx48J4EXyALuBtAwNYIjF5kcmUU 2237abMKmMq1ULoiRbgsDEkTSsKSGFCJ6Z8vY/2xYiSacmtyAfCDdCNTVZoVF8vSTQOoEwSnOrngBkws 2238MYGMBMg8/bMBLSYKS7pYEXP0PqT+ZmBT0Xuy+Pplj5yn4aM9nk72JD8/Wi+Gr98sD9eWSMOwkapD 2239BbUv91XSvmyVkICt2tmXR4tWmrcUCsjWOpw87YidEC8i0gdTSOFhouJUNxR+4NYBG0MftoCTD9F7 22402rTtxG3oPwY1b2HncYwhrlmj6Wq924xtGDWqfdNxap+OYxplEurnMVo9RWks+rH8qKEtx7kZT5zJ 22414H7oOFclrN6uFe+d+nW2aIUsSgs/42EIPuOhXq+jEo3S6tX6w2ilNkDnIpHCWdEQhFgwj9pkk7FN 2242l/y5eQvRSIQ5+TrL05lewxWpt/Lbhes5cJF3mLET1MGhcKCF+40tNWnUulxrpojwDo2sObdje3Bz 2243N3QeHqf3D7OjEXMVV8LN3ZlvuzoWHqiUcNKHtwNd0IbvPGKYYM31nPKCgkUILw3KL+Y8l7aO1ArS 2244Ad37nIU0fCj5NE5gQCuC5sOSu+UdI2NeXg/lFkQIlFpdWVaWZRfvqGiirC9o6liJ9FXGYrSY9mI1 2245D/Ncozgn13vJvsznr7DnkJWXsyMH7e42ljdJ+aqNDF1bFnKWFLdj31xtaJYK6EXFgqmV/ymD/ROG 2246+n8O9H8f5vsGOWXsL1+1k3g= 2247""") 2248 2249##file activate.fish 2250ACTIVATE_FISH = convert(""" 2251eJyVVWFv2jAQ/c6vuBoqQVWC9nVSNVGVCaS2VC2rNLWVZZILWAs2s52wVvvxsyEJDrjbmgpK7PP5 22523bt3d22YLbmGlGcIq1wbmCPkGhPYcLMEEsGciwGLDS+YwSjlekngLFVyBe73GXSXxqw/DwbuTS8x 2253yyKpFr1WG15lDjETQhpQuQBuIOEKY5O9tlppLqxHKSDByjVAPwEy+mXtCq5MzjIUBTCRgEKTKwFG 2254gpBqxTLYXgN2myspVigMaYF92tZSowGZJf4mFExxNs9Qb614CgZtmH0BpEOn11f0cXI/+za8pnfD 22552ZjA1sg9zlV/8QvcMhxbNu0QwgYokn/d+n02nt6Opzcjcnx1vXcIoN74O4ymWQXmHURfJw9jenc/ 2256vbmb0enj6P5+cuVhqlKm3S0u2XRtRbA2QQAhV7VhBF0rsgUX9Ur1rBUXJgVSy8O751k8mzY5OrKH 2257RW3eaQhYGTr8hrXO59ALhxQ83mCsDLAid3T72CCSdJhaFE+fXgicXAARUiR2WeVO37gH3oYHzFKo 22589k7CaPZ1UeNwH1tWuXA4uFKYYcEa8vaKqXl7q1UpygMPhFLvlVKyNzsSM3S2km7UBOl4xweUXk5u 22596e3wZmQ9leY1XE/Ili670tr9g/5POBBpGIJXCCF79L1siarl/dbESa8mD8PL61GpzqpzuMS7tqeB 22601YkALrRBloBMbR9yLcVx7frQAgUqR7NZIuzkEu110gbNit1enNs82Rx5utq7Z3prU78HFRgulqNC 2261OTwbqJa9vkJFclQgZSjbKeBgSsUtCtt9D8OwAbIVJuewQdfvQRaoFE9wd1TmCuRG7OgJ1bVXGHc7 2262z5WDL/WW36v2oi37CyVBak61+yPBA9C1qqGxzKQqZ0oPuocU9hpud0PIp8sDHkXR1HKkNlzjuUWA 2263a0enFUyzOWZA4yXGP+ZMI3Tdt2OuqU/SO4q64526cPE0A7ZyW2PMbWZiZ5HamIZ2RcCKLXhcDl2b 2264vXL+eccQoRzem80mekPDEiyiWK4GWqZmwxQOmPM0eIfgp1P9cqrBsewR2p/DPMtt+pfcYM+Ls2uh 2265hALufTAdmGl8B1H3VPd2af8fQAc4PgqjlIBL9cGQqNpXaAwe3LrtVn8AkZTUxg== 2266""") 2267 2268##file activate.csh 2269ACTIVATE_CSH = convert(""" 2270eJx9VG1P2zAQ/u5fcYQKNgTNPtN1WxlIQ4KCUEGaxuQ6yYVYSuzKdhqVX7+zk3bpy5YPUXL3PPfc 2271ne98DLNCWshliVDV1kGCUFvMoJGugMjq2qQIiVSxSJ1cCofD1BYRnOVGV0CfZ0N2DD91DalQSjsw 2272tQLpIJMGU1euvPe7QeJlkKzgWixlhnAt4aoUVsLnLBiy5NtbJWQ5THX1ZciYKKWwkOFaE04dUm6D 2273r/zh7pq/3D7Nnid3/HEy+wFHY/gEJydg0aFaQrBFgz1c5DG1IhTs+UZgsBC2GMFBlaeH+8dZXwcW 2274VPvCjXdlAvCfQsE7al0+07XjZvrSCUevR5dnkVeKlFYZmUztG4BdzL2u9KyLVabTU0bdfg7a0hgs 2275cSmUg6UwUiQl2iHrcbcVGNvPCiLOe7+cRwG13z9qRGgx2z6DHjfm/Op2yqeT+xvOLzs0PTKHDz2V 2276tkckFHoQfQRXoGJAj9el0FyJCmEMhzgMS4sB7KPOE2ExoLcSieYwDvR+cP8cg11gKkVJc2wRcm1g 2277QhYFlXiTaTfO2ki0fQoiFM4tLuO4aZrhOzqR4dIPcWx17hphMBY+Srwh7RTyN83XOWkcSPh1Pg/k 2278TXX/jbJTbMtUmcxZ+/bbqOsy82suFQg/BhdSOTRhMNBHlUarCpU7JzBhmkKmRejKOQzayQe6MWoa 2279n1wqWmuh6LZAaHxcdeqIlVLhIBJdO9/kbl0It2oEXQj+eGjJOuvOIR/YGRqvFhttUB2XTvLXYN2H 228037CBdbW2W7j2r2+VsCn0doVWcFG1/4y1VwBjfwAyoZhD 2281""") 2282 2283##file activate.bat 2284ACTIVATE_BAT = convert(""" 2285eJx9UdEKgjAUfW6wfxjiIH+hEDKUFHSKLCMI7kNOEkIf9P9pTJ3OLJ/03HPPPed4Es9XS9qqwqgT 2286PbGKKOdXL4aAFS7A4gvAwgijuiKlqOpGlATS2NeMLE+TjJM9RkQ+SmqAXLrBo1LLIeLdiWlD6jZt 2287r7VNubWkndkXaxg5GO3UaOOKS6drO3luDDiO5my3iA0YAKGzPRV1ack8cOdhysI0CYzIPzjSiH5X 22880QcvC8Lfaj0emsVKYF2rhL5L3fCkVjV76kShi59NHwDniAHzkgDgqBcwOgTMx+gDQQqXCw== 2289""") 2290 2291##file deactivate.bat 2292DEACTIVATE_BAT = convert(""" 2293eJxzSE3OyFfIT0vj4ipOLVEI8wwKCXX0iXf1C7Pl4spMU0hJTcvMS01RiPf3cYmHyQYE+fsGhCho 2294cCkAAUibEkTEVhWLMlUlLk6QGixStlyaeCyJDPHw9/Pw93VFsQguim4ZXAJoIUw5DhX47XUM8UCx 2295EchHtwsohN1bILUgw61c/Vy4AJYPYm4= 2296""") 2297 2298##file activate.ps1 2299ACTIVATE_PS = convert(""" 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 2386QastYw== 2387""") 2388 2389##file distutils-init.py 2390DISTUTILS_INIT = convert(""" 2391eJytV1uL4zYUfvevOE0ottuMW9q3gVDa3aUMXXbLMlDKMBiNrSTqOJKRlMxkf33PkXyRbGe7Dw2E 2392UXTu37lpxLFV2oIyifAncxmOL0xLIfcG+gv80x9VW6maw7o/CANSWWBwFtqeWMPlGY6qPjV8A0bB 2393C4eKSTgZ5LRgFeyErMEeOBhbN+Ipgeizhjtnhkn7DdyjuNLPoCS0l/ayQTG0djwZC08cLXozeMss 2394aG5EzQ0IScpnWtHSTXuxByV/QCmxE7y+eS0uxWeoheaVVfqSJHiU7Mhhi6gULbOHorshkrEnKxpT 23950n3A8Y8SMpuwZx6aoix3ouFlmW8gHRSkeSJ2g7hU+kiHLDaQw3bmRDaTGfTnty7gPm0FHbIBg9U9 2396oh1kZzAFLaue2R6htPCtAda2nGlDSUJ4PZBgCJBGVcwKTAMz/vJiLD+Oin5Z5QlvDPdulC6EsiyE 2397NFzb7McNTKJzbJqzphx92VKRFY1idenzmq3K0emRcbWBD0ryqc4NZGmKOOOX9Pz5x+/l27tP797c 2398f/z0d+4NruGNai8uAM0bfsYaw8itFk8ny41jsfpyO+BWlpqfhcG4yxLdi/0tQqoT4a8Vby382mt8 2399p7XSo7aWGdPBc+b6utaBmCQ7rQKQoWtAuthQCiold2KfJIPTT8xwg9blPumc+YDZC/wYGdAyHpJk 2400vUbHbHWAp5No6pK/WhhLEWrFjUwtPEv1Agf8YmnsuXUQYkeZoHm8ogP16gt2uHoxcEMdf2C6pmbw 2401hUMsWGhanboh4IzzmsIpWs134jVPqD/c74bZHdY69UKKSn/+KfVhxLgUlToemayLMYQOqfEC61bh 2402cbhwaqoGUzIyZRFHPmau5juaWqwRn3mpWmoEA5nhzS5gog/5jbcFQqOZvmBasZtwYlG93k5GEiyw 2403buHhMWLjDarEGpMGB2LFs5nIJkhp/nUmZneFaRth++lieJtHepIvKgx6PJqIlD9X2j6pG1i9x3pZ 24045bHuCPFiirGHeO7McvoXkz786GaKVzC9DSpnOxJdc4xm6NSVq7lNEnKdVlnpu9BNYoKX2Iq3wvgh 2405gGEUM66kK6j4NiyoneuPLSwaCWDxczgaolEWpiMyDVDb7dNuLAbriL8ig8mmeju31oNvQdpnvEPC 24061vAXbWacGRVrGt/uXN/gU0CDDwgooKRrHfTBb1/s9lYZ8ZqOBU0yLvpuP6+K9hLFsvIjeNhBi0KL 2407MlOuWRn3FRwx5oHXjl0YImUx0+gLzjGchrgzca026ETmYJzPD+IpuKzNi8AFn048Thd63OdD86M6 240884zE8yQm0VqXdbbgvub2pKVnS76icBGdeTHHXTKspUmr4NYo/furFLKiMdQzFjHJNcdAnMhltBJK 24090/IKX3DVFqvPJ2dLE7bDBkH0l/PJ29074+F0CsGYOxsb7U3myTUncYfXqnLLfa6sJybX4g+hmcjO 2410kMRBfA1JellfRRKJcyRpxdS4rIl6FdmQCWjo/o9Qz7yKffoP4JHjOvABcRn4CZIT2RH4jnxmfpVG 2411qgLaAvQBNfuO6X0/Ux02nb4FKx3vgP+XnkX0QW9pLy/NsXgdN24dD3LxO2Nwil7Zlc1dqtP3d7/h 2412kzp1/+7hGBuY4pk0XD/0Ao/oTe/XGrfyM773aB7iUhgkpy+dwAMalxMP0DrBcsVw/6p25+/hobP9 2413GBknrWExDhLJ1bwt1NcCNblaFbMKCyvmX0PeRaQ= 2414""") 2415 2416##file distutils.cfg 2417DISTUTILS_CFG = convert(""" 2418eJxNj00KwkAMhfc9xYNuxe4Ft57AjYiUtDO1wXSmNJnK3N5pdSEEAu8nH6lxHVlRhtDHMPATA4uH 2419xJ4EFmGbvfJiicSHFRzUSISMY6hq3GLCRLnIvSTnEefN0FIjw5tF0Hkk9Q5dRunBsVoyFi24aaLg 24209FDOlL0FPGluf4QjcInLlxd6f6rqkgPu/5nHLg0cXCscXoozRrP51DRT3j9QNl99AP53T2Q= 2421""") 2422 2423##file activate_this.py 2424ACTIVATE_THIS = convert(""" 2425eJyNU01v2zAMvetXEB4K21jmDOstQA4dMGCHbeihlyEIDMWmG62yJEiKE//7kXKdpN2KzYBt8euR 2426fKSyLPs8wiEo8wh4wqZTGou4V6Hm0wJa1cSiTkJdr8+GsoTRHuCotBayiWqQEYGtMCgfD1KjGYBe 24275a3p0cRKiAe2NtLADikftnDco0ko/SFEVgEZ8aRC5GLux7i3BpSJ6J1H+i7A2CjiHq9z7JRZuuQq 2428siwTIvpxJYCeuWaBpwZdhB+yxy/eWz+ZvVSU8C4E9FFZkyxFsvCT/ZzL8gcz9aXVE14Yyp2M+2W0 2429y7n5mp0qN+avKXvbsyyzUqjeWR8hjGE+2iCE1W1tQ82hsCZN9UzlJr+/e/iab8WfqsmPI6pWeUPd 2430FrMsd4H/55poeO9n54COhUs+sZNEzNtg/wanpjpuqHJaxs76HtZryI/K3H7KJ/KDIhqcbJ7kI4ar 2431XL+sMgXnX0D+Te2Iy5xdP8yueSlQB/x/ED2BTAtyE3K4SYUN6AMNfbO63f4lBW3bUJPbTL+mjSxS 2432PyRfJkZRgj+VbFv+EzHFi5pKwUEepa4JslMnwkowSRCXI+m5XvEOvtuBrxHdhLalG0JofYBok6qj 2433YdN2dEngUlbC4PG60M1WEN0piu7Nq7on0mgyyUw3iV1etLo6r/81biWdQ9MWHFaePWZYaq+nmp+t 2434s3az+sj7eA0jfgPfeoN1 2435""") 2436 2437MH_MAGIC = 0xfeedface 2438MH_CIGAM = 0xcefaedfe 2439MH_MAGIC_64 = 0xfeedfacf 2440MH_CIGAM_64 = 0xcffaedfe 2441FAT_MAGIC = 0xcafebabe 2442BIG_ENDIAN = '>' 2443LITTLE_ENDIAN = '<' 2444LC_LOAD_DYLIB = 0xc 2445maxint = majver == 3 and getattr(sys, 'maxsize') or getattr(sys, 'maxint') 2446 2447 2448class fileview(object): 2449 """ 2450 A proxy for file-like objects that exposes a given view of a file. 2451 Modified from macholib. 2452 """ 2453 2454 def __init__(self, fileobj, start=0, size=maxint): 2455 if isinstance(fileobj, fileview): 2456 self._fileobj = fileobj._fileobj 2457 else: 2458 self._fileobj = fileobj 2459 self._start = start 2460 self._end = start + size 2461 self._pos = 0 2462 2463 def __repr__(self): 2464 return '<fileview [%d, %d] %r>' % ( 2465 self._start, self._end, self._fileobj) 2466 2467 def tell(self): 2468 return self._pos 2469 2470 def _checkwindow(self, seekto, op): 2471 if not (self._start <= seekto <= self._end): 2472 raise IOError("%s to offset %d is outside window [%d, %d]" % ( 2473 op, seekto, self._start, self._end)) 2474 2475 def seek(self, offset, whence=0): 2476 seekto = offset 2477 if whence == os.SEEK_SET: 2478 seekto += self._start 2479 elif whence == os.SEEK_CUR: 2480 seekto += self._start + self._pos 2481 elif whence == os.SEEK_END: 2482 seekto += self._end 2483 else: 2484 raise IOError("Invalid whence argument to seek: %r" % (whence,)) 2485 self._checkwindow(seekto, 'seek') 2486 self._fileobj.seek(seekto) 2487 self._pos = seekto - self._start 2488 2489 def write(self, bytes): 2490 here = self._start + self._pos 2491 self._checkwindow(here, 'write') 2492 self._checkwindow(here + len(bytes), 'write') 2493 self._fileobj.seek(here, os.SEEK_SET) 2494 self._fileobj.write(bytes) 2495 self._pos += len(bytes) 2496 2497 def read(self, size=maxint): 2498 assert size >= 0 2499 here = self._start + self._pos 2500 self._checkwindow(here, 'read') 2501 size = min(size, self._end - here) 2502 self._fileobj.seek(here, os.SEEK_SET) 2503 bytes = self._fileobj.read(size) 2504 self._pos += len(bytes) 2505 return bytes 2506 2507 2508def read_data(file, endian, num=1): 2509 """ 2510 Read a given number of 32-bits unsigned integers from the given file 2511 with the given endianness. 2512 """ 2513 res = struct.unpack(endian + 'L' * num, file.read(num * 4)) 2514 if len(res) == 1: 2515 return res[0] 2516 return res 2517 2518 2519def mach_o_change(path, what, value): 2520 """ 2521 Replace a given name (what) in any LC_LOAD_DYLIB command found in 2522 the given binary with a new name (value), provided it's shorter. 2523 """ 2524 2525 def do_macho(file, bits, endian): 2526 # Read Mach-O header (the magic number is assumed read by the caller) 2527 cputype, cpusubtype, filetype, ncmds, sizeofcmds, flags = read_data(file, endian, 6) 2528 # 64-bits header has one more field. 2529 if bits == 64: 2530 read_data(file, endian) 2531 # The header is followed by ncmds commands 2532 for n in range(ncmds): 2533 where = file.tell() 2534 # Read command header 2535 cmd, cmdsize = read_data(file, endian, 2) 2536 if cmd == LC_LOAD_DYLIB: 2537 # The first data field in LC_LOAD_DYLIB commands is the 2538 # offset of the name, starting from the beginning of the 2539 # command. 2540 name_offset = read_data(file, endian) 2541 file.seek(where + name_offset, os.SEEK_SET) 2542 # Read the NUL terminated string 2543 load = file.read(cmdsize - name_offset).decode() 2544 load = load[:load.index('\0')] 2545 # If the string is what is being replaced, overwrite it. 2546 if load == what: 2547 file.seek(where + name_offset, os.SEEK_SET) 2548 file.write(value.encode() + '\0'.encode()) 2549 # Seek to the next command 2550 file.seek(where + cmdsize, os.SEEK_SET) 2551 2552 def do_file(file, offset=0, size=maxint): 2553 file = fileview(file, offset, size) 2554 # Read magic number 2555 magic = read_data(file, BIG_ENDIAN) 2556 if magic == FAT_MAGIC: 2557 # Fat binaries contain nfat_arch Mach-O binaries 2558 nfat_arch = read_data(file, BIG_ENDIAN) 2559 for n in range(nfat_arch): 2560 # Read arch header 2561 cputype, cpusubtype, offset, size, align = read_data(file, BIG_ENDIAN, 5) 2562 do_file(file, offset, size) 2563 elif magic == MH_MAGIC: 2564 do_macho(file, 32, BIG_ENDIAN) 2565 elif magic == MH_CIGAM: 2566 do_macho(file, 32, LITTLE_ENDIAN) 2567 elif magic == MH_MAGIC_64: 2568 do_macho(file, 64, BIG_ENDIAN) 2569 elif magic == MH_CIGAM_64: 2570 do_macho(file, 64, LITTLE_ENDIAN) 2571 2572 assert(len(what) >= len(value)) 2573 do_file(open(path, 'r+b')) 2574 2575 2576if __name__ == '__main__': 2577 main() 2578 2579## TODO: 2580## Copy python.exe.manifest 2581## Monkeypatch distutils.sysconfig 2582