Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
keewenaw
GitHub Repository: keewenaw/ethereum-wallet-cracker
Path: blob/main/test/lib/python3.9/site-packages/pip/_internal/commands/install.py
4804 views
1
import errno
2
import operator
3
import os
4
import shutil
5
import site
6
from optparse import SUPPRESS_HELP, Values
7
from typing import Iterable, List, Optional
8
9
from pip._vendor.packaging.utils import canonicalize_name
10
11
from pip._internal.cache import WheelCache
12
from pip._internal.cli import cmdoptions
13
from pip._internal.cli.cmdoptions import make_target_python
14
from pip._internal.cli.req_command import (
15
RequirementCommand,
16
warn_if_run_as_root,
17
with_cleanup,
18
)
19
from pip._internal.cli.status_codes import ERROR, SUCCESS
20
from pip._internal.exceptions import CommandError, InstallationError
21
from pip._internal.locations import get_scheme
22
from pip._internal.metadata import get_environment
23
from pip._internal.models.format_control import FormatControl
24
from pip._internal.operations.build.build_tracker import get_build_tracker
25
from pip._internal.operations.check import ConflictDetails, check_install_conflicts
26
from pip._internal.req import install_given_reqs
27
from pip._internal.req.req_install import InstallRequirement
28
from pip._internal.utils.compat import WINDOWS
29
from pip._internal.utils.distutils_args import parse_distutils_args
30
from pip._internal.utils.filesystem import test_writable_dir
31
from pip._internal.utils.logging import getLogger
32
from pip._internal.utils.misc import (
33
ensure_dir,
34
get_pip_version,
35
protect_pip_from_modification_on_windows,
36
write_output,
37
)
38
from pip._internal.utils.temp_dir import TempDirectory
39
from pip._internal.utils.virtualenv import (
40
running_under_virtualenv,
41
virtualenv_no_global,
42
)
43
from pip._internal.wheel_builder import (
44
BinaryAllowedPredicate,
45
build,
46
should_build_for_install_command,
47
)
48
49
logger = getLogger(__name__)
50
51
52
def get_check_binary_allowed(format_control: FormatControl) -> BinaryAllowedPredicate:
53
def check_binary_allowed(req: InstallRequirement) -> bool:
54
canonical_name = canonicalize_name(req.name or "")
55
allowed_formats = format_control.get_allowed_formats(canonical_name)
56
return "binary" in allowed_formats
57
58
return check_binary_allowed
59
60
61
class InstallCommand(RequirementCommand):
62
"""
63
Install packages from:
64
65
- PyPI (and other indexes) using requirement specifiers.
66
- VCS project urls.
67
- Local project directories.
68
- Local or remote source archives.
69
70
pip also supports installing from "requirements files", which provide
71
an easy way to specify a whole environment to be installed.
72
"""
73
74
usage = """
75
%prog [options] <requirement specifier> [package-index-options] ...
76
%prog [options] -r <requirements file> [package-index-options] ...
77
%prog [options] [-e] <vcs project url> ...
78
%prog [options] [-e] <local project path> ...
79
%prog [options] <archive url/path> ..."""
80
81
def add_options(self) -> None:
82
self.cmd_opts.add_option(cmdoptions.requirements())
83
self.cmd_opts.add_option(cmdoptions.constraints())
84
self.cmd_opts.add_option(cmdoptions.no_deps())
85
self.cmd_opts.add_option(cmdoptions.pre())
86
87
self.cmd_opts.add_option(cmdoptions.editable())
88
self.cmd_opts.add_option(
89
"-t",
90
"--target",
91
dest="target_dir",
92
metavar="dir",
93
default=None,
94
help=(
95
"Install packages into <dir>. "
96
"By default this will not replace existing files/folders in "
97
"<dir>. Use --upgrade to replace existing packages in <dir> "
98
"with new versions."
99
),
100
)
101
cmdoptions.add_target_python_options(self.cmd_opts)
102
103
self.cmd_opts.add_option(
104
"--user",
105
dest="use_user_site",
106
action="store_true",
107
help=(
108
"Install to the Python user install directory for your "
109
"platform. Typically ~/.local/, or %APPDATA%\\Python on "
110
"Windows. (See the Python documentation for site.USER_BASE "
111
"for full details.)"
112
),
113
)
114
self.cmd_opts.add_option(
115
"--no-user",
116
dest="use_user_site",
117
action="store_false",
118
help=SUPPRESS_HELP,
119
)
120
self.cmd_opts.add_option(
121
"--root",
122
dest="root_path",
123
metavar="dir",
124
default=None,
125
help="Install everything relative to this alternate root directory.",
126
)
127
self.cmd_opts.add_option(
128
"--prefix",
129
dest="prefix_path",
130
metavar="dir",
131
default=None,
132
help=(
133
"Installation prefix where lib, bin and other top-level "
134
"folders are placed"
135
),
136
)
137
138
self.cmd_opts.add_option(cmdoptions.src())
139
140
self.cmd_opts.add_option(
141
"-U",
142
"--upgrade",
143
dest="upgrade",
144
action="store_true",
145
help=(
146
"Upgrade all specified packages to the newest available "
147
"version. The handling of dependencies depends on the "
148
"upgrade-strategy used."
149
),
150
)
151
152
self.cmd_opts.add_option(
153
"--upgrade-strategy",
154
dest="upgrade_strategy",
155
default="only-if-needed",
156
choices=["only-if-needed", "eager"],
157
help=(
158
"Determines how dependency upgrading should be handled "
159
"[default: %default]. "
160
'"eager" - dependencies are upgraded regardless of '
161
"whether the currently installed version satisfies the "
162
"requirements of the upgraded package(s). "
163
'"only-if-needed" - are upgraded only when they do not '
164
"satisfy the requirements of the upgraded package(s)."
165
),
166
)
167
168
self.cmd_opts.add_option(
169
"--force-reinstall",
170
dest="force_reinstall",
171
action="store_true",
172
help="Reinstall all packages even if they are already up-to-date.",
173
)
174
175
self.cmd_opts.add_option(
176
"-I",
177
"--ignore-installed",
178
dest="ignore_installed",
179
action="store_true",
180
help=(
181
"Ignore the installed packages, overwriting them. "
182
"This can break your system if the existing package "
183
"is of a different version or was installed "
184
"with a different package manager!"
185
),
186
)
187
188
self.cmd_opts.add_option(cmdoptions.ignore_requires_python())
189
self.cmd_opts.add_option(cmdoptions.no_build_isolation())
190
self.cmd_opts.add_option(cmdoptions.use_pep517())
191
self.cmd_opts.add_option(cmdoptions.no_use_pep517())
192
self.cmd_opts.add_option(cmdoptions.check_build_deps())
193
194
self.cmd_opts.add_option(cmdoptions.config_settings())
195
self.cmd_opts.add_option(cmdoptions.install_options())
196
self.cmd_opts.add_option(cmdoptions.global_options())
197
198
self.cmd_opts.add_option(
199
"--compile",
200
action="store_true",
201
dest="compile",
202
default=True,
203
help="Compile Python source files to bytecode",
204
)
205
206
self.cmd_opts.add_option(
207
"--no-compile",
208
action="store_false",
209
dest="compile",
210
help="Do not compile Python source files to bytecode",
211
)
212
213
self.cmd_opts.add_option(
214
"--no-warn-script-location",
215
action="store_false",
216
dest="warn_script_location",
217
default=True,
218
help="Do not warn when installing scripts outside PATH",
219
)
220
self.cmd_opts.add_option(
221
"--no-warn-conflicts",
222
action="store_false",
223
dest="warn_about_conflicts",
224
default=True,
225
help="Do not warn about broken dependencies",
226
)
227
self.cmd_opts.add_option(cmdoptions.no_binary())
228
self.cmd_opts.add_option(cmdoptions.only_binary())
229
self.cmd_opts.add_option(cmdoptions.prefer_binary())
230
self.cmd_opts.add_option(cmdoptions.require_hashes())
231
self.cmd_opts.add_option(cmdoptions.progress_bar())
232
self.cmd_opts.add_option(cmdoptions.root_user_action())
233
234
index_opts = cmdoptions.make_option_group(
235
cmdoptions.index_group,
236
self.parser,
237
)
238
239
self.parser.insert_option_group(0, index_opts)
240
self.parser.insert_option_group(0, self.cmd_opts)
241
242
@with_cleanup
243
def run(self, options: Values, args: List[str]) -> int:
244
if options.use_user_site and options.target_dir is not None:
245
raise CommandError("Can not combine '--user' and '--target'")
246
247
cmdoptions.check_install_build_global(options)
248
upgrade_strategy = "to-satisfy-only"
249
if options.upgrade:
250
upgrade_strategy = options.upgrade_strategy
251
252
cmdoptions.check_dist_restriction(options, check_target=True)
253
254
install_options = options.install_options or []
255
256
logger.verbose("Using %s", get_pip_version())
257
options.use_user_site = decide_user_install(
258
options.use_user_site,
259
prefix_path=options.prefix_path,
260
target_dir=options.target_dir,
261
root_path=options.root_path,
262
isolated_mode=options.isolated_mode,
263
)
264
265
target_temp_dir: Optional[TempDirectory] = None
266
target_temp_dir_path: Optional[str] = None
267
if options.target_dir:
268
options.ignore_installed = True
269
options.target_dir = os.path.abspath(options.target_dir)
270
if (
271
# fmt: off
272
os.path.exists(options.target_dir) and
273
not os.path.isdir(options.target_dir)
274
# fmt: on
275
):
276
raise CommandError(
277
"Target path exists but is not a directory, will not continue."
278
)
279
280
# Create a target directory for using with the target option
281
target_temp_dir = TempDirectory(kind="target")
282
target_temp_dir_path = target_temp_dir.path
283
self.enter_context(target_temp_dir)
284
285
global_options = options.global_options or []
286
287
session = self.get_default_session(options)
288
289
target_python = make_target_python(options)
290
finder = self._build_package_finder(
291
options=options,
292
session=session,
293
target_python=target_python,
294
ignore_requires_python=options.ignore_requires_python,
295
)
296
wheel_cache = WheelCache(options.cache_dir, options.format_control)
297
298
build_tracker = self.enter_context(get_build_tracker())
299
300
directory = TempDirectory(
301
delete=not options.no_clean,
302
kind="install",
303
globally_managed=True,
304
)
305
306
try:
307
reqs = self.get_requirements(args, options, finder, session)
308
309
# Only when installing is it permitted to use PEP 660.
310
# In other circumstances (pip wheel, pip download) we generate
311
# regular (i.e. non editable) metadata and wheels.
312
for req in reqs:
313
req.permit_editable_wheels = True
314
315
reject_location_related_install_options(reqs, options.install_options)
316
317
preparer = self.make_requirement_preparer(
318
temp_build_dir=directory,
319
options=options,
320
build_tracker=build_tracker,
321
session=session,
322
finder=finder,
323
use_user_site=options.use_user_site,
324
verbosity=self.verbosity,
325
)
326
resolver = self.make_resolver(
327
preparer=preparer,
328
finder=finder,
329
options=options,
330
wheel_cache=wheel_cache,
331
use_user_site=options.use_user_site,
332
ignore_installed=options.ignore_installed,
333
ignore_requires_python=options.ignore_requires_python,
334
force_reinstall=options.force_reinstall,
335
upgrade_strategy=upgrade_strategy,
336
use_pep517=options.use_pep517,
337
)
338
339
self.trace_basic_info(finder)
340
341
requirement_set = resolver.resolve(
342
reqs, check_supported_wheels=not options.target_dir
343
)
344
345
try:
346
pip_req = requirement_set.get_requirement("pip")
347
except KeyError:
348
modifying_pip = False
349
else:
350
# If we're not replacing an already installed pip,
351
# we're not modifying it.
352
modifying_pip = pip_req.satisfied_by is None
353
protect_pip_from_modification_on_windows(modifying_pip=modifying_pip)
354
355
check_binary_allowed = get_check_binary_allowed(finder.format_control)
356
357
reqs_to_build = [
358
r
359
for r in requirement_set.requirements.values()
360
if should_build_for_install_command(r, check_binary_allowed)
361
]
362
363
_, build_failures = build(
364
reqs_to_build,
365
wheel_cache=wheel_cache,
366
verify=True,
367
build_options=[],
368
global_options=[],
369
)
370
371
# If we're using PEP 517, we cannot do a legacy setup.py install
372
# so we fail here.
373
pep517_build_failure_names: List[str] = [
374
r.name for r in build_failures if r.use_pep517 # type: ignore
375
]
376
if pep517_build_failure_names:
377
raise InstallationError(
378
"Could not build wheels for {}, which is required to "
379
"install pyproject.toml-based projects".format(
380
", ".join(pep517_build_failure_names)
381
)
382
)
383
384
# For now, we just warn about failures building legacy
385
# requirements, as we'll fall through to a setup.py install for
386
# those.
387
for r in build_failures:
388
if not r.use_pep517:
389
r.legacy_install_reason = 8368
390
391
to_install = resolver.get_installation_order(requirement_set)
392
393
# Check for conflicts in the package set we're installing.
394
conflicts: Optional[ConflictDetails] = None
395
should_warn_about_conflicts = (
396
not options.ignore_dependencies and options.warn_about_conflicts
397
)
398
if should_warn_about_conflicts:
399
conflicts = self._determine_conflicts(to_install)
400
401
# Don't warn about script install locations if
402
# --target or --prefix has been specified
403
warn_script_location = options.warn_script_location
404
if options.target_dir or options.prefix_path:
405
warn_script_location = False
406
407
installed = install_given_reqs(
408
to_install,
409
install_options,
410
global_options,
411
root=options.root_path,
412
home=target_temp_dir_path,
413
prefix=options.prefix_path,
414
warn_script_location=warn_script_location,
415
use_user_site=options.use_user_site,
416
pycompile=options.compile,
417
)
418
419
lib_locations = get_lib_location_guesses(
420
user=options.use_user_site,
421
home=target_temp_dir_path,
422
root=options.root_path,
423
prefix=options.prefix_path,
424
isolated=options.isolated_mode,
425
)
426
env = get_environment(lib_locations)
427
428
installed.sort(key=operator.attrgetter("name"))
429
items = []
430
for result in installed:
431
item = result.name
432
try:
433
installed_dist = env.get_distribution(item)
434
if installed_dist is not None:
435
item = f"{item}-{installed_dist.version}"
436
except Exception:
437
pass
438
items.append(item)
439
440
if conflicts is not None:
441
self._warn_about_conflicts(
442
conflicts,
443
resolver_variant=self.determine_resolver_variant(options),
444
)
445
446
installed_desc = " ".join(items)
447
if installed_desc:
448
write_output(
449
"Successfully installed %s",
450
installed_desc,
451
)
452
except OSError as error:
453
show_traceback = self.verbosity >= 1
454
455
message = create_os_error_message(
456
error,
457
show_traceback,
458
options.use_user_site,
459
)
460
logger.error(message, exc_info=show_traceback) # noqa
461
462
return ERROR
463
464
if options.target_dir:
465
assert target_temp_dir
466
self._handle_target_dir(
467
options.target_dir, target_temp_dir, options.upgrade
468
)
469
if options.root_user_action == "warn":
470
warn_if_run_as_root()
471
return SUCCESS
472
473
def _handle_target_dir(
474
self, target_dir: str, target_temp_dir: TempDirectory, upgrade: bool
475
) -> None:
476
ensure_dir(target_dir)
477
478
# Checking both purelib and platlib directories for installed
479
# packages to be moved to target directory
480
lib_dir_list = []
481
482
# Checking both purelib and platlib directories for installed
483
# packages to be moved to target directory
484
scheme = get_scheme("", home=target_temp_dir.path)
485
purelib_dir = scheme.purelib
486
platlib_dir = scheme.platlib
487
data_dir = scheme.data
488
489
if os.path.exists(purelib_dir):
490
lib_dir_list.append(purelib_dir)
491
if os.path.exists(platlib_dir) and platlib_dir != purelib_dir:
492
lib_dir_list.append(platlib_dir)
493
if os.path.exists(data_dir):
494
lib_dir_list.append(data_dir)
495
496
for lib_dir in lib_dir_list:
497
for item in os.listdir(lib_dir):
498
if lib_dir == data_dir:
499
ddir = os.path.join(data_dir, item)
500
if any(s.startswith(ddir) for s in lib_dir_list[:-1]):
501
continue
502
target_item_dir = os.path.join(target_dir, item)
503
if os.path.exists(target_item_dir):
504
if not upgrade:
505
logger.warning(
506
"Target directory %s already exists. Specify "
507
"--upgrade to force replacement.",
508
target_item_dir,
509
)
510
continue
511
if os.path.islink(target_item_dir):
512
logger.warning(
513
"Target directory %s already exists and is "
514
"a link. pip will not automatically replace "
515
"links, please remove if replacement is "
516
"desired.",
517
target_item_dir,
518
)
519
continue
520
if os.path.isdir(target_item_dir):
521
shutil.rmtree(target_item_dir)
522
else:
523
os.remove(target_item_dir)
524
525
shutil.move(os.path.join(lib_dir, item), target_item_dir)
526
527
def _determine_conflicts(
528
self, to_install: List[InstallRequirement]
529
) -> Optional[ConflictDetails]:
530
try:
531
return check_install_conflicts(to_install)
532
except Exception:
533
logger.exception(
534
"Error while checking for conflicts. Please file an issue on "
535
"pip's issue tracker: https://github.com/pypa/pip/issues/new"
536
)
537
return None
538
539
def _warn_about_conflicts(
540
self, conflict_details: ConflictDetails, resolver_variant: str
541
) -> None:
542
package_set, (missing, conflicting) = conflict_details
543
if not missing and not conflicting:
544
return
545
546
parts: List[str] = []
547
if resolver_variant == "legacy":
548
parts.append(
549
"pip's legacy dependency resolver does not consider dependency "
550
"conflicts when selecting packages. This behaviour is the "
551
"source of the following dependency conflicts."
552
)
553
else:
554
assert resolver_variant == "2020-resolver"
555
parts.append(
556
"pip's dependency resolver does not currently take into account "
557
"all the packages that are installed. This behaviour is the "
558
"source of the following dependency conflicts."
559
)
560
561
# NOTE: There is some duplication here, with commands/check.py
562
for project_name in missing:
563
version = package_set[project_name][0]
564
for dependency in missing[project_name]:
565
message = (
566
"{name} {version} requires {requirement}, "
567
"which is not installed."
568
).format(
569
name=project_name,
570
version=version,
571
requirement=dependency[1],
572
)
573
parts.append(message)
574
575
for project_name in conflicting:
576
version = package_set[project_name][0]
577
for dep_name, dep_version, req in conflicting[project_name]:
578
message = (
579
"{name} {version} requires {requirement}, but {you} have "
580
"{dep_name} {dep_version} which is incompatible."
581
).format(
582
name=project_name,
583
version=version,
584
requirement=req,
585
dep_name=dep_name,
586
dep_version=dep_version,
587
you=("you" if resolver_variant == "2020-resolver" else "you'll"),
588
)
589
parts.append(message)
590
591
logger.critical("\n".join(parts))
592
593
594
def get_lib_location_guesses(
595
user: bool = False,
596
home: Optional[str] = None,
597
root: Optional[str] = None,
598
isolated: bool = False,
599
prefix: Optional[str] = None,
600
) -> List[str]:
601
scheme = get_scheme(
602
"",
603
user=user,
604
home=home,
605
root=root,
606
isolated=isolated,
607
prefix=prefix,
608
)
609
return [scheme.purelib, scheme.platlib]
610
611
612
def site_packages_writable(root: Optional[str], isolated: bool) -> bool:
613
return all(
614
test_writable_dir(d)
615
for d in set(get_lib_location_guesses(root=root, isolated=isolated))
616
)
617
618
619
def decide_user_install(
620
use_user_site: Optional[bool],
621
prefix_path: Optional[str] = None,
622
target_dir: Optional[str] = None,
623
root_path: Optional[str] = None,
624
isolated_mode: bool = False,
625
) -> bool:
626
"""Determine whether to do a user install based on the input options.
627
628
If use_user_site is False, no additional checks are done.
629
If use_user_site is True, it is checked for compatibility with other
630
options.
631
If use_user_site is None, the default behaviour depends on the environment,
632
which is provided by the other arguments.
633
"""
634
# In some cases (config from tox), use_user_site can be set to an integer
635
# rather than a bool, which 'use_user_site is False' wouldn't catch.
636
if (use_user_site is not None) and (not use_user_site):
637
logger.debug("Non-user install by explicit request")
638
return False
639
640
if use_user_site:
641
if prefix_path:
642
raise CommandError(
643
"Can not combine '--user' and '--prefix' as they imply "
644
"different installation locations"
645
)
646
if virtualenv_no_global():
647
raise InstallationError(
648
"Can not perform a '--user' install. User site-packages "
649
"are not visible in this virtualenv."
650
)
651
logger.debug("User install by explicit request")
652
return True
653
654
# If we are here, user installs have not been explicitly requested/avoided
655
assert use_user_site is None
656
657
# user install incompatible with --prefix/--target
658
if prefix_path or target_dir:
659
logger.debug("Non-user install due to --prefix or --target option")
660
return False
661
662
# If user installs are not enabled, choose a non-user install
663
if not site.ENABLE_USER_SITE:
664
logger.debug("Non-user install because user site-packages disabled")
665
return False
666
667
# If we have permission for a non-user install, do that,
668
# otherwise do a user install.
669
if site_packages_writable(root=root_path, isolated=isolated_mode):
670
logger.debug("Non-user install because site-packages writeable")
671
return False
672
673
logger.info(
674
"Defaulting to user installation because normal site-packages "
675
"is not writeable"
676
)
677
return True
678
679
680
def reject_location_related_install_options(
681
requirements: List[InstallRequirement], options: Optional[List[str]]
682
) -> None:
683
"""If any location-changing --install-option arguments were passed for
684
requirements or on the command-line, then show a deprecation warning.
685
"""
686
687
def format_options(option_names: Iterable[str]) -> List[str]:
688
return ["--{}".format(name.replace("_", "-")) for name in option_names]
689
690
offenders = []
691
692
for requirement in requirements:
693
install_options = requirement.install_options
694
location_options = parse_distutils_args(install_options)
695
if location_options:
696
offenders.append(
697
"{!r} from {}".format(
698
format_options(location_options.keys()), requirement
699
)
700
)
701
702
if options:
703
location_options = parse_distutils_args(options)
704
if location_options:
705
offenders.append(
706
"{!r} from command line".format(format_options(location_options.keys()))
707
)
708
709
if not offenders:
710
return
711
712
raise CommandError(
713
"Location-changing options found in --install-option: {}."
714
" This is unsupported, use pip-level options like --user,"
715
" --prefix, --root, and --target instead.".format("; ".join(offenders))
716
)
717
718
719
def create_os_error_message(
720
error: OSError, show_traceback: bool, using_user_site: bool
721
) -> str:
722
"""Format an error message for an OSError
723
724
It may occur anytime during the execution of the install command.
725
"""
726
parts = []
727
728
# Mention the error if we are not going to show a traceback
729
parts.append("Could not install packages due to an OSError")
730
if not show_traceback:
731
parts.append(": ")
732
parts.append(str(error))
733
else:
734
parts.append(".")
735
736
# Spilt the error indication from a helper message (if any)
737
parts[-1] += "\n"
738
739
# Suggest useful actions to the user:
740
# (1) using user site-packages or (2) verifying the permissions
741
if error.errno == errno.EACCES:
742
user_option_part = "Consider using the `--user` option"
743
permissions_part = "Check the permissions"
744
745
if not running_under_virtualenv() and not using_user_site:
746
parts.extend(
747
[
748
user_option_part,
749
" or ",
750
permissions_part.lower(),
751
]
752
)
753
else:
754
parts.append(permissions_part)
755
parts.append(".\n")
756
757
# Suggest the user to enable Long Paths if path length is
758
# more than 260
759
if (
760
WINDOWS
761
and error.errno == errno.ENOENT
762
and error.filename
763
and len(error.filename) > 260
764
):
765
parts.append(
766
"HINT: This error might have occurred since "
767
"this system does not have Windows Long Path "
768
"support enabled. You can find information on "
769
"how to enable this at "
770
"https://pip.pypa.io/warnings/enable-long-paths\n"
771
)
772
773
return "".join(parts).strip() + "\n"
774
775