summaryrefslogtreecommitdiff
path: root/bin
diff options
context:
space:
mode:
authorMike Crute <mike@crute.us>2019-09-05 17:52:28 -0700
committerMike Crute <mike@crute.us>2019-09-05 17:52:28 -0700
commit7e3b6a543890b57e2d795728711734dc2f60db5f (patch)
treeaaa10c0216b22e84630195f3b3b38294037d76e3 /bin
parentcac339bed31b3c494b6aa2f161c1f8c7211e7dbd (diff)
downloaddotfiles-7e3b6a543890b57e2d795728711734dc2f60db5f.tar.bz2
dotfiles-7e3b6a543890b57e2d795728711734dc2f60db5f.tar.xz
dotfiles-7e3b6a543890b57e2d795728711734dc2f60db5f.zip
Drop python2 virtualenv
Diffstat (limited to 'bin')
-rwxr-xr-xbin/virtualenv2339
1 files changed, 0 insertions, 2339 deletions
diff --git a/bin/virtualenv b/bin/virtualenv
deleted file mode 100755
index e451f16..0000000
--- a/bin/virtualenv
+++ /dev/null
@@ -1,2339 +0,0 @@
1#!/usr/bin/env python
2"""Create a "virtual" Python installation"""
3
4import os
5import sys
6
7# If we are running in a new interpreter to create a virtualenv,
8# we do NOT want paths from our existing location interfering with anything,
9# So we remove this file's directory from sys.path - most likely to be
10# the previous interpreter's site-packages. Solves #705, #763, #779
11if os.environ.get('VIRTUALENV_INTERPRETER_RUNNING'):
12 for path in sys.path[:]:
13 if os.path.realpath(os.path.dirname(__file__)) == os.path.realpath(path):
14 sys.path.remove(path)
15
16import base64
17import codecs
18import optparse
19import re
20import shutil
21import logging
22import zlib
23import errno
24import glob
25import distutils.sysconfig
26import struct
27import subprocess
28import pkgutil
29import tempfile
30import textwrap
31from distutils.util import strtobool
32from os.path import join
33
34try:
35 import ConfigParser
36except ImportError:
37 import configparser as ConfigParser
38
39__version__ = "15.1.0.dev0"
40virtualenv_version = __version__ # legacy
41
42if sys.version_info < (2, 6):
43 print('ERROR: %s' % sys.exc_info()[1])
44 print('ERROR: this script requires Python 2.6 or greater.')
45 sys.exit(101)
46
47try:
48 basestring
49except NameError:
50 basestring = str
51
52py_version = 'python%s.%s' % (sys.version_info[0], sys.version_info[1])
53
54is_jython = sys.platform.startswith('java')
55is_pypy = hasattr(sys, 'pypy_version_info')
56is_win = (sys.platform == 'win32')
57is_cygwin = (sys.platform == 'cygwin')
58is_darwin = (sys.platform == 'darwin')
59abiflags = getattr(sys, 'abiflags', '')
60
61user_dir = os.path.expanduser('~')
62if is_win:
63 default_storage_dir = os.path.join(user_dir, 'virtualenv')
64else:
65 default_storage_dir = os.path.join(user_dir, '.virtualenv')
66default_config_file = os.path.join(default_storage_dir, 'virtualenv.ini')
67
68if is_pypy:
69 expected_exe = 'pypy'
70elif is_jython:
71 expected_exe = 'jython'
72else:
73 expected_exe = 'python'
74
75# Return a mapping of version -> Python executable
76# Only provided for Windows, where the information in the registry is used
77if not is_win:
78 def get_installed_pythons():
79 return {}
80else:
81 try:
82 import winreg
83 except ImportError:
84 import _winreg as winreg
85
86 def get_installed_pythons():
87 try:
88 python_core = winreg.CreateKey(winreg.HKEY_LOCAL_MACHINE,
89 "Software\\Python\\PythonCore")
90 except WindowsError:
91 # No registered Python installations
92 return {}
93 i = 0
94 versions = []
95 while True:
96 try:
97 versions.append(winreg.EnumKey(python_core, i))
98 i = i + 1
99 except WindowsError:
100 break
101 exes = dict()
102 for ver in versions:
103 try:
104 path = winreg.QueryValue(python_core, "%s\\InstallPath" % ver)
105 except WindowsError:
106 continue
107 exes[ver] = join(path, "python.exe")
108
109 winreg.CloseKey(python_core)
110
111 # Add the major versions
112 # Sort the keys, then repeatedly update the major version entry
113 # Last executable (i.e., highest version) wins with this approach
114 for ver in sorted(exes):
115 exes[ver[0]] = exes[ver]
116
117 return exes
118
119REQUIRED_MODULES = ['os', 'posix', 'posixpath', 'nt', 'ntpath', 'genericpath',
120 'fnmatch', 'locale', 'encodings', 'codecs',
121 'stat', 'UserDict', 'readline', 'copy_reg', 'types',
122 're', 'sre', 'sre_parse', 'sre_constants', 'sre_compile',
123 'zlib']
124
125REQUIRED_FILES = ['lib-dynload', 'config']
126
127majver, minver = sys.version_info[:2]
128if majver == 2:
129 if minver >= 6:
130 REQUIRED_MODULES.extend(['warnings', 'linecache', '_abcoll', 'abc'])
131 if minver >= 7:
132 REQUIRED_MODULES.extend(['_weakrefset'])
133elif majver == 3:
134 # Some extra modules are needed for Python 3, but different ones
135 # for different versions.
136 REQUIRED_MODULES.extend([
137 '_abcoll', 'warnings', 'linecache', 'abc', 'io', '_weakrefset',
138 'copyreg', 'tempfile', 'random', '__future__', 'collections',
139 'keyword', 'tarfile', 'shutil', 'struct', 'copy', 'tokenize',
140 'token', 'functools', 'heapq', 'bisect', 'weakref', 'reprlib'
141 ])
142 if minver >= 2:
143 REQUIRED_FILES[-1] = 'config-%s' % majver
144 if minver >= 3:
145 import sysconfig
146 platdir = sysconfig.get_config_var('PLATDIR')
147 REQUIRED_FILES.append(platdir)
148 REQUIRED_MODULES.extend([
149 'base64', '_dummy_thread', 'hashlib', 'hmac',
150 'imp', 'importlib', 'rlcompleter'
151 ])
152 if minver >= 4:
153 REQUIRED_MODULES.extend([
154 'operator',
155 '_collections_abc',
156 '_bootlocale',
157 ])
158
159if is_pypy:
160 # these are needed to correctly display the exceptions that may happen
161 # during the bootstrap
162 REQUIRED_MODULES.extend(['traceback', 'linecache'])
163
164 if majver == 3:
165 # _functools is needed to import locale during stdio initialization and
166 # needs to be copied on PyPy because it's not built in
167 REQUIRED_MODULES.append('_functools')
168
169
170class Logger(object):
171
172 """
173 Logging object for use in command-line script. Allows ranges of
174 levels, to avoid some redundancy of displayed information.
175 """
176
177 DEBUG = logging.DEBUG
178 INFO = logging.INFO
179 NOTIFY = (logging.INFO+logging.WARN)/2
180 WARN = WARNING = logging.WARN
181 ERROR = logging.ERROR
182 FATAL = logging.FATAL
183
184 LEVELS = [DEBUG, INFO, NOTIFY, WARN, ERROR, FATAL]
185
186 def __init__(self, consumers):
187 self.consumers = consumers
188 self.indent = 0
189 self.in_progress = None
190 self.in_progress_hanging = False
191
192 def debug(self, msg, *args, **kw):
193 self.log(self.DEBUG, msg, *args, **kw)
194
195 def info(self, msg, *args, **kw):
196 self.log(self.INFO, msg, *args, **kw)
197
198 def notify(self, msg, *args, **kw):
199 self.log(self.NOTIFY, msg, *args, **kw)
200
201 def warn(self, msg, *args, **kw):
202 self.log(self.WARN, msg, *args, **kw)
203
204 def error(self, msg, *args, **kw):
205 self.log(self.ERROR, msg, *args, **kw)
206
207 def fatal(self, msg, *args, **kw):
208 self.log(self.FATAL, msg, *args, **kw)
209
210 def log(self, level, msg, *args, **kw):
211 if args:
212 if kw:
213 raise TypeError(
214 "You may give positional or keyword arguments, not both")
215 args = args or kw
216 rendered = None
217 for consumer_level, consumer in self.consumers:
218 if self.level_matches(level, consumer_level):
219 if (self.in_progress_hanging
220 and consumer in (sys.stdout, sys.stderr)):
221 self.in_progress_hanging = False
222 sys.stdout.write('\n')
223 sys.stdout.flush()
224 if rendered is None:
225 if args:
226 rendered = msg % args
227 else:
228 rendered = msg
229 rendered = ' '*self.indent + rendered
230 if hasattr(consumer, 'write'):
231 consumer.write(rendered+'\n')
232 else:
233 consumer(rendered)
234
235 def start_progress(self, msg):
236 assert not self.in_progress, (
237 "Tried to start_progress(%r) while in_progress %r"
238 % (msg, self.in_progress))
239 if self.level_matches(self.NOTIFY, self._stdout_level()):
240 sys.stdout.write(msg)
241 sys.stdout.flush()
242 self.in_progress_hanging = True
243 else:
244 self.in_progress_hanging = False
245 self.in_progress = msg
246
247 def end_progress(self, msg='done.'):
248 assert self.in_progress, (
249 "Tried to end_progress without start_progress")
250 if self.stdout_level_matches(self.NOTIFY):
251 if not self.in_progress_hanging:
252 # Some message has been printed out since start_progress
253 sys.stdout.write('...' + self.in_progress + msg + '\n')
254 sys.stdout.flush()
255 else:
256 sys.stdout.write(msg + '\n')
257 sys.stdout.flush()
258 self.in_progress = None
259 self.in_progress_hanging = False
260
261 def show_progress(self):
262 """If we are in a progress scope, and no log messages have been
263 shown, write out another '.'"""
264 if self.in_progress_hanging:
265 sys.stdout.write('.')
266 sys.stdout.flush()
267
268 def stdout_level_matches(self, level):
269 """Returns true if a message at this level will go to stdout"""
270 return self.level_matches(level, self._stdout_level())
271
272 def _stdout_level(self):
273 """Returns the level that stdout runs at"""
274 for level, consumer in self.consumers:
275 if consumer is sys.stdout:
276 return level
277 return self.FATAL
278
279 def level_matches(self, level, consumer_level):
280 """
281 >>> l = Logger([])
282 >>> l.level_matches(3, 4)
283 False
284 >>> l.level_matches(3, 2)
285 True
286 >>> l.level_matches(slice(None, 3), 3)
287 False
288 >>> l.level_matches(slice(None, 3), 2)
289 True
290 >>> l.level_matches(slice(1, 3), 1)
291 True
292 >>> l.level_matches(slice(2, 3), 1)
293 False
294 """
295 if isinstance(level, slice):
296 start, stop = level.start, level.stop
297 if start is not None and start > consumer_level:
298 return False
299 if stop is not None and stop <= consumer_level:
300 return False
301 return True
302 else:
303 return level >= consumer_level
304
305 #@classmethod
306 def level_for_integer(cls, level):
307 levels = cls.LEVELS
308 if level < 0:
309 return levels[0]
310 if level >= len(levels):
311 return levels[-1]
312 return levels[level]
313
314 level_for_integer = classmethod(level_for_integer)
315
316# create a silent logger just to prevent this from being undefined
317# will be overridden with requested verbosity main() is called.
318logger = Logger([(Logger.LEVELS[-1], sys.stdout)])
319
320def mkdir(path):
321 if not os.path.exists(path):
322 logger.info('Creating %s', path)
323 os.makedirs(path)
324 else:
325 logger.info('Directory %s already exists', path)
326
327def copyfileordir(src, dest, symlink=True):
328 if os.path.isdir(src):
329 shutil.copytree(src, dest, symlink)
330 else:
331 shutil.copy2(src, dest)
332
333def copyfile(src, dest, symlink=True):
334 if not os.path.exists(src):
335 # Some bad symlink in the src
336 logger.warn('Cannot find file %s (bad symlink)', src)
337 return
338 if os.path.exists(dest):
339 logger.debug('File %s already exists', dest)
340 return
341 if not os.path.exists(os.path.dirname(dest)):
342 logger.info('Creating parent directories for %s', os.path.dirname(dest))
343 os.makedirs(os.path.dirname(dest))
344 if not os.path.islink(src):
345 srcpath = os.path.abspath(src)
346 else:
347 srcpath = os.readlink(src)
348 if symlink and hasattr(os, 'symlink') and not is_win:
349 logger.info('Symlinking %s', dest)
350 try:
351 os.symlink(srcpath, dest)
352 except (OSError, NotImplementedError):
353 logger.info('Symlinking failed, copying to %s', dest)
354 copyfileordir(src, dest, symlink)
355 else:
356 logger.info('Copying to %s', dest)
357 copyfileordir(src, dest, symlink)
358
359def writefile(dest, content, overwrite=True):
360 if not os.path.exists(dest):
361 logger.info('Writing %s', dest)
362 with open(dest, 'wb') as f:
363 f.write(content.encode('utf-8'))
364 return
365 else:
366 with open(dest, 'rb') as f:
367 c = f.read()
368 if c != content.encode("utf-8"):
369 if not overwrite:
370 logger.notify('File %s exists with different content; not overwriting', dest)
371 return
372 logger.notify('Overwriting %s with new content', dest)
373 with open(dest, 'wb') as f:
374 f.write(content.encode('utf-8'))
375 else:
376 logger.info('Content %s already in place', dest)
377
378def rmtree(dir):
379 if os.path.exists(dir):
380 logger.notify('Deleting tree %s', dir)
381 shutil.rmtree(dir)
382 else:
383 logger.info('Do not need to delete %s; already gone', dir)
384
385def make_exe(fn):
386 if hasattr(os, 'chmod'):
387 oldmode = os.stat(fn).st_mode & 0xFFF # 0o7777
388 newmode = (oldmode | 0x16D) & 0xFFF # 0o555, 0o7777
389 os.chmod(fn, newmode)
390 logger.info('Changed mode of %s to %s', fn, oct(newmode))
391
392def _find_file(filename, dirs):
393 for dir in reversed(dirs):
394 files = glob.glob(os.path.join(dir, filename))
395 if files and os.path.isfile(files[0]):
396 return True, files[0]
397 return False, filename
398
399def file_search_dirs():
400 here = os.path.dirname(os.path.abspath(__file__))
401 dirs = [here, join(here, 'virtualenv_support'),
402 join(user_dir, '.virtualenv_support')]
403 if os.path.splitext(os.path.dirname(__file__))[0] != 'virtualenv':
404 # Probably some boot script; just in case virtualenv is installed...
405 try:
406 import virtualenv
407 except ImportError:
408 pass
409 else:
410 dirs.append(os.path.join(
411 os.path.dirname(virtualenv.__file__), 'virtualenv_support'))
412 return [d for d in dirs if os.path.isdir(d)]
413
414
415class UpdatingDefaultsHelpFormatter(optparse.IndentedHelpFormatter):
416 """
417 Custom help formatter for use in ConfigOptionParser that updates
418 the defaults before expanding them, allowing them to show up correctly
419 in the help listing
420 """
421 def expand_default(self, option):
422 if self.parser is not None:
423 self.parser.update_defaults(self.parser.defaults)
424 return optparse.IndentedHelpFormatter.expand_default(self, option)
425
426
427class ConfigOptionParser(optparse.OptionParser):
428 """
429 Custom option parser which updates its defaults by checking the
430 configuration files and environmental variables
431 """
432 def __init__(self, *args, **kwargs):
433 self.config = ConfigParser.RawConfigParser()
434 self.files = self.get_config_files()
435 self.config.read(self.files)
436 optparse.OptionParser.__init__(self, *args, **kwargs)
437
438 def get_config_files(self):
439 config_file = os.environ.get('VIRTUALENV_CONFIG_FILE', False)
440 if config_file and os.path.exists(config_file):
441 return [config_file]
442 return [default_config_file]
443
444 def update_defaults(self, defaults):
445 """
446 Updates the given defaults with values from the config files and
447 the environ. Does a little special handling for certain types of
448 options (lists).
449 """
450 # Then go and look for the other sources of configuration:
451 config = {}
452 # 1. config files
453 config.update(dict(self.get_config_section('virtualenv')))
454 # 2. environmental variables
455 config.update(dict(self.get_environ_vars()))
456 # Then set the options with those values
457 for key, val in config.items():
458 key = key.replace('_', '-')
459 if not key.startswith('--'):
460 key = '--%s' % key # only prefer long opts
461 option = self.get_option(key)
462 if option is not None:
463 # ignore empty values
464 if not val:
465 continue
466 # handle multiline configs
467 if option.action == 'append':
468 val = val.split()
469 else:
470 option.nargs = 1
471 if option.action == 'store_false':
472 val = not strtobool(val)
473 elif option.action in ('store_true', 'count'):
474 val = strtobool(val)
475 try:
476 val = option.convert_value(key, val)
477 except optparse.OptionValueError:
478 e = sys.exc_info()[1]
479 print("An error occurred during configuration: %s" % e)
480 sys.exit(3)
481 defaults[option.dest] = val
482 return defaults
483
484 def get_config_section(self, name):
485 """
486 Get a section of a configuration
487 """
488 if self.config.has_section(name):
489 return self.config.items(name)
490 return []
491
492 def get_environ_vars(self, prefix='VIRTUALENV_'):
493 """
494 Returns a generator with all environmental vars with prefix VIRTUALENV
495 """
496 for key, val in os.environ.items():
497 if key.startswith(prefix):
498 yield (key.replace(prefix, '').lower(), val)
499
500 def get_default_values(self):
501 """
502 Overridding to make updating the defaults after instantiation of
503 the option parser possible, update_defaults() does the dirty work.
504 """
505 if not self.process_default_values:
506 # Old, pre-Optik 1.5 behaviour.
507 return optparse.Values(self.defaults)
508
509 defaults = self.update_defaults(self.defaults.copy()) # ours
510 for option in self._get_all_options():
511 default = defaults.get(option.dest)
512 if isinstance(default, basestring):
513 opt_str = option.get_opt_string()
514 defaults[option.dest] = option.check_value(opt_str, default)
515 return optparse.Values(defaults)
516
517
518def main():
519 parser = ConfigOptionParser(
520 version=virtualenv_version,
521 usage="%prog [OPTIONS] DEST_DIR",
522 formatter=UpdatingDefaultsHelpFormatter())
523
524 parser.add_option(
525 '-v', '--verbose',
526 action='count',
527 dest='verbose',
528 default=0,
529 help="Increase verbosity.")
530
531 parser.add_option(
532 '-q', '--quiet',
533 action='count',
534 dest='quiet',
535 default=0,
536 help='Decrease verbosity.')
537
538 parser.add_option(
539 '-p', '--python',
540 dest='python',
541 metavar='PYTHON_EXE',
542 help='The Python interpreter to use, e.g., --python=python2.5 will use the python2.5 '
543 'interpreter to create the new environment. The default is the interpreter that '
544 'virtualenv was installed with (%s)' % sys.executable)
545
546 parser.add_option(
547 '--clear',
548 dest='clear',
549 action='store_true',
550 help="Clear out the non-root install and start from scratch.")
551
552 parser.set_defaults(system_site_packages=False)
553 parser.add_option(
554 '--no-site-packages',
555 dest='system_site_packages',
556 action='store_false',
557 help="DEPRECATED. Retained only for backward compatibility. "
558 "Not having access to global site-packages is now the default behavior.")
559
560 parser.add_option(
561 '--system-site-packages',
562 dest='system_site_packages',
563 action='store_true',
564 help="Give the virtual environment access to the global site-packages.")
565
566 parser.add_option(
567 '--always-copy',
568 dest='symlink',
569 action='store_false',
570 default=True,
571 help="Always copy files rather than symlinking.")
572
573 parser.add_option(
574 '--unzip-setuptools',
575 dest='unzip_setuptools',
576 action='store_true',
577 help="Unzip Setuptools when installing it.")
578
579 parser.add_option(
580 '--relocatable',
581 dest='relocatable',
582 action='store_true',
583 help='Make an EXISTING virtualenv environment relocatable. '
584 'This fixes up scripts and makes all .pth files relative.')
585
586 parser.add_option(
587 '--no-setuptools',
588 dest='no_setuptools',
589 action='store_true',
590 help='Do not install setuptools in the new virtualenv.')
591
592 parser.add_option(
593 '--no-pip',
594 dest='no_pip',
595 action='store_true',
596 help='Do not install pip in the new virtualenv.')
597
598 parser.add_option(
599 '--no-wheel',
600 dest='no_wheel',
601 action='store_true',
602 help='Do not install wheel in the new virtualenv.')
603
604 default_search_dirs = file_search_dirs()
605 parser.add_option(
606 '--extra-search-dir',
607 dest="search_dirs",
608 action="append",
609 metavar='DIR',
610 default=default_search_dirs,
611 help="Directory to look for setuptools/pip distributions in. "
612 "This option can be used multiple times.")
613
614 parser.add_option(
615 "--download",
616 dest="download",
617 default=True,
618 action="store_true",
619 help="Download preinstalled packages from PyPI.",
620 )
621
622 parser.add_option(
623 "--no-download",
624 '--never-download',
625 dest="download",
626 action="store_false",
627 help="Do not download preinstalled packages from PyPI.",
628 )
629
630 parser.add_option(
631 '--prompt',
632 dest='prompt',
633 help='Provides an alternative prompt prefix for this environment.')
634
635 parser.add_option(
636 '--setuptools',
637 dest='setuptools',
638 action='store_true',
639 help="DEPRECATED. Retained only for backward compatibility. This option has no effect.")
640
641 parser.add_option(
642 '--distribute',
643 dest='distribute',
644 action='store_true',
645 help="DEPRECATED. Retained only for backward compatibility. This option has no effect.")
646
647 if 'extend_parser' in globals():
648 extend_parser(parser)
649
650 options, args = parser.parse_args()
651
652 global logger
653
654 if 'adjust_options' in globals():
655 adjust_options(options, args)
656
657 verbosity = options.verbose - options.quiet
658 logger = Logger([(Logger.level_for_integer(2 - verbosity), sys.stdout)])
659
660 if options.python and not os.environ.get('VIRTUALENV_INTERPRETER_RUNNING'):
661 env = os.environ.copy()
662 interpreter = resolve_interpreter(options.python)
663 if interpreter == sys.executable:
664 logger.warn('Already using interpreter %s' % interpreter)
665 else:
666 logger.notify('Running virtualenv with interpreter %s' % interpreter)
667 env['VIRTUALENV_INTERPRETER_RUNNING'] = 'true'
668 file = __file__
669 if file.endswith('.pyc'):
670 file = file[:-1]
671 popen = subprocess.Popen([interpreter, file] + sys.argv[1:], env=env)
672 raise SystemExit(popen.wait())
673
674 if not args:
675 print('You must provide a DEST_DIR')
676 parser.print_help()
677 sys.exit(2)
678 if len(args) > 1:
679 print('There must be only one argument: DEST_DIR (you gave %s)' % (
680 ' '.join(args)))
681 parser.print_help()
682 sys.exit(2)
683
684 home_dir = args[0]
685
686 if os.path.exists(home_dir) and os.path.isfile(home_dir):
687 logger.fatal('ERROR: File already exists and is not a directory.')
688 logger.fatal('Please provide a different path or delete the file.')
689 sys.exit(3)
690
691 if os.environ.get('WORKING_ENV'):
692 logger.fatal('ERROR: you cannot run virtualenv while in a workingenv')
693 logger.fatal('Please deactivate your workingenv, then re-run this script')
694 sys.exit(3)
695
696 if 'PYTHONHOME' in os.environ:
697 logger.warn('PYTHONHOME is set. You *must* activate the virtualenv before using it')
698 del os.environ['PYTHONHOME']
699
700 if options.relocatable:
701 make_environment_relocatable(home_dir)
702 return
703
704 create_environment(home_dir,
705 site_packages=options.system_site_packages,
706 clear=options.clear,
707 unzip_setuptools=options.unzip_setuptools,
708 prompt=options.prompt,
709 search_dirs=options.search_dirs,
710 download=options.download,
711 no_setuptools=options.no_setuptools,
712 no_pip=options.no_pip,
713 no_wheel=options.no_wheel,
714 symlink=options.symlink)
715 if 'after_install' in globals():
716 after_install(options, home_dir)
717
718def call_subprocess(cmd, show_stdout=True,
719 filter_stdout=None, cwd=None,
720 raise_on_returncode=True, extra_env=None,
721 remove_from_env=None, stdin=None):
722 cmd_parts = []
723 for part in cmd:
724 if len(part) > 45:
725 part = part[:20]+"..."+part[-20:]
726 if ' ' in part or '\n' in part or '"' in part or "'" in part:
727 part = '"%s"' % part.replace('"', '\\"')
728 if hasattr(part, 'decode'):
729 try:
730 part = part.decode(sys.getdefaultencoding())
731 except UnicodeDecodeError:
732 part = part.decode(sys.getfilesystemencoding())
733 cmd_parts.append(part)
734 cmd_desc = ' '.join(cmd_parts)
735 if show_stdout:
736 stdout = None
737 else:
738 stdout = subprocess.PIPE
739 logger.debug("Running command %s" % cmd_desc)
740 if extra_env or remove_from_env:
741 env = os.environ.copy()
742 if extra_env:
743 env.update(extra_env)
744 if remove_from_env:
745 for varname in remove_from_env:
746 env.pop(varname, None)
747 else:
748 env = None
749 try:
750 proc = subprocess.Popen(
751 cmd, stderr=subprocess.STDOUT,
752 stdin=None if stdin is None else subprocess.PIPE,
753 stdout=stdout,
754 cwd=cwd, env=env)
755 except Exception:
756 e = sys.exc_info()[1]
757 logger.fatal(
758 "Error %s while executing command %s" % (e, cmd_desc))
759 raise
760 all_output = []
761 if stdout is not None:
762 if stdin is not None:
763 proc.stdin.write(stdin)
764 proc.stdin.close()
765
766 stdout = proc.stdout
767 encoding = sys.getdefaultencoding()
768 fs_encoding = sys.getfilesystemencoding()
769 while 1:
770 line = stdout.readline()
771 try:
772 line = line.decode(encoding)
773 except UnicodeDecodeError:
774 line = line.decode(fs_encoding)
775 if not line:
776 break
777 line = line.rstrip()
778 all_output.append(line)
779 if filter_stdout:
780 level = filter_stdout(line)
781 if isinstance(level, tuple):
782 level, line = level
783 logger.log(level, line)
784 if not logger.stdout_level_matches(level):
785 logger.show_progress()
786 else:
787 logger.info(line)
788 else:
789 proc.communicate(stdin)
790 proc.wait()
791 if proc.returncode:
792 if raise_on_returncode:
793 if all_output:
794 logger.notify('Complete output from command %s:' % cmd_desc)
795 logger.notify('\n'.join(all_output) + '\n----------------------------------------')
796 raise OSError(
797 "Command %s failed with error code %s"
798 % (cmd_desc, proc.returncode))
799 else:
800 logger.warn(
801 "Command %s had error code %s"
802 % (cmd_desc, proc.returncode))
803
804def filter_install_output(line):
805 if line.strip().startswith('running'):
806 return Logger.INFO
807 return Logger.DEBUG
808
809def find_wheels(projects, search_dirs):
810 """Find wheels from which we can import PROJECTS.
811
812 Scan through SEARCH_DIRS for a wheel for each PROJECT in turn. Return
813 a list of the first wheel found for each PROJECT
814 """
815
816 wheels = []
817
818 # Look through SEARCH_DIRS for the first suitable wheel. Don't bother
819 # about version checking here, as this is simply to get something we can
820 # then use to install the correct version.
821 for project in projects:
822 for dirname in search_dirs:
823 # This relies on only having "universal" wheels available.
824 # The pattern could be tightened to require -py2.py3-none-any.whl.
825 files = glob.glob(os.path.join(dirname, project + '-*.whl'))
826 if files:
827 wheels.append(os.path.abspath(files[0]))
828 break
829 else:
830 # We're out of luck, so quit with a suitable error
831 logger.fatal('Cannot find a wheel for %s' % (project,))
832
833 return wheels
834
835def install_wheel(project_names, py_executable, search_dirs=None,
836 download=False):
837 if search_dirs is None:
838 search_dirs = file_search_dirs()
839
840 wheels = find_wheels(['setuptools', 'pip'], search_dirs)
841 pythonpath = os.pathsep.join(wheels)
842
843 # PIP_FIND_LINKS uses space as the path separator and thus cannot have paths
844 # with spaces in them. Convert any of those to local file:// URL form.
845 try:
846 from urlparse import urljoin
847 from urllib import pathname2url
848 except ImportError:
849 from urllib.parse import urljoin
850 from urllib.request import pathname2url
851 def space_path2url(p):
852 if ' ' not in p:
853 return p
854 return urljoin('file:', pathname2url(os.path.abspath(p)))
855 findlinks = ' '.join(space_path2url(d) for d in search_dirs)
856
857 SCRIPT = textwrap.dedent("""
858 import sys
859 import pkgutil
860 import tempfile
861 import os
862
863 import pip
864
865 cert_data = pkgutil.get_data("pip._vendor.requests", "cacert.pem")
866 if cert_data is not None:
867 cert_file = tempfile.NamedTemporaryFile(delete=False)
868 cert_file.write(cert_data)
869 cert_file.close()
870 else:
871 cert_file = None
872
873 try:
874 args = ["install", "--ignore-installed"]
875 if cert_file is not None:
876 args += ["--cert", cert_file.name]
877 args += sys.argv[1:]
878
879 sys.exit(pip.main(args))
880 finally:
881 if cert_file is not None:
882 os.remove(cert_file.name)
883 """).encode("utf8")
884
885 cmd = [py_executable, '-'] + project_names
886 logger.start_progress('Installing %s...' % (', '.join(project_names)))
887 logger.indent += 2
888
889 env = {
890 "PYTHONPATH": pythonpath,
891 "JYTHONPATH": pythonpath, # for Jython < 3.x
892 "PIP_FIND_LINKS": findlinks,
893 "PIP_USE_WHEEL": "1",
894 "PIP_ONLY_BINARY": ":all:",
895 "PIP_PRE": "1",
896 "PIP_USER": "0",
897 }
898
899 if not download:
900 env["PIP_NO_INDEX"] = "1"
901
902 try:
903 call_subprocess(cmd, show_stdout=False, extra_env=env, stdin=SCRIPT)
904 finally:
905 logger.indent -= 2
906 logger.end_progress()
907
908
909def create_environment(home_dir, site_packages=False, clear=False,
910 unzip_setuptools=False,
911 prompt=None, search_dirs=None, download=False,
912 no_setuptools=False, no_pip=False, no_wheel=False,
913 symlink=True):
914 """
915 Creates a new environment in ``home_dir``.
916
917 If ``site_packages`` is true, then the global ``site-packages/``
918 directory will be on the path.
919
920 If ``clear`` is true (default False) then the environment will
921 first be cleared.
922 """
923 home_dir, lib_dir, inc_dir, bin_dir = path_locations(home_dir)
924
925 py_executable = os.path.abspath(install_python(
926 home_dir, lib_dir, inc_dir, bin_dir,
927 site_packages=site_packages, clear=clear, symlink=symlink))
928
929 install_distutils(home_dir)
930
931 to_install = []
932
933 if not no_setuptools:
934 to_install.append('setuptools')
935
936 if not no_pip:
937 to_install.append('pip')
938
939 if not no_wheel:
940 to_install.append('wheel')
941
942 if to_install:
943 install_wheel(
944 to_install,
945 py_executable,
946 search_dirs,
947 download=download,
948 )
949
950 install_activate(home_dir, bin_dir, prompt)
951
952 install_python_config(home_dir, bin_dir, prompt)
953
954def is_executable_file(fpath):
955 return os.path.isfile(fpath) and os.access(fpath, os.X_OK)
956
957def path_locations(home_dir):
958 """Return the path locations for the environment (where libraries are,
959 where scripts go, etc)"""
960 home_dir = os.path.abspath(home_dir)
961 # XXX: We'd use distutils.sysconfig.get_python_inc/lib but its
962 # prefix arg is broken: http://bugs.python.org/issue3386
963 if is_win:
964 # Windows has lots of problems with executables with spaces in
965 # the name; this function will remove them (using the ~1
966 # format):
967 mkdir(home_dir)
968 if ' ' in home_dir:
969 import ctypes
970 GetShortPathName = ctypes.windll.kernel32.GetShortPathNameW
971 size = max(len(home_dir)+1, 256)
972 buf = ctypes.create_unicode_buffer(size)
973 try:
974 u = unicode
975 except NameError:
976 u = str
977 ret = GetShortPathName(u(home_dir), buf, size)
978 if not ret:
979 print('Error: the path "%s" has a space in it' % home_dir)
980 print('We could not determine the short pathname for it.')
981 print('Exiting.')
982 sys.exit(3)
983 home_dir = str(buf.value)
984 lib_dir = join(home_dir, 'Lib')
985 inc_dir = join(home_dir, 'Include')
986 bin_dir = join(home_dir, 'Scripts')
987 if is_jython:
988 lib_dir = join(home_dir, 'Lib')
989 inc_dir = join(home_dir, 'Include')
990 bin_dir = join(home_dir, 'bin')
991 elif is_pypy:
992 lib_dir = home_dir
993 inc_dir = join(home_dir, 'include')
994 bin_dir = join(home_dir, 'bin')
995 elif not is_win:
996 lib_dir = join(home_dir, 'lib', py_version)
997 inc_dir = join(home_dir, 'include', py_version + abiflags)
998 bin_dir = join(home_dir, 'bin')
999 return home_dir, lib_dir, inc_dir, bin_dir
1000
1001
1002def change_prefix(filename, dst_prefix):
1003 prefixes = [sys.prefix]
1004
1005 if is_darwin:
1006 prefixes.extend((
1007 os.path.join("/Library/Python", sys.version[:3], "site-packages"),
1008 os.path.join(sys.prefix, "Extras", "lib", "python"),
1009 os.path.join("~", "Library", "Python", sys.version[:3], "site-packages"),
1010 # Python 2.6 no-frameworks
1011 os.path.join("~", ".local", "lib","python", sys.version[:3], "site-packages"),
1012 # System Python 2.7 on OSX Mountain Lion
1013 os.path.join("~", "Library", "Python", sys.version[:3], "lib", "python", "site-packages")))
1014
1015 if hasattr(sys, 'real_prefix'):
1016 prefixes.append(sys.real_prefix)
1017 if hasattr(sys, 'base_prefix'):
1018 prefixes.append(sys.base_prefix)
1019 prefixes = list(map(os.path.expanduser, prefixes))
1020 prefixes = list(map(os.path.abspath, prefixes))
1021 # Check longer prefixes first so we don't split in the middle of a filename
1022 prefixes = sorted(prefixes, key=len, reverse=True)
1023 filename = os.path.abspath(filename)
1024 # On Windows, make sure drive letter is uppercase
1025 if is_win and filename[0] in 'abcdefghijklmnopqrstuvwxyz':
1026 filename = filename[0].upper() + filename[1:]
1027 for i, prefix in enumerate(prefixes):
1028 if is_win and prefix[0] in 'abcdefghijklmnopqrstuvwxyz':
1029 prefixes[i] = prefix[0].upper() + prefix[1:]
1030 for src_prefix in prefixes:
1031 if filename.startswith(src_prefix):
1032 _, relpath = filename.split(src_prefix, 1)
1033 if src_prefix != os.sep: # sys.prefix == "/"
1034 assert relpath[0] == os.sep
1035 relpath = relpath[1:]
1036 return join(dst_prefix, relpath)
1037 assert False, "Filename %s does not start with any of these prefixes: %s" % \
1038 (filename, prefixes)
1039
1040def copy_required_modules(dst_prefix, symlink):
1041 import imp
1042
1043 for modname in REQUIRED_MODULES:
1044 if modname in sys.builtin_module_names:
1045 logger.info("Ignoring built-in bootstrap module: %s" % modname)
1046 continue
1047 try:
1048 f, filename, _ = imp.find_module(modname)
1049 except ImportError:
1050 logger.info("Cannot import bootstrap module: %s" % modname)
1051 else:
1052 if f is not None:
1053 f.close()
1054 # special-case custom readline.so on OS X, but not for pypy:
1055 if modname == 'readline' and sys.platform == 'darwin' and not (
1056 is_pypy or filename.endswith(join('lib-dynload', 'readline.so'))):
1057 dst_filename = join(dst_prefix, 'lib', 'python%s' % sys.version[:3], 'readline.so')
1058 elif modname == 'readline' and sys.platform == 'win32':
1059 # special-case for Windows, where readline is not a
1060 # standard module, though it may have been installed in
1061 # site-packages by a third-party package
1062 pass
1063 else:
1064 dst_filename = change_prefix(filename, dst_prefix)
1065 copyfile(filename, dst_filename, symlink)
1066 if filename.endswith('.pyc'):
1067 pyfile = filename[:-1]
1068 if os.path.exists(pyfile):
1069 copyfile(pyfile, dst_filename[:-1], symlink)
1070
1071def copy_tcltk(src, dest, symlink):
1072 """ copy tcl/tk libraries on Windows (issue #93) """
1073 if majver == 2:
1074 libver = '8.5'
1075 else:
1076 libver = '8.6'
1077 for name in ['tcl', 'tk']:
1078 srcdir = src + '/tcl/' + name + libver
1079 dstdir = dest + '/tcl/' + name + libver
1080 copyfileordir(srcdir, dstdir, symlink)
1081
1082def subst_path(prefix_path, prefix, home_dir):
1083 prefix_path = os.path.normpath(prefix_path)
1084 prefix = os.path.normpath(prefix)
1085 home_dir = os.path.normpath(home_dir)
1086 if not prefix_path.startswith(prefix):
1087 logger.warn('Path not in prefix %r %r', prefix_path, prefix)
1088 return
1089 return prefix_path.replace(prefix, home_dir, 1)
1090
1091
1092def install_python(home_dir, lib_dir, inc_dir, bin_dir, site_packages, clear, symlink=True):
1093 """Install just the base environment, no distutils patches etc"""
1094 if sys.executable.startswith(bin_dir):
1095 print('Please use the *system* python to run this script')
1096 return
1097
1098 if clear:
1099 rmtree(lib_dir)
1100 ## FIXME: why not delete it?
1101 ## Maybe it should delete everything with #!/path/to/venv/python in it
1102 logger.notify('Not deleting %s', bin_dir)
1103
1104 if hasattr(sys, 'real_prefix'):
1105 logger.notify('Using real prefix %r' % sys.real_prefix)
1106 prefix = sys.real_prefix
1107 elif hasattr(sys, 'base_prefix'):
1108 logger.notify('Using base prefix %r' % sys.base_prefix)
1109 prefix = sys.base_prefix
1110 else:
1111 prefix = sys.prefix
1112 mkdir(lib_dir)
1113 fix_lib64(lib_dir, symlink)
1114 stdlib_dirs = [os.path.dirname(os.__file__)]
1115 if is_win:
1116 stdlib_dirs.append(join(os.path.dirname(stdlib_dirs[0]), 'DLLs'))
1117 elif is_darwin:
1118 stdlib_dirs.append(join(stdlib_dirs[0], 'site-packages'))
1119 if hasattr(os, 'symlink'):
1120 logger.info('Symlinking Python bootstrap modules')
1121 else:
1122 logger.info('Copying Python bootstrap modules')
1123 logger.indent += 2
1124 try:
1125 # copy required files...
1126 for stdlib_dir in stdlib_dirs:
1127 if not os.path.isdir(stdlib_dir):
1128 continue
1129 for fn in os.listdir(stdlib_dir):
1130 bn = os.path.splitext(fn)[0]
1131 if fn != 'site-packages' and bn in REQUIRED_FILES:
1132 copyfile(join(stdlib_dir, fn), join(lib_dir, fn), symlink)
1133 # ...and modules
1134 copy_required_modules(home_dir, symlink)
1135 finally:
1136 logger.indent -= 2
1137 # ...copy tcl/tk
1138 if is_win:
1139 copy_tcltk(prefix, home_dir, symlink)
1140 mkdir(join(lib_dir, 'site-packages'))
1141 import site
1142 site_filename = site.__file__
1143 if site_filename.endswith('.pyc') or site_filename.endswith('.pyo'):
1144 site_filename = site_filename[:-1]
1145 elif site_filename.endswith('$py.class'):
1146 site_filename = site_filename.replace('$py.class', '.py')
1147 site_filename_dst = change_prefix(site_filename, home_dir)
1148 site_dir = os.path.dirname(site_filename_dst)
1149 writefile(site_filename_dst, SITE_PY)
1150 writefile(join(site_dir, 'orig-prefix.txt'), prefix)
1151 site_packages_filename = join(site_dir, 'no-global-site-packages.txt')
1152 if not site_packages:
1153 writefile(site_packages_filename, '')
1154
1155 if is_pypy or is_win:
1156 stdinc_dir = join(prefix, 'include')
1157 else:
1158 stdinc_dir = join(prefix, 'include', py_version + abiflags)
1159 if os.path.exists(stdinc_dir):
1160 copyfile(stdinc_dir, inc_dir, symlink)
1161 else:
1162 logger.debug('No include dir %s' % stdinc_dir)
1163
1164 platinc_dir = distutils.sysconfig.get_python_inc(plat_specific=1)
1165 if platinc_dir != stdinc_dir:
1166 platinc_dest = distutils.sysconfig.get_python_inc(
1167 plat_specific=1, prefix=home_dir)
1168 if platinc_dir == platinc_dest:
1169 # Do platinc_dest manually due to a CPython bug;
1170 # not http://bugs.python.org/issue3386 but a close cousin
1171 platinc_dest = subst_path(platinc_dir, prefix, home_dir)
1172 if platinc_dest:
1173 # PyPy's stdinc_dir and prefix are relative to the original binary
1174 # (traversing virtualenvs), whereas the platinc_dir is relative to
1175 # the inner virtualenv and ignores the prefix argument.
1176 # This seems more evolved than designed.
1177 copyfile(platinc_dir, platinc_dest, symlink)
1178
1179 # pypy never uses exec_prefix, just ignore it
1180 if sys.exec_prefix != prefix and not is_pypy:
1181 if is_win:
1182 exec_dir = join(sys.exec_prefix, 'lib')
1183 elif is_jython:
1184 exec_dir = join(sys.exec_prefix, 'Lib')
1185 else:
1186 exec_dir = join(sys.exec_prefix, 'lib', py_version)
1187 for fn in os.listdir(exec_dir):
1188 copyfile(join(exec_dir, fn), join(lib_dir, fn), symlink)
1189
1190 if is_jython:
1191 # Jython has either jython-dev.jar and javalib/ dir, or just
1192 # jython.jar
1193 for name in 'jython-dev.jar', 'javalib', 'jython.jar':
1194 src = join(prefix, name)
1195 if os.path.exists(src):
1196 copyfile(src, join(home_dir, name), symlink)
1197 # XXX: registry should always exist after Jython 2.5rc1
1198 src = join(prefix, 'registry')
1199 if os.path.exists(src):
1200 copyfile(src, join(home_dir, 'registry'), symlink=False)
1201 copyfile(join(prefix, 'cachedir'), join(home_dir, 'cachedir'),
1202 symlink=False)
1203
1204 mkdir(bin_dir)
1205 py_executable = join(bin_dir, os.path.basename(sys.executable))
1206 if 'Python.framework' in prefix:
1207 # OS X framework builds cause validation to break
1208 # https://github.com/pypa/virtualenv/issues/322
1209 if os.environ.get('__PYVENV_LAUNCHER__'):
1210 del os.environ["__PYVENV_LAUNCHER__"]
1211 if re.search(r'/Python(?:-32|-64)*$', py_executable):
1212 # The name of the python executable is not quite what
1213 # we want, rename it.
1214 py_executable = os.path.join(
1215 os.path.dirname(py_executable), 'python')
1216
1217 logger.notify('New %s executable in %s', expected_exe, py_executable)
1218 pcbuild_dir = os.path.dirname(sys.executable)
1219 pyd_pth = os.path.join(lib_dir, 'site-packages', 'virtualenv_builddir_pyd.pth')
1220 if is_win and os.path.exists(os.path.join(pcbuild_dir, 'build.bat')):
1221 logger.notify('Detected python running from build directory %s', pcbuild_dir)
1222 logger.notify('Writing .pth file linking to build directory for *.pyd files')
1223 writefile(pyd_pth, pcbuild_dir)
1224 else:
1225 pcbuild_dir = None
1226 if os.path.exists(pyd_pth):
1227 logger.info('Deleting %s (not Windows env or not build directory python)' % pyd_pth)
1228 os.unlink(pyd_pth)
1229
1230 if sys.executable != py_executable:
1231 ## FIXME: could I just hard link?
1232 executable = sys.executable
1233 shutil.copyfile(executable, py_executable)
1234 make_exe(py_executable)
1235 if is_win or is_cygwin:
1236 pythonw = os.path.join(os.path.dirname(sys.executable), 'pythonw.exe')
1237 if os.path.exists(pythonw):
1238 logger.info('Also created pythonw.exe')
1239 shutil.copyfile(pythonw, os.path.join(os.path.dirname(py_executable), 'pythonw.exe'))
1240 python_d = os.path.join(os.path.dirname(sys.executable), 'python_d.exe')
1241 python_d_dest = os.path.join(os.path.dirname(py_executable), 'python_d.exe')
1242 if os.path.exists(python_d):
1243 logger.info('Also created python_d.exe')
1244 shutil.copyfile(python_d, python_d_dest)
1245 elif os.path.exists(python_d_dest):
1246 logger.info('Removed python_d.exe as it is no longer at the source')
1247 os.unlink(python_d_dest)
1248 # we need to copy the DLL to enforce that windows will load the correct one.
1249 # may not exist if we are cygwin.
1250 py_executable_dll = 'python%s%s.dll' % (
1251 sys.version_info[0], sys.version_info[1])
1252 py_executable_dll_d = 'python%s%s_d.dll' % (
1253 sys.version_info[0], sys.version_info[1])
1254 pythondll = os.path.join(os.path.dirname(sys.executable), py_executable_dll)
1255 pythondll_d = os.path.join(os.path.dirname(sys.executable), py_executable_dll_d)
1256 pythondll_d_dest = os.path.join(os.path.dirname(py_executable), py_executable_dll_d)
1257 if os.path.exists(pythondll):
1258 logger.info('Also created %s' % py_executable_dll)
1259 shutil.copyfile(pythondll, os.path.join(os.path.dirname(py_executable), py_executable_dll))
1260 if os.path.exists(pythondll_d):
1261 logger.info('Also created %s' % py_executable_dll_d)
1262 shutil.copyfile(pythondll_d, pythondll_d_dest)
1263 elif os.path.exists(pythondll_d_dest):
1264 logger.info('Removed %s as the source does not exist' % pythondll_d_dest)
1265 os.unlink(pythondll_d_dest)
1266 if is_pypy:
1267 # make a symlink python --> pypy-c
1268 python_executable = os.path.join(os.path.dirname(py_executable), 'python')
1269 if sys.platform in ('win32', 'cygwin'):
1270 python_executable += '.exe'
1271 logger.info('Also created executable %s' % python_executable)
1272 copyfile(py_executable, python_executable, symlink)
1273
1274 if is_win:
1275 for name in ['libexpat.dll', 'libpypy.dll', 'libpypy-c.dll',
1276 'libeay32.dll', 'ssleay32.dll', 'sqlite3.dll',
1277 'tcl85.dll', 'tk85.dll']:
1278 src = join(prefix, name)
1279 if os.path.exists(src):
1280 copyfile(src, join(bin_dir, name), symlink)
1281
1282 for d in sys.path:
1283 if d.endswith('lib_pypy'):
1284 break
1285 else:
1286 logger.fatal('Could not find lib_pypy in sys.path')
1287 raise SystemExit(3)
1288 logger.info('Copying lib_pypy')
1289 copyfile(d, os.path.join(home_dir, 'lib_pypy'), symlink)
1290
1291 if os.path.splitext(os.path.basename(py_executable))[0] != expected_exe:
1292 secondary_exe = os.path.join(os.path.dirname(py_executable),
1293 expected_exe)
1294 py_executable_ext = os.path.splitext(py_executable)[1]
1295 if py_executable_ext.lower() == '.exe':
1296 # python2.4 gives an extension of '.4' :P
1297 secondary_exe += py_executable_ext
1298 if os.path.exists(secondary_exe):
1299 logger.warn('Not overwriting existing %s script %s (you must use %s)'
1300 % (expected_exe, secondary_exe, py_executable))
1301 else:
1302 logger.notify('Also creating executable in %s' % secondary_exe)
1303 shutil.copyfile(sys.executable, secondary_exe)
1304 make_exe(secondary_exe)
1305
1306 if '.framework' in prefix:
1307 if 'Python.framework' in prefix:
1308 logger.debug('MacOSX Python framework detected')
1309 # Make sure we use the embedded interpreter inside
1310 # the framework, even if sys.executable points to
1311 # the stub executable in ${sys.prefix}/bin
1312 # See http://groups.google.com/group/python-virtualenv/
1313 # browse_thread/thread/17cab2f85da75951
1314 original_python = os.path.join(
1315 prefix, 'Resources/Python.app/Contents/MacOS/Python')
1316 if 'EPD' in prefix:
1317 logger.debug('EPD framework detected')
1318 original_python = os.path.join(prefix, 'bin/python')
1319 shutil.copy(original_python, py_executable)
1320
1321 # Copy the framework's dylib into the virtual
1322 # environment
1323 virtual_lib = os.path.join(home_dir, '.Python')
1324
1325 if os.path.exists(virtual_lib):
1326 os.unlink(virtual_lib)
1327 copyfile(
1328 os.path.join(prefix, 'Python'),
1329 virtual_lib,
1330 symlink)
1331
1332 # And then change the install_name of the copied python executable
1333 try:
1334 mach_o_change(py_executable,
1335 os.path.join(prefix, 'Python'),
1336 '@executable_path/../.Python')
1337 except:
1338 e = sys.exc_info()[1]
1339 logger.warn("Could not call mach_o_change: %s. "
1340 "Trying to call install_name_tool instead." % e)
1341 try:
1342 call_subprocess(
1343 ["install_name_tool", "-change",
1344 os.path.join(prefix, 'Python'),
1345 '@executable_path/../.Python',
1346 py_executable])
1347 except:
1348 logger.fatal("Could not call install_name_tool -- you must "
1349 "have Apple's development tools installed")
1350 raise
1351
1352 if not is_win:
1353 # Ensure that 'python', 'pythonX' and 'pythonX.Y' all exist
1354 py_exe_version_major = 'python%s' % sys.version_info[0]
1355 py_exe_version_major_minor = 'python%s.%s' % (
1356 sys.version_info[0], sys.version_info[1])
1357 py_exe_no_version = 'python'
1358 required_symlinks = [ py_exe_no_version, py_exe_version_major,
1359 py_exe_version_major_minor ]
1360
1361 py_executable_base = os.path.basename(py_executable)
1362
1363 if py_executable_base in required_symlinks:
1364 # Don't try to symlink to yourself.
1365 required_symlinks.remove(py_executable_base)
1366
1367 for pth in required_symlinks:
1368 full_pth = join(bin_dir, pth)
1369 if os.path.exists(full_pth):
1370 os.unlink(full_pth)
1371 if symlink:
1372 os.symlink(py_executable_base, full_pth)
1373 else:
1374 copyfile(py_executable, full_pth, symlink)
1375
1376 if is_win and ' ' in py_executable:
1377 # There's a bug with subprocess on Windows when using a first
1378 # argument that has a space in it. Instead we have to quote
1379 # the value:
1380 py_executable = '"%s"' % py_executable
1381 # NOTE: keep this check as one line, cmd.exe doesn't cope with line breaks
1382 cmd = [py_executable, '-c', 'import sys;out=sys.stdout;'
1383 'getattr(out, "buffer", out).write(sys.prefix.encode("utf-8"))']
1384 logger.info('Testing executable with %s %s "%s"' % tuple(cmd))
1385 try:
1386 proc = subprocess.Popen(cmd,
1387 stdout=subprocess.PIPE)
1388 proc_stdout, proc_stderr = proc.communicate()
1389 except OSError:
1390 e = sys.exc_info()[1]
1391 if e.errno == errno.EACCES:
1392 logger.fatal('ERROR: The executable %s could not be run: %s' % (py_executable, e))
1393 sys.exit(100)
1394 else:
1395 raise e
1396
1397 proc_stdout = proc_stdout.strip().decode("utf-8")
1398 proc_stdout = os.path.normcase(os.path.abspath(proc_stdout))
1399 norm_home_dir = os.path.normcase(os.path.abspath(home_dir))
1400 if hasattr(norm_home_dir, 'decode'):
1401 norm_home_dir = norm_home_dir.decode(sys.getfilesystemencoding())
1402 if proc_stdout != norm_home_dir:
1403 logger.fatal(
1404 'ERROR: The executable %s is not functioning' % py_executable)
1405 logger.fatal(
1406 'ERROR: It thinks sys.prefix is %r (should be %r)'
1407 % (proc_stdout, norm_home_dir))
1408 logger.fatal(
1409 'ERROR: virtualenv is not compatible with this system or executable')
1410 if is_win:
1411 logger.fatal(
1412 'Note: some Windows users have reported this error when they '
1413 'installed Python for "Only this user" or have multiple '
1414 'versions of Python installed. Copying the appropriate '
1415 'PythonXX.dll to the virtualenv Scripts/ directory may fix '
1416 'this problem.')
1417 sys.exit(100)
1418 else:
1419 logger.info('Got sys.prefix result: %r' % proc_stdout)
1420
1421 pydistutils = os.path.expanduser('~/.pydistutils.cfg')
1422 if os.path.exists(pydistutils):
1423 logger.notify('Please make sure you remove any previous custom paths from '
1424 'your %s file.' % pydistutils)
1425 ## FIXME: really this should be calculated earlier
1426
1427 fix_local_scheme(home_dir, symlink)
1428
1429 if site_packages:
1430 if os.path.exists(site_packages_filename):
1431 logger.info('Deleting %s' % site_packages_filename)
1432 os.unlink(site_packages_filename)
1433
1434 return py_executable
1435
1436
1437def install_activate(home_dir, bin_dir, prompt=None):
1438 if is_win or is_jython and os._name == 'nt':
1439 files = {
1440 'activate.bat': ACTIVATE_BAT,
1441 'deactivate.bat': DEACTIVATE_BAT,
1442 'activate.ps1': ACTIVATE_PS,
1443 }
1444
1445 # MSYS needs paths of the form /c/path/to/file
1446 drive, tail = os.path.splitdrive(home_dir.replace(os.sep, '/'))
1447 home_dir_msys = (drive and "/%s%s" or "%s%s") % (drive[:1], tail)
1448
1449 # Run-time conditional enables (basic) Cygwin compatibility
1450 home_dir_sh = ("""$(if [ "$OSTYPE" "==" "cygwin" ]; then cygpath -u '%s'; else echo '%s'; fi;)""" %
1451 (home_dir, home_dir_msys))
1452 files['activate'] = ACTIVATE_SH.replace('__VIRTUAL_ENV__', home_dir_sh)
1453
1454 else:
1455 files = {'activate': ACTIVATE_SH}
1456
1457 # suppling activate.fish in addition to, not instead of, the
1458 # bash script support.
1459 files['activate.fish'] = ACTIVATE_FISH
1460
1461 # same for csh/tcsh support...
1462 files['activate.csh'] = ACTIVATE_CSH
1463
1464 files['activate_this.py'] = ACTIVATE_THIS
1465
1466 install_files(home_dir, bin_dir, prompt, files)
1467
1468def install_files(home_dir, bin_dir, prompt, files):
1469 if hasattr(home_dir, 'decode'):
1470 home_dir = home_dir.decode(sys.getfilesystemencoding())
1471 vname = os.path.basename(home_dir)
1472 for name, content in files.items():
1473 content = content.replace('__VIRTUAL_PROMPT__', prompt or '')
1474 content = content.replace('__VIRTUAL_WINPROMPT__', prompt or '(%s)' % vname)
1475 content = content.replace('__VIRTUAL_ENV__', home_dir)
1476 content = content.replace('__VIRTUAL_NAME__', vname)
1477 content = content.replace('__BIN_NAME__', os.path.basename(bin_dir))
1478 writefile(os.path.join(bin_dir, name), content)
1479
1480def install_python_config(home_dir, bin_dir, prompt=None):
1481 if sys.platform == 'win32' or is_jython and os._name == 'nt':
1482 files = {}
1483 else:
1484 files = {'python-config': PYTHON_CONFIG}
1485 install_files(home_dir, bin_dir, prompt, files)
1486 for name, content in files.items():
1487 make_exe(os.path.join(bin_dir, name))
1488
1489def install_distutils(home_dir):
1490 distutils_path = change_prefix(distutils.__path__[0], home_dir)
1491 mkdir(distutils_path)
1492 ## FIXME: maybe this prefix setting should only be put in place if
1493 ## there's a local distutils.cfg with a prefix setting?
1494 home_dir = os.path.abspath(home_dir)
1495 ## FIXME: this is breaking things, removing for now:
1496 #distutils_cfg = DISTUTILS_CFG + "\n[install]\nprefix=%s\n" % home_dir
1497 writefile(os.path.join(distutils_path, '__init__.py'), DISTUTILS_INIT)
1498 writefile(os.path.join(distutils_path, 'distutils.cfg'), DISTUTILS_CFG, overwrite=False)
1499
1500def fix_local_scheme(home_dir, symlink=True):
1501 """
1502 Platforms that use the "posix_local" install scheme (like Ubuntu with
1503 Python 2.7) need to be given an additional "local" location, sigh.
1504 """
1505 try:
1506 import sysconfig
1507 except ImportError:
1508 pass
1509 else:
1510 if sysconfig._get_default_scheme() == 'posix_local':
1511 local_path = os.path.join(home_dir, 'local')
1512 if not os.path.exists(local_path):
1513 os.mkdir(local_path)
1514 for subdir_name in os.listdir(home_dir):
1515 if subdir_name == 'local':
1516 continue
1517 copyfile(os.path.abspath(os.path.join(home_dir, subdir_name)), \
1518 os.path.join(local_path, subdir_name), symlink)
1519
1520def fix_lib64(lib_dir, symlink=True):
1521 """
1522 Some platforms (particularly Gentoo on x64) put things in lib64/pythonX.Y
1523 instead of lib/pythonX.Y. If this is such a platform we'll just create a
1524 symlink so lib64 points to lib
1525 """
1526 # PyPy's library path scheme is not affected by this.
1527 # Return early or we will die on the following assert.
1528 if is_pypy:
1529 logger.debug('PyPy detected, skipping lib64 symlinking')
1530 return
1531 # Check we have a lib64 library path
1532 if not [p for p in distutils.sysconfig.get_config_vars().values()
1533 if isinstance(p, basestring) and 'lib64' in p]:
1534 return
1535
1536 logger.debug('This system uses lib64; symlinking lib64 to lib')
1537
1538 assert os.path.basename(lib_dir) == 'python%s' % sys.version[:3], (
1539 "Unexpected python lib dir: %r" % lib_dir)
1540 lib_parent = os.path.dirname(lib_dir)
1541 top_level = os.path.dirname(lib_parent)
1542 lib_dir = os.path.join(top_level, 'lib')
1543 lib64_link = os.path.join(top_level, 'lib64')
1544 assert os.path.basename(lib_parent) == 'lib', (
1545 "Unexpected parent dir: %r" % lib_parent)
1546 if os.path.lexists(lib64_link):
1547 return
1548 if symlink:
1549 os.symlink('lib', lib64_link)
1550 else:
1551 copyfile('lib', lib64_link)
1552
1553def resolve_interpreter(exe):
1554 """
1555 If the executable given isn't an absolute path, search $PATH for the interpreter
1556 """
1557 # If the "executable" is a version number, get the installed executable for
1558 # that version
1559 python_versions = get_installed_pythons()
1560 if exe in python_versions:
1561 exe = python_versions[exe]
1562
1563 if os.path.abspath(exe) != exe:
1564 paths = os.environ.get('PATH', '').split(os.pathsep)
1565 for path in paths:
1566 if os.path.exists(join(path, exe)):
1567 exe = join(path, exe)
1568 break
1569 if not os.path.exists(exe):
1570 logger.fatal('The executable %s (from --python=%s) does not exist' % (exe, exe))
1571 raise SystemExit(3)
1572 if not is_executable(exe):
1573 logger.fatal('The executable %s (from --python=%s) is not executable' % (exe, exe))
1574 raise SystemExit(3)
1575 return exe
1576
1577def is_executable(exe):
1578 """Checks a file is executable"""
1579 return os.access(exe, os.X_OK)
1580
1581############################################################
1582## Relocating the environment:
1583
1584def make_environment_relocatable(home_dir):
1585 """
1586 Makes the already-existing environment use relative paths, and takes out
1587 the #!-based environment selection in scripts.
1588 """
1589 home_dir, lib_dir, inc_dir, bin_dir = path_locations(home_dir)
1590 activate_this = os.path.join(bin_dir, 'activate_this.py')
1591 if not os.path.exists(activate_this):
1592 logger.fatal(
1593 'The environment doesn\'t have a file %s -- please re-run virtualenv '
1594 'on this environment to update it' % activate_this)
1595 fixup_scripts(home_dir, bin_dir)
1596 fixup_pth_and_egg_link(home_dir)
1597 ## FIXME: need to fix up distutils.cfg
1598
1599OK_ABS_SCRIPTS = ['python', 'python%s' % sys.version[:3],
1600 'activate', 'activate.bat', 'activate_this.py',
1601 'activate.fish', 'activate.csh']
1602
1603def fixup_scripts(home_dir, bin_dir):
1604 if is_win:
1605 new_shebang_args = (
1606 '%s /c' % os.path.normcase(os.environ.get('COMSPEC', 'cmd.exe')),
1607 '', '.exe')
1608 else:
1609 new_shebang_args = ('/usr/bin/env', sys.version[:3], '')
1610
1611 # This is what we expect at the top of scripts:
1612 shebang = '#!%s' % os.path.normcase(os.path.join(
1613 os.path.abspath(bin_dir), 'python%s' % new_shebang_args[2]))
1614 # This is what we'll put:
1615 new_shebang = '#!%s python%s%s' % new_shebang_args
1616
1617 for filename in os.listdir(bin_dir):
1618 filename = os.path.join(bin_dir, filename)
1619 if not os.path.isfile(filename):
1620 # ignore subdirs, e.g. .svn ones.
1621 continue
1622 lines = None
1623 with open(filename, 'rb') as f:
1624 try:
1625 lines = f.read().decode('utf-8').splitlines()
1626 except UnicodeDecodeError:
1627 # This is probably a binary program instead
1628 # of a script, so just ignore it.
1629 continue
1630 if not lines:
1631 logger.warn('Script %s is an empty file' % filename)
1632 continue
1633
1634 old_shebang = lines[0].strip()
1635 old_shebang = old_shebang[0:2] + os.path.normcase(old_shebang[2:])
1636
1637 if not old_shebang.startswith(shebang):
1638 if os.path.basename(filename) in OK_ABS_SCRIPTS:
1639 logger.debug('Cannot make script %s relative' % filename)
1640 elif lines[0].strip() == new_shebang:
1641 logger.info('Script %s has already been made relative' % filename)
1642 else:
1643 logger.warn('Script %s cannot be made relative (it\'s not a normal script that starts with %s)'
1644 % (filename, shebang))
1645 continue
1646 logger.notify('Making script %s relative' % filename)
1647 script = relative_script([new_shebang] + lines[1:])
1648 with open(filename, 'wb') as f:
1649 f.write('\n'.join(script).encode('utf-8'))
1650
1651
1652def relative_script(lines):
1653 "Return a script that'll work in a relocatable environment."
1654 activate = "import os; activate_this=os.path.join(os.path.dirname(os.path.realpath(__file__)), 'activate_this.py'); exec(compile(open(activate_this).read(), activate_this, 'exec'), dict(__file__=activate_this)); del os, activate_this"
1655 # Find the last future statement in the script. If we insert the activation
1656 # line before a future statement, Python will raise a SyntaxError.
1657 activate_at = None
1658 for idx, line in reversed(list(enumerate(lines))):
1659 if line.split()[:3] == ['from', '__future__', 'import']:
1660 activate_at = idx + 1
1661 break
1662 if activate_at is None:
1663 # Activate after the shebang.
1664 activate_at = 1
1665 return lines[:activate_at] + ['', activate, ''] + lines[activate_at:]
1666
1667def fixup_pth_and_egg_link(home_dir, sys_path=None):
1668 """Makes .pth and .egg-link files use relative paths"""
1669 home_dir = os.path.normcase(os.path.abspath(home_dir))
1670 if sys_path is None:
1671 sys_path = sys.path
1672 for path in sys_path:
1673 if not path:
1674 path = '.'
1675 if not os.path.isdir(path):
1676 continue
1677 path = os.path.normcase(os.path.abspath(path))
1678 if not path.startswith(home_dir):
1679 logger.debug('Skipping system (non-environment) directory %s' % path)
1680 continue
1681 for filename in os.listdir(path):
1682 filename = os.path.join(path, filename)
1683 if filename.endswith('.pth'):
1684 if not os.access(filename, os.W_OK):
1685 logger.warn('Cannot write .pth file %s, skipping' % filename)
1686 else:
1687 fixup_pth_file(filename)
1688 if filename.endswith('.egg-link'):
1689 if not os.access(filename, os.W_OK):
1690 logger.warn('Cannot write .egg-link file %s, skipping' % filename)
1691 else:
1692 fixup_egg_link(filename)
1693
1694def fixup_pth_file(filename):
1695 lines = []
1696 prev_lines = []
1697 with open(filename) as f:
1698 prev_lines = f.readlines()
1699 for line in prev_lines:
1700 line = line.strip()
1701 if (not line or line.startswith('#') or line.startswith('import ')
1702 or os.path.abspath(line) != line):
1703 lines.append(line)
1704 else:
1705 new_value = make_relative_path(filename, line)
1706 if line != new_value:
1707 logger.debug('Rewriting path %s as %s (in %s)' % (line, new_value, filename))
1708 lines.append(new_value)
1709 if lines == prev_lines:
1710 logger.info('No changes to .pth file %s' % filename)
1711 return
1712 logger.notify('Making paths in .pth file %s relative' % filename)
1713 with open(filename, 'w') as f:
1714 f.write('\n'.join(lines) + '\n')
1715
1716def fixup_egg_link(filename):
1717 with open(filename) as f:
1718 link = f.readline().strip()
1719 if os.path.abspath(link) != link:
1720 logger.debug('Link in %s already relative' % filename)
1721 return
1722 new_link = make_relative_path(filename, link)
1723 logger.notify('Rewriting link %s in %s as %s' % (link, filename, new_link))
1724 with open(filename, 'w') as f:
1725 f.write(new_link)
1726
1727def make_relative_path(source, dest, dest_is_directory=True):
1728 """
1729 Make a filename relative, where the filename is dest, and it is
1730 being referred to from the filename source.
1731
1732 >>> make_relative_path('/usr/share/something/a-file.pth',
1733 ... '/usr/share/another-place/src/Directory')
1734 '../another-place/src/Directory'
1735 >>> make_relative_path('/usr/share/something/a-file.pth',
1736 ... '/home/user/src/Directory')
1737 '../../../home/user/src/Directory'
1738 >>> make_relative_path('/usr/share/a-file.pth', '/usr/share/')
1739 './'
1740 """
1741 source = os.path.dirname(source)
1742 if not dest_is_directory:
1743 dest_filename = os.path.basename(dest)
1744 dest = os.path.dirname(dest)
1745 dest = os.path.normpath(os.path.abspath(dest))
1746 source = os.path.normpath(os.path.abspath(source))
1747 dest_parts = dest.strip(os.path.sep).split(os.path.sep)
1748 source_parts = source.strip(os.path.sep).split(os.path.sep)
1749 while dest_parts and source_parts and dest_parts[0] == source_parts[0]:
1750 dest_parts.pop(0)
1751 source_parts.pop(0)
1752 full_parts = ['..']*len(source_parts) + dest_parts
1753 if not dest_is_directory:
1754 full_parts.append(dest_filename)
1755 if not full_parts:
1756 # Special case for the current directory (otherwise it'd be '')
1757 return './'
1758 return os.path.sep.join(full_parts)
1759
1760
1761
1762############################################################
1763## Bootstrap script creation:
1764
1765def create_bootstrap_script(extra_text, python_version=''):
1766 """
1767 Creates a bootstrap script, which is like this script but with
1768 extend_parser, adjust_options, and after_install hooks.
1769
1770 This returns a string that (written to disk of course) can be used
1771 as a bootstrap script with your own customizations. The script
1772 will be the standard virtualenv.py script, with your extra text
1773 added (your extra text should be Python code).
1774
1775 If you include these functions, they will be called:
1776
1777 ``extend_parser(optparse_parser)``:
1778 You can add or remove options from the parser here.
1779
1780 ``adjust_options(options, args)``:
1781 You can change options here, or change the args (if you accept
1782 different kinds of arguments, be sure you modify ``args`` so it is
1783 only ``[DEST_DIR]``).
1784
1785 ``after_install(options, home_dir)``:
1786
1787 After everything is installed, this function is called. This
1788 is probably the function you are most likely to use. An
1789 example would be::
1790
1791 def after_install(options, home_dir):
1792 subprocess.call([join(home_dir, 'bin', 'easy_install'),
1793 'MyPackage'])
1794 subprocess.call([join(home_dir, 'bin', 'my-package-script'),
1795 'setup', home_dir])
1796
1797 This example immediately installs a package, and runs a setup
1798 script from that package.
1799
1800 If you provide something like ``python_version='2.5'`` then the
1801 script will start with ``#!/usr/bin/env python2.5`` instead of
1802 ``#!/usr/bin/env python``. You can use this when the script must
1803 be run with a particular Python version.
1804 """
1805 filename = __file__
1806 if filename.endswith('.pyc'):
1807 filename = filename[:-1]
1808 with codecs.open(filename, 'r', encoding='utf-8') as f:
1809 content = f.read()
1810 py_exe = 'python%s' % python_version
1811 content = (('#!/usr/bin/env %s\n' % py_exe)
1812 + '## WARNING: This file is generated\n'
1813 + content)
1814 return content.replace('##EXT' 'END##', extra_text)
1815
1816##EXTEND##
1817
1818def convert(s):
1819 b = base64.b64decode(s.encode('ascii'))
1820 return zlib.decompress(b).decode('utf-8')
1821
1822##file site.py
1823SITE_PY = convert("""
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
1899SvUGyakponQdqjnC/dKG61lUrvOjqVRpjs5qrbdeulbM1JTRuXYE0geNXVIwCE4xg1eUxV6ZXWHJ
1900J4C6zqoHKW2jbWJISkHBTrqAc/5lTle8QCl1hidNZ63oL0MX1/AqUkWawE7udWhlSXfD9JiGcfRD
1901e8DNePVpQKc7jKwb8qwHsUCr9Trkuen+k4bRfq0Bw4bB3sG8M0npIZSBjcltIsRGfJITynv4apde
1902r4GCBcODvgoX0TBdArOPYXMt1glsIIAn12B9cZ8AEFor4R8IHDnRAZljdkb4drPc/3OoCeK3/vnn
1903nuZVme7/TRSwCxKcShT2ENNt/A42PpGMxOnH95OQkaPUXPHnGssDwCGhAKgj7ZS/xCfos7GS6Urn
1904l/j6AF9oP4Fet7qXsih1937XOEQJeKbG5DU8U4Z+IaZ7WdhTnMqkBRorHyxmWEHopiGYz574tJZp
1905qvPdz96dn4LviMUYKEF87nYKw3G8BI/QdfIdVzi2QOEBO7wukY1LdGEpyWIZec16g9YoctTby8uw
190660SB4W6vThS4jBPloj3GaTMsU04QISvDWphlZdZutUEKu22I4igzzBKzi5ISWH2eAF6mpzFviWCv
1907hKUeJgLPp8hJVpmMxTRZgB4FlQsKdQpCgsTFekbivDzjGHheKlMGBQ+LbZlcrys83YDOEZVgYPMf
1908T76cn32gsoTDV43X3cOcU9oJTDmJ5BhTBDHaAV/ctD/kqtmsj2f1K4SB2gf+tF9xdsoxD9Dpx4FF
1909/NN+xXVox85OkGcACqou2uKBGwCnW5/cNLLAuNp9MH7cFMAGMx8MxSKx7EUnerjz63KibdkyJRT3
1910MS+fcICzKmxKmu7spqS1P3qOqwLPuZbj/kbwtk+2zGcOXW86b4aS39xPRwqxJBYw6rb2xzDZYZ2m
1911ejoOsw1xC21rtY39OXNipU67RYaiDEQcu50nLpP1K2HdnDnQS6PuABPfanSNJPaq8tHP2Uh7GB4m
1912ltidfYrpSGUsZAQwkiF17U8NPhRaBFAglP07diR3Onl+6M3RsQYPz1HrLrCNP4Ai1Lm4VOORl8CJ
19138OVXdhz5FaGFevRIhI6nkskst3li+Llbo1f50p9jrwxQEBPFroyzazlmWFMD8yuf2AMhWNK2Hqkv
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
1964AVijEPwfucjncQ==
1965""")
1966
1967##file activate.sh
1968ACTIVATE_SH = convert("""
1969eJytVd9v2kAMfs9fYQLq2m4MscdNVKMqEkgtVIQxbeuUHolpTgsXdHehpT/+9/mSEBJS2MOaB0ji
1970z77P9menDpOAK5jzEGERKw0zhFihD/dcB2CrKJYewoyLFvM0XzGNNpzOZbSAGVPBqVWHdRSDx4SI
1971NMhYANfgc4meDteW5ePGC45P4MkCumKhUENzDsu1H3lw1vJx1RJxGMKns6O2lWDqINGgotAHFCsu
1972I7FAoWHFJGezEFWGqsEvaD5C42naHb93X+A3+elYCgVaxgh8DmQAys9HL2SS0mIaWBgm7mTN/O3G
1973kzu6vHCng/HkW/fSve5O+hTOpnhfQAcoEry5jKVjNypoO0fgwzKSOgHm79KUK06Jfc7/RebHpD8a
19749kdXvT2UcnuFWG6p0stNB0mWUUQ1q3uiGRVEMfXHR03dTuQATPjwqIIPcB9wL4CArRAY/ZHJixYL
1975Y9YBtcAoLQtFevOoI9QaHcEdMSAB0d08kuZhyUiSmav6CPCdVBnFOjNrLu6yMCWgKRA0TInBC5i4
1976QwX3JG/mm581GKnSsSSxJTFHf9MAKr8w5T/vOv1mUurn5/zlT6fvTntjZzAaNl9rQ5JkU5KIc0GX
1977inagwU57T2eddqWlTrvaS6d9sImZeUMkhWysveF0m37NcGub9Dpgi0j4qGiOzATjDr06OBjOYQOo
19787RBoGtNm9Denv1i0LVI7lxJDXLHSSBeWRflsyyqw7diuW3h0XdvK6lBMyaoMG1UyHdTsoYBuue75
1979YOgOu1c91/2cwYpznPPeDoQpGL2xSm09NKp7BsvQ2hnT3aMs07lUnskpxewvBk73/LLnXo9HV9eT
1980ijB3hWBO2ygoiWg/bKuZxqCCQq0DD3vkWIVvI2KosIw+vqW1gIItEG5KJb+xb09g65ktwYKgTc51
1981uGJ/EFQs0ayEWLCQM5V9N4g+1+8UbXOJzF8bqhKtIqIwicWvzNFROZJlpfD8A7Vc044R0FxkcezG
1982VzsV75usvTdYef+57v5n1b225qhXfwEmxHEs
1983""")
1984
1985##file activate.fish
1986ACTIVATE_FISH = convert("""
1987eJyFVVFv2zYQftevuMoOnBS1gr0WGIZ08RADSRw4boBhGGhGOsUcKFIjKbUu9uN7lC2JsrXWDzZM
1988fnf38e6+uwlsdsJCLiRCUVkHrwiVxYy+hHqDbQKvQl3z1ImaO0xyYXdbeP9FuJ1QwMFUSnmcP4dL
19892DlXfry+9v/sDqVMUl3AFVi0Vmj1PokmcKtBaecNQTjIhMHUyX0SRXmlKIpWkGEbDuYZzBZfCVcL
19904youUdVQ6AyBqwwMusoocBrcDsmpKbgEQgijVYHKJbMI6DMhoEUHWmbhLdTcCP4q0TYokYNDev5c
1991QTxlq/tb9rJcbz7f3LOnm81d3GD8x3uav30FfwrnwCEOYRyAKot+FvXPzd3q8W71sBiJ3d2dMugu
1992fsxjCPsBmz+Wz3fsab16eNqw1ctivV7eBnwm8EzeuQIsSrcHqVMqwHbqq8/aarKSO+oYKhKXUn9p
1993SmWw0DVBdQ7bBlwaTR62bc+1tpaYb5PhUyScu48CRgvDLQbtMrMnMQ6dY5022JDRRrwJxWUfJwwP
1994ge0YIAVGfcUC1M8s8MxitFZjmR9W64hui7p4fBlWMZ5y81b/9cvfMbz7FWZKq4yOTeW1hbNBEWU+
1995b+/ejXMu95lOx696uXb8Go4T+Kw8R2EMSqx5KLkkCkQ+ZBZFbZsHL4OYseAvY3EPO5MYTBuhDZQa
1996TwPza8Y+LR/Z483Dgjwd4R3f7bTXx9Znkw6T6PAL83/hRD3jNAKFjuEx9NJkq5t+fabLvdvRwbw4
1997nEFTzwO6U+q34cvY7fL55tP94tg58XEA/q7LfdPsaUXFoEIMJdHF5iSW0+48CnDQ82G7n3XzAD6q
1998Bmo5XuOA0NQ67ir7AXJtQhtLKO7XhC0l39PGOBsHPvzBuHUSjoOnA0ldozGC9gZ5rek3+y3ALHO/
1999kT7AP379lQZLSnFDLtwWihfYxw4nZd+ZR7myfkI2ZTRCuRxmF/bCzkbhcElvYamW9PbDGrvqPKC0
2000+D/uLi/sFcxGjOHylYagZzzsjjhw206RQwrWIwOxS2dnk+40xOjX8bTPegz/gdWVSXuaowNuOLda
2001wYyNuRPSTcd/B48Ppeg=
2002""")
2003
2004##file activate.csh
2005ACTIVATE_CSH = convert("""
2006eJx1U2FP2zAQ/e5f8TAV3Soo+0zXbYUiDQkKQgVp2ibjJNfFUuIg22nVf885SVFLO3+I7Lt3fr6X
2007d8eY58ZjYQpCWfuAhFB7yrAyIYf0Ve1SQmLsuU6DWepAw9TnEoOFq0rwdjAUx/hV1Ui1tVWAqy1M
2008QGYcpaFYx+yVI67LkKwx1UuTEaYGl4X2Bl+zJpAlP/6V2hTDtCq/DYXQhdEeGW040Q/Eb+t9V/e3
2009U/V88zh/mtyqh8n8J47G+IKTE3gKZJdoYrK3h5MRU1tGYS83gqNc+3yEgyyP93cP820evHLvr2H8
2010kaYB/peoyY7aVHzpJnE9e+6I5Z+ji4GMTNJWNuOQq6MA1N25p8pW9HWdVWlfsNpPDbdxjgpaahuw
20111M7opCA/FFu1uwxC7L8KUqmto1KyQe3rx0I0Eovdf7BVe67U5c1MzSZ310pddGheZoFPWyytRkzU
2012aCA/I+RkBXhFXr5aWV0SxjhUI6jwdAj8kmhPzX7nTfJFkM3MImp2VdVFFq1vLHSU5szYQK4Ri+Jd
2013xlW2JBtOGcyYVW7SnB3v6RS91g3gKapZ0oWxbHVteYIIq3iv7QeuSrUj6KSqQ+yqsxDj1ivNQxKF
2014YON10Q+NH/ARS95i5Tuqq2Vxfvc23f/FO6zrtXXmJr+ZtMY9/A15ZXFWtmch2rEQ4g1ryVHH
2015""")
2016
2017##file activate.bat
2018ACTIVATE_BAT = convert("""
2019eJx9Ul9LhEAQfxf8DoOclI/dYyFkaCmcq4gZQTBUrincuZFbff12T133TM+nnd35/Zvxlr7XDFhV
2020mUZHOVhFlOWP3g4DUriIWoVomYZpNBWUtGpaWgImO191pFkSpzlcmgaI70jVX7n2Qp8tuByg+46O
2021CMHbMq64T+nmlJt082D1T44muCDk2prgEHF4mdI9RaS/QwSt3zSyIAaftRccvqVTBziD1x/WlPD5
2022xd729NDBb8Nr4DU9QNMKsJeH9pkhPedhQsIkDuCDCa6A+NF9IevVFAohkqizdHetg/tkWvPoftWJ
2023MCqnOxv7/x7Np6yv9P2Ker5dmX8yNyCkkWnbZy3N5LarczlqL8htx2EM9rQ/2H5BvIsIEi8OEG8U
2024+g8CsNTr
2025""")
2026
2027##file deactivate.bat
2028DEACTIVATE_BAT = convert("""
2029eJyFkN0KgkAUhO8F32EQpHqFQEjQUPAPMaErqVxzId3IrV6/XST/UDx3c86c4WMO5FYysKJQFVVp
2030CEfqxsnJ9DI7SA25i20fFqs3HO+GYLsDZ7h8GM3xfLHrg1QNvpSX4CWpQGvokZk4uqrQAjXjyElB
2031a5IjCz0r+2dHcehHCe5MZNmB5R7TdqMqECMptHZh6DN/utb7Zs6Cej8OXYE5J04YOKFvD4GkHuJ0
2032pilSd1jG6n87tDZ+BUwUOepI6CGSkFMYWf0ihvT33Qj1A+tCkSI=
2033""")
2034
2035##file activate.ps1
2036ACTIVATE_PS = convert("""
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
2123rKQDxR6N/rffXv+lROXet/9Q+l9I4D1U
2124""")
2125
2126##file distutils-init.py
2127DISTUTILS_INIT = convert("""
2128eJytV1uL4zYUfvevOE0ottuMW9q3gVDa3aUMXXbLMlDKMBiNrSTqOJKRlMxkf33PkXyRbGe7Dw2E
2129UXTu37lpxLFV2oIyifAncxmOL0xLIfcG+gv80x9VW6maw7o/CANSWWBwFtqeWMPlGY6qPjV8A0bB
2130C4eKSTgZ5LRgFeyErMEeOBhbN+Ipgeizhjtnhkn7DdyjuNLPoCS0l/ayQTG0djwZC08cLXozeMss
2131aG5EzQ0IScpnWtHSTXuxByV/QCmxE7y+eS0uxWeoheaVVfqSJHiU7Mhhi6gULbOHorshkrEnKxpT
21320n3A8Y8SMpuwZx6aoix3ouFlmW8gHRSkeSJ2g7hU+kiHLDaQw3bmRDaTGfTnty7gPm0FHbIBg9U9
2133oh1kZzAFLaue2R6htPCtAda2nGlDSUJ4PZBgCJBGVcwKTAMz/vJiLD+Oin5Z5QlvDPdulC6EsiyE
2134NFzb7McNTKJzbJqzphx92VKRFY1idenzmq3K0emRcbWBD0ryqc4NZGmKOOOX9Pz5x+/l27tP797c
2135f/z0d+4NruGNai8uAM0bfsYaw8itFk8ny41jsfpyO+BWlpqfhcG4yxLdi/0tQqoT4a8Vby382mt8
2136p7XSo7aWGdPBc+b6utaBmCQ7rQKQoWtAuthQCiold2KfJIPTT8xwg9blPumc+YDZC/wYGdAyHpJk
2137vUbHbHWAp5No6pK/WhhLEWrFjUwtPEv1Agf8YmnsuXUQYkeZoHm8ogP16gt2uHoxcEMdf2C6pmbw
2138hUMsWGhanboh4IzzmsIpWs134jVPqD/c74bZHdY69UKKSn/+KfVhxLgUlToemayLMYQOqfEC61bh
2139cbhwaqoGUzIyZRFHPmau5juaWqwRn3mpWmoEA5nhzS5gog/5jbcFQqOZvmBasZtwYlG93k5GEiyw
2140buHhMWLjDarEGpMGB2LFs5nIJkhp/nUmZneFaRth++lieJtHepIvKgx6PJqIlD9X2j6pG1i9x3pZ
21415bHuCPFiirGHeO7McvoXkz786GaKVzC9DSpnOxJdc4xm6NSVq7lNEnKdVlnpu9BNYoKX2Iq3wvgh
2142gGEUM66kK6j4NiyoneuPLSwaCWDxczgaolEWpiMyDVDb7dNuLAbriL8ig8mmeju31oNvQdpnvEPC
21431vAXbWacGRVrGt/uXN/gU0CDDwgooKRrHfTBb1/s9lYZ8ZqOBU0yLvpuP6+K9hLFsvIjeNhBi0KL
2144MlOuWRn3FRwx5oHXjl0YImUx0+gLzjGchrgzca026ETmYJzPD+IpuKzNi8AFn048Thd63OdD86M6
214584zE8yQm0VqXdbbgvub2pKVnS76icBGdeTHHXTKspUmr4NYo/furFLKiMdQzFjHJNcdAnMhltBJK
21460/IKX3DVFqvPJ2dLE7bDBkH0l/PJ29074+F0CsGYOxsb7U3myTUncYfXqnLLfa6sJybX4g+hmcjO
2147kMRBfA1JellfRRKJcyRpxdS4rIl6FdmQCWjo/o9Qz7yKffoP4JHjOvABcRn4CZIT2RH4jnxmfpVG
2148qgLaAvQBNfuO6X0/Ux02nb4FKx3vgP+XnkX0QW9pLy/NsXgdN24dD3LxO2Nwil7Zlc1dqtP3d7/h
2149kzp1/+7hGBuY4pk0XD/0Ao/oTe/XGrfyM773aB7iUhgkpy+dwAMalxMP0DrBcsVw/6p25+/hobP9
2150GBknrWExDhLJ1bwt1NcCNblaFbMKCyvmX0PeRaQ=
2151""")
2152
2153##file distutils.cfg
2154DISTUTILS_CFG = convert("""
2155eJxNj00KwkAMhfc9xYNuxe4Ft57AjYiUtDO1wXSmNJnK3N5pdSEEAu8nH6lxHVlRhtDHMPATA4uH
2156xJ4EFmGbvfJiicSHFRzUSISMY6hq3GLCRLnIvSTnEefN0FIjw5tF0Hkk9Q5dRunBsVoyFi24aaLg
21579FDOlL0FPGluf4QjcInLlxd6f6rqkgPu/5nHLg0cXCscXoozRrP51DRT3j9QNl99AP53T2Q=
2158""")
2159
2160##file activate_this.py
2161ACTIVATE_THIS = convert("""
2162eJyNU01v2zAMvetXEB4K21jnDOstQA4dMGCHbeihlyEIDMWmE62yJEiKE//7kXKdpEWLzYBt8evx
2163kRSzLPs6wiEoswM8YdMpjUXcq1Dz6RZa1cSiTkJdr86GsoTRHuCotBayiWqQEYGtMCgfD1KjGYBe
21645a3p0cRKiEe2NtLAFikftnDco0ko/SFEVgEZ8aRCZDIPY9xbA8pE9M4jfW/B2CjiHq9zbJVZuOQq
2165siwTIvpxKYCembPAU4Muwi/Z4zfvrZ/MXipKeB8C+qisSZYiWfjJfs+0/MFMdWn1hJcO5U7G/SLa
2166xVx8zU6VG/PXLXvfsyyzUqjeWR8hjGE+2iCE1W1tQ82hsCJN9dzKaoexyB/uH79TnjwvxcW0ntSb
2167yZ8jq1Z5Q1UXsyy3gf9nbjTEj7NzQMfCJa/YSmrQ+2D/BqfiOi6sclrGzvoeVivIj8rcfcmnIQRF
21687XCyeZI7DFe5/lhlCs5PRf5QW66VXT/NrlQ46oD/D6InkOmi3IQcbhKxAX2g4a+Xd5s3UtCtG2py
2169m8eg6WYWqR6SL5OjKMGfSrYt/6kxxQtOpeAgj1LXBNmpE2ElmCSIy5H0zFd8gJ924HWijWhb2hRC
21706wNEm1QdDZtuSZcEprIUBo/XRNcbQe1OUbQ/r3hPTaPJJDNtFLu8KHV5XoNr3Eo6h6YtOKw8e8yw
2171VF5PnJ+ts3a9/Mz38RpG/AUSzYUW
2172""")
2173
2174##file python-config
2175PYTHON_CONFIG = convert("""
2176eJyNVV1P2zAUfc+v8ODBiSABxlulTipbO6p1LWqBgVhlhcZpPYUkctzSivHfd6+dpGloGH2Ja/ue
2177e+65Hz78xNhtf3x90xmw7vCWsRPGLvpDNuz87MKfdKMWSWxZ4ilNpCLZJiuWc66SVFUOZkkcirll
2178rfxIBAzOMtImDzSVPBRrekwoX/OZu/0r4lm0DHiG60g86u8sjPw5rCyy86NRkB8QuuBRSqfAKESn
21793orLTCQxE3GYkC9tYp8fk89OSwNsmXgizrhUtnumeSgeo5GbLUMk49Rv+2nK48Cm/qMwfp333J2/
2180dVcAGE0CIQHBsgIeEr4Wij0LtWDLzJ9ze5YEvH2WI6CHTAVcSu9ZCsXtgxu81CIvp6/k4eXsdfo7
2181PvDCRD75yi41QitfzlcPp1OI7i/1/iQitqnr0iMgQ+A6wa+IKwwdxyk9IiXNAzgquTFU8NIxAVjM
2182osm1Zz526e+shQ4hKRVci69nPC3Kw4NQEmkQ65E7OodxorSvxjvpBjQHDmWFIQ1mlmzlS5vedseT
2183/mgIEsMJ7Lxz2bLAF9M5xeLEhdbHxpWOw0GdkJApMVBRF1y+a0z3c9WZPAXGFcFrJgCIB+024uad
21840CrzmEoRa3Ub4swNIHPGf7QDV+2uj2OiFWsChgCwjKqN6rp5izpbH6Wc1O1TclQTP/XVwi6anTr1
21851sbubjZLI1+VptPSdCfwnFBrB1jvebrTA9uUhU2/9gad7xPqeFkaQcnnLbCViZK8d7R1kxzFrIJV
21868EaLYmKYpvGVkig+3C5HCXbM1jGCGekiM2pRCVPyRyXYdPf6kcbWEQ36F5V4Gq9N7icNNw+JHwRE
2187LTgxRXACpvnQv/PuT0xCCAywY/K4hE6Now2qDwaSE5FB+1agsoUveYDepS83qFcF1NufvULD3fTl
2188g6Hgf7WBt6lzMeiyyWVn3P1WVbwaczHmTzE9A5SyItTVgFYyvs/L/fXlaNgbw8v3azT+0eikVlWD
2189/vBHbzQumP23uBCjsYdrL9OWARwxs/nuLOzeXbPJTa/Xv6sUmQir5pC1YRLz3eA+CD8Z0XpcW8v9
2190MZWF36ryyXXf3yBIz6nzqz8Muyz0m5Qj7OexfYo/Ph3LqvkHUg7AuA==
2191""")
2192
2193MH_MAGIC = 0xfeedface
2194MH_CIGAM = 0xcefaedfe
2195MH_MAGIC_64 = 0xfeedfacf
2196MH_CIGAM_64 = 0xcffaedfe
2197FAT_MAGIC = 0xcafebabe
2198BIG_ENDIAN = '>'
2199LITTLE_ENDIAN = '<'
2200LC_LOAD_DYLIB = 0xc
2201maxint = majver == 3 and getattr(sys, 'maxsize') or getattr(sys, 'maxint')
2202
2203
2204class fileview(object):
2205 """
2206 A proxy for file-like objects that exposes a given view of a file.
2207 Modified from macholib.
2208 """
2209
2210 def __init__(self, fileobj, start=0, size=maxint):
2211 if isinstance(fileobj, fileview):
2212 self._fileobj = fileobj._fileobj
2213 else:
2214 self._fileobj = fileobj
2215 self._start = start
2216 self._end = start + size
2217 self._pos = 0
2218
2219 def __repr__(self):
2220 return '<fileview [%d, %d] %r>' % (
2221 self._start, self._end, self._fileobj)
2222
2223 def tell(self):
2224 return self._pos
2225
2226 def _checkwindow(self, seekto, op):
2227 if not (self._start <= seekto <= self._end):
2228 raise IOError("%s to offset %d is outside window [%d, %d]" % (
2229 op, seekto, self._start, self._end))
2230
2231 def seek(self, offset, whence=0):
2232 seekto = offset
2233 if whence == os.SEEK_SET:
2234 seekto += self._start
2235 elif whence == os.SEEK_CUR:
2236 seekto += self._start + self._pos
2237 elif whence == os.SEEK_END:
2238 seekto += self._end
2239 else:
2240 raise IOError("Invalid whence argument to seek: %r" % (whence,))
2241 self._checkwindow(seekto, 'seek')
2242 self._fileobj.seek(seekto)
2243 self._pos = seekto - self._start
2244
2245 def write(self, bytes):
2246 here = self._start + self._pos
2247 self._checkwindow(here, 'write')
2248 self._checkwindow(here + len(bytes), 'write')
2249 self._fileobj.seek(here, os.SEEK_SET)
2250 self._fileobj.write(bytes)
2251 self._pos += len(bytes)
2252
2253 def read(self, size=maxint):
2254 assert size >= 0
2255 here = self._start + self._pos
2256 self._checkwindow(here, 'read')
2257 size = min(size, self._end - here)
2258 self._fileobj.seek(here, os.SEEK_SET)
2259 bytes = self._fileobj.read(size)
2260 self._pos += len(bytes)
2261 return bytes
2262
2263
2264def read_data(file, endian, num=1):
2265 """
2266 Read a given number of 32-bits unsigned integers from the given file
2267 with the given endianness.
2268 """
2269 res = struct.unpack(endian + 'L' * num, file.read(num * 4))
2270 if len(res) == 1:
2271 return res[0]
2272 return res
2273
2274
2275def mach_o_change(path, what, value):
2276 """
2277 Replace a given name (what) in any LC_LOAD_DYLIB command found in
2278 the given binary with a new name (value), provided it's shorter.
2279 """
2280
2281 def do_macho(file, bits, endian):
2282 # Read Mach-O header (the magic number is assumed read by the caller)
2283 cputype, cpusubtype, filetype, ncmds, sizeofcmds, flags = read_data(file, endian, 6)
2284 # 64-bits header has one more field.
2285 if bits == 64:
2286 read_data(file, endian)
2287 # The header is followed by ncmds commands
2288 for n in range(ncmds):
2289 where = file.tell()
2290 # Read command header
2291 cmd, cmdsize = read_data(file, endian, 2)
2292 if cmd == LC_LOAD_DYLIB:
2293 # The first data field in LC_LOAD_DYLIB commands is the
2294 # offset of the name, starting from the beginning of the
2295 # command.
2296 name_offset = read_data(file, endian)
2297 file.seek(where + name_offset, os.SEEK_SET)
2298 # Read the NUL terminated string
2299 load = file.read(cmdsize - name_offset).decode()
2300 load = load[:load.index('\0')]
2301 # If the string is what is being replaced, overwrite it.
2302 if load == what:
2303 file.seek(where + name_offset, os.SEEK_SET)
2304 file.write(value.encode() + '\0'.encode())
2305 # Seek to the next command
2306 file.seek(where + cmdsize, os.SEEK_SET)
2307
2308 def do_file(file, offset=0, size=maxint):
2309 file = fileview(file, offset, size)
2310 # Read magic number
2311 magic = read_data(file, BIG_ENDIAN)
2312 if magic == FAT_MAGIC:
2313 # Fat binaries contain nfat_arch Mach-O binaries
2314 nfat_arch = read_data(file, BIG_ENDIAN)
2315 for n in range(nfat_arch):
2316 # Read arch header
2317 cputype, cpusubtype, offset, size, align = read_data(file, BIG_ENDIAN, 5)
2318 do_file(file, offset, size)
2319 elif magic == MH_MAGIC:
2320 do_macho(file, 32, BIG_ENDIAN)
2321 elif magic == MH_CIGAM:
2322 do_macho(file, 32, LITTLE_ENDIAN)
2323 elif magic == MH_MAGIC_64:
2324 do_macho(file, 64, BIG_ENDIAN)
2325 elif magic == MH_CIGAM_64:
2326 do_macho(file, 64, LITTLE_ENDIAN)
2327
2328 assert(len(what) >= len(value))
2329
2330 with open(path, 'r+b') as f:
2331 do_file(f)
2332
2333
2334if __name__ == '__main__':
2335 main()
2336
2337# TODO:
2338# Copy python.exe.manifest
2339# Monkeypatch distutils.sysconfig