| Server IP : 54.37.205.81 / Your IP : 216.73.216.76 Web Server : nginx/1.22.1 System : Linux vps-249481fa 6.1.0-50-cloud-amd64 #1 SMP PREEMPT_DYNAMIC Debian 6.1.176-1 (2026-07-02) x86_64 User : debian ( 1000) PHP Version : 8.2.32 Disable Function : NONE MySQL : OFF | cURL : ON | WGET : ON | Perl : ON | Python : OFF | Sudo : ON | Pkexec : OFF Directory : /lib/python3/dist-packages/pipx/ |
Upload File : |
import shutil
import sys
from contextlib import contextmanager
from threading import Event, Thread
from typing import Generator, List
from pipx.constants import WINDOWS
from pipx.emojis import EMOJI_SUPPORT
stderr_is_tty = sys.stderr.isatty()
CLEAR_LINE = "\033[K"
EMOJI_ANIMATION_FRAMES = ["⣷", "⣯", "⣟", "⡿", "⢿", "⣻", "⣽", "⣾"]
NONEMOJI_ANIMATION_FRAMES = ["", ".", "..", "..."]
EMOJI_FRAME_PERIOD = 0.1
NONEMOJI_FRAME_PERIOD = 1
MINIMUM_COLS_ALLOW_ANIMATION = 16
if WINDOWS:
import ctypes
class _CursorInfo(ctypes.Structure):
_fields_ = [("size", ctypes.c_int), ("visible", ctypes.c_byte)]
def _env_supports_animation() -> bool:
(term_cols, _) = shutil.get_terminal_size(fallback=(0, 0))
return stderr_is_tty and term_cols > MINIMUM_COLS_ALLOW_ANIMATION
@contextmanager
def animate(
message: str, do_animation: bool, *, delay: float = 0
) -> Generator[None, None, None]:
if not do_animation or not _env_supports_animation():
# No animation, just a single print of message
sys.stderr.write(f"{message}...\n")
yield
return
event = Event()
if EMOJI_SUPPORT:
animate_at_beginning_of_line = True
symbols = EMOJI_ANIMATION_FRAMES
period = EMOJI_FRAME_PERIOD
else:
animate_at_beginning_of_line = False
symbols = NONEMOJI_ANIMATION_FRAMES
period = NONEMOJI_FRAME_PERIOD
thread_kwargs = {
"message": message,
"event": event,
"symbols": symbols,
"delay": delay,
"period": period,
"animate_at_beginning_of_line": animate_at_beginning_of_line,
}
t = Thread(target=print_animation, kwargs=thread_kwargs)
t.start()
try:
yield
finally:
event.set()
clear_line()
def print_animation(
*,
message: str,
event: Event,
symbols: List[str],
delay: float,
period: float,
animate_at_beginning_of_line: bool,
) -> None:
(term_cols, _) = shutil.get_terminal_size(fallback=(9999, 24))
event.wait(delay)
while not event.wait(0):
for s in symbols:
if animate_at_beginning_of_line:
max_message_len = term_cols - len(f"{s} ... ")
cur_line = f"{s} {message:.{max_message_len}}"
if len(message) > max_message_len:
cur_line += "..."
else:
max_message_len = term_cols - len("... ")
cur_line = f"{message:.{max_message_len}}{s}"
clear_line()
sys.stderr.write(cur_line)
sys.stderr.flush()
if event.wait(period):
break
# for Windows pre-ANSI-terminal-support (before Windows 10 TH2 (v1511))
# https://stackoverflow.com/a/10455937
def win_cursor(visible: bool) -> None:
ci = _CursorInfo()
handle = ctypes.windll.kernel32.GetStdHandle(-11) # type: ignore[attr-defined]
ctypes.windll.kernel32.GetConsoleCursorInfo(handle, ctypes.byref(ci)) # type: ignore[attr-defined]
ci.visible = visible
ctypes.windll.kernel32.SetConsoleCursorInfo(handle, ctypes.byref(ci)) # type: ignore[attr-defined]
def hide_cursor() -> None:
if stderr_is_tty:
if WINDOWS:
win_cursor(visible=False)
else:
sys.stderr.write("\033[?25l")
sys.stderr.flush()
def show_cursor() -> None:
if stderr_is_tty:
if WINDOWS:
win_cursor(visible=True)
else:
sys.stderr.write("\033[?25h")
sys.stderr.flush()
def clear_line() -> None:
"""Clears current line and positions cursor at start of line"""
sys.stderr.write(f"\r{CLEAR_LINE}")
sys.stdout.write(f"\r{CLEAR_LINE}")