Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(434)

Side by Side Diff: recipe_engine/third_party/setuptools/command/easy_install.py

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

Powered by Google App Engine
This is Rietveld 408576698