comparison venv/lib/python2.7/site-packages/setuptools/command/easy_install.py @ 0:d67268158946 draft

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