mirror of https://github.com/pypa/pip
Complete type annotations in pip/_internal/cli
Co-authored-by: Tzu-ping Chung <uranusjr@gmail.com>
This commit is contained in:
parent
9c2c52bdbd
commit
44b3c90bfd
|
@ -0,0 +1 @@
|
||||||
|
Fixed all the annotations from ``pip/_internal/cli``.
|
|
@ -1,7 +1,7 @@
|
||||||
import itertools
|
import itertools
|
||||||
import sys
|
import sys
|
||||||
from signal import SIGINT, default_int_handler, signal
|
from signal import SIGINT, default_int_handler, signal
|
||||||
from typing import Any, Dict, List
|
from typing import Any
|
||||||
|
|
||||||
from pip._vendor.progress.bar import Bar, FillingCirclesBar, IncrementalBar
|
from pip._vendor.progress.bar import Bar, FillingCirclesBar, IncrementalBar
|
||||||
from pip._vendor.progress.spinner import Spinner
|
from pip._vendor.progress.spinner import Spinner
|
||||||
|
@ -18,8 +18,7 @@ except Exception:
|
||||||
colorama = None
|
colorama = None
|
||||||
|
|
||||||
|
|
||||||
def _select_progress_class(preferred, fallback):
|
def _select_progress_class(preferred: Bar, fallback: Bar) -> Bar:
|
||||||
# type: (Bar, Bar) -> Bar
|
|
||||||
encoding = getattr(preferred.file, "encoding", None)
|
encoding = getattr(preferred.file, "encoding", None)
|
||||||
|
|
||||||
# If we don't know what encoding this file is in, then we'll just assume
|
# If we don't know what encoding this file is in, then we'll just assume
|
||||||
|
@ -67,8 +66,7 @@ class InterruptibleMixin:
|
||||||
download has already completed, for example.
|
download has already completed, for example.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args: Any, **kwargs: Any) -> None:
|
||||||
# type: (List[Any], Dict[Any, Any]) -> None
|
|
||||||
"""
|
"""
|
||||||
Save the original SIGINT handler for later.
|
Save the original SIGINT handler for later.
|
||||||
"""
|
"""
|
||||||
|
@ -85,8 +83,7 @@ class InterruptibleMixin:
|
||||||
if self.original_handler is None:
|
if self.original_handler is None:
|
||||||
self.original_handler = default_int_handler
|
self.original_handler = default_int_handler
|
||||||
|
|
||||||
def finish(self):
|
def finish(self) -> None:
|
||||||
# type: () -> None
|
|
||||||
"""
|
"""
|
||||||
Restore the original SIGINT handler after finishing.
|
Restore the original SIGINT handler after finishing.
|
||||||
|
|
||||||
|
@ -108,8 +105,7 @@ class InterruptibleMixin:
|
||||||
|
|
||||||
|
|
||||||
class SilentBar(Bar):
|
class SilentBar(Bar):
|
||||||
def update(self):
|
def update(self) -> None:
|
||||||
# type: () -> None
|
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
@ -122,28 +118,24 @@ class BlueEmojiBar(IncrementalBar):
|
||||||
|
|
||||||
|
|
||||||
class DownloadProgressMixin:
|
class DownloadProgressMixin:
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args: Any, **kwargs: Any) -> None:
|
||||||
# type: (List[Any], Dict[Any, Any]) -> None
|
|
||||||
# https://github.com/python/mypy/issues/5887
|
# https://github.com/python/mypy/issues/5887
|
||||||
super().__init__(*args, **kwargs) # type: ignore
|
super().__init__(*args, **kwargs) # type: ignore
|
||||||
self.message = (" " * (get_indentation() + 2)) + self.message # type: str
|
self.message = (" " * (get_indentation() + 2)) + self.message # type: str
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def downloaded(self):
|
def downloaded(self) -> str:
|
||||||
# type: () -> str
|
|
||||||
return format_size(self.index) # type: ignore
|
return format_size(self.index) # type: ignore
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def download_speed(self):
|
def download_speed(self) -> str:
|
||||||
# type: () -> str
|
|
||||||
# Avoid zero division errors...
|
# Avoid zero division errors...
|
||||||
if self.avg == 0.0: # type: ignore
|
if self.avg == 0.0: # type: ignore
|
||||||
return "..."
|
return "..."
|
||||||
return format_size(1 / self.avg) + "/s" # type: ignore
|
return format_size(1 / self.avg) + "/s" # type: ignore
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def pretty_eta(self):
|
def pretty_eta(self) -> str:
|
||||||
# type: () -> str
|
|
||||||
if self.eta: # type: ignore
|
if self.eta: # type: ignore
|
||||||
return f"eta {self.eta_td}" # type: ignore
|
return f"eta {self.eta_td}" # type: ignore
|
||||||
return ""
|
return ""
|
||||||
|
@ -158,8 +150,7 @@ class DownloadProgressMixin:
|
||||||
|
|
||||||
|
|
||||||
class WindowsMixin:
|
class WindowsMixin:
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args: Any, **kwargs: Any) -> None:
|
||||||
# type: (List[Any], Dict[Any, Any]) -> None
|
|
||||||
# The Windows terminal does not support the hide/show cursor ANSI codes
|
# The Windows terminal does not support the hide/show cursor ANSI codes
|
||||||
# even with colorama. So we'll ensure that hide_cursor is False on
|
# even with colorama. So we'll ensure that hide_cursor is False on
|
||||||
# Windows.
|
# Windows.
|
||||||
|
@ -221,14 +212,12 @@ class DownloadProgressSpinner(
|
||||||
file = sys.stdout
|
file = sys.stdout
|
||||||
suffix = "%(downloaded)s %(download_speed)s"
|
suffix = "%(downloaded)s %(download_speed)s"
|
||||||
|
|
||||||
def next_phase(self):
|
def next_phase(self) -> str:
|
||||||
# type: () -> str
|
|
||||||
if not hasattr(self, "_phaser"):
|
if not hasattr(self, "_phaser"):
|
||||||
self._phaser = itertools.cycle(self.phases)
|
self._phaser = itertools.cycle(self.phases)
|
||||||
return next(self._phaser)
|
return next(self._phaser)
|
||||||
|
|
||||||
def update(self):
|
def update(self) -> None:
|
||||||
# type: () -> None
|
|
||||||
message = self.message % self
|
message = self.message % self
|
||||||
phase = self.next_phase()
|
phase = self.next_phase()
|
||||||
suffix = self.suffix % self
|
suffix = self.suffix % self
|
||||||
|
|
|
@ -50,14 +50,12 @@ class SessionCommandMixin(CommandContextMixIn):
|
||||||
A class mixin for command classes needing _build_session().
|
A class mixin for command classes needing _build_session().
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self) -> None:
|
||||||
# type: () -> None
|
|
||||||
super().__init__()
|
super().__init__()
|
||||||
self._session = None # Optional[PipSession]
|
self._session: Optional[PipSession] = None
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def _get_index_urls(cls, options):
|
def _get_index_urls(cls, options: Values) -> Optional[List[str]]:
|
||||||
# type: (Values) -> Optional[List[str]]
|
|
||||||
"""Return a list of index urls from user-provided options."""
|
"""Return a list of index urls from user-provided options."""
|
||||||
index_urls = []
|
index_urls = []
|
||||||
if not getattr(options, "no_index", False):
|
if not getattr(options, "no_index", False):
|
||||||
|
@ -70,8 +68,7 @@ class SessionCommandMixin(CommandContextMixIn):
|
||||||
# Return None rather than an empty list
|
# Return None rather than an empty list
|
||||||
return index_urls or None
|
return index_urls or None
|
||||||
|
|
||||||
def get_default_session(self, options):
|
def get_default_session(self, options: Values) -> PipSession:
|
||||||
# type: (Values) -> PipSession
|
|
||||||
"""Get a default-managed session."""
|
"""Get a default-managed session."""
|
||||||
if self._session is None:
|
if self._session is None:
|
||||||
self._session = self.enter_context(self._build_session(options))
|
self._session = self.enter_context(self._build_session(options))
|
||||||
|
@ -81,8 +78,12 @@ class SessionCommandMixin(CommandContextMixIn):
|
||||||
assert self._session is not None
|
assert self._session is not None
|
||||||
return self._session
|
return self._session
|
||||||
|
|
||||||
def _build_session(self, options, retries=None, timeout=None):
|
def _build_session(
|
||||||
# type: (Values, Optional[int], Optional[int]) -> PipSession
|
self,
|
||||||
|
options: Values,
|
||||||
|
retries: Optional[int] = None,
|
||||||
|
timeout: Optional[int] = None,
|
||||||
|
) -> PipSession:
|
||||||
assert not options.cache_dir or os.path.isabs(options.cache_dir)
|
assert not options.cache_dir or os.path.isabs(options.cache_dir)
|
||||||
session = PipSession(
|
session = PipSession(
|
||||||
cache=(
|
cache=(
|
||||||
|
@ -126,8 +127,7 @@ class IndexGroupCommand(Command, SessionCommandMixin):
|
||||||
This also corresponds to the commands that permit the pip version check.
|
This also corresponds to the commands that permit the pip version check.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def handle_pip_version_check(self, options):
|
def handle_pip_version_check(self, options: Values) -> None:
|
||||||
# type: (Values) -> None
|
|
||||||
"""
|
"""
|
||||||
Do the pip version check if not disabled.
|
Do the pip version check if not disabled.
|
||||||
|
|
||||||
|
@ -154,8 +154,7 @@ KEEPABLE_TEMPDIR_TYPES = [
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
def warn_if_run_as_root():
|
def warn_if_run_as_root() -> None:
|
||||||
# type: () -> None
|
|
||||||
"""Output a warning for sudo users on Unix.
|
"""Output a warning for sudo users on Unix.
|
||||||
|
|
||||||
In a virtual environment, sudo pip still writes to virtualenv.
|
In a virtual environment, sudo pip still writes to virtualenv.
|
||||||
|
@ -184,19 +183,18 @@ def warn_if_run_as_root():
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def with_cleanup(func):
|
def with_cleanup(func: Any) -> Any:
|
||||||
# type: (Any) -> Any
|
|
||||||
"""Decorator for common logic related to managing temporary
|
"""Decorator for common logic related to managing temporary
|
||||||
directories.
|
directories.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def configure_tempdir_registry(registry):
|
def configure_tempdir_registry(registry: TempDirectoryTypeRegistry) -> None:
|
||||||
# type: (TempDirectoryTypeRegistry) -> None
|
|
||||||
for t in KEEPABLE_TEMPDIR_TYPES:
|
for t in KEEPABLE_TEMPDIR_TYPES:
|
||||||
registry.set_delete(t, False)
|
registry.set_delete(t, False)
|
||||||
|
|
||||||
def wrapper(self, options, args):
|
def wrapper(
|
||||||
# type: (RequirementCommand, Values, List[Any]) -> Optional[int]
|
self: RequirementCommand, options: Values, args: List[Any]
|
||||||
|
) -> Optional[int]:
|
||||||
assert self.tempdir_registry is not None
|
assert self.tempdir_registry is not None
|
||||||
if options.no_clean:
|
if options.no_clean:
|
||||||
configure_tempdir_registry(self.tempdir_registry)
|
configure_tempdir_registry(self.tempdir_registry)
|
||||||
|
@ -214,15 +212,13 @@ def with_cleanup(func):
|
||||||
|
|
||||||
|
|
||||||
class RequirementCommand(IndexGroupCommand):
|
class RequirementCommand(IndexGroupCommand):
|
||||||
def __init__(self, *args, **kw):
|
def __init__(self, *args: Any, **kw: Any) -> None:
|
||||||
# type: (Any, Any) -> None
|
|
||||||
super().__init__(*args, **kw)
|
super().__init__(*args, **kw)
|
||||||
|
|
||||||
self.cmd_opts.add_option(cmdoptions.no_clean())
|
self.cmd_opts.add_option(cmdoptions.no_clean())
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def determine_resolver_variant(options):
|
def determine_resolver_variant(options: Values) -> str:
|
||||||
# type: (Values) -> str
|
|
||||||
"""Determines which resolver should be used, based on the given options."""
|
"""Determines which resolver should be used, based on the given options."""
|
||||||
if "legacy-resolver" in options.deprecated_features_enabled:
|
if "legacy-resolver" in options.deprecated_features_enabled:
|
||||||
return "legacy"
|
return "legacy"
|
||||||
|
@ -232,15 +228,14 @@ class RequirementCommand(IndexGroupCommand):
|
||||||
@classmethod
|
@classmethod
|
||||||
def make_requirement_preparer(
|
def make_requirement_preparer(
|
||||||
cls,
|
cls,
|
||||||
temp_build_dir, # type: TempDirectory
|
temp_build_dir: TempDirectory,
|
||||||
options, # type: Values
|
options: Values,
|
||||||
req_tracker, # type: RequirementTracker
|
req_tracker: RequirementTracker,
|
||||||
session, # type: PipSession
|
session: PipSession,
|
||||||
finder, # type: PackageFinder
|
finder: PackageFinder,
|
||||||
use_user_site, # type: bool
|
use_user_site: bool,
|
||||||
download_dir=None, # type: str
|
download_dir: Optional[str] = None,
|
||||||
):
|
) -> RequirementPreparer:
|
||||||
# type: (...) -> RequirementPreparer
|
|
||||||
"""
|
"""
|
||||||
Create a RequirementPreparer instance for the given parameters.
|
Create a RequirementPreparer instance for the given parameters.
|
||||||
"""
|
"""
|
||||||
|
@ -283,19 +278,18 @@ class RequirementCommand(IndexGroupCommand):
|
||||||
@classmethod
|
@classmethod
|
||||||
def make_resolver(
|
def make_resolver(
|
||||||
cls,
|
cls,
|
||||||
preparer, # type: RequirementPreparer
|
preparer: RequirementPreparer,
|
||||||
finder, # type: PackageFinder
|
finder: PackageFinder,
|
||||||
options, # type: Values
|
options: Values,
|
||||||
wheel_cache=None, # type: Optional[WheelCache]
|
wheel_cache: Optional[WheelCache] = None,
|
||||||
use_user_site=False, # type: bool
|
use_user_site: bool = False,
|
||||||
ignore_installed=True, # type: bool
|
ignore_installed: bool = True,
|
||||||
ignore_requires_python=False, # type: bool
|
ignore_requires_python: bool = False,
|
||||||
force_reinstall=False, # type: bool
|
force_reinstall: bool = False,
|
||||||
upgrade_strategy="to-satisfy-only", # type: str
|
upgrade_strategy: str = "to-satisfy-only",
|
||||||
use_pep517=None, # type: Optional[bool]
|
use_pep517: Optional[bool] = None,
|
||||||
py_version_info=None, # type: Optional[Tuple[int, ...]]
|
py_version_info: Optional[Tuple[int, ...]] = None,
|
||||||
):
|
) -> BaseResolver:
|
||||||
# type: (...) -> BaseResolver
|
|
||||||
"""
|
"""
|
||||||
Create a Resolver instance for the given parameters.
|
Create a Resolver instance for the given parameters.
|
||||||
"""
|
"""
|
||||||
|
@ -342,12 +336,11 @@ class RequirementCommand(IndexGroupCommand):
|
||||||
|
|
||||||
def get_requirements(
|
def get_requirements(
|
||||||
self,
|
self,
|
||||||
args, # type: List[str]
|
args: List[str],
|
||||||
options, # type: Values
|
options: Values,
|
||||||
finder, # type: PackageFinder
|
finder: PackageFinder,
|
||||||
session, # type: PipSession
|
session: PipSession,
|
||||||
):
|
) -> List[InstallRequirement]:
|
||||||
# type: (...) -> List[InstallRequirement]
|
|
||||||
"""
|
"""
|
||||||
Parse command-line arguments into the corresponding requirements.
|
Parse command-line arguments into the corresponding requirements.
|
||||||
"""
|
"""
|
||||||
|
@ -421,8 +414,7 @@ class RequirementCommand(IndexGroupCommand):
|
||||||
return requirements
|
return requirements
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def trace_basic_info(finder):
|
def trace_basic_info(finder: PackageFinder) -> None:
|
||||||
# type: (PackageFinder) -> None
|
|
||||||
"""
|
"""
|
||||||
Trace basic information about the provided objects.
|
Trace basic information about the provided objects.
|
||||||
"""
|
"""
|
||||||
|
@ -434,12 +426,11 @@ class RequirementCommand(IndexGroupCommand):
|
||||||
|
|
||||||
def _build_package_finder(
|
def _build_package_finder(
|
||||||
self,
|
self,
|
||||||
options, # type: Values
|
options: Values,
|
||||||
session, # type: PipSession
|
session: PipSession,
|
||||||
target_python=None, # type: Optional[TargetPython]
|
target_python: Optional[TargetPython] = None,
|
||||||
ignore_requires_python=None, # type: Optional[bool]
|
ignore_requires_python: Optional[bool] = None,
|
||||||
):
|
) -> PackageFinder:
|
||||||
# type: (...) -> PackageFinder
|
|
||||||
"""
|
"""
|
||||||
Create a package finder appropriate to this requirement command.
|
Create a package finder appropriate to this requirement command.
|
||||||
|
|
||||||
|
|
|
@ -14,25 +14,22 @@ logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
class SpinnerInterface:
|
class SpinnerInterface:
|
||||||
def spin(self):
|
def spin(self) -> None:
|
||||||
# type: () -> None
|
|
||||||
raise NotImplementedError()
|
raise NotImplementedError()
|
||||||
|
|
||||||
def finish(self, final_status):
|
def finish(self, final_status: str) -> None:
|
||||||
# type: (str) -> None
|
|
||||||
raise NotImplementedError()
|
raise NotImplementedError()
|
||||||
|
|
||||||
|
|
||||||
class InteractiveSpinner(SpinnerInterface):
|
class InteractiveSpinner(SpinnerInterface):
|
||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
message,
|
message: str,
|
||||||
file=None,
|
file: IO[str] = None,
|
||||||
spin_chars="-\\|/",
|
spin_chars: str = "-\\|/",
|
||||||
# Empirically, 8 updates/second looks nice
|
# Empirically, 8 updates/second looks nice
|
||||||
min_update_interval_seconds=0.125,
|
min_update_interval_seconds: float = 0.125,
|
||||||
):
|
):
|
||||||
# type: (str, IO[str], str, float) -> None
|
|
||||||
self._message = message
|
self._message = message
|
||||||
if file is None:
|
if file is None:
|
||||||
file = sys.stdout
|
file = sys.stdout
|
||||||
|
@ -45,8 +42,7 @@ class InteractiveSpinner(SpinnerInterface):
|
||||||
self._file.write(" " * get_indentation() + self._message + " ... ")
|
self._file.write(" " * get_indentation() + self._message + " ... ")
|
||||||
self._width = 0
|
self._width = 0
|
||||||
|
|
||||||
def _write(self, status):
|
def _write(self, status: str) -> None:
|
||||||
# type: (str) -> None
|
|
||||||
assert not self._finished
|
assert not self._finished
|
||||||
# Erase what we wrote before by backspacing to the beginning, writing
|
# Erase what we wrote before by backspacing to the beginning, writing
|
||||||
# spaces to overwrite the old text, and then backspacing again
|
# spaces to overwrite the old text, and then backspacing again
|
||||||
|
@ -58,16 +54,14 @@ class InteractiveSpinner(SpinnerInterface):
|
||||||
self._file.flush()
|
self._file.flush()
|
||||||
self._rate_limiter.reset()
|
self._rate_limiter.reset()
|
||||||
|
|
||||||
def spin(self):
|
def spin(self) -> None:
|
||||||
# type: () -> None
|
|
||||||
if self._finished:
|
if self._finished:
|
||||||
return
|
return
|
||||||
if not self._rate_limiter.ready():
|
if not self._rate_limiter.ready():
|
||||||
return
|
return
|
||||||
self._write(next(self._spin_cycle))
|
self._write(next(self._spin_cycle))
|
||||||
|
|
||||||
def finish(self, final_status):
|
def finish(self, final_status: str) -> None:
|
||||||
# type: (str) -> None
|
|
||||||
if self._finished:
|
if self._finished:
|
||||||
return
|
return
|
||||||
self._write(final_status)
|
self._write(final_status)
|
||||||
|
@ -81,29 +75,25 @@ class InteractiveSpinner(SpinnerInterface):
|
||||||
# act as a keep-alive for systems like Travis-CI that take lack-of-output as
|
# act as a keep-alive for systems like Travis-CI that take lack-of-output as
|
||||||
# an indication that a task has frozen.
|
# an indication that a task has frozen.
|
||||||
class NonInteractiveSpinner(SpinnerInterface):
|
class NonInteractiveSpinner(SpinnerInterface):
|
||||||
def __init__(self, message, min_update_interval_seconds=60):
|
def __init__(self, message: str, min_update_interval_seconds: float = 60.0) -> None:
|
||||||
# type: (str, float) -> None
|
|
||||||
self._message = message
|
self._message = message
|
||||||
self._finished = False
|
self._finished = False
|
||||||
self._rate_limiter = RateLimiter(min_update_interval_seconds)
|
self._rate_limiter = RateLimiter(min_update_interval_seconds)
|
||||||
self._update("started")
|
self._update("started")
|
||||||
|
|
||||||
def _update(self, status):
|
def _update(self, status: str) -> None:
|
||||||
# type: (str) -> None
|
|
||||||
assert not self._finished
|
assert not self._finished
|
||||||
self._rate_limiter.reset()
|
self._rate_limiter.reset()
|
||||||
logger.info("%s: %s", self._message, status)
|
logger.info("%s: %s", self._message, status)
|
||||||
|
|
||||||
def spin(self):
|
def spin(self) -> None:
|
||||||
# type: () -> None
|
|
||||||
if self._finished:
|
if self._finished:
|
||||||
return
|
return
|
||||||
if not self._rate_limiter.ready():
|
if not self._rate_limiter.ready():
|
||||||
return
|
return
|
||||||
self._update("still running...")
|
self._update("still running...")
|
||||||
|
|
||||||
def finish(self, final_status):
|
def finish(self, final_status: str) -> None:
|
||||||
# type: (str) -> None
|
|
||||||
if self._finished:
|
if self._finished:
|
||||||
return
|
return
|
||||||
self._update(f"finished with status '{final_status}'")
|
self._update(f"finished with status '{final_status}'")
|
||||||
|
@ -111,25 +101,21 @@ class NonInteractiveSpinner(SpinnerInterface):
|
||||||
|
|
||||||
|
|
||||||
class RateLimiter:
|
class RateLimiter:
|
||||||
def __init__(self, min_update_interval_seconds):
|
def __init__(self, min_update_interval_seconds: float) -> None:
|
||||||
# type: (float) -> None
|
|
||||||
self._min_update_interval_seconds = min_update_interval_seconds
|
self._min_update_interval_seconds = min_update_interval_seconds
|
||||||
self._last_update = 0 # type: float
|
self._last_update = 0 # type: float
|
||||||
|
|
||||||
def ready(self):
|
def ready(self) -> bool:
|
||||||
# type: () -> bool
|
|
||||||
now = time.time()
|
now = time.time()
|
||||||
delta = now - self._last_update
|
delta = now - self._last_update
|
||||||
return delta >= self._min_update_interval_seconds
|
return delta >= self._min_update_interval_seconds
|
||||||
|
|
||||||
def reset(self):
|
def reset(self) -> None:
|
||||||
# type: () -> None
|
|
||||||
self._last_update = time.time()
|
self._last_update = time.time()
|
||||||
|
|
||||||
|
|
||||||
@contextlib.contextmanager
|
@contextlib.contextmanager
|
||||||
def open_spinner(message):
|
def open_spinner(message: str) -> Iterator[SpinnerInterface]:
|
||||||
# type: (str) -> Iterator[SpinnerInterface]
|
|
||||||
# Interactive spinner goes directly to sys.stdout rather than being routed
|
# Interactive spinner goes directly to sys.stdout rather than being routed
|
||||||
# through the logging system, but it acts like it has level INFO,
|
# 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.
|
# i.e. it's only displayed if we're at level INFO or better.
|
||||||
|
@ -153,8 +139,7 @@ def open_spinner(message):
|
||||||
|
|
||||||
|
|
||||||
@contextlib.contextmanager
|
@contextlib.contextmanager
|
||||||
def hidden_cursor(file):
|
def hidden_cursor(file: IO[str]) -> Iterator[None]:
|
||||||
# type: (IO[str]) -> Iterator[None]
|
|
||||||
# The Windows terminal does not support the hide/show cursor ANSI codes,
|
# The Windows terminal does not support the hide/show cursor ANSI codes,
|
||||||
# even via colorama. So don't even try.
|
# even via colorama. So don't even try.
|
||||||
if WINDOWS:
|
if WINDOWS:
|
||||||
|
|
Loading…
Reference in New Issue