Воркед
This commit is contained in:
1 parent
ac32c55ab9
commit
ee94d8e529
1138 files changed
+55771
-77901
No files matched your search
@@ -1,7 +1,5 @@
|
||||
import inspect
|
||||
import io
|
||||
import os
|
||||
import platform
|
||||
import sys
|
||||
import threading
|
||||
import zlib
|
||||
@@ -34,6 +32,8 @@ from typing import (
|
||||
cast,
|
||||
)
|
||||
|
||||
from pip._vendor.rich._null_file import NULL_FILE
|
||||
|
||||
if sys.version_info >= (3, 8):
|
||||
from typing import Literal, Protocol, runtime_checkable
|
||||
else:
|
||||
@@ -46,6 +46,7 @@ else:
|
||||
from . import errors, themes
|
||||
from ._emoji_replace import _emoji_replace
|
||||
from ._export_format import CONSOLE_HTML_FORMAT, CONSOLE_SVG_FORMAT
|
||||
from ._fileno import get_fileno
|
||||
from ._log_render import FormatTimeCallable, LogRender
|
||||
from .align import Align, AlignMethod
|
||||
from .color import ColorSystem, blend_rgb
|
||||
@@ -74,7 +75,7 @@ if TYPE_CHECKING:
|
||||
|
||||
JUPYTER_DEFAULT_COLUMNS = 115
|
||||
JUPYTER_DEFAULT_LINES = 100
|
||||
WINDOWS = platform.system() == "Windows"
|
||||
WINDOWS = sys.platform == "win32"
|
||||
|
||||
HighlighterType = Callable[[Union[str, "Text"]], "Text"]
|
||||
JustifyMethod = Literal["default", "left", "center", "right", "full"]
|
||||
@@ -88,15 +89,15 @@ class NoChange:
|
||||
NO_CHANGE = NoChange()
|
||||
|
||||
try:
|
||||
_STDIN_FILENO = sys.__stdin__.fileno()
|
||||
_STDIN_FILENO = sys.__stdin__.fileno() # type: ignore[union-attr]
|
||||
except Exception:
|
||||
_STDIN_FILENO = 0
|
||||
try:
|
||||
_STDOUT_FILENO = sys.__stdout__.fileno()
|
||||
_STDOUT_FILENO = sys.__stdout__.fileno() # type: ignore[union-attr]
|
||||
except Exception:
|
||||
_STDOUT_FILENO = 1
|
||||
try:
|
||||
_STDERR_FILENO = sys.__stderr__.fileno()
|
||||
_STDERR_FILENO = sys.__stderr__.fileno() # type: ignore[union-attr]
|
||||
except Exception:
|
||||
_STDERR_FILENO = 2
|
||||
|
||||
@@ -104,7 +105,11 @@ _STD_STREAMS = (_STDIN_FILENO, _STDOUT_FILENO, _STDERR_FILENO)
|
||||
_STD_STREAMS_OUTPUT = (_STDOUT_FILENO, _STDERR_FILENO)
|
||||
|
||||
|
||||
_TERM_COLORS = {"256color": ColorSystem.EIGHT_BIT, "16color": ColorSystem.STANDARD}
|
||||
_TERM_COLORS = {
|
||||
"kitty": ColorSystem.EIGHT_BIT,
|
||||
"256color": ColorSystem.EIGHT_BIT,
|
||||
"16color": ColorSystem.STANDARD,
|
||||
}
|
||||
|
||||
|
||||
class ConsoleDimensions(NamedTuple):
|
||||
@@ -272,6 +277,7 @@ class ConsoleRenderable(Protocol):
|
||||
|
||||
# A type that may be rendered by Console.
|
||||
RenderableType = Union[ConsoleRenderable, RichCast, str]
|
||||
"""A string or any object that may be rendered by Rich."""
|
||||
|
||||
# The result of calling a __rich_console__ method.
|
||||
RenderResult = Iterable[Union[RenderableType, Segment]]
|
||||
@@ -516,7 +522,11 @@ def _is_jupyter() -> bool: # pragma: no cover
|
||||
return False
|
||||
ipython = get_ipython() # type: ignore[name-defined]
|
||||
shell = ipython.__class__.__name__
|
||||
if "google.colab" in str(ipython.__class__) or shell == "ZMQInteractiveShell":
|
||||
if (
|
||||
"google.colab" in str(ipython.__class__)
|
||||
or os.getenv("DATABRICKS_RUNTIME_VERSION")
|
||||
or shell == "ZMQInteractiveShell"
|
||||
):
|
||||
return True # Jupyter notebook or qtconsole
|
||||
elif shell == "TerminalInteractiveShell":
|
||||
return False # Terminal running IPython
|
||||
@@ -697,7 +707,11 @@ class Console:
|
||||
self._height = height
|
||||
|
||||
self._color_system: Optional[ColorSystem]
|
||||
self._force_terminal = force_terminal
|
||||
|
||||
self._force_terminal = None
|
||||
if force_terminal is not None:
|
||||
self._force_terminal = force_terminal
|
||||
|
||||
self._file = file
|
||||
self.quiet = quiet
|
||||
self.stderr = stderr
|
||||
@@ -739,13 +753,15 @@ class Console:
|
||||
self._is_alt_screen = False
|
||||
|
||||
def __repr__(self) -> str:
|
||||
return f"<console width={self.width} {str(self._color_system)}>"
|
||||
return f"<console width={self.width} {self._color_system!s}>"
|
||||
|
||||
@property
|
||||
def file(self) -> IO[str]:
|
||||
"""Get the file object to write to."""
|
||||
file = self._file or (sys.stderr if self.stderr else sys.stdout)
|
||||
file = getattr(file, "rich_proxied_file", file)
|
||||
if file is None:
|
||||
file = NULL_FILE
|
||||
return file
|
||||
|
||||
@file.setter
|
||||
@@ -928,6 +944,16 @@ class Console:
|
||||
# Return False for Idle which claims to be a tty but can't handle ansi codes
|
||||
return False
|
||||
|
||||
if self.is_jupyter:
|
||||
# return False for Jupyter, which may have FORCE_COLOR set
|
||||
return False
|
||||
|
||||
# If FORCE_COLOR env var has any value at all, we assume a terminal.
|
||||
force_color = self._environ.get("FORCE_COLOR")
|
||||
if force_color is not None:
|
||||
self._force_terminal = True
|
||||
return True
|
||||
|
||||
isatty: Optional[Callable[[], bool]] = getattr(self.file, "isatty", None)
|
||||
try:
|
||||
return False if isatty is None else isatty()
|
||||
@@ -979,19 +1005,14 @@ class Console:
|
||||
width: Optional[int] = None
|
||||
height: Optional[int] = None
|
||||
|
||||
if WINDOWS: # pragma: no cover
|
||||
streams = _STD_STREAMS_OUTPUT if WINDOWS else _STD_STREAMS
|
||||
for file_descriptor in streams:
|
||||
try:
|
||||
width, height = os.get_terminal_size()
|
||||
width, height = os.get_terminal_size(file_descriptor)
|
||||
except (AttributeError, ValueError, OSError): # Probably not a terminal
|
||||
pass
|
||||
else:
|
||||
for file_descriptor in _STD_STREAMS:
|
||||
try:
|
||||
width, height = os.get_terminal_size(file_descriptor)
|
||||
except (AttributeError, ValueError, OSError):
|
||||
pass
|
||||
else:
|
||||
break
|
||||
else:
|
||||
break
|
||||
|
||||
columns = self._environ.get("COLUMNS")
|
||||
if columns is not None and columns.isdigit():
|
||||
@@ -1125,7 +1146,7 @@ class Console:
|
||||
status: RenderableType,
|
||||
*,
|
||||
spinner: str = "dots",
|
||||
spinner_style: str = "status.spinner",
|
||||
spinner_style: StyleType = "status.spinner",
|
||||
speed: float = 1.0,
|
||||
refresh_per_second: float = 12.5,
|
||||
) -> "Status":
|
||||
@@ -1282,7 +1303,7 @@ class Console:
|
||||
|
||||
renderable = rich_cast(renderable)
|
||||
if hasattr(renderable, "__rich_console__") and not isclass(renderable):
|
||||
render_iterable = renderable.__rich_console__(self, _options) # type: ignore[union-attr]
|
||||
render_iterable = renderable.__rich_console__(self, _options)
|
||||
elif isinstance(renderable, str):
|
||||
text_renderable = self.render_str(
|
||||
renderable, highlight=_options.highlight, markup=_options.markup
|
||||
@@ -1359,9 +1380,14 @@ class Console:
|
||||
extra_lines = render_options.height - len(lines)
|
||||
if extra_lines > 0:
|
||||
pad_line = [
|
||||
[Segment(" " * render_options.max_width, style), Segment("\n")]
|
||||
if new_lines
|
||||
else [Segment(" " * render_options.max_width, style)]
|
||||
(
|
||||
[
|
||||
Segment(" " * render_options.max_width, style),
|
||||
Segment("\n"),
|
||||
]
|
||||
if new_lines
|
||||
else [Segment(" " * render_options.max_width, style)]
|
||||
)
|
||||
]
|
||||
lines.extend(pad_line * extra_lines)
|
||||
|
||||
@@ -1410,9 +1436,11 @@ class Console:
|
||||
rich_text.overflow = overflow
|
||||
else:
|
||||
rich_text = Text(
|
||||
_emoji_replace(text, default_variant=self._emoji_variant)
|
||||
if emoji_enabled
|
||||
else text,
|
||||
(
|
||||
_emoji_replace(text, default_variant=self._emoji_variant)
|
||||
if emoji_enabled
|
||||
else text
|
||||
),
|
||||
justify=justify,
|
||||
overflow=overflow,
|
||||
style=style,
|
||||
@@ -1502,14 +1530,18 @@ class Console:
|
||||
if text:
|
||||
sep_text = Text(sep, justify=justify, end=end)
|
||||
append(sep_text.join(text))
|
||||
del text[:]
|
||||
text.clear()
|
||||
|
||||
for renderable in objects:
|
||||
renderable = rich_cast(renderable)
|
||||
if isinstance(renderable, str):
|
||||
append_text(
|
||||
self.render_str(
|
||||
renderable, emoji=emoji, markup=markup, highlighter=_highlighter
|
||||
renderable,
|
||||
emoji=emoji,
|
||||
markup=markup,
|
||||
highlight=highlight,
|
||||
highlighter=_highlighter,
|
||||
)
|
||||
)
|
||||
elif isinstance(renderable, Text):
|
||||
@@ -1701,7 +1733,7 @@ class Console:
|
||||
indent: Union[None, int, str] = 2,
|
||||
highlight: bool = True,
|
||||
skip_keys: bool = False,
|
||||
ensure_ascii: bool = True,
|
||||
ensure_ascii: bool = False,
|
||||
check_circular: bool = True,
|
||||
allow_nan: bool = True,
|
||||
default: Optional[Callable[[Any], Any]] = None,
|
||||
@@ -1899,7 +1931,6 @@ class Console:
|
||||
end (str, optional): String to write at end of print data. Defaults to "\\\\n".
|
||||
style (Union[str, Style], optional): A style to apply to output. Defaults to None.
|
||||
justify (str, optional): One of "left", "right", "center", or "full". Defaults to ``None``.
|
||||
overflow (str, optional): Overflow method: "crop", "fold", or "ellipsis". Defaults to None.
|
||||
emoji (Optional[bool], optional): Enable emoji code, or ``None`` to use console default. Defaults to None.
|
||||
markup (Optional[bool], optional): Enable markup, or ``None`` to use console default. Defaults to None.
|
||||
highlight (Optional[bool], optional): Enable automatic highlighting, or ``None`` to use console default. Defaults to None.
|
||||
@@ -1960,6 +1991,20 @@ class Console:
|
||||
):
|
||||
buffer_extend(line)
|
||||
|
||||
def on_broken_pipe(self) -> None:
|
||||
"""This function is called when a `BrokenPipeError` is raised.
|
||||
|
||||
This can occur when piping Textual output in Linux and macOS.
|
||||
The default implementation is to exit the app, but you could implement
|
||||
this method in a subclass to change the behavior.
|
||||
|
||||
See https://docs.python.org/3/library/signal.html#note-on-sigpipe for details.
|
||||
"""
|
||||
self.quiet = True
|
||||
devnull = os.open(os.devnull, os.O_WRONLY)
|
||||
os.dup2(devnull, sys.stdout.fileno())
|
||||
raise SystemExit(1)
|
||||
|
||||
def _check_buffer(self) -> None:
|
||||
"""Check if the buffer may be rendered. Render it if it can (e.g. Console.quiet is False)
|
||||
Rendering is supported on Windows, Unix and Jupyter environments. For
|
||||
@@ -1969,13 +2014,21 @@ class Console:
|
||||
if self.quiet:
|
||||
del self._buffer[:]
|
||||
return
|
||||
|
||||
try:
|
||||
self._write_buffer()
|
||||
except BrokenPipeError:
|
||||
self.on_broken_pipe()
|
||||
|
||||
def _write_buffer(self) -> None:
|
||||
"""Write the buffer to the output file."""
|
||||
|
||||
with self._lock:
|
||||
if self.record:
|
||||
if self.record and not self._buffer_index:
|
||||
with self._record_buffer_lock:
|
||||
self._record_buffer.extend(self._buffer[:])
|
||||
|
||||
if self._buffer_index == 0:
|
||||
|
||||
if self.is_jupyter: # pragma: no cover
|
||||
from .jupyter import display
|
||||
|
||||
@@ -1985,31 +2038,50 @@ class Console:
|
||||
if WINDOWS:
|
||||
use_legacy_windows_render = False
|
||||
if self.legacy_windows:
|
||||
try:
|
||||
fileno = get_fileno(self.file)
|
||||
if fileno is not None:
|
||||
use_legacy_windows_render = (
|
||||
self.file.fileno() in _STD_STREAMS_OUTPUT
|
||||
fileno in _STD_STREAMS_OUTPUT
|
||||
)
|
||||
except (ValueError, io.UnsupportedOperation):
|
||||
pass
|
||||
|
||||
if use_legacy_windows_render:
|
||||
from pip._vendor.rich._win32_console import LegacyWindowsTerm
|
||||
from pip._vendor.rich._windows_renderer import legacy_windows_render
|
||||
|
||||
legacy_windows_render(
|
||||
self._buffer[:], LegacyWindowsTerm(self.file)
|
||||
)
|
||||
buffer = self._buffer[:]
|
||||
if self.no_color and self._color_system:
|
||||
buffer = list(Segment.remove_color(buffer))
|
||||
|
||||
legacy_windows_render(buffer, LegacyWindowsTerm(self.file))
|
||||
else:
|
||||
# Either a non-std stream on legacy Windows, or modern Windows.
|
||||
text = self._render_buffer(self._buffer[:])
|
||||
# https://bugs.python.org/issue37871
|
||||
# https://github.com/python/cpython/issues/82052
|
||||
# We need to avoid writing more than 32Kb in a single write, due to the above bug
|
||||
write = self.file.write
|
||||
for line in text.splitlines(True):
|
||||
try:
|
||||
write(line)
|
||||
except UnicodeEncodeError as error:
|
||||
error.reason = f"{error.reason}\n*** You may need to add PYTHONIOENCODING=utf-8 to your environment ***"
|
||||
raise
|
||||
# Worse case scenario, every character is 4 bytes of utf-8
|
||||
MAX_WRITE = 32 * 1024 // 4
|
||||
try:
|
||||
if len(text) <= MAX_WRITE:
|
||||
write(text)
|
||||
else:
|
||||
batch: List[str] = []
|
||||
batch_append = batch.append
|
||||
size = 0
|
||||
for line in text.splitlines(True):
|
||||
if size + len(line) > MAX_WRITE and batch:
|
||||
write("".join(batch))
|
||||
batch.clear()
|
||||
size = 0
|
||||
batch_append(line)
|
||||
size += len(line)
|
||||
if batch:
|
||||
write("".join(batch))
|
||||
batch.clear()
|
||||
except UnicodeEncodeError as error:
|
||||
error.reason = f"{error.reason}\n*** You may need to add PYTHONIOENCODING=utf-8 to your environment ***"
|
||||
raise
|
||||
else:
|
||||
text = self._render_buffer(self._buffer[:])
|
||||
try:
|
||||
@@ -2122,7 +2194,7 @@ class Console:
|
||||
|
||||
"""
|
||||
text = self.export_text(clear=clear, styles=styles)
|
||||
with open(path, "wt", encoding="utf-8") as write_file:
|
||||
with open(path, "w", encoding="utf-8") as write_file:
|
||||
write_file.write(text)
|
||||
|
||||
def export_html(
|
||||
@@ -2228,7 +2300,7 @@ class Console:
|
||||
code_format=code_format,
|
||||
inline_styles=inline_styles,
|
||||
)
|
||||
with open(path, "wt", encoding="utf-8") as write_file:
|
||||
with open(path, "w", encoding="utf-8") as write_file:
|
||||
write_file.write(html)
|
||||
|
||||
def export_svg(
|
||||
@@ -2238,18 +2310,24 @@ class Console:
|
||||
theme: Optional[TerminalTheme] = None,
|
||||
clear: bool = True,
|
||||
code_format: str = CONSOLE_SVG_FORMAT,
|
||||
font_aspect_ratio: float = 0.61,
|
||||
unique_id: Optional[str] = None,
|
||||
) -> str:
|
||||
"""
|
||||
Generate an SVG from the console contents (requires record=True in Console constructor).
|
||||
|
||||
Args:
|
||||
path (str): The path to write the SVG to.
|
||||
title (str): The title of the tab in the output image
|
||||
title (str, optional): The title of the tab in the output image
|
||||
theme (TerminalTheme, optional): The ``TerminalTheme`` object to use to style the terminal
|
||||
clear (bool, optional): Clear record buffer after exporting. Defaults to ``True``
|
||||
code_format (str): Format string used to generate the SVG. Rich will inject a number of variables
|
||||
code_format (str, optional): Format string used to generate the SVG. Rich will inject a number of variables
|
||||
into the string in order to form the final SVG output. The default template used and the variables
|
||||
injected by Rich can be found by inspecting the ``console.CONSOLE_SVG_FORMAT`` variable.
|
||||
font_aspect_ratio (float, optional): The width to height ratio of the font used in the ``code_format``
|
||||
string. Defaults to 0.61, which is the width to height ratio of Fira Code (the default font).
|
||||
If you aren't specifying a different font inside ``code_format``, you probably don't need this.
|
||||
unique_id (str, optional): unique id that is used as the prefix for various elements (CSS styles, node
|
||||
ids). If not set, this defaults to a computed value based on the recorded content.
|
||||
"""
|
||||
|
||||
from pip._vendor.rich.cells import cell_len
|
||||
@@ -2293,7 +2371,7 @@ class Console:
|
||||
|
||||
width = self.width
|
||||
char_height = 20
|
||||
char_width = char_height * 0.61
|
||||
char_width = char_height * font_aspect_ratio
|
||||
line_height = char_height * 1.22
|
||||
|
||||
margin_top = 1
|
||||
@@ -2345,14 +2423,16 @@ class Console:
|
||||
if clear:
|
||||
self._record_buffer.clear()
|
||||
|
||||
unique_id = "terminal-" + str(
|
||||
zlib.adler32(
|
||||
("".join(segment.text for segment in segments)).encode(
|
||||
"utf-8", "ignore"
|
||||
if unique_id is None:
|
||||
unique_id = "terminal-" + str(
|
||||
zlib.adler32(
|
||||
("".join(repr(segment) for segment in segments)).encode(
|
||||
"utf-8",
|
||||
"ignore",
|
||||
)
|
||||
+ title.encode("utf-8", "ignore")
|
||||
)
|
||||
+ title.encode("utf-8", "ignore")
|
||||
)
|
||||
)
|
||||
y = 0
|
||||
for y, line in enumerate(Segment.split_and_crop_lines(segments, length=width)):
|
||||
x = 0
|
||||
@@ -2482,25 +2562,34 @@ class Console:
|
||||
theme: Optional[TerminalTheme] = None,
|
||||
clear: bool = True,
|
||||
code_format: str = CONSOLE_SVG_FORMAT,
|
||||
font_aspect_ratio: float = 0.61,
|
||||
unique_id: Optional[str] = None,
|
||||
) -> None:
|
||||
"""Generate an SVG file from the console contents (requires record=True in Console constructor).
|
||||
|
||||
Args:
|
||||
path (str): The path to write the SVG to.
|
||||
title (str): The title of the tab in the output image
|
||||
title (str, optional): The title of the tab in the output image
|
||||
theme (TerminalTheme, optional): The ``TerminalTheme`` object to use to style the terminal
|
||||
clear (bool, optional): Clear record buffer after exporting. Defaults to ``True``
|
||||
code_format (str): Format string used to generate the SVG. Rich will inject a number of variables
|
||||
code_format (str, optional): Format string used to generate the SVG. Rich will inject a number of variables
|
||||
into the string in order to form the final SVG output. The default template used and the variables
|
||||
injected by Rich can be found by inspecting the ``console.CONSOLE_SVG_FORMAT`` variable.
|
||||
font_aspect_ratio (float, optional): The width to height ratio of the font used in the ``code_format``
|
||||
string. Defaults to 0.61, which is the width to height ratio of Fira Code (the default font).
|
||||
If you aren't specifying a different font inside ``code_format``, you probably don't need this.
|
||||
unique_id (str, optional): unique id that is used as the prefix for various elements (CSS styles, node
|
||||
ids). If not set, this defaults to a computed value based on the recorded content.
|
||||
"""
|
||||
svg = self.export_svg(
|
||||
title=title,
|
||||
theme=theme,
|
||||
clear=clear,
|
||||
code_format=code_format,
|
||||
font_aspect_ratio=font_aspect_ratio,
|
||||
unique_id=unique_id,
|
||||
)
|
||||
with open(path, "wt", encoding="utf-8") as write_file:
|
||||
with open(path, "w", encoding="utf-8") as write_file:
|
||||
write_file.write(svg)
|
||||
|
||||
|
||||
|
||||
Reference in new issue
Block a user