comparison env/lib/python3.7/site-packages/setuptools/command/easy_install.py @ 0:26e78fe6e8c4 draft

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