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