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