Загрузить файлы в «venv/Lib/site-packages/pip/_internal/cli»
This commit is contained in:
447
venv/Lib/site-packages/pip/_internal/cli/req_command.py
Normal file
447
venv/Lib/site-packages/pip/_internal/cli/req_command.py
Normal file
@@ -0,0 +1,447 @@
|
||||
"""Contains the RequirementCommand base class.
|
||||
|
||||
This class is in a separate module so the commands that do not always
|
||||
need PackageFinder capability don't unnecessarily import the
|
||||
PackageFinder machinery and all its vendored dependencies, etc.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import logging
|
||||
import os
|
||||
from functools import partial
|
||||
from optparse import Values
|
||||
from typing import Any, Callable, TypeVar
|
||||
|
||||
from pip._internal.build_env import (
|
||||
BuildEnvironmentInstaller,
|
||||
InprocessBuildEnvironmentInstaller,
|
||||
SubprocessBuildEnvironmentInstaller,
|
||||
)
|
||||
from pip._internal.cache import WheelCache
|
||||
from pip._internal.cli import cmdoptions
|
||||
from pip._internal.cli.cmdoptions import make_target_python
|
||||
from pip._internal.cli.index_command import IndexGroupCommand
|
||||
from pip._internal.cli.index_command import SessionCommandMixin as SessionCommandMixin
|
||||
from pip._internal.exceptions import (
|
||||
CommandError,
|
||||
PreviousBuildDirError,
|
||||
UnsupportedPythonVersion,
|
||||
)
|
||||
from pip._internal.index.collector import LinkCollector
|
||||
from pip._internal.index.package_finder import PackageFinder
|
||||
from pip._internal.models.selection_prefs import SelectionPreferences
|
||||
from pip._internal.models.target_python import TargetPython
|
||||
from pip._internal.network.session import PipSession
|
||||
from pip._internal.operations.build.build_tracker import BuildTracker
|
||||
from pip._internal.operations.prepare import RequirementPreparer
|
||||
from pip._internal.req.constructors import (
|
||||
install_req_from_editable,
|
||||
install_req_from_line,
|
||||
install_req_from_parsed_requirement,
|
||||
install_req_from_req_string,
|
||||
)
|
||||
from pip._internal.req.pep723 import PEP723Exception, pep723_metadata
|
||||
from pip._internal.req.req_dependency_group import parse_dependency_groups
|
||||
from pip._internal.req.req_file import parse_requirements
|
||||
from pip._internal.req.req_install import InstallRequirement
|
||||
from pip._internal.resolution.base import BaseResolver
|
||||
from pip._internal.utils.packaging import check_requires_python
|
||||
from pip._internal.utils.temp_dir import (
|
||||
TempDirectory,
|
||||
TempDirectoryTypeRegistry,
|
||||
tempdir_kinds,
|
||||
)
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def should_ignore_regular_constraints(options: Values) -> bool:
|
||||
"""
|
||||
Check if regular constraints should be ignored because
|
||||
we are in a isolated build process and build constraints
|
||||
feature is enabled but no build constraints were passed.
|
||||
"""
|
||||
|
||||
return os.environ.get("_PIP_IN_BUILD_IGNORE_CONSTRAINTS") == "1"
|
||||
|
||||
|
||||
KEEPABLE_TEMPDIR_TYPES = [
|
||||
tempdir_kinds.BUILD_ENV,
|
||||
tempdir_kinds.EPHEM_WHEEL_CACHE,
|
||||
tempdir_kinds.REQ_BUILD,
|
||||
]
|
||||
|
||||
|
||||
_CommandT = TypeVar("_CommandT", bound="RequirementCommand")
|
||||
|
||||
|
||||
def with_cleanup(
|
||||
func: Callable[[_CommandT, Values, list[str]], int],
|
||||
) -> Callable[[_CommandT, Values, list[str]], int]:
|
||||
"""Decorator for common logic related to managing temporary
|
||||
directories.
|
||||
"""
|
||||
|
||||
def configure_tempdir_registry(registry: TempDirectoryTypeRegistry) -> None:
|
||||
for t in KEEPABLE_TEMPDIR_TYPES:
|
||||
registry.set_delete(t, False)
|
||||
|
||||
def wrapper(self: _CommandT, options: Values, args: list[str]) -> int:
|
||||
assert self.tempdir_registry is not None
|
||||
if options.no_clean:
|
||||
configure_tempdir_registry(self.tempdir_registry)
|
||||
|
||||
try:
|
||||
return func(self, options, args)
|
||||
except PreviousBuildDirError:
|
||||
# This kind of conflict can occur when the user passes an explicit
|
||||
# build directory with a pre-existing folder. In that case we do
|
||||
# not want to accidentally remove it.
|
||||
configure_tempdir_registry(self.tempdir_registry)
|
||||
raise
|
||||
|
||||
return wrapper
|
||||
|
||||
|
||||
def parse_constraint_files(
|
||||
constraint_files: list[str],
|
||||
finder: PackageFinder,
|
||||
options: Values,
|
||||
session: PipSession,
|
||||
) -> list[InstallRequirement]:
|
||||
requirements = []
|
||||
for filename in constraint_files:
|
||||
for parsed_req in parse_requirements(
|
||||
filename,
|
||||
constraint=True,
|
||||
finder=finder,
|
||||
options=options,
|
||||
session=session,
|
||||
):
|
||||
req_to_add = install_req_from_parsed_requirement(
|
||||
parsed_req,
|
||||
isolated=options.isolated_mode,
|
||||
user_supplied=False,
|
||||
)
|
||||
requirements.append(req_to_add)
|
||||
|
||||
return requirements
|
||||
|
||||
|
||||
class RequirementCommand(IndexGroupCommand):
|
||||
def __init__(self, *args: Any, **kw: Any) -> None:
|
||||
super().__init__(*args, **kw)
|
||||
|
||||
self.cmd_opts.add_option(cmdoptions.dependency_groups())
|
||||
self.cmd_opts.add_option(cmdoptions.no_clean())
|
||||
|
||||
@staticmethod
|
||||
def determine_resolver_variant(options: Values) -> str:
|
||||
"""Determines which resolver should be used, based on the given options."""
|
||||
if "legacy-resolver" in options.deprecated_features_enabled:
|
||||
return "legacy"
|
||||
|
||||
return "resolvelib"
|
||||
|
||||
@classmethod
|
||||
def make_requirement_preparer(
|
||||
cls,
|
||||
temp_build_dir: TempDirectory,
|
||||
options: Values,
|
||||
build_tracker: BuildTracker,
|
||||
session: PipSession,
|
||||
finder: PackageFinder,
|
||||
use_user_site: bool,
|
||||
download_dir: str | None = None,
|
||||
verbosity: int = 0,
|
||||
) -> RequirementPreparer:
|
||||
"""
|
||||
Create a RequirementPreparer instance for the given parameters.
|
||||
"""
|
||||
temp_build_dir_path = temp_build_dir.path
|
||||
assert temp_build_dir_path is not None
|
||||
legacy_resolver = False
|
||||
|
||||
resolver_variant = cls.determine_resolver_variant(options)
|
||||
if resolver_variant == "resolvelib":
|
||||
lazy_wheel = "fast-deps" in options.features_enabled
|
||||
if lazy_wheel:
|
||||
logger.warning(
|
||||
"pip is using lazily downloaded wheels using HTTP "
|
||||
"range requests to obtain dependency information. "
|
||||
"This experimental feature is enabled through "
|
||||
"--use-feature=fast-deps and it is not ready for "
|
||||
"production."
|
||||
)
|
||||
else:
|
||||
legacy_resolver = True
|
||||
lazy_wheel = False
|
||||
if "fast-deps" in options.features_enabled:
|
||||
logger.warning(
|
||||
"fast-deps has no effect when used with the legacy resolver."
|
||||
)
|
||||
|
||||
# Handle build constraints
|
||||
build_constraints = getattr(options, "build_constraints", [])
|
||||
build_constraint_feature_enabled = (
|
||||
"build-constraint" in options.features_enabled
|
||||
)
|
||||
|
||||
env_installer: BuildEnvironmentInstaller
|
||||
if "inprocess-build-deps" in options.features_enabled:
|
||||
build_constraint_reqs = parse_constraint_files(
|
||||
build_constraints, finder, options, session
|
||||
)
|
||||
env_installer = InprocessBuildEnvironmentInstaller(
|
||||
finder=finder,
|
||||
build_tracker=build_tracker,
|
||||
build_constraints=build_constraint_reqs,
|
||||
verbosity=verbosity,
|
||||
wheel_cache=WheelCache(options.cache_dir),
|
||||
)
|
||||
else:
|
||||
env_installer = SubprocessBuildEnvironmentInstaller(
|
||||
finder,
|
||||
build_constraints=build_constraints,
|
||||
build_constraint_feature_enabled=build_constraint_feature_enabled,
|
||||
)
|
||||
|
||||
return RequirementPreparer(
|
||||
build_dir=temp_build_dir_path,
|
||||
src_dir=options.src_dir,
|
||||
download_dir=download_dir,
|
||||
build_isolation=options.build_isolation,
|
||||
build_isolation_installer=env_installer,
|
||||
check_build_deps=options.check_build_deps,
|
||||
build_tracker=build_tracker,
|
||||
session=session,
|
||||
progress_bar=options.progress_bar,
|
||||
finder=finder,
|
||||
require_hashes=options.require_hashes,
|
||||
use_user_site=use_user_site,
|
||||
lazy_wheel=lazy_wheel,
|
||||
verbosity=verbosity,
|
||||
legacy_resolver=legacy_resolver,
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def make_resolver(
|
||||
cls,
|
||||
preparer: RequirementPreparer,
|
||||
finder: PackageFinder,
|
||||
options: Values,
|
||||
wheel_cache: WheelCache | None = None,
|
||||
use_user_site: bool = False,
|
||||
ignore_installed: bool = True,
|
||||
ignore_requires_python: bool = False,
|
||||
force_reinstall: bool = False,
|
||||
upgrade_strategy: str = "to-satisfy-only",
|
||||
py_version_info: tuple[int, ...] | None = None,
|
||||
) -> BaseResolver:
|
||||
"""
|
||||
Create a Resolver instance for the given parameters.
|
||||
"""
|
||||
make_install_req = partial(
|
||||
install_req_from_req_string,
|
||||
isolated=options.isolated_mode,
|
||||
)
|
||||
resolver_variant = cls.determine_resolver_variant(options)
|
||||
# The long import name and duplicated invocation is needed to convince
|
||||
# Mypy into correctly typechecking. Otherwise it would complain the
|
||||
# "Resolver" class being redefined.
|
||||
if resolver_variant == "resolvelib":
|
||||
import pip._internal.resolution.resolvelib.resolver
|
||||
|
||||
return pip._internal.resolution.resolvelib.resolver.Resolver(
|
||||
preparer=preparer,
|
||||
finder=finder,
|
||||
wheel_cache=wheel_cache,
|
||||
make_install_req=make_install_req,
|
||||
use_user_site=use_user_site,
|
||||
ignore_dependencies=options.ignore_dependencies,
|
||||
ignore_installed=ignore_installed,
|
||||
ignore_requires_python=ignore_requires_python,
|
||||
force_reinstall=force_reinstall,
|
||||
upgrade_strategy=upgrade_strategy,
|
||||
py_version_info=py_version_info,
|
||||
)
|
||||
import pip._internal.resolution.legacy.resolver
|
||||
|
||||
return pip._internal.resolution.legacy.resolver.Resolver(
|
||||
preparer=preparer,
|
||||
finder=finder,
|
||||
wheel_cache=wheel_cache,
|
||||
make_install_req=make_install_req,
|
||||
use_user_site=use_user_site,
|
||||
ignore_dependencies=options.ignore_dependencies,
|
||||
ignore_installed=ignore_installed,
|
||||
ignore_requires_python=ignore_requires_python,
|
||||
force_reinstall=force_reinstall,
|
||||
upgrade_strategy=upgrade_strategy,
|
||||
py_version_info=py_version_info,
|
||||
)
|
||||
|
||||
def get_requirements(
|
||||
self,
|
||||
args: list[str],
|
||||
options: Values,
|
||||
finder: PackageFinder,
|
||||
session: PipSession,
|
||||
) -> list[InstallRequirement]:
|
||||
"""
|
||||
Parse command-line arguments into the corresponding requirements.
|
||||
"""
|
||||
requirements: list[InstallRequirement] = []
|
||||
|
||||
if not should_ignore_regular_constraints(options):
|
||||
constraints = parse_constraint_files(
|
||||
options.constraints, finder, options, session
|
||||
)
|
||||
requirements.extend(constraints)
|
||||
|
||||
for req in args:
|
||||
if not req.strip():
|
||||
continue
|
||||
req_to_add = install_req_from_line(
|
||||
req,
|
||||
comes_from=None,
|
||||
isolated=options.isolated_mode,
|
||||
user_supplied=True,
|
||||
config_settings=getattr(options, "config_settings", None),
|
||||
)
|
||||
requirements.append(req_to_add)
|
||||
|
||||
if options.dependency_groups:
|
||||
for req in parse_dependency_groups(options.dependency_groups):
|
||||
req_to_add = install_req_from_req_string(
|
||||
req,
|
||||
isolated=options.isolated_mode,
|
||||
user_supplied=True,
|
||||
)
|
||||
requirements.append(req_to_add)
|
||||
|
||||
for req in options.editables:
|
||||
req_to_add = install_req_from_editable(
|
||||
req,
|
||||
user_supplied=True,
|
||||
isolated=options.isolated_mode,
|
||||
config_settings=getattr(options, "config_settings", None),
|
||||
)
|
||||
requirements.append(req_to_add)
|
||||
|
||||
# NOTE: options.require_hashes may be set if --require-hashes is True
|
||||
for filename in options.requirements:
|
||||
for parsed_req in parse_requirements(
|
||||
filename, finder=finder, options=options, session=session
|
||||
):
|
||||
req_to_add = install_req_from_parsed_requirement(
|
||||
parsed_req,
|
||||
isolated=options.isolated_mode,
|
||||
user_supplied=True,
|
||||
config_settings=(
|
||||
parsed_req.options.get("config_settings")
|
||||
if parsed_req.options
|
||||
else None
|
||||
),
|
||||
)
|
||||
requirements.append(req_to_add)
|
||||
|
||||
if options.requirements_from_scripts:
|
||||
if len(options.requirements_from_scripts) > 1:
|
||||
raise CommandError("--requirements-from-script can only be given once")
|
||||
|
||||
script = options.requirements_from_scripts[0]
|
||||
try:
|
||||
script_metadata = pep723_metadata(script)
|
||||
except PEP723Exception as exc:
|
||||
raise CommandError(exc.msg)
|
||||
|
||||
script_requires_python = script_metadata.get("requires-python", "")
|
||||
|
||||
if script_requires_python and not options.ignore_requires_python:
|
||||
target_python = make_target_python(options)
|
||||
|
||||
if not check_requires_python(
|
||||
requires_python=script_requires_python,
|
||||
version_info=target_python.py_version_info,
|
||||
):
|
||||
raise UnsupportedPythonVersion(
|
||||
f"Script {script!r} requires a different Python: "
|
||||
f"{target_python.py_version} not in {script_requires_python!r}"
|
||||
)
|
||||
|
||||
for req in script_metadata.get("dependencies", []):
|
||||
req_to_add = install_req_from_req_string(
|
||||
req,
|
||||
isolated=options.isolated_mode,
|
||||
user_supplied=True,
|
||||
)
|
||||
requirements.append(req_to_add)
|
||||
|
||||
# If any requirement has hash options, enable hash checking.
|
||||
if any(req.has_hash_options for req in requirements):
|
||||
options.require_hashes = True
|
||||
|
||||
if not (
|
||||
args
|
||||
or options.editables
|
||||
or options.requirements
|
||||
or options.dependency_groups
|
||||
or options.requirements_from_scripts
|
||||
):
|
||||
opts = {"name": self.name}
|
||||
if options.find_links:
|
||||
raise CommandError(
|
||||
"You must give at least one requirement to {name} "
|
||||
'(maybe you meant "pip {name} {links}"?)'.format(
|
||||
**dict(opts, links=" ".join(options.find_links))
|
||||
)
|
||||
)
|
||||
else:
|
||||
raise CommandError(
|
||||
"You must give at least one requirement to {name} "
|
||||
'(see "pip help {name}")'.format(**opts)
|
||||
)
|
||||
|
||||
return requirements
|
||||
|
||||
@staticmethod
|
||||
def trace_basic_info(finder: PackageFinder) -> None:
|
||||
"""
|
||||
Trace basic information about the provided objects.
|
||||
"""
|
||||
# Display where finder is looking for packages
|
||||
search_scope = finder.search_scope
|
||||
locations = search_scope.get_formatted_locations()
|
||||
if locations:
|
||||
logger.info(locations)
|
||||
|
||||
def _build_package_finder(
|
||||
self,
|
||||
options: Values,
|
||||
session: PipSession,
|
||||
target_python: TargetPython | None = None,
|
||||
ignore_requires_python: bool | None = None,
|
||||
) -> PackageFinder:
|
||||
"""
|
||||
Create a package finder appropriate to this requirement command.
|
||||
|
||||
:param ignore_requires_python: Whether to ignore incompatible
|
||||
"Requires-Python" values in links. Defaults to False.
|
||||
"""
|
||||
link_collector = LinkCollector.create(session, options=options)
|
||||
selection_prefs = SelectionPreferences(
|
||||
allow_yanked=True,
|
||||
format_control=options.format_control,
|
||||
release_control=options.release_control,
|
||||
prefer_binary=options.prefer_binary,
|
||||
ignore_requires_python=ignore_requires_python,
|
||||
)
|
||||
|
||||
return PackageFinder.create(
|
||||
link_collector=link_collector,
|
||||
selection_prefs=selection_prefs,
|
||||
target_python=target_python,
|
||||
uploaded_prior_to=options.uploaded_prior_to,
|
||||
)
|
||||
235
venv/Lib/site-packages/pip/_internal/cli/spinners.py
Normal file
235
venv/Lib/site-packages/pip/_internal/cli/spinners.py
Normal file
@@ -0,0 +1,235 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import contextlib
|
||||
import itertools
|
||||
import logging
|
||||
import sys
|
||||
import time
|
||||
from collections.abc import Generator
|
||||
from typing import IO, Final
|
||||
|
||||
from pip._vendor.rich.console import (
|
||||
Console,
|
||||
ConsoleOptions,
|
||||
RenderableType,
|
||||
RenderResult,
|
||||
)
|
||||
from pip._vendor.rich.live import Live
|
||||
from pip._vendor.rich.measure import Measurement
|
||||
from pip._vendor.rich.text import Text
|
||||
|
||||
from pip._internal.utils.compat import WINDOWS
|
||||
from pip._internal.utils.logging import get_console, get_indentation
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
SPINNER_CHARS: Final = r"-\|/"
|
||||
SPINS_PER_SECOND: Final = 8
|
||||
|
||||
|
||||
class SpinnerInterface:
|
||||
def spin(self) -> None:
|
||||
raise NotImplementedError()
|
||||
|
||||
def finish(self, final_status: str) -> None:
|
||||
raise NotImplementedError()
|
||||
|
||||
|
||||
class InteractiveSpinner(SpinnerInterface):
|
||||
def __init__(
|
||||
self,
|
||||
message: str,
|
||||
file: IO[str] | None = None,
|
||||
spin_chars: str = SPINNER_CHARS,
|
||||
# Empirically, 8 updates/second looks nice
|
||||
min_update_interval_seconds: float = 1 / SPINS_PER_SECOND,
|
||||
):
|
||||
self._message = message
|
||||
if file is None:
|
||||
file = sys.stdout
|
||||
self._file = file
|
||||
self._rate_limiter = RateLimiter(min_update_interval_seconds)
|
||||
self._finished = False
|
||||
|
||||
self._spin_cycle = itertools.cycle(spin_chars)
|
||||
|
||||
self._file.write(" " * get_indentation() + self._message + " ... ")
|
||||
self._width = 0
|
||||
|
||||
def _write(self, status: str) -> None:
|
||||
assert not self._finished
|
||||
# Erase what we wrote before by backspacing to the beginning, writing
|
||||
# spaces to overwrite the old text, and then backspacing again
|
||||
backup = "\b" * self._width
|
||||
self._file.write(backup + " " * self._width + backup)
|
||||
# Now we have a blank slate to add our status
|
||||
self._file.write(status)
|
||||
self._width = len(status)
|
||||
self._file.flush()
|
||||
self._rate_limiter.reset()
|
||||
|
||||
def spin(self) -> None:
|
||||
if self._finished:
|
||||
return
|
||||
if not self._rate_limiter.ready():
|
||||
return
|
||||
self._write(next(self._spin_cycle))
|
||||
|
||||
def finish(self, final_status: str) -> None:
|
||||
if self._finished:
|
||||
return
|
||||
self._write(final_status)
|
||||
self._file.write("\n")
|
||||
self._file.flush()
|
||||
self._finished = True
|
||||
|
||||
|
||||
# Used for dumb terminals, non-interactive installs (no tty), etc.
|
||||
# We still print updates occasionally (once every 60 seconds by default) to
|
||||
# act as a keep-alive for systems like Travis-CI that take lack-of-output as
|
||||
# an indication that a task has frozen.
|
||||
class NonInteractiveSpinner(SpinnerInterface):
|
||||
def __init__(self, message: str, min_update_interval_seconds: float = 60.0) -> None:
|
||||
self._message = message
|
||||
self._finished = False
|
||||
self._rate_limiter = RateLimiter(min_update_interval_seconds)
|
||||
self._update("started")
|
||||
|
||||
def _update(self, status: str) -> None:
|
||||
assert not self._finished
|
||||
self._rate_limiter.reset()
|
||||
logger.info("%s: %s", self._message, status)
|
||||
|
||||
def spin(self) -> None:
|
||||
if self._finished:
|
||||
return
|
||||
if not self._rate_limiter.ready():
|
||||
return
|
||||
self._update("still running...")
|
||||
|
||||
def finish(self, final_status: str) -> None:
|
||||
if self._finished:
|
||||
return
|
||||
self._update(f"finished with status '{final_status}'")
|
||||
self._finished = True
|
||||
|
||||
|
||||
class RateLimiter:
|
||||
def __init__(self, min_update_interval_seconds: float) -> None:
|
||||
self._min_update_interval_seconds = min_update_interval_seconds
|
||||
self._last_update: float = 0
|
||||
|
||||
def ready(self) -> bool:
|
||||
now = time.time()
|
||||
delta = now - self._last_update
|
||||
return delta >= self._min_update_interval_seconds
|
||||
|
||||
def reset(self) -> None:
|
||||
self._last_update = time.time()
|
||||
|
||||
|
||||
@contextlib.contextmanager
|
||||
def open_spinner(message: str) -> Generator[SpinnerInterface, None, None]:
|
||||
# Interactive spinner goes directly to sys.stdout rather than being routed
|
||||
# through the logging system, but it acts like it has level INFO,
|
||||
# i.e. it's only displayed if we're at level INFO or better.
|
||||
# Non-interactive spinner goes through the logging system, so it is always
|
||||
# in sync with logging configuration.
|
||||
if sys.stdout.isatty() and logger.getEffectiveLevel() <= logging.INFO:
|
||||
spinner: SpinnerInterface = InteractiveSpinner(message)
|
||||
else:
|
||||
spinner = NonInteractiveSpinner(message)
|
||||
try:
|
||||
with hidden_cursor(sys.stdout):
|
||||
yield spinner
|
||||
except KeyboardInterrupt:
|
||||
spinner.finish("canceled")
|
||||
raise
|
||||
except Exception:
|
||||
spinner.finish("error")
|
||||
raise
|
||||
else:
|
||||
spinner.finish("done")
|
||||
|
||||
|
||||
class _PipRichSpinner:
|
||||
"""
|
||||
Custom rich spinner that matches the style of the legacy spinners.
|
||||
|
||||
(*) Updates will be handled in a background thread by a rich live panel
|
||||
which will call render() automatically at the appropriate time.
|
||||
"""
|
||||
|
||||
def __init__(self, label: str) -> None:
|
||||
self.label = label
|
||||
self._spin_cycle = itertools.cycle(SPINNER_CHARS)
|
||||
self._spinner_text = ""
|
||||
self._finished = False
|
||||
self._indent = get_indentation() * " "
|
||||
|
||||
def __rich_console__(
|
||||
self, console: Console, options: ConsoleOptions
|
||||
) -> RenderResult:
|
||||
yield self.render()
|
||||
|
||||
def __rich_measure__(
|
||||
self, console: Console, options: ConsoleOptions
|
||||
) -> Measurement:
|
||||
text = self.render()
|
||||
return Measurement.get(console, options, text)
|
||||
|
||||
def render(self) -> RenderableType:
|
||||
if not self._finished:
|
||||
self._spinner_text = next(self._spin_cycle)
|
||||
|
||||
return Text.assemble(self._indent, self.label, " ... ", self._spinner_text)
|
||||
|
||||
def finish(self, status: str) -> None:
|
||||
"""Stop spinning and set a final status message."""
|
||||
self._spinner_text = status
|
||||
self._finished = True
|
||||
|
||||
|
||||
@contextlib.contextmanager
|
||||
def open_rich_spinner(label: str, console: Console | None = None) -> Generator[None]:
|
||||
if not logger.isEnabledFor(logging.INFO):
|
||||
# Don't show spinner if --quiet is given.
|
||||
yield
|
||||
return
|
||||
|
||||
console = console or get_console()
|
||||
spinner = _PipRichSpinner(label)
|
||||
with Live(spinner, refresh_per_second=SPINS_PER_SECOND, console=console):
|
||||
try:
|
||||
yield
|
||||
except KeyboardInterrupt:
|
||||
spinner.finish("canceled")
|
||||
raise
|
||||
except Exception:
|
||||
spinner.finish("error")
|
||||
raise
|
||||
else:
|
||||
spinner.finish("done")
|
||||
|
||||
|
||||
HIDE_CURSOR = "\x1b[?25l"
|
||||
SHOW_CURSOR = "\x1b[?25h"
|
||||
|
||||
|
||||
@contextlib.contextmanager
|
||||
def hidden_cursor(file: IO[str]) -> Generator[None, None, None]:
|
||||
# The Windows terminal does not support the hide/show cursor ANSI codes,
|
||||
# even via colorama. So don't even try.
|
||||
if WINDOWS:
|
||||
yield
|
||||
# We don't want to clutter the output with control characters if we're
|
||||
# writing to a file, or if the user is running with --quiet.
|
||||
# See https://github.com/pypa/pip/issues/3418
|
||||
elif not file.isatty() or logger.getEffectiveLevel() > logging.INFO:
|
||||
yield
|
||||
else:
|
||||
file.write(HIDE_CURSOR)
|
||||
try:
|
||||
yield
|
||||
finally:
|
||||
file.write(SHOW_CURSOR)
|
||||
6
venv/Lib/site-packages/pip/_internal/cli/status_codes.py
Normal file
6
venv/Lib/site-packages/pip/_internal/cli/status_codes.py
Normal file
@@ -0,0 +1,6 @@
|
||||
SUCCESS = 0
|
||||
ERROR = 1
|
||||
UNKNOWN_ERROR = 2
|
||||
VIRTUALENV_NOT_FOUND = 3
|
||||
PREVIOUS_BUILD_DIR_ERROR = 4
|
||||
NO_MATCHES_FOUND = 23
|
||||
Reference in New Issue
Block a user