Mercurial > repos > shellac > guppy_basecaller
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 |