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