From 47bb80ff677903e5012a1a4c22eefeca14f874ef Mon Sep 17 00:00:00 2001 From: Will McGugan Date: Sat, 6 Jun 2020 12:08:07 +0100 Subject: [PATCH] overflow --- README.md | 4 +- examples/{just.py => justify.py} | 0 examples/overflow.py | 11 +++ rich/_log_render.py | 2 +- rich/_ratio.py | 28 ------- rich/console.py | 43 ++++++---- rich/containers.py | 24 +++--- rich/markdown.py | 10 +-- rich/progress.py | 8 +- rich/table.py | 71 ++++------------ rich/text.py | 134 ++++++++++++++++++++----------- tests/_card_render.py | 2 +- tests/test_columns.py | 9 ++- tests/test_log.py | 7 +- tests/test_logging.py | 2 +- tests/test_progress.py | 4 +- tests/test_text.py | 27 ++++--- 17 files changed, 199 insertions(+), 187 deletions(-) rename examples/{just.py => justify.py} (100%) create mode 100644 examples/overflow.py diff --git a/README.md b/README.md index c749ddcc..c976671b 100644 --- a/README.md +++ b/README.md @@ -8,12 +8,14 @@ # Rich -Rich is a Python library for rendering _rich_ text and beautiful formatting to the terminal. +Rich is a Python library for _rich_ text and beautiful formatting in the terminal. The [Rich API](https://rich.readthedocs.io/en/latest/) makes it easy to add colorful text (up to 16.7 million colors) with styles (bold, italic, underline etc.) to your script or application. Rich can also render pretty tables, progress bars, markdown, syntax highlighted source code, and tracebacks -- out of the box. ![Features](https://github.com/willmcgugan/rich/raw/master/imgs/features.png) +For a video introduction to Rich see [calmcode.io](https://calmcode.io/rich/introduction.html). + ## Compatibility Rich works with Linux, OSX, and Windows. True color / emoji works with new Windows Terminal, classic terminal is limited to 8 colors. diff --git a/examples/just.py b/examples/justify.py similarity index 100% rename from examples/just.py rename to examples/justify.py diff --git a/examples/overflow.py b/examples/overflow.py new file mode 100644 index 00000000..85f57d21 --- /dev/null +++ b/examples/overflow.py @@ -0,0 +1,11 @@ +from typing import List +from rich.console import Console, OverflowMethod +from rich.text import Text + +console = Console() +supercali = "supercalifragilisticexpialidocious" + +overflow_methods: List[OverflowMethod] = ["fold", "crop", "ellipsis"] +for overflow in overflow_methods: + console.rule(overflow) + console.print(supercali, overflow=overflow, width=10) diff --git a/rich/_log_render.py b/rich/_log_render.py index aaf877ec..63d7dc62 100644 --- a/rich/_log_render.py +++ b/rich/_log_render.py @@ -48,7 +48,7 @@ class LogRender: output.add_column(style="log.time") if self.show_level: output.add_column(style="log.level", width=8) - output.add_column(ratio=1, style="log.message", justify=None) + output.add_column(ratio=1, style="log.message") if self.show_path and path: output.add_column(style="log.path") row: List["RenderableType"] = [] diff --git a/rich/_ratio.py b/rich/_ratio.py index c358d5c0..5564e749 100644 --- a/rich/_ratio.py +++ b/rich/_ratio.py @@ -3,34 +3,6 @@ from math import ceil from typing import List -def ratio_distribute(total: int, ratios: List[int]) -> List[int]: - """Divide an integer total in to parts based on ratios. - - Args: - total (int): The total to divide. - ratios (List[int]): A list of integer ratios. - minimums (List[int]): List of minimum values for each slot. - - Returns: - List[int]: A list of integers garanteed to sum to total. - """ - total_ratio = sum(ratios) - assert total_ratio > 0, "Sum of ratios must be > 0" - - total_remaining = total - distributed_total: List[int] = [] - append = distributed_total.append - - for ratio in ratios: - if total_ratio > 0: - distributed = int(ceil(ratio * total_remaining / total_ratio)) - else: - distributed = total_remaining - append(distributed) - total_ratio -= ratio - total_remaining -= distributed - return distributed_total - def ratio_reduce( total: int, ratios: List[int], maximums: List[int], values: List[int] diff --git a/rich/console.py b/rich/console.py index 4756130d..703c4f72 100644 --- a/rich/console.py +++ b/rich/console.py @@ -58,8 +58,8 @@ if TYPE_CHECKING: # pragma: no cover WINDOWS = platform.system() == "Windows" HighlighterType = Callable[[Union[str, "Text"]], "Text"] -JustifyValues = Optional[Literal["left", "center", "right", "full"]] -OverflowValues = Literal["crop", "fold", "ellipsis"] +JustifyMethod = Literal["default", "left", "center", "right", "full"] +OverflowMethod = Literal["crop", "fold", "ellipsis"] CONSOLE_HTML_FORMAT = """\ @@ -91,8 +91,8 @@ class ConsoleOptions: max_width: int is_terminal: bool encoding: str - justify: Optional[JustifyValues] = None - overflow: Optional[OverflowValues] = None + justify: Optional[JustifyMethod] = None + overflow: Optional[OverflowMethod] = None no_wrap: bool = False def update( @@ -100,8 +100,8 @@ class ConsoleOptions: width: int = None, min_width: int = None, max_width: int = None, - justify: JustifyValues = None, - overflow: OverflowValues = None, + justify: JustifyMethod = None, + overflow: OverflowMethod = None, no_wrap: bool = None, ) -> "ConsoleOptions": """Update values, return a copy.""" @@ -557,7 +557,8 @@ class Console: self, text: str, style: Union[str, Style] = "", - justify: JustifyValues = None, + justify: JustifyMethod = None, + overflow: OverflowMethod = None, emoji: bool = None, markup: bool = None, highlighter: HighlighterType = None, @@ -568,7 +569,8 @@ class Console: Args: text (str): Text to render. style (Union[str, Style], optional): Style to apply to rendered text. - justify (str, optional): One of "left", "right", "center", or "full". Defaults to ``None``. + justify (str, optional): Justify method: "left", "center", "full", "right". Defaults to ``None``. + overflow (str, optional): Overflow method: "crop", "fold", or "ellipsis". Defaults to ``None``. emoji (Optional[bool], optional): Enable emoji, or ``None`` to use Console default. markup (Optional[bool], optional): Enable markup, or ``None`` to use Console default. highlighter (HighlighterType, optional): Optional highlighter to apply. @@ -581,10 +583,13 @@ class Console: if markup_enabled: rich_text = render_markup(text, style=style, emoji=emoji_enabled) + rich_text.justify = justify + rich_text.overflow = overflow else: rich_text = Text( _emoji_replace(text) if emoji_enabled else text, justify=justify, + overflow=overflow, style=style, ) @@ -626,7 +631,7 @@ class Console: objects: Iterable[Any], sep: str, end: str, - justify: JustifyValues = None, + justify: JustifyMethod = None, emoji: bool = None, markup: bool = None, highlight: bool = None, @@ -732,10 +737,12 @@ class Console: sep=" ", end="\n", style: Union[str, Style] = None, - justify: JustifyValues = None, + justify: JustifyMethod = None, + overflow: OverflowMethod = None, emoji: bool = None, markup: bool = None, highlight: bool = None, + width: int = None, ) -> None: r"""Print to the console. @@ -744,10 +751,12 @@ class Console: sep (str, optional): String to write between print data. Defaults to " ". 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``. - 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. + justify (str, optional): Overflowmethod: "left", "right", "center", or "full". Defaults to ``None``. + overflow (str, optional): Overflow method: "crop", "fold", or "ellipisis". 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``. + width (Optional[int], optional): Width of output, or ``None`` to auto-detect. Defaults to ``None``. """ if not objects: self.line() @@ -763,7 +772,9 @@ class Console: markup=markup, highlight=highlight, ) - render_options = self.options + render_options = self.options.update( + justify=justify, overflow=overflow, width=width + ) extend = self._buffer.extend render = self.render if style is None: @@ -804,7 +815,7 @@ class Console: *objects: Any, sep=" ", end="\n", - justify: JustifyValues = None, + justify: JustifyMethod = None, emoji: bool = None, markup: bool = None, highlight: bool = None, diff --git a/rich/containers.py b/rich/containers.py index e6c117b6..0e2a2c5c 100644 --- a/rich/containers.py +++ b/rich/containers.py @@ -19,7 +19,8 @@ if TYPE_CHECKING: Console, ConsoleOptions, ConsoleRenderable, - OverflowValues, + JustifyMethod, + OverflowMethod, RenderResult, RenderableType, ) @@ -110,32 +111,35 @@ class Lines: self, console: "Console", width: int, - align: Literal["none", "left", "center", "right", "full"] = "left", - overflow: Optional["OverflowValues"] = None, + justify: "JustifyMethod" = "left", + overflow: "OverflowMethod" = "fold", ) -> None: - """Pad each line with spaces to a given width. + """Justify and overflow text to a given width. Args: + console (Console): Console instance. width (int): Number of characters per line. + justify (str, optional): Default justify method for text: "left", "center", "full" or "right". Defaults to "left". + overflow (str, optional): Default overflow for text: "crop", "fold", or "ellipisis". Defaults to "fold". """ from .text import Text - if align == "left": + if justify == "left": for line in self._lines: line.truncate(width, overflow=overflow, pad=True) - elif align == "center": + elif justify == "center": for line in self._lines: line.rstrip() line.truncate(width, overflow=overflow) - line.pad_right((width - cell_len(line.plain)) // 2) - line.pad_left(width - cell_len(line.plain)) - elif align == "right": + line.pad_left((width - cell_len(line.plain)) // 2) + line.pad_right(width - cell_len(line.plain)) + elif justify == "right": for line in self._lines: line.rstrip() line.truncate(width, overflow=overflow) line.pad_left(width - cell_len(line.plain)) - elif align == "full": + elif justify == "full": for line_index, line in enumerate(self._lines): if line_index == len(self._lines) - 1: break diff --git a/rich/markdown.py b/rich/markdown.py index fde5f0ff..c80b7527 100644 --- a/rich/markdown.py +++ b/rich/markdown.py @@ -10,7 +10,7 @@ from .console import ( Console, ConsoleOptions, ConsoleRenderable, - JustifyValues, + JustifyMethod, RenderResult, Segment, ) @@ -113,13 +113,13 @@ class Paragraph(TextElement): """A Paragraph.""" style_name = "markdown.paragraph" - justify: JustifyValues + justify: JustifyMethod @classmethod def create(cls, markdown: "Markdown", node) -> "Paragraph": return cls(justify=markdown.justify or "left") - def __init__(self, justify: JustifyValues) -> None: + def __init__(self, justify: JustifyMethod) -> None: self.justify = justify def __rich_console__( @@ -382,7 +382,7 @@ class Markdown(JupyterMixin): Args: markup (str): A string containing markdown. code_theme (str, optional): Pygments theme for code blocks. Defaults to "monokai". - justify (JustifyValues, optional): Justify value for paragraphs. Defaults to None. + justify (JustifyMethod, optional): Justify value for paragraphs. Defaults to None. style (Union[str, Style], optional): Optional style to apply to markdown. hyperlinks (bool, optional): Enable hyperlinks. Defaults to ``True``. """ @@ -403,7 +403,7 @@ class Markdown(JupyterMixin): self, markup: str, code_theme: str = "monokai", - justify: JustifyValues = None, + justify: JustifyMethod = None, style: Union[str, Style] = "none", hyperlinks: bool = True, ) -> None: diff --git a/rich/progress.py b/rich/progress.py index 11083837..e0ecc8a5 100644 --- a/rich/progress.py +++ b/rich/progress.py @@ -27,7 +27,7 @@ from typing import ( from . import get_console from .bar import Bar -from .console import Console, JustifyValues, RenderGroup, RenderableType +from .console import Console, JustifyMethod, RenderGroup, RenderableType from .highlighter import Highlighter from . import filesize from .live_render import LiveRender @@ -129,7 +129,7 @@ class TextColumn(ProgressColumn): self, text_format: str, style: StyleType = "none", - justify: JustifyValues = "left", + justify: JustifyMethod = "left", markup: bool = True, highlighter: Highlighter = None, ) -> None: @@ -688,7 +688,7 @@ class Progress: sep=" ", end="\n", style: Union[str, Style] = None, - justify: JustifyValues = None, + justify: JustifyMethod = None, emoji: bool = None, markup: bool = None, highlight: bool = None, @@ -716,7 +716,7 @@ class Progress: *objects: Any, sep=" ", end="\n", - justify: JustifyValues = None, + justify: JustifyMethod = None, emoji: bool = None, markup: bool = None, highlight: bool = None, diff --git a/rich/table.py b/rich/table.py index 69d1a656..f66ef3d0 100644 --- a/rich/table.py +++ b/rich/table.py @@ -29,7 +29,8 @@ if TYPE_CHECKING: from .console import ( Console, ConsoleOptions, - JustifyValues, + JustifyMethod, + OverflowMethod, RenderableType, RenderResult, ) @@ -58,10 +59,10 @@ class Column: style: StyleType = "none" """StyleType: The style of the column.""" - justify: "JustifyValues" = "left" + justify: "JustifyMethod" = "left" """str: How to justify text within the column ("left", "center", "right", or "full")""" - overflow: "OverflowValues" = "ellipsis" + overflow: "OverflowMethod" = "ellipsis" width: Optional[int] = None """Optional[int]: Width of the column, or ``None`` (default) to auto calculate width.""" @@ -230,8 +231,12 @@ class Table(JupyterMixin): table_width = ( sum(self._calculate_column_widths(console, max_width)) + extra_width ) - - return Measurement(1, table_width) + _measure_column = self._measure_column + minimum_width = max( + _measure_column(console, column, max_width).minimum + for column in self.columns + ) + return Measurement(minimum_width, table_width) @property def padding(self) -> Tuple[int, int, int, int]: @@ -251,8 +256,8 @@ class Table(JupyterMixin): header_style: StyleType = None, footer_style: StyleType = None, style: StyleType = None, - justify: "JustifyValues" = "left", - overflow: "OverflowValues" = "ellipsis", + justify: "JustifyMethod" = "left", + overflow: "OverflowMethod" = "ellipsis", width: int = None, ratio: int = None, no_wrap: bool = False, @@ -267,7 +272,7 @@ class Table(JupyterMixin): header_style (Union[str, Style], optional): Style for the header. Defaults to "none". footer_style (Union[str, Style], optional): Style for the header. Defaults to "none". style (Union[str, Style], optional): Style for the column cells. Defaults to "none". - justify (JustifyValues, optional): Alignment for cells. Defaults to "left". + justify (JustifyMethod, optional): Alignment for cells. Defaults to "left". width (int, optional): A minimum width in characters. Defaults to None. ratio (int, optional): Flexible ratio for the column. Defaults to None. no_wrap (bool, optional): Set to ``True`` to disable wrapping of this column. @@ -398,6 +403,7 @@ class Table(JupyterMixin): widths[index] = fixed_widths[index] + next(iter_flex_widths) table_width = sum(widths) + # Reduce rows that not no_wrap if table_width > max_width: excess_width = table_width - max_width widths = ratio_reduce( @@ -408,6 +414,7 @@ class Table(JupyterMixin): ) table_width = sum(widths) + # Reduce rows that are no_wrap if table_width > max_width: excess_width = table_width - max_width widths = ratio_reduce( @@ -425,54 +432,6 @@ class Table(JupyterMixin): ) table_width = sum(widths) - # flex_widths = [_range.span for _range in width_ranges] - # excess_width = table_width - max_width - - # widths = [ - # max(width_range.minimum + 2, width - excess_width) - # for width_range, width, excess_width in zip( - # width_ranges, widths, ratio_divide(excess_width, flex_widths) - # ) - # ] - - # flex_widths = [ - # 0 if column.no_wrap else 1 - # for width_range, column in zip(width_ranges, columns) - # ] - # if any(flex_widths): - # widths = [ - # max(width_range.minimum, width - excess_width) - # for width_range, width, excess_width in zip( - # width_ranges, - # widths, - # ratio_divide(excess_width, flex_widths), - # ) - # ] - table_width = sum(widths) - # if table_width > max_width: - # excess_width = table_width - max_width - # flex_widths = [0 if column.no_wrap else 1 for column in columns] - - # if any(flex_widths): - # widths = [ - # max(width_range.minimum, width - excess_width) - # for width_range, width, excess_width in zip( - # width_ranges, - # widths, - # ratio_divide(excess_width, flex_widths), - # ) - # ] - # table_width = sum(widths) - # if table_width > max_width: - # flex_widths = [1 for column in columns] - # excess_width = table_width - max_width - # widths = [ - # width - excess_width - # for width_range, width, excess_width in zip( - # width_ranges, widths, ratio_divide(excess_width, flex_widths), - # ) - # ] - elif table_width < max_width and self.expand: pad_widths = ratio_divide(max_width - table_width, widths) widths = [_width + pad for _width, pad in zip(widths, pad_widths)] diff --git a/rich/text.py b/rich/text.py index 1534255e..aa42996b 100644 --- a/rich/text.py +++ b/rich/text.py @@ -30,13 +30,17 @@ if TYPE_CHECKING: # pragma: no cover from .console import ( Console, ConsoleOptions, - JustifyValues, - OverflowValues, + JustifyMethod, + OverflowMethod, RenderResult, RenderableType, ) -DEFAULT_OVERFLOW = "fold" +DEFAULT_JUSTIFY: "JustifyMethod" = "default" +DEFAULT_OVERFLOW: "OverflowMethod" = "fold" + + +_re_whitespace = re.compile(r"\s+$") class Span(NamedTuple): @@ -98,7 +102,8 @@ class Text(JupyterMixin): Args: text (str, optional): Default unstyled text. Defaults to "". style (Union[str, Style], optional): Base style for text. Defaults to "". - justify (str, optional): Default alignment for text, "left", "center", "full" or "right". Defaults to None. + justify (str, optional): Justify method: "left", "center", "full", "right". Defaults to None. + overflow (str, optional): Overflow method: "crop", "fold", "ellipsis". Defaults to None. end (str, optional): Character to end text with. Defaults to "\n". tab_size (int): Number of spaces per tab, or ``None`` to use ``console.tab_size``. Defaults to 8. """ @@ -107,8 +112,8 @@ class Text(JupyterMixin): self, text: str = "", style: Union[str, Style] = "", - justify: "JustifyValues" = None, - overflow: "OverflowValues" = None, + justify: "JustifyMethod" = None, + overflow: "OverflowMethod" = None, no_wrap: bool = False, end: str = "\n", tab_size: Optional[int] = 8, @@ -162,14 +167,16 @@ class Text(JupyterMixin): text: str, style: Union[str, Style] = "", emoji: bool = True, - justify: "JustifyValues" = None, + justify: "JustifyMethod" = None, + overflow: "OverflowMethod" = None, ) -> "Text": """Create Text instance from markup. Args: text (str): A string containing console markup. emoji (bool, optional): Also render emoji code. Defaults to True. - justify (str, optional): Default alignment for text, "left", "center", "full" or "right". Defaults to None. + justify (str, optional): Justify method: "left", "center", "full", "right". Defaults to None. + overflow (str, optional): Overflow method: "crop", "fold", "ellipsis". Defaults to None. Returns: Text: A Text instance with markup rendered. @@ -178,6 +185,7 @@ class Text(JupyterMixin): rendered_text = render(text, style, emoji=emoji) rendered_text.justify = justify + rendered_text.overflow = overflow return rendered_text @classmethod @@ -185,7 +193,8 @@ class Text(JupyterMixin): cls, *parts: Union[str, "Text", Tuple[str, StyleType]], style: Union[str, Style] = "", - justify: "JustifyValues" = None, + justify: "JustifyMethod" = None, + overflow: "OverflowMethod" = None, end: str = "\n", tab_size: int = 8, ) -> "Text": @@ -194,14 +203,17 @@ class Text(JupyterMixin): Args: style (Union[str, Style], optional): Base style for text. Defaults to "". - justify (str, optional): Default alignment for text, "left", "center", "full" or "right". Defaults to None. + justify (str, optional): Justify method: "left", "center", "full", "right". Defaults to None. + overflow (str, optional): Overflow method: "crop", "fold", "ellipsis". Defaults to None. end (str, optional): Character to end text with. Defaults to "\n". tab_size (int): Number of spaces per tab, or ``None`` to use ``console.tab_size``. Defaults to 8. Returns: Text: A new text instance. """ - text = cls(style=style, justify=justify, end=end, tab_size=tab_size) + text = cls( + style=style, justify=justify, overflow=overflow, end=end, tab_size=tab_size + ) append = text.append for part in parts: if isinstance(part, (Text, str)): @@ -221,11 +233,12 @@ class Text(JupyterMixin): @plain.setter def plain(self, new_text: str) -> None: """Set the text to a new value.""" - self._text[:] = [new_text] - old_length = self._length - self._length = len(new_text) - if old_length > self._length: - self._trim_spans() + if new_text != self.plain: + self._text[:] = [new_text] + old_length = self._length + self._length = len(new_text) + if old_length > self._length: + self._trim_spans() @property def spans(self) -> List[Span]: @@ -253,7 +266,7 @@ class Text(JupyterMixin): """Return a copy of this instance.""" copy_self = Text( self.plain, - style=self.style.copy() if isinstance(self.style, Style) else self.style, + style=self.style, justify=self.justify, overflow=self.overflow, no_wrap=self.no_wrap, @@ -369,6 +382,20 @@ class Text(JupyterMixin): """Trip whitespace from end of text.""" self.plain = self.plain.rstrip() + def rstrip_end(self, size: int): + """Remove whitespace beyond a certain width at the end of the text. + + Args: + size (int): The desired size of the text. + """ + text_length = len(self) + if text_length > size: + excess = text_length - size + whitespace_match = _re_whitespace.search(self.plain) + if whitespace_match is not None: + whitespace_count = len(whitespace_match.group(0)) + self.plain = self.plain[: -min(whitespace_count, excess)] + def set_length(self, new_length: int) -> None: """Set new length of the text, clipping or padding is required.""" length = len(self) @@ -389,10 +416,13 @@ class Text(JupyterMixin): self, console: "Console", options: "ConsoleOptions" ) -> Iterable[Segment]: tab_size: int = console.tab_size or self.tab_size or 8 # type: ignore - overflow = cast( - "OverflowValues", self.overflow or options.overflow or DEFAULT_OVERFLOW + justify = cast( + "JustifyMethod", self.justify or options.justify or DEFAULT_OVERFLOW ) - justify = self.justify or options.justify + overflow = cast( + "OverflowMethod", self.overflow or options.overflow or DEFAULT_OVERFLOW + ) + if self.no_wrap or options.no_wrap: render_text = self if overflow in ("crop", "ellipsis"): @@ -401,7 +431,7 @@ class Text(JupyterMixin): if justify: lines = Lines([render_text]) lines.justify( - console, options.max_width, align=justify, overflow=overflow + console, options.max_width, justify=justify, overflow=overflow ) render_text = lines[0] yield from render_text.render(console, end=self.end) @@ -413,13 +443,6 @@ class Text(JupyterMixin): overflow=overflow, tab_size=tab_size or 8, ) - # new_line = Segment.line() - # for last, line in loop_last(lines): - # yield from line.render(console) - # if not last: - # yield new_line - # else: - # yield Segment(self.end) all_lines = Text("\n").join(lines) yield from all_lines.render(console, end=self.end) @@ -544,20 +567,20 @@ class Text(JupyterMixin): def truncate( self, max_width: int, - overflow: Optional["OverflowValues"] = None, + overflow: Optional["OverflowMethod"] = None, pad: bool = False, ) -> None: """Truncate text if it is longer that a given width. Args: max_width (int): Maximum number of characters in text. - ellipsis (bool): Replace last character with ellipsis if truncated. + overflow (str, optional): Overflow method: "crop", "fold", or "ellipisis". Defaults to None, to use self.overflow. + pad (bool, optional): Pad with spaces if the length is less than max_width. Defaults to False. """ - length = cell_len(self.plain) + _overflow = overflow or self.overflow or DEFAULT_OVERFLOW if length > max_width: - overflow = overflow or self.overflow or DEFAULT_OVERFLOW - if overflow == "ellipsis": + if _overflow == "ellipsis": self.plain = set_cell_size(self.plain, max_width - 1).rstrip() + "…" else: self.plain = set_cell_size(self.plain, max_width) @@ -571,13 +594,18 @@ class Text(JupyterMixin): new_length = self._length spans: List[Span] = [] append = spans.append + _Span = Span for span in self._spans: if span.end < new_length: append(span) continue if span.start >= new_length: continue - append(span.right_crop(new_length)) + if span.end > new_length: + start, end, style = span + append(_Span(start, min(new_length, end), style)) + else: + append(span) self._spans[:] = spans def pad_left(self, count: int, character: str = " ") -> None: @@ -741,7 +769,7 @@ class Text(JupyterMixin): if new_span is None: break span = new_span - line_index += 1 + line_index = (line_index + 1) % len(line_ranges) line_start, line_end = line_ranges[line_index] return new_lines @@ -754,21 +782,26 @@ class Text(JupyterMixin): self, console: "Console", width: int, - justify: Optional["JustifyValues"] = "left", - overflow: Optional["OverflowValues"] = None, + justify: "JustifyMethod" = None, + overflow: "OverflowMethod" = None, tab_size: int = 8, ) -> Lines: """Word wrap the text. Args: + console (Console): Console instance. width (int): Number of characters per line. - justify (bool, optional): True to pad lines with spaces. Defaults to False. + emoji (bool, optional): Also render emoji code. Defaults to True. + justify (str, optional): Justify method: "left", "center", "full", "right". Defaults to "left". + overflow (str, optional): Overflow method: "crop", "fold", or "ellipisis". Defaults to None. + tab_size (int, optional): Default tab size. Defaults to 8. Returns: Lines: Number of lines. """ - wrap_overflow: "OverflowValues" = cast( - "OverflowValues", overflow or self.overflow or DEFAULT_OVERFLOW + wrap_justify = cast("JustifyMethod", justify or self.justify or DEFAULT_JUSTIFY) + wrap_overflow = cast( + "OverflowMethod", overflow or self.overflow or DEFAULT_OVERFLOW ) lines: Lines = Lines() for line in self.split(): @@ -776,13 +809,16 @@ class Text(JupyterMixin): line = line.tabs_to_spaces(tab_size) offsets = divide_line(str(line), width, fold=wrap_overflow == "fold") new_lines = line.divide(offsets) - if justify: - new_lines.justify(console, width, align=justify, overflow=overflow) + for line in new_lines: + line.rstrip_end(width) + if wrap_justify: + new_lines.justify( + console, width, justify=wrap_justify, overflow=wrap_overflow + ) lines.extend(new_lines) - if wrap_overflow in ("crop", "ellipsis"): - for line in lines: - line.truncate(width, wrap_overflow) + for line in lines: + line.truncate(width, wrap_overflow) return lines @@ -801,3 +837,11 @@ class Text(JupyterMixin): line.set_length(width) append(line) return lines + + +if __name__ == "__main__": + from rich.console import Console + + console = Console() + t = Text("foo bar", justify="left") + print(repr(t.wrap(console, 4))) diff --git a/tests/_card_render.py b/tests/_card_render.py index f19caa36..f7e0f5ec 100644 --- a/tests/_card_render.py +++ b/tests/_card_render.py @@ -1 +1 @@ -expected='\x1b[3m Rich features \x1b[0m\n\x1b[1;31m \x1b[0m \n\x1b[1;31m \x1b[0m\x1b[1;31m Colors\x1b[0m\x1b[1;31m \x1b[0m\x1b[1;33m256\x1b[0m colors or \x1b[1;32m16.7 million\x1b[0m colors \x1b[34m(if supported by your terminal)\x1b[0m. \n \n \x1b[48;5;16m \x1b[0m\x1b[48;5;17m \x1b[0m\x1b[48;5;18m \x1b[0m\x1b[48;5;19m \x1b[0m\x1b[48;5;20m \x1b[0m\x1b[48;5;21m \x1b[0m\x1b[48;5;52m \x1b[0m\x1b[48;5;53m \x1b[0m\x1b[48;5;54m \x1b[0m\x1b[48;5;55m \x1b[0m\x1b[48;5;56m \x1b[0m\x1b[48;5;57m \x1b[0m\x1b[48;5;88m \x1b[0m\x1b[48;5;89m \x1b[0m\x1b[48;5;90m \x1b[0m\x1b[48;5;91m \x1b[0m\x1b[48;5;92m \x1b[0m\x1b[48;5;93m \x1b[0m\x1b[48;5;124m \x1b[0m\x1b[48;5;125m \x1b[0m\x1b[48;5;126m \x1b[0m\x1b[48;5;127m \x1b[0m\x1b[48;5;128m \x1b[0m\x1b[48;5;129m \x1b[0m\x1b[48;5;160m \x1b[0m\x1b[48;5;161m \x1b[0m\x1b[48;5;162m \x1b[0m\x1b[48;5;163m \x1b[0m\x1b[48;5;164m \x1b[0m\x1b[48;5;165m \x1b[0m\x1b[48;5;196m \x1b[0m\x1b[48;5;197m \x1b[0m\x1b[48;5;198m \x1b[0m\x1b[48;5;199m \x1b[0m\x1b[48;5;200m \x1b[0m\x1b[48;5;201m \x1b[0m \n \x1b[48;5;22m \x1b[0m\x1b[48;5;23m \x1b[0m\x1b[48;5;24m \x1b[0m\x1b[48;5;25m \x1b[0m\x1b[48;5;26m \x1b[0m\x1b[48;5;27m \x1b[0m\x1b[48;5;58m \x1b[0m\x1b[48;5;59m \x1b[0m\x1b[48;5;60m \x1b[0m\x1b[48;5;61m \x1b[0m\x1b[48;5;62m \x1b[0m\x1b[48;5;63m \x1b[0m\x1b[48;5;94m \x1b[0m\x1b[48;5;95m \x1b[0m\x1b[48;5;96m \x1b[0m\x1b[48;5;97m \x1b[0m\x1b[48;5;98m \x1b[0m\x1b[48;5;99m \x1b[0m\x1b[48;5;130m \x1b[0m\x1b[48;5;131m \x1b[0m\x1b[48;5;132m \x1b[0m\x1b[48;5;133m \x1b[0m\x1b[48;5;134m \x1b[0m\x1b[48;5;135m \x1b[0m\x1b[48;5;166m \x1b[0m\x1b[48;5;167m \x1b[0m\x1b[48;5;168m \x1b[0m\x1b[48;5;169m \x1b[0m\x1b[48;5;170m \x1b[0m\x1b[48;5;171m \x1b[0m\x1b[48;5;202m \x1b[0m\x1b[48;5;203m \x1b[0m\x1b[48;5;204m \x1b[0m\x1b[48;5;205m \x1b[0m\x1b[48;5;206m \x1b[0m\x1b[48;5;207m \x1b[0m \n \x1b[48;5;28m \x1b[0m\x1b[48;5;29m \x1b[0m\x1b[48;5;30m \x1b[0m\x1b[48;5;31m \x1b[0m\x1b[48;5;32m \x1b[0m\x1b[48;5;33m \x1b[0m\x1b[48;5;64m \x1b[0m\x1b[48;5;65m \x1b[0m\x1b[48;5;66m \x1b[0m\x1b[48;5;67m \x1b[0m\x1b[48;5;68m \x1b[0m\x1b[48;5;69m \x1b[0m\x1b[48;5;100m \x1b[0m\x1b[48;5;101m \x1b[0m\x1b[48;5;102m \x1b[0m\x1b[48;5;103m \x1b[0m\x1b[48;5;104m \x1b[0m\x1b[48;5;105m \x1b[0m\x1b[48;5;136m \x1b[0m\x1b[48;5;137m \x1b[0m\x1b[48;5;138m \x1b[0m\x1b[48;5;139m \x1b[0m\x1b[48;5;140m \x1b[0m\x1b[48;5;141m \x1b[0m\x1b[48;5;172m \x1b[0m\x1b[48;5;173m \x1b[0m\x1b[48;5;174m \x1b[0m\x1b[48;5;175m \x1b[0m\x1b[48;5;176m \x1b[0m\x1b[48;5;177m \x1b[0m\x1b[48;5;208m \x1b[0m\x1b[48;5;209m \x1b[0m\x1b[48;5;210m \x1b[0m\x1b[48;5;211m \x1b[0m\x1b[48;5;212m \x1b[0m\x1b[48;5;213m \x1b[0m \n \x1b[48;5;34m \x1b[0m\x1b[48;5;35m \x1b[0m\x1b[48;5;36m \x1b[0m\x1b[48;5;37m \x1b[0m\x1b[48;5;38m \x1b[0m\x1b[48;5;39m \x1b[0m\x1b[48;5;70m \x1b[0m\x1b[48;5;71m \x1b[0m\x1b[48;5;72m \x1b[0m\x1b[48;5;73m \x1b[0m\x1b[48;5;74m \x1b[0m\x1b[48;5;75m \x1b[0m\x1b[48;5;106m \x1b[0m\x1b[48;5;107m \x1b[0m\x1b[48;5;108m \x1b[0m\x1b[48;5;109m \x1b[0m\x1b[48;5;110m \x1b[0m\x1b[48;5;111m \x1b[0m\x1b[48;5;142m \x1b[0m\x1b[48;5;143m \x1b[0m\x1b[48;5;144m \x1b[0m\x1b[48;5;145m \x1b[0m\x1b[48;5;146m \x1b[0m\x1b[48;5;147m \x1b[0m\x1b[48;5;178m \x1b[0m\x1b[48;5;179m \x1b[0m\x1b[48;5;180m \x1b[0m\x1b[48;5;181m \x1b[0m\x1b[48;5;182m \x1b[0m\x1b[48;5;183m \x1b[0m\x1b[48;5;214m \x1b[0m\x1b[48;5;215m \x1b[0m\x1b[48;5;216m \x1b[0m\x1b[48;5;217m \x1b[0m\x1b[48;5;218m \x1b[0m\x1b[48;5;219m \x1b[0m \n \x1b[48;5;40m \x1b[0m\x1b[48;5;41m \x1b[0m\x1b[48;5;42m \x1b[0m\x1b[48;5;43m \x1b[0m\x1b[48;5;44m \x1b[0m\x1b[48;5;45m \x1b[0m\x1b[48;5;76m \x1b[0m\x1b[48;5;77m \x1b[0m\x1b[48;5;78m \x1b[0m\x1b[48;5;79m \x1b[0m\x1b[48;5;80m \x1b[0m\x1b[48;5;81m \x1b[0m\x1b[48;5;112m \x1b[0m\x1b[48;5;113m \x1b[0m\x1b[48;5;114m \x1b[0m\x1b[48;5;115m \x1b[0m\x1b[48;5;116m \x1b[0m\x1b[48;5;117m \x1b[0m\x1b[48;5;148m \x1b[0m\x1b[48;5;149m \x1b[0m\x1b[48;5;150m \x1b[0m\x1b[48;5;151m \x1b[0m\x1b[48;5;152m \x1b[0m\x1b[48;5;153m \x1b[0m\x1b[48;5;184m \x1b[0m\x1b[48;5;185m \x1b[0m\x1b[48;5;186m \x1b[0m\x1b[48;5;187m \x1b[0m\x1b[48;5;188m \x1b[0m\x1b[48;5;189m \x1b[0m\x1b[48;5;220m \x1b[0m\x1b[48;5;221m \x1b[0m\x1b[48;5;222m \x1b[0m\x1b[48;5;223m \x1b[0m\x1b[48;5;224m \x1b[0m\x1b[48;5;225m \x1b[0m \n \x1b[48;5;46m \x1b[0m\x1b[48;5;47m \x1b[0m\x1b[48;5;48m \x1b[0m\x1b[48;5;49m \x1b[0m\x1b[48;5;50m \x1b[0m\x1b[48;5;51m \x1b[0m\x1b[48;5;82m \x1b[0m\x1b[48;5;83m \x1b[0m\x1b[48;5;84m \x1b[0m\x1b[48;5;85m \x1b[0m\x1b[48;5;86m \x1b[0m\x1b[48;5;87m \x1b[0m\x1b[48;5;118m \x1b[0m\x1b[48;5;119m \x1b[0m\x1b[48;5;120m \x1b[0m\x1b[48;5;121m \x1b[0m\x1b[48;5;122m \x1b[0m\x1b[48;5;123m \x1b[0m\x1b[48;5;154m \x1b[0m\x1b[48;5;155m \x1b[0m\x1b[48;5;156m \x1b[0m\x1b[48;5;157m \x1b[0m\x1b[48;5;158m \x1b[0m\x1b[48;5;159m \x1b[0m\x1b[48;5;190m \x1b[0m\x1b[48;5;191m \x1b[0m\x1b[48;5;192m \x1b[0m\x1b[48;5;193m \x1b[0m\x1b[48;5;194m \x1b[0m\x1b[48;5;195m \x1b[0m\x1b[48;5;226m \x1b[0m\x1b[48;5;227m \x1b[0m\x1b[48;5;228m \x1b[0m\x1b[48;5;229m \x1b[0m\x1b[48;5;230m \x1b[0m\x1b[48;5;231m \x1b[0m \n\x1b[1;31m \x1b[0m \n\x1b[1;31m \x1b[0m\x1b[1;31m Styles\x1b[0m\x1b[1;31m \x1b[0mAll ansi styles: \x1b[1mbold\x1b[0m, \x1b[2mdim\x1b[0m, \x1b[3mitalic\x1b[0m, \x1b[4munderline\x1b[0m, \x1b[9mstrikethrough\x1b[0m, \x1b[7mreverse\x1b[0m, and \n even \x1b[5mblink\x1b[0m. \n\x1b[1;31m \x1b[0m \n\x1b[1;31m \x1b[0m\x1b[1;31m Text\x1b[0m\x1b[1;31m \x1b[0mWord wrap text. Justify \x1b[32mleft\x1b[0m, \x1b[33mcenter\x1b[0m, \x1b[34mright\x1b[0m or \x1b[31mfull\x1b[0m. \n \n \x1b[32mLorem ipsum dolor \x1b[0m \x1b[33m Lorem ipsum dolor \x1b[0m \x1b[34m Lorem ipsum dolor\x1b[0m \x1b[31mLorem\x1b[0m\x1b[31m \x1b[0m\x1b[31mipsum\x1b[0m\x1b[31m \x1b[0m\x1b[31mdolor\x1b[0m \n \x1b[32msit amet, \x1b[0m \x1b[33m sit amet, \x1b[0m \x1b[34m sit amet,\x1b[0m \x1b[31msit\x1b[0m\x1b[31m \x1b[0m\x1b[31mamet,\x1b[0m \n \x1b[32mconsectetur \x1b[0m \x1b[33m consectetur \x1b[0m \x1b[34m consectetur\x1b[0m \x1b[31mconsectetur\x1b[0m \n \x1b[32madipiscing elit. \x1b[0m \x1b[33m adipiscing elit. \x1b[0m \x1b[34m adipiscing elit.\x1b[0m \x1b[31madipiscing\x1b[0m\x1b[31m \x1b[0m\x1b[31melit.\x1b[0m \n \x1b[32mQuisque in metus \x1b[0m \x1b[33m Quisque in metus \x1b[0m \x1b[34m Quisque in metus\x1b[0m \x1b[31mQuisque\x1b[0m\x1b[31m \x1b[0m\x1b[31min\x1b[0m\x1b[31m \x1b[0m\x1b[31mmetus\x1b[0m \n \x1b[32msed sapien \x1b[0m \x1b[33m sed sapien \x1b[0m \x1b[34m sed sapien\x1b[0m \x1b[31msed\x1b[0m\x1b[31m \x1b[0m\x1b[31msapien\x1b[0m \n \x1b[32multricies pretium a\x1b[0m \x1b[33multricies pretium a\x1b[0m \x1b[34m ultricies pretium\x1b[0m \x1b[31multricies\x1b[0m\x1b[31m \x1b[0m\x1b[31mpretium\x1b[0m\x1b[31m \x1b[0m\x1b[31ma\x1b[0m \n \x1b[32mat justo. Maecenas \x1b[0m \x1b[33mat justo. Maecenas \x1b[0m \x1b[34m a at justo.\x1b[0m \x1b[31mat\x1b[0m\x1b[31m \x1b[0m\x1b[31mjusto.\x1b[0m\x1b[31m \x1b[0m\x1b[31mMaecenas\x1b[0m \n \x1b[32mluctus velit et \x1b[0m \x1b[33m luctus velit et \x1b[0m \x1b[34m Maecenas luctus\x1b[0m \x1b[31mluctus\x1b[0m\x1b[31m \x1b[0m\x1b[31mvelit\x1b[0m\x1b[31m \x1b[0m\x1b[31met\x1b[0m \n \x1b[32mauctor maximus. \x1b[0m \x1b[33m auctor maximus. \x1b[0m \x1b[34m velit et auctor\x1b[0m \x1b[31mauctor\x1b[0m\x1b[31m \x1b[0m\x1b[31mmaximus.\x1b[0m \n \x1b[32mDonec faucibus vel \x1b[0m \x1b[33mDonec faucibus vel \x1b[0m \x1b[34m maximus. Donec\x1b[0m \x1b[31mDonec\x1b[0m\x1b[31m \x1b[0m\x1b[31mfaucibus\x1b[0m\x1b[31m \x1b[0m\x1b[31mvel\x1b[0m \n \x1b[32marcu id pretium. \x1b[0m \x1b[33m arcu id pretium. \x1b[0m \x1b[34m faucibus vel arcu\x1b[0m \x1b[31marcu id pretium.\x1b[0m \n \x1b[34m id pretium.\x1b[0m \n\x1b[1;31m \x1b[0m \n\x1b[1;31m \x1b[0m\x1b[1;31m CJK support\x1b[0m\x1b[1;31m \x1b[0m\x1b[31m╔══════════════════════════════╗\x1b[0m \n \x1b[31m║\x1b[0m该库支持中文,日文和韩文文本!\x1b[31m║\x1b[0m\x1b[31m \x1b[0m \n \x1b[31m╚══════════════════════════════╝\x1b[0m \n\x1b[1;31m \x1b[0m \n\x1b[1;31m \x1b[0m\x1b[1;31m Emoji\x1b[0m\x1b[1;31m \x1b[0mRender emoji code: :+1: :apple: :ant: Render emoji code: 👍 🍎 🐜 🐻 🥖 🚌 \n :bear: :baguette_bread: :bus: \n\x1b[1;31m \x1b[0m \n\x1b[1;31m \x1b[0m\x1b[1;31m Console markup\x1b[0m\x1b[1;31m \x1b[0m[bold magenta]Rich[/] supports a \x1b[1;35mRich\x1b[0m supports a simple \x1b[3mbbcode\x1b[0m like \n simple [i]bbcode[/i] like \x1b[1mmarkup\x1b[0m, you can use to insert \x1b[33mcolor\x1b[0m \n [b]markup[/b], you can use to insert and \x1b[4mstyle\x1b[0m. \n [yellow]color[/] and \n [underline]style[/]. \n\x1b[1;31m \x1b[0m \n\x1b[1;31m \x1b[0m\x1b[1;31m Tables\x1b[0m\x1b[1;31m \x1b[0m\x1b[3m Star Wars box office \x1b[0m \n ┏━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━┓ \n ┃\x1b[1;35m \x1b[0m\x1b[1;35mDate \x1b[0m\x1b[1;35m \x1b[0m┃\x1b[1;35m \x1b[0m\x1b[1;35mTitle \x1b[0m\x1b[1;35m \x1b[0m┃\x1b[1;35m \x1b[0m\x1b[1;35m Production\x1b[0m\x1b[1;35m \x1b[0m┃\x1b[1;35m \x1b[0m\x1b[1;35m Box Office\x1b[0m\x1b[1;35m \x1b[0m┃ \n ┃ ┃ ┃\x1b[1;35m \x1b[0m\x1b[1;35m Budget\x1b[0m\x1b[1;35m \x1b[0m┃ ┃ \n ┡━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━┩ \n │\x1b[2m \x1b[0m\x1b[2mDec 20, 2019\x1b[0m\x1b[2m \x1b[0m│ Star Wars: The Rise of │ $275,000,000 │ $375,126,118 │ \n │ │ Skywalker │ │ │ \n │\x1b[2m \x1b[0m\x1b[2mMay 25, 2018\x1b[0m\x1b[2m \x1b[0m│ \x1b[31mSolo\x1b[0m: A Star Wars Story │ $275,000,000 │ $393,151,347 │ \n │\x1b[2m \x1b[0m\x1b[2mDec 15, 2017\x1b[0m\x1b[2m \x1b[0m│ Star Wars Ep. VIII: The │ $262,000,000 │ \x1b[1m$1,332,539,889\x1b[0m │ \n │ │ Last Jedi │ │ │ \n └──────────────┴──────────────────────────┴─────────────────┴────────────────┘ \n\x1b[1;31m \x1b[0m \n\x1b[1;31m \x1b[0m\x1b[1;31mSyntax highlighting\x1b[0m\x1b[1;31m \x1b[0m\x1b[1;38;2;227;227;221;48;2;39;40;34m \x1b[0m\x1b[38;2;101;102;96;48;2;39;40;34m 1 \x1b[0m\x1b[38;2;102;217;239;48;2;39;40;34mdef\x1b[0m\x1b[38;2;248;248;242;48;2;39;40;34m \x1b[0m\x1b[38;2;166;226;46;48;2;39;40;34miter_last\x1b[0m\x1b[38;2;248;248;242;48;2;39;40;34m(\x1b[0m\x1b[38;2;248;248;242;48;2;39;40;34mvalues\x1b[0m\x1b[38;2;248;248;242;48;2;39;40;34m:\x1b[0m\x1b[38;2;248;248;242;48;2;39;40;34m \x1b[0m\x1b[38;2;248;248;242;48;2;39;40;34mIterable\x1b[0m\x1b[38;2;248;248;242;48;2;39;40;34m[\x1b[0m\x1b[38;2;248;248;242;48;2;39;40;34mT\x1b[0m\x1b[38;2;248;248;242;48;2;39;40;34m]\x1b[0m\x1b[38;2;248;248;242;48;2;39;40;34m)\x1b[0m\x1b[38;2;248;248;242;48;2;39;40;34m \x1b[0m\x1b[38;2;249;38;114;48;2;39;40;34m-\x1b[0m\x1b[38;2;249;38;114;48;2;39;40;34m>\x1b[0m\x1b[38;2;248;248;242;48;2;39;40;34m \x1b[0m\x1b[38;2;248;248;242;48;2;39;40;34mIterable\x1b[0m\x1b[38;2;248;248;242;48;2;39;40;34m[\x1b[0m\x1b[38;2;248;248;242;48;2;39;40;34mTuple\x1b[0m\x1b[38;2;248;248;242;48;2;39;40;34m[\x1b[0m\x1b[38;2;248;248;242;48;2;39;40;34mbool\x1b[0m\x1b[38;2;248;248;242;48;2;39;40;34m,\x1b[0m\x1b[38;2;248;248;242;48;2;39;40;34m \x1b[0m\x1b[38;2;248;248;242;48;2;39;40;34mT\x1b[0m\x1b[38;2;248;248;242;48;2;39;40;34m]\x1b[0m\x1b[38;2;248;248;242;48;2;39;40;34m]\x1b[0m\x1b[38;2;248;248;242;48;2;39;40;34m:\x1b[0m\x1b[48;2;39;40;34m \x1b[0m \n \x1b[1;38;2;227;227;221;48;2;39;40;34m \x1b[0m\x1b[38;2;101;102;96;48;2;39;40;34m 2 \x1b[0m\x1b[38;2;248;248;242;48;2;39;40;34m \x1b[0m\x1b[38;2;230;219;116;48;2;39;40;34m"""Iterate and generate a tuple with a flag for last value."""\x1b[0m\x1b[48;2;39;40;34m \x1b[0m \n \x1b[1;38;2;227;227;221;48;2;39;40;34m \x1b[0m\x1b[38;2;101;102;96;48;2;39;40;34m 3 \x1b[0m\x1b[38;2;248;248;242;48;2;39;40;34m \x1b[0m\x1b[38;2;248;248;242;48;2;39;40;34miter_values\x1b[0m\x1b[38;2;248;248;242;48;2;39;40;34m \x1b[0m\x1b[38;2;249;38;114;48;2;39;40;34m=\x1b[0m\x1b[38;2;248;248;242;48;2;39;40;34m \x1b[0m\x1b[38;2;248;248;242;48;2;39;40;34miter\x1b[0m\x1b[38;2;248;248;242;48;2;39;40;34m(\x1b[0m\x1b[38;2;248;248;242;48;2;39;40;34mvalues\x1b[0m\x1b[38;2;248;248;242;48;2;39;40;34m)\x1b[0m\x1b[48;2;39;40;34m \x1b[0m \n \x1b[1;38;2;227;227;221;48;2;39;40;34m \x1b[0m\x1b[38;2;101;102;96;48;2;39;40;34m 4 \x1b[0m\x1b[38;2;248;248;242;48;2;39;40;34m \x1b[0m\x1b[38;2;102;217;239;48;2;39;40;34mtry\x1b[0m\x1b[38;2;248;248;242;48;2;39;40;34m:\x1b[0m\x1b[48;2;39;40;34m \x1b[0m \n \x1b[1;38;2;227;227;221;48;2;39;40;34m \x1b[0m\x1b[38;2;101;102;96;48;2;39;40;34m 5 \x1b[0m\x1b[38;2;248;248;242;48;2;39;40;34m \x1b[0m\x1b[38;2;248;248;242;48;2;39;40;34mprevious_value\x1b[0m\x1b[38;2;248;248;242;48;2;39;40;34m \x1b[0m\x1b[38;2;249;38;114;48;2;39;40;34m=\x1b[0m\x1b[38;2;248;248;242;48;2;39;40;34m \x1b[0m\x1b[38;2;248;248;242;48;2;39;40;34mnext\x1b[0m\x1b[38;2;248;248;242;48;2;39;40;34m(\x1b[0m\x1b[38;2;248;248;242;48;2;39;40;34miter_values\x1b[0m\x1b[38;2;248;248;242;48;2;39;40;34m)\x1b[0m\x1b[48;2;39;40;34m \x1b[0m \n \x1b[1;38;2;227;227;221;48;2;39;40;34m \x1b[0m\x1b[38;2;101;102;96;48;2;39;40;34m 6 \x1b[0m\x1b[38;2;248;248;242;48;2;39;40;34m \x1b[0m\x1b[38;2;102;217;239;48;2;39;40;34mexcept\x1b[0m\x1b[38;2;248;248;242;48;2;39;40;34m \x1b[0m\x1b[38;2;166;226;46;48;2;39;40;34mStopIteration\x1b[0m\x1b[38;2;248;248;242;48;2;39;40;34m:\x1b[0m\x1b[48;2;39;40;34m \x1b[0m \n \x1b[1;38;2;227;227;221;48;2;39;40;34m \x1b[0m\x1b[38;2;101;102;96;48;2;39;40;34m 7 \x1b[0m\x1b[38;2;248;248;242;48;2;39;40;34m \x1b[0m\x1b[38;2;102;217;239;48;2;39;40;34mreturn\x1b[0m\x1b[48;2;39;40;34m \x1b[0m \n \x1b[1;38;2;227;227;221;48;2;39;40;34m \x1b[0m\x1b[38;2;101;102;96;48;2;39;40;34m 8 \x1b[0m\x1b[38;2;248;248;242;48;2;39;40;34m \x1b[0m\x1b[38;2;102;217;239;48;2;39;40;34mfor\x1b[0m\x1b[38;2;248;248;242;48;2;39;40;34m \x1b[0m\x1b[38;2;248;248;242;48;2;39;40;34mvalue\x1b[0m\x1b[38;2;248;248;242;48;2;39;40;34m \x1b[0m\x1b[38;2;249;38;114;48;2;39;40;34min\x1b[0m\x1b[38;2;248;248;242;48;2;39;40;34m \x1b[0m\x1b[38;2;248;248;242;48;2;39;40;34miter_values\x1b[0m\x1b[38;2;248;248;242;48;2;39;40;34m:\x1b[0m\x1b[48;2;39;40;34m \x1b[0m \n \x1b[1;38;2;227;227;221;48;2;39;40;34m \x1b[0m\x1b[38;2;101;102;96;48;2;39;40;34m 9 \x1b[0m\x1b[38;2;248;248;242;48;2;39;40;34m \x1b[0m\x1b[38;2;102;217;239;48;2;39;40;34myield\x1b[0m\x1b[38;2;248;248;242;48;2;39;40;34m \x1b[0m\x1b[38;2;102;217;239;48;2;39;40;34mFalse\x1b[0m\x1b[38;2;248;248;242;48;2;39;40;34m,\x1b[0m\x1b[38;2;248;248;242;48;2;39;40;34m \x1b[0m\x1b[38;2;248;248;242;48;2;39;40;34mprevious_value\x1b[0m\x1b[48;2;39;40;34m \x1b[0m \n \x1b[1;38;2;227;227;221;48;2;39;40;34m \x1b[0m\x1b[38;2;101;102;96;48;2;39;40;34m10 \x1b[0m\x1b[38;2;248;248;242;48;2;39;40;34m \x1b[0m\x1b[38;2;248;248;242;48;2;39;40;34mprevious_value\x1b[0m\x1b[38;2;248;248;242;48;2;39;40;34m \x1b[0m\x1b[38;2;249;38;114;48;2;39;40;34m=\x1b[0m\x1b[38;2;248;248;242;48;2;39;40;34m \x1b[0m\x1b[38;2;248;248;242;48;2;39;40;34mvalue\x1b[0m\x1b[48;2;39;40;34m \x1b[0m \n \x1b[1;38;2;227;227;221;48;2;39;40;34m \x1b[0m\x1b[38;2;101;102;96;48;2;39;40;34m11 \x1b[0m\x1b[38;2;248;248;242;48;2;39;40;34m \x1b[0m\x1b[38;2;102;217;239;48;2;39;40;34myield\x1b[0m\x1b[38;2;248;248;242;48;2;39;40;34m \x1b[0m\x1b[38;2;102;217;239;48;2;39;40;34mTrue\x1b[0m\x1b[38;2;248;248;242;48;2;39;40;34m,\x1b[0m\x1b[38;2;248;248;242;48;2;39;40;34m \x1b[0m\x1b[38;2;248;248;242;48;2;39;40;34mprevious_value\x1b[0m\x1b[48;2;39;40;34m \x1b[0m \n\x1b[1;31m \x1b[0m \n\x1b[1;31m \x1b[0m\x1b[1;31m Markdown\x1b[0m\x1b[1;31m \x1b[0m# Markdown ╔════════════════════════════════════╗ \n ║ \x1b[1mMarkdown\x1b[0m ║ \n Supports much of the *markdown*, ╚════════════════════════════════════╝ \n __syntax__! \n Supports much of the \x1b[3mmarkdown\x1b[0m, \x1b[1msyntax\x1b[0m! \n - Headers \n - Basic formatting: **bold**, \x1b[1;33m • \x1b[0mHeaders \n *italic*, `code` \x1b[1;33m • \x1b[0mBasic formatting: \x1b[1mbold\x1b[0m, \x1b[3mitalic\x1b[0m, \n - Block quotes \x1b[1;33m \x1b[0m\x1b[38;5;15;40mcode\x1b[0m \n - Lists, and more... \x1b[1;33m • \x1b[0mBlock quotes \n \x1b[1;33m • \x1b[0mLists, and more… \n\x1b[1;31m \x1b[0m \n\x1b[1;31m \x1b[0m\x1b[1;31m And more\x1b[0m\x1b[1;31m \x1b[0mProgress bars, styled logging handler, tracebacks, etc... \n\x1b[1;31m \x1b[0m \n' \ No newline at end of file +expected='\x1b[3m Rich features \x1b[0m\n\x1b[1;31m \x1b[0m \n\x1b[1;31m \x1b[0m\x1b[1;31m Colors\x1b[0m\x1b[1;31m \x1b[0m\x1b[1;33m256\x1b[0m colors or \x1b[1;32m16.7 million\x1b[0m colors \x1b[34m(if supported by your terminal)\x1b[0m. \n \n \x1b[48;5;16m \x1b[0m\x1b[48;5;17m \x1b[0m\x1b[48;5;18m \x1b[0m\x1b[48;5;19m \x1b[0m\x1b[48;5;20m \x1b[0m\x1b[48;5;21m \x1b[0m\x1b[48;5;52m \x1b[0m\x1b[48;5;53m \x1b[0m\x1b[48;5;54m \x1b[0m\x1b[48;5;55m \x1b[0m\x1b[48;5;56m \x1b[0m\x1b[48;5;57m \x1b[0m\x1b[48;5;88m \x1b[0m\x1b[48;5;89m \x1b[0m\x1b[48;5;90m \x1b[0m\x1b[48;5;91m \x1b[0m\x1b[48;5;92m \x1b[0m\x1b[48;5;93m \x1b[0m\x1b[48;5;124m \x1b[0m\x1b[48;5;125m \x1b[0m\x1b[48;5;126m \x1b[0m\x1b[48;5;127m \x1b[0m\x1b[48;5;128m \x1b[0m\x1b[48;5;129m \x1b[0m\x1b[48;5;160m \x1b[0m\x1b[48;5;161m \x1b[0m\x1b[48;5;162m \x1b[0m\x1b[48;5;163m \x1b[0m\x1b[48;5;164m \x1b[0m\x1b[48;5;165m \x1b[0m\x1b[48;5;196m \x1b[0m\x1b[48;5;197m \x1b[0m\x1b[48;5;198m \x1b[0m\x1b[48;5;199m \x1b[0m\x1b[48;5;200m \x1b[0m\x1b[48;5;201m \x1b[0m \n \x1b[48;5;22m \x1b[0m\x1b[48;5;23m \x1b[0m\x1b[48;5;24m \x1b[0m\x1b[48;5;25m \x1b[0m\x1b[48;5;26m \x1b[0m\x1b[48;5;27m \x1b[0m\x1b[48;5;58m \x1b[0m\x1b[48;5;59m \x1b[0m\x1b[48;5;60m \x1b[0m\x1b[48;5;61m \x1b[0m\x1b[48;5;62m \x1b[0m\x1b[48;5;63m \x1b[0m\x1b[48;5;94m \x1b[0m\x1b[48;5;95m \x1b[0m\x1b[48;5;96m \x1b[0m\x1b[48;5;97m \x1b[0m\x1b[48;5;98m \x1b[0m\x1b[48;5;99m \x1b[0m\x1b[48;5;130m \x1b[0m\x1b[48;5;131m \x1b[0m\x1b[48;5;132m \x1b[0m\x1b[48;5;133m \x1b[0m\x1b[48;5;134m \x1b[0m\x1b[48;5;135m \x1b[0m\x1b[48;5;166m \x1b[0m\x1b[48;5;167m \x1b[0m\x1b[48;5;168m \x1b[0m\x1b[48;5;169m \x1b[0m\x1b[48;5;170m \x1b[0m\x1b[48;5;171m \x1b[0m\x1b[48;5;202m \x1b[0m\x1b[48;5;203m \x1b[0m\x1b[48;5;204m \x1b[0m\x1b[48;5;205m \x1b[0m\x1b[48;5;206m \x1b[0m\x1b[48;5;207m \x1b[0m \n \x1b[48;5;28m \x1b[0m\x1b[48;5;29m \x1b[0m\x1b[48;5;30m \x1b[0m\x1b[48;5;31m \x1b[0m\x1b[48;5;32m \x1b[0m\x1b[48;5;33m \x1b[0m\x1b[48;5;64m \x1b[0m\x1b[48;5;65m \x1b[0m\x1b[48;5;66m \x1b[0m\x1b[48;5;67m \x1b[0m\x1b[48;5;68m \x1b[0m\x1b[48;5;69m \x1b[0m\x1b[48;5;100m \x1b[0m\x1b[48;5;101m \x1b[0m\x1b[48;5;102m \x1b[0m\x1b[48;5;103m \x1b[0m\x1b[48;5;104m \x1b[0m\x1b[48;5;105m \x1b[0m\x1b[48;5;136m \x1b[0m\x1b[48;5;137m \x1b[0m\x1b[48;5;138m \x1b[0m\x1b[48;5;139m \x1b[0m\x1b[48;5;140m \x1b[0m\x1b[48;5;141m \x1b[0m\x1b[48;5;172m \x1b[0m\x1b[48;5;173m \x1b[0m\x1b[48;5;174m \x1b[0m\x1b[48;5;175m \x1b[0m\x1b[48;5;176m \x1b[0m\x1b[48;5;177m \x1b[0m\x1b[48;5;208m \x1b[0m\x1b[48;5;209m \x1b[0m\x1b[48;5;210m \x1b[0m\x1b[48;5;211m \x1b[0m\x1b[48;5;212m \x1b[0m\x1b[48;5;213m \x1b[0m \n \x1b[48;5;34m \x1b[0m\x1b[48;5;35m \x1b[0m\x1b[48;5;36m \x1b[0m\x1b[48;5;37m \x1b[0m\x1b[48;5;38m \x1b[0m\x1b[48;5;39m \x1b[0m\x1b[48;5;70m \x1b[0m\x1b[48;5;71m \x1b[0m\x1b[48;5;72m \x1b[0m\x1b[48;5;73m \x1b[0m\x1b[48;5;74m \x1b[0m\x1b[48;5;75m \x1b[0m\x1b[48;5;106m \x1b[0m\x1b[48;5;107m \x1b[0m\x1b[48;5;108m \x1b[0m\x1b[48;5;109m \x1b[0m\x1b[48;5;110m \x1b[0m\x1b[48;5;111m \x1b[0m\x1b[48;5;142m \x1b[0m\x1b[48;5;143m \x1b[0m\x1b[48;5;144m \x1b[0m\x1b[48;5;145m \x1b[0m\x1b[48;5;146m \x1b[0m\x1b[48;5;147m \x1b[0m\x1b[48;5;178m \x1b[0m\x1b[48;5;179m \x1b[0m\x1b[48;5;180m \x1b[0m\x1b[48;5;181m \x1b[0m\x1b[48;5;182m \x1b[0m\x1b[48;5;183m \x1b[0m\x1b[48;5;214m \x1b[0m\x1b[48;5;215m \x1b[0m\x1b[48;5;216m \x1b[0m\x1b[48;5;217m \x1b[0m\x1b[48;5;218m \x1b[0m\x1b[48;5;219m \x1b[0m \n \x1b[48;5;40m \x1b[0m\x1b[48;5;41m \x1b[0m\x1b[48;5;42m \x1b[0m\x1b[48;5;43m \x1b[0m\x1b[48;5;44m \x1b[0m\x1b[48;5;45m \x1b[0m\x1b[48;5;76m \x1b[0m\x1b[48;5;77m \x1b[0m\x1b[48;5;78m \x1b[0m\x1b[48;5;79m \x1b[0m\x1b[48;5;80m \x1b[0m\x1b[48;5;81m \x1b[0m\x1b[48;5;112m \x1b[0m\x1b[48;5;113m \x1b[0m\x1b[48;5;114m \x1b[0m\x1b[48;5;115m \x1b[0m\x1b[48;5;116m \x1b[0m\x1b[48;5;117m \x1b[0m\x1b[48;5;148m \x1b[0m\x1b[48;5;149m \x1b[0m\x1b[48;5;150m \x1b[0m\x1b[48;5;151m \x1b[0m\x1b[48;5;152m \x1b[0m\x1b[48;5;153m \x1b[0m\x1b[48;5;184m \x1b[0m\x1b[48;5;185m \x1b[0m\x1b[48;5;186m \x1b[0m\x1b[48;5;187m \x1b[0m\x1b[48;5;188m \x1b[0m\x1b[48;5;189m \x1b[0m\x1b[48;5;220m \x1b[0m\x1b[48;5;221m \x1b[0m\x1b[48;5;222m \x1b[0m\x1b[48;5;223m \x1b[0m\x1b[48;5;224m \x1b[0m\x1b[48;5;225m \x1b[0m \n \x1b[48;5;46m \x1b[0m\x1b[48;5;47m \x1b[0m\x1b[48;5;48m \x1b[0m\x1b[48;5;49m \x1b[0m\x1b[48;5;50m \x1b[0m\x1b[48;5;51m \x1b[0m\x1b[48;5;82m \x1b[0m\x1b[48;5;83m \x1b[0m\x1b[48;5;84m \x1b[0m\x1b[48;5;85m \x1b[0m\x1b[48;5;86m \x1b[0m\x1b[48;5;87m \x1b[0m\x1b[48;5;118m \x1b[0m\x1b[48;5;119m \x1b[0m\x1b[48;5;120m \x1b[0m\x1b[48;5;121m \x1b[0m\x1b[48;5;122m \x1b[0m\x1b[48;5;123m \x1b[0m\x1b[48;5;154m \x1b[0m\x1b[48;5;155m \x1b[0m\x1b[48;5;156m \x1b[0m\x1b[48;5;157m \x1b[0m\x1b[48;5;158m \x1b[0m\x1b[48;5;159m \x1b[0m\x1b[48;5;190m \x1b[0m\x1b[48;5;191m \x1b[0m\x1b[48;5;192m \x1b[0m\x1b[48;5;193m \x1b[0m\x1b[48;5;194m \x1b[0m\x1b[48;5;195m \x1b[0m\x1b[48;5;226m \x1b[0m\x1b[48;5;227m \x1b[0m\x1b[48;5;228m \x1b[0m\x1b[48;5;229m \x1b[0m\x1b[48;5;230m \x1b[0m\x1b[48;5;231m \x1b[0m \n\x1b[1;31m \x1b[0m \n\x1b[1;31m \x1b[0m\x1b[1;31m Styles\x1b[0m\x1b[1;31m \x1b[0mAll ansi styles: \x1b[1mbold\x1b[0m, \x1b[2mdim\x1b[0m, \x1b[3mitalic\x1b[0m, \x1b[4munderline\x1b[0m, \x1b[9mstrikethrough\x1b[0m, \x1b[7mreverse\x1b[0m, and \n even \x1b[5mblink\x1b[0m. \n\x1b[1;31m \x1b[0m \n\x1b[1;31m \x1b[0m\x1b[1;31m Text\x1b[0m\x1b[1;31m \x1b[0mWord wrap text. Justify \x1b[32mleft\x1b[0m, \x1b[33mcenter\x1b[0m, \x1b[34mright\x1b[0m or \x1b[31mfull\x1b[0m. \n \n \x1b[32mLorem ipsum dolor \x1b[0m \x1b[33mLorem ipsum dolor \x1b[0m \x1b[34m Lorem ipsum dolor\x1b[0m \x1b[31mLorem\x1b[0m\x1b[31m \x1b[0m\x1b[31mipsum\x1b[0m\x1b[31m \x1b[0m\x1b[31mdolor\x1b[0m \n \x1b[32msit amet, \x1b[0m \x1b[33m sit amet, \x1b[0m \x1b[34m sit amet,\x1b[0m \x1b[31msit\x1b[0m\x1b[31m \x1b[0m\x1b[31mamet,\x1b[0m \n \x1b[32mconsectetur \x1b[0m \x1b[33m consectetur \x1b[0m \x1b[34m consectetur\x1b[0m \x1b[31mconsectetur\x1b[0m \n \x1b[32madipiscing elit. \x1b[0m \x1b[33m adipiscing elit. \x1b[0m \x1b[34m adipiscing elit.\x1b[0m \x1b[31madipiscing\x1b[0m\x1b[31m \x1b[0m\x1b[31melit.\x1b[0m \n \x1b[32mQuisque in metus \x1b[0m \x1b[33m Quisque in metus \x1b[0m \x1b[34m Quisque in metus\x1b[0m \x1b[31mQuisque\x1b[0m\x1b[31m \x1b[0m\x1b[31min\x1b[0m\x1b[31m \x1b[0m\x1b[31mmetus\x1b[0m\x1b[31m \x1b[0m\x1b[31msed\x1b[0m \n \x1b[32msed sapien \x1b[0m \x1b[33m sed sapien \x1b[0m \x1b[34m sed sapien\x1b[0m \x1b[31msapien\x1b[0m\x1b[31m \x1b[0m\x1b[31multricies\x1b[0m \n \x1b[32multricies pretium \x1b[0m \x1b[33multricies pretium \x1b[0m \x1b[34multricies pretium a\x1b[0m \x1b[31mpretium\x1b[0m\x1b[31m \x1b[0m\x1b[31ma\x1b[0m\x1b[31m \x1b[0m\x1b[31mat\x1b[0m\x1b[31m \x1b[0m\x1b[31mjusto.\x1b[0m \n \x1b[32ma at justo. \x1b[0m \x1b[33m a at justo. \x1b[0m \x1b[34m at justo. Maecenas\x1b[0m \x1b[31mMaecenas\x1b[0m\x1b[31m \x1b[0m\x1b[31mluctus\x1b[0m \n \x1b[32mMaecenas luctus \x1b[0m \x1b[33m Maecenas luctus \x1b[0m \x1b[34m luctus velit et\x1b[0m \x1b[31mvelit\x1b[0m\x1b[31m \x1b[0m\x1b[31met\x1b[0m\x1b[31m \x1b[0m\x1b[31mauctor\x1b[0m \n \x1b[32mvelit et auctor \x1b[0m \x1b[33m velit et auctor \x1b[0m \x1b[34m auctor maximus.\x1b[0m \x1b[31mmaximus.\x1b[0m\x1b[31m \x1b[0m\x1b[31mDonec\x1b[0m \n \x1b[32mmaximus. Donec \x1b[0m \x1b[33m maximus. Donec \x1b[0m \x1b[34m Donec faucibus vel\x1b[0m \x1b[31mfaucibus\x1b[0m\x1b[31m \x1b[0m\x1b[31mvel\x1b[0m\x1b[31m \x1b[0m\x1b[31marcu\x1b[0m\x1b[31m \x1b[0m\x1b[31mid\x1b[0m \n \x1b[32mfaucibus vel arcu \x1b[0m \x1b[33mfaucibus vel arcu \x1b[0m \x1b[34m arcu id pretium.\x1b[0m \x1b[31mpretium.\x1b[0m \n \x1b[32mid pretium. \x1b[0m \x1b[33m id pretium. \x1b[0m \n\x1b[1;31m \x1b[0m \n\x1b[1;31m \x1b[0m\x1b[1;31m CJK support\x1b[0m\x1b[1;31m \x1b[0m\x1b[31m╔══════════════════════════════╗\x1b[0m \n \x1b[31m║\x1b[0m该库支持中文,日文和韩文文本!\x1b[31m║\x1b[0m\x1b[31m \x1b[0m \n \x1b[31m╚══════════════════════════════╝\x1b[0m \n\x1b[1;31m \x1b[0m \n\x1b[1;31m \x1b[0m\x1b[1;31m Emoji\x1b[0m\x1b[1;31m \x1b[0mRender emoji code: :+1: :apple: :ant: Render emoji code: 👍 🍎 🐜 🐻 🥖 🚌 \n :bear: :baguette_bread: :bus: \n\x1b[1;31m \x1b[0m \n\x1b[1;31m \x1b[0m\x1b[1;31m Console markup\x1b[0m\x1b[1;31m \x1b[0m[bold magenta]Rich[/] supports a \x1b[1;35mRich\x1b[0m supports a simple \x1b[3mbbcode\x1b[0m like \n simple [i]bbcode[/i] like \x1b[1mmarkup\x1b[0m, you can use to insert \x1b[33mcolor\x1b[0m \n [b]markup[/b], you can use to insert and \x1b[4mstyle\x1b[0m. \n [yellow]color[/] and \n [underline]style[/]. \n\x1b[1;31m \x1b[0m \n\x1b[1;31m \x1b[0m\x1b[1;31m Tables\x1b[0m\x1b[1;31m \x1b[0m\x1b[3m Star Wars box office \x1b[0m \n ┏━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━┓ \n ┃\x1b[1;35m \x1b[0m\x1b[1;35mDate \x1b[0m\x1b[1;35m \x1b[0m┃\x1b[1;35m \x1b[0m\x1b[1;35mTitle \x1b[0m\x1b[1;35m \x1b[0m┃\x1b[1;35m \x1b[0m\x1b[1;35m Production\x1b[0m\x1b[1;35m \x1b[0m┃\x1b[1;35m \x1b[0m\x1b[1;35m Box Office\x1b[0m\x1b[1;35m \x1b[0m┃ \n ┃ ┃ ┃\x1b[1;35m \x1b[0m\x1b[1;35m Budget\x1b[0m\x1b[1;35m \x1b[0m┃ ┃ \n ┡━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━┩ \n │\x1b[2m \x1b[0m\x1b[2mDec 20, \x1b[0m\x1b[2m \x1b[0m│ Star Wars: The Rise of │ $275,000,000 │ $375,126,118 │ \n │\x1b[2m \x1b[0m\x1b[2m2019 \x1b[0m\x1b[2m \x1b[0m│ Skywalker │ │ │ \n │\x1b[2m \x1b[0m\x1b[2mMay 25, \x1b[0m\x1b[2m \x1b[0m│ \x1b[31mSolo\x1b[0m: A Star Wars Story │ $275,000,000 │ $393,151,347 │ \n │\x1b[2m \x1b[0m\x1b[2m2018 \x1b[0m\x1b[2m \x1b[0m│ │ │ │ \n │\x1b[2m \x1b[0m\x1b[2mDec 15, \x1b[0m\x1b[2m \x1b[0m│ Star Wars Ep. VIII: The Last │ $262,000,000 │ \x1b[1m$1,332,539,889\x1b[0m │ \n │\x1b[2m \x1b[0m\x1b[2m2017 \x1b[0m\x1b[2m \x1b[0m│ Jedi │ │ │ \n └─────────────┴──────────────────────────────┴──────────────┴────────────────┘ \n\x1b[1;31m \x1b[0m \n\x1b[1;31m \x1b[0m\x1b[1;31mSyntax highlighting\x1b[0m\x1b[1;31m \x1b[0m\x1b[1;38;2;227;227;221;48;2;39;40;34m \x1b[0m\x1b[38;2;101;102;96;48;2;39;40;34m 1 \x1b[0m\x1b[38;2;102;217;239;48;2;39;40;34mdef\x1b[0m\x1b[38;2;248;248;242;48;2;39;40;34m \x1b[0m\x1b[38;2;166;226;46;48;2;39;40;34miter_last\x1b[0m\x1b[38;2;248;248;242;48;2;39;40;34m(\x1b[0m\x1b[38;2;248;248;242;48;2;39;40;34mvalues\x1b[0m\x1b[38;2;248;248;242;48;2;39;40;34m:\x1b[0m\x1b[38;2;248;248;242;48;2;39;40;34m \x1b[0m\x1b[38;2;248;248;242;48;2;39;40;34mIterable\x1b[0m\x1b[38;2;248;248;242;48;2;39;40;34m[\x1b[0m\x1b[38;2;248;248;242;48;2;39;40;34mT\x1b[0m\x1b[38;2;248;248;242;48;2;39;40;34m]\x1b[0m\x1b[38;2;248;248;242;48;2;39;40;34m)\x1b[0m\x1b[38;2;248;248;242;48;2;39;40;34m \x1b[0m\x1b[38;2;249;38;114;48;2;39;40;34m-\x1b[0m\x1b[38;2;249;38;114;48;2;39;40;34m>\x1b[0m\x1b[38;2;248;248;242;48;2;39;40;34m \x1b[0m\x1b[38;2;248;248;242;48;2;39;40;34mIterable\x1b[0m\x1b[38;2;248;248;242;48;2;39;40;34m[\x1b[0m\x1b[38;2;248;248;242;48;2;39;40;34mTuple\x1b[0m\x1b[38;2;248;248;242;48;2;39;40;34m[\x1b[0m\x1b[38;2;248;248;242;48;2;39;40;34mbool\x1b[0m\x1b[38;2;248;248;242;48;2;39;40;34m,\x1b[0m\x1b[38;2;248;248;242;48;2;39;40;34m \x1b[0m\x1b[38;2;248;248;242;48;2;39;40;34mT\x1b[0m\x1b[38;2;248;248;242;48;2;39;40;34m]\x1b[0m\x1b[38;2;248;248;242;48;2;39;40;34m]\x1b[0m\x1b[38;2;248;248;242;48;2;39;40;34m:\x1b[0m\x1b[48;2;39;40;34m \x1b[0m \n \x1b[1;38;2;227;227;221;48;2;39;40;34m \x1b[0m\x1b[38;2;101;102;96;48;2;39;40;34m 2 \x1b[0m\x1b[38;2;248;248;242;48;2;39;40;34m \x1b[0m\x1b[38;2;230;219;116;48;2;39;40;34m"""Iterate and generate a tuple with a flag for last value."""\x1b[0m\x1b[48;2;39;40;34m \x1b[0m \n \x1b[1;38;2;227;227;221;48;2;39;40;34m \x1b[0m\x1b[38;2;101;102;96;48;2;39;40;34m 3 \x1b[0m\x1b[38;2;248;248;242;48;2;39;40;34m \x1b[0m\x1b[38;2;248;248;242;48;2;39;40;34miter_values\x1b[0m\x1b[38;2;248;248;242;48;2;39;40;34m \x1b[0m\x1b[38;2;249;38;114;48;2;39;40;34m=\x1b[0m\x1b[38;2;248;248;242;48;2;39;40;34m \x1b[0m\x1b[38;2;248;248;242;48;2;39;40;34miter\x1b[0m\x1b[38;2;248;248;242;48;2;39;40;34m(\x1b[0m\x1b[38;2;248;248;242;48;2;39;40;34mvalues\x1b[0m\x1b[38;2;248;248;242;48;2;39;40;34m)\x1b[0m\x1b[48;2;39;40;34m \x1b[0m \n \x1b[1;38;2;227;227;221;48;2;39;40;34m \x1b[0m\x1b[38;2;101;102;96;48;2;39;40;34m 4 \x1b[0m\x1b[38;2;248;248;242;48;2;39;40;34m \x1b[0m\x1b[38;2;102;217;239;48;2;39;40;34mtry\x1b[0m\x1b[38;2;248;248;242;48;2;39;40;34m:\x1b[0m\x1b[48;2;39;40;34m \x1b[0m \n \x1b[1;38;2;227;227;221;48;2;39;40;34m \x1b[0m\x1b[38;2;101;102;96;48;2;39;40;34m 5 \x1b[0m\x1b[38;2;248;248;242;48;2;39;40;34m \x1b[0m\x1b[38;2;248;248;242;48;2;39;40;34mprevious_value\x1b[0m\x1b[38;2;248;248;242;48;2;39;40;34m \x1b[0m\x1b[38;2;249;38;114;48;2;39;40;34m=\x1b[0m\x1b[38;2;248;248;242;48;2;39;40;34m \x1b[0m\x1b[38;2;248;248;242;48;2;39;40;34mnext\x1b[0m\x1b[38;2;248;248;242;48;2;39;40;34m(\x1b[0m\x1b[38;2;248;248;242;48;2;39;40;34miter_values\x1b[0m\x1b[38;2;248;248;242;48;2;39;40;34m)\x1b[0m\x1b[48;2;39;40;34m \x1b[0m \n \x1b[1;38;2;227;227;221;48;2;39;40;34m \x1b[0m\x1b[38;2;101;102;96;48;2;39;40;34m 6 \x1b[0m\x1b[38;2;248;248;242;48;2;39;40;34m \x1b[0m\x1b[38;2;102;217;239;48;2;39;40;34mexcept\x1b[0m\x1b[38;2;248;248;242;48;2;39;40;34m \x1b[0m\x1b[38;2;166;226;46;48;2;39;40;34mStopIteration\x1b[0m\x1b[38;2;248;248;242;48;2;39;40;34m:\x1b[0m\x1b[48;2;39;40;34m \x1b[0m \n \x1b[1;38;2;227;227;221;48;2;39;40;34m \x1b[0m\x1b[38;2;101;102;96;48;2;39;40;34m 7 \x1b[0m\x1b[38;2;248;248;242;48;2;39;40;34m \x1b[0m\x1b[38;2;102;217;239;48;2;39;40;34mreturn\x1b[0m\x1b[48;2;39;40;34m \x1b[0m \n \x1b[1;38;2;227;227;221;48;2;39;40;34m \x1b[0m\x1b[38;2;101;102;96;48;2;39;40;34m 8 \x1b[0m\x1b[38;2;248;248;242;48;2;39;40;34m \x1b[0m\x1b[38;2;102;217;239;48;2;39;40;34mfor\x1b[0m\x1b[38;2;248;248;242;48;2;39;40;34m \x1b[0m\x1b[38;2;248;248;242;48;2;39;40;34mvalue\x1b[0m\x1b[38;2;248;248;242;48;2;39;40;34m \x1b[0m\x1b[38;2;249;38;114;48;2;39;40;34min\x1b[0m\x1b[38;2;248;248;242;48;2;39;40;34m \x1b[0m\x1b[38;2;248;248;242;48;2;39;40;34miter_values\x1b[0m\x1b[38;2;248;248;242;48;2;39;40;34m:\x1b[0m\x1b[48;2;39;40;34m \x1b[0m \n \x1b[1;38;2;227;227;221;48;2;39;40;34m \x1b[0m\x1b[38;2;101;102;96;48;2;39;40;34m 9 \x1b[0m\x1b[38;2;248;248;242;48;2;39;40;34m \x1b[0m\x1b[38;2;102;217;239;48;2;39;40;34myield\x1b[0m\x1b[38;2;248;248;242;48;2;39;40;34m \x1b[0m\x1b[38;2;102;217;239;48;2;39;40;34mFalse\x1b[0m\x1b[38;2;248;248;242;48;2;39;40;34m,\x1b[0m\x1b[38;2;248;248;242;48;2;39;40;34m \x1b[0m\x1b[38;2;248;248;242;48;2;39;40;34mprevious_value\x1b[0m\x1b[48;2;39;40;34m \x1b[0m \n \x1b[1;38;2;227;227;221;48;2;39;40;34m \x1b[0m\x1b[38;2;101;102;96;48;2;39;40;34m10 \x1b[0m\x1b[38;2;248;248;242;48;2;39;40;34m \x1b[0m\x1b[38;2;248;248;242;48;2;39;40;34mprevious_value\x1b[0m\x1b[38;2;248;248;242;48;2;39;40;34m \x1b[0m\x1b[38;2;249;38;114;48;2;39;40;34m=\x1b[0m\x1b[38;2;248;248;242;48;2;39;40;34m \x1b[0m\x1b[38;2;248;248;242;48;2;39;40;34mvalue\x1b[0m\x1b[48;2;39;40;34m \x1b[0m \n \x1b[1;38;2;227;227;221;48;2;39;40;34m \x1b[0m\x1b[38;2;101;102;96;48;2;39;40;34m11 \x1b[0m\x1b[38;2;248;248;242;48;2;39;40;34m \x1b[0m\x1b[38;2;102;217;239;48;2;39;40;34myield\x1b[0m\x1b[38;2;248;248;242;48;2;39;40;34m \x1b[0m\x1b[38;2;102;217;239;48;2;39;40;34mTrue\x1b[0m\x1b[38;2;248;248;242;48;2;39;40;34m,\x1b[0m\x1b[38;2;248;248;242;48;2;39;40;34m \x1b[0m\x1b[38;2;248;248;242;48;2;39;40;34mprevious_value\x1b[0m\x1b[48;2;39;40;34m \x1b[0m \n\x1b[1;31m \x1b[0m \n\x1b[1;31m \x1b[0m\x1b[1;31m Markdown\x1b[0m\x1b[1;31m \x1b[0m# Markdown ╔════════════════════════════════════╗ \n ║ \x1b[1mMarkdown\x1b[0m ║ \n Supports much of the *markdown*, ╚════════════════════════════════════╝ \n __syntax__! \n Supports much of the \x1b[3mmarkdown\x1b[0m, \x1b[1msyntax\x1b[0m! \n - Headers \n - Basic formatting: **bold**, \x1b[1;33m • \x1b[0mHeaders \n *italic*, `code` \x1b[1;33m • \x1b[0mBasic formatting: \x1b[1mbold\x1b[0m, \x1b[3mitalic\x1b[0m, \n - Block quotes \x1b[1;33m \x1b[0m\x1b[38;5;15;40mcode\x1b[0m \n - Lists, and more... \x1b[1;33m • \x1b[0mBlock quotes \n \x1b[1;33m • \x1b[0mLists, and more… \n\x1b[1;31m \x1b[0m \n\x1b[1;31m \x1b[0m\x1b[1;31m And more\x1b[0m\x1b[1;31m \x1b[0mProgress bars, styled logging handler, tracebacks, etc... \n\x1b[1;31m \x1b[0m \n' \ No newline at end of file diff --git a/tests/test_columns.py b/tests/test_columns.py index b418b65d..380f61bd 100644 --- a/tests/test_columns.py +++ b/tests/test_columns.py @@ -30,11 +30,18 @@ COLUMN_DATA = [ def render(): console = Console(file=io.StringIO(), width=100) + console.rule("empty") + empty_columns = Columns([]) + console.print(empty_columns) columns = Columns(COLUMN_DATA) console.rule("optimal") console.print(columns) + console.rule("optimal, expand") + columns.expand = True + console.print(columns) console.rule("columm first, optimal") columns.column_first = True + columns.expand = False console.print(columns) console.rule("column first, right to left") columns.right_to_left = True @@ -53,7 +60,7 @@ def render(): def test_render(): - expected = "───────────────────────────────────────────── optimal ──────────────────────────────────────────────\nUrsus americanus American buffalo Bison bison American crow \nCorvus brachyrhynchos American marten Martes americana American racer \nColuber constrictor American woodcock Scolopax minor Anaconda (unidentified)\nEunectes sp. Andean goose Chloephaga melanoptera Ant \nAnteater, australian spiny Tachyglossus aculeatus Anteater, giant Myrmecophaga tridactyla\n────────────────────────────────────── columm first, optimal ───────────────────────────────────────\nUrsus americanus American marten Scolopax minor Ant \nAmerican buffalo Martes americana Anaconda (unidentified) Anteater, australian spiny\nBison bison American racer Eunectes sp. Tachyglossus aculeatus \nAmerican crow Coluber constrictor Andean goose Anteater, giant \nCorvus brachyrhynchos American woodcock Chloephaga melanoptera Myrmecophaga tridactyla \n─────────────────────────────────── column first, right to left ────────────────────────────────────\nAnt Scolopax minor American marten Ursus americanus \nAnteater, australian spiny Anaconda (unidentified) Martes americana American buffalo \nTachyglossus aculeatus Eunectes sp. American racer Bison bison \nAnteater, giant Andean goose Coluber constrictor American crow \nMyrmecophaga tridactyla Chloephaga melanoptera American woodcock Corvus brachyrhynchos\n────────────────────────────────────── equal columns, expand ───────────────────────────────────────\nChloephaga melanoptera American racer Ursus americanus \nAnt Coluber constrictor American buffalo \nAnteater, australian spiny American woodcock Bison bison \nTachyglossus aculeatus Scolopax minor American crow \nAnteater, giant Anaconda (unidentified) Corvus brachyrhynchos \nMyrmecophaga tridactyla Eunectes sp. American marten \n Andean goose Martes americana \n─────────────────────────────────────────── fixed width ────────────────────────────────────────────\nTachyglossus Chloephaga Anaconda Coluber Corvus Ursus americanus\naculeatus melanoptera (unidentified) constrictor brachyrhynchos \nAnteater, giant Ant Eunectes sp. American American marten American buffalo\n woodcock \nMyrmecophaga Anteater, Andean goose Scolopax minor Martes americana Bison bison \ntridactyla australian \n spiny \n American racer American crow \n\n" + expected = "────────────────────────────────────────────── empty ───────────────────────────────────────────────\n───────────────────────────────────────────── optimal ──────────────────────────────────────────────\nUrsus americanus American buffalo Bison bison American crow \nCorvus brachyrhynchos American marten Martes americana American racer \nColuber constrictor American woodcock Scolopax minor Anaconda (unidentified)\nEunectes sp. Andean goose Chloephaga melanoptera Ant \nAnteater, australian spiny Tachyglossus aculeatus Anteater, giant Myrmecophaga tridactyla\n───────────────────────────────────────── optimal, expand ──────────────────────────────────────────\nUrsus americanus American buffalo Bison bison American crow \nCorvus brachyrhynchos American marten Martes americana American racer \nColuber constrictor American woodcock Scolopax minor Anaconda (unidentified)\nEunectes sp. Andean goose Chloephaga melanoptera Ant \nAnteater, australian spiny Tachyglossus aculeatus Anteater, giant Myrmecophaga tridactyla\n────────────────────────────────────── columm first, optimal ───────────────────────────────────────\nUrsus americanus American marten Scolopax minor Ant \nAmerican buffalo Martes americana Anaconda (unidentified) Anteater, australian spiny\nBison bison American racer Eunectes sp. Tachyglossus aculeatus \nAmerican crow Coluber constrictor Andean goose Anteater, giant \nCorvus brachyrhynchos American woodcock Chloephaga melanoptera Myrmecophaga tridactyla \n─────────────────────────────────── column first, right to left ────────────────────────────────────\nAnt Scolopax minor American marten Ursus americanus \nAnteater, australian spiny Anaconda (unidentified) Martes americana American buffalo \nTachyglossus aculeatus Eunectes sp. American racer Bison bison \nAnteater, giant Andean goose Coluber constrictor American crow \nMyrmecophaga tridactyla Chloephaga melanoptera American woodcock Corvus brachyrhynchos\n────────────────────────────────────── equal columns, expand ───────────────────────────────────────\nChloephaga melanoptera American racer Ursus americanus \nAnt Coluber constrictor American buffalo \nAnteater, australian spiny American woodcock Bison bison \nTachyglossus aculeatus Scolopax minor American crow \nAnteater, giant Anaconda (unidentified) Corvus brachyrhynchos \nMyrmecophaga tridactyla Eunectes sp. American marten \n Andean goose Martes americana \n─────────────────────────────────────────── fixed width ────────────────────────────────────────────\nTachyglossus Chloephaga Anaconda Coluber Corvus Ursus americanus \naculeatus melanoptera (unidentified) constrictor brachyrhynchos \nAnteater, giant Ant Eunectes sp. American American marten American buffalo \n woodcock \nMyrmecophaga Anteater, Andean goose Scolopax minor Martes americana Bison bison \ntridactyla australian \n spiny \n American racer American crow \n\n" assert render() == expected diff --git a/tests/test_log.py b/tests/test_log.py index b0fd3051..47d3b8e7 100644 --- a/tests/test_log.py +++ b/tests/test_log.py @@ -23,10 +23,11 @@ def render_log(): def test_log(): - expected = "\n\x1b[2;36m[TIME]\x1b[0m\x1b[2;36m \x1b[0mHello from \x1b[1m<\x1b[0m\x1b[1;38;5;13mconsole\x1b[0m\x1b[39m \x1b[0m\x1b[3;33mwidth\x1b[0m\x1b[39m=\x1b[0m\x1b[1;34m80\x1b[0m\x1b[39m ColorSystem.TRUECOLOR\x1b[0m\x1b[1m>\x1b[0m ! \x1b[2mtest_log.py:20\x1b[0m\x1b[2m \x1b[0m\n\x1b[2;36m \x1b[0m\x1b[2;36m \x1b[0m\x1b[1m[\x1b[0m\x1b[1;34m1\x1b[0m, \x1b[1;34m2\x1b[0m, \x1b[1;34m3\x1b[0m\x1b[1m]\x1b[0m \x1b[2mtest_log.py:21\x1b[0m\x1b[2m \x1b[0m\n \x1b[3m Locals \x1b[0m \n \x1b[34m╭─────────┬────────────────────────────────────────╮\x1b[0m \n \x1b[34m│\x1b[0m\x1b[32m'console'\x1b[0m\x1b[34m│\x1b[0m\x1b[1m<\x1b[0m\x1b[1;38;5;13mconsole\x1b[0m\x1b[39m \x1b[0m\x1b[3;33mwidth\x1b[0m\x1b[39m=\x1b[0m\x1b[1;34m80\x1b[0m\x1b[39m ColorSystem.TRUECOLOR\x1b[0m\x1b[1m>\x1b[0m\x1b[34m│\x1b[0m \n \x1b[34m╰─────────┴────────────────────────────────────────╯\x1b[0m \n" + expected = "\n\x1b[2;36m[TIME]\x1b[0m\x1b[2;36m \x1b[0mHello from \x1b[1m<\x1b[0m\x1b[1;38;5;13mconsole\x1b[0m\x1b[39m \x1b[0m\x1b[3;33mwidth\x1b[0m\x1b[39m=\x1b[0m\x1b[1;34m80\x1b[0m\x1b[39m ColorSystem.TRUECOLOR\x1b[0m\x1b[1m>\x1b[0m ! \x1b[2mtest_log.py:20\x1b[0m\n\x1b[2;36m \x1b[0m\x1b[2;36m \x1b[0m\x1b[1m[\x1b[0m\x1b[1;34m1\x1b[0m, \x1b[1;34m2\x1b[0m, \x1b[1;34m3\x1b[0m\x1b[1m]\x1b[0m \x1b[2mtest_log.py:21\x1b[0m\n \x1b[3m Locals \x1b[0m \n \x1b[34m╭─────────┬────────────────────────────────────────╮\x1b[0m \n \x1b[34m│\x1b[0m\x1b[32m'console'\x1b[0m\x1b[34m│\x1b[0m\x1b[1m<\x1b[0m\x1b[1;38;5;13mconsole\x1b[0m\x1b[39m \x1b[0m\x1b[3;33mwidth\x1b[0m\x1b[39m=\x1b[0m\x1b[1;34m80\x1b[0m\x1b[39m ColorSystem.TRUECOLOR\x1b[0m\x1b[1m>\x1b[0m\x1b[34m│\x1b[0m \n \x1b[34m╰─────────┴────────────────────────────────────────╯\x1b[0m \n" assert render_log() == expected if __name__ == "__main__": - print(render_log()) - print(repr(render_log())) + render = render_log() + print(render) + print(repr(render)) diff --git a/tests/test_logging.py b/tests/test_logging.py index 3ca2b0ac..23b19212 100644 --- a/tests/test_logging.py +++ b/tests/test_logging.py @@ -21,7 +21,7 @@ def make_log(): def test_log(): render = make_log() - expected = "\x1b[2;36m[DATE]\x1b[0m\x1b[2;36m \x1b[0m\x1b[32mDEBUG\x1b[0m foo \x1b[2mtest_logging.py:17\x1b[0m\x1b[2m \x1b[0m\n" + expected = "\x1b[2;36m[DATE]\x1b[0m\x1b[2;36m \x1b[0m\x1b[32mDEBUG\x1b[0m foo \x1b[2mtest_logging.py:17\x1b[0m\n" assert render == expected diff --git a/tests/test_progress.py b/tests/test_progress.py index eddd709c..d5ed5a6c 100644 --- a/tests/test_progress.py +++ b/tests/test_progress.py @@ -145,7 +145,7 @@ def test_track() -> None: assert value == next(expected_values) result = console.file.getvalue() print(repr(result)) - expected = "test \x1b[38;5;237m━━━━━━━━━━━━━━━━━━━━━\x1b[0m \x1b[35m 0%\x1b[0m \x1b[36m-:--:--\x1b[0m \x1b[?25l\r\x1b[2Ktest \x1b[38;5;237m━━━━━━━━━━━━━━━━━━━━━\x1b[0m \x1b[35m 0%\x1b[0m \x1b[36m-:--:--\x1b[0m \r\x1b[2Ktest \x1b[38;2;249;38;114m━━━━━━━\x1b[0m\x1b[38;5;237m╺\x1b[0m\x1b[38;5;237m━━━━━━━━━━━━━\x1b[0m \x1b[35m 33%\x1b[0m \x1b[36m-:--:--\x1b[0m \r\x1b[2Ktest \x1b[38;2;249;38;114m━━━━━━━━━━━━━━\x1b[0m\x1b[38;5;237m╺\x1b[0m\x1b[38;5;237m━━━━━━\x1b[0m \x1b[35m 67%\x1b[0m \x1b[36m0:00:06\x1b[0m \r\x1b[2Ktest \x1b[38;2;114;156;31m━━━━━━━━━━━━━━━━━━━━━\x1b[0m \x1b[35m100%\x1b[0m \x1b[36m0:00:00\x1b[0m \r\x1b[2Ktest \x1b[38;2;114;156;31m━━━━━━━━━━━━━━━━━━━━━\x1b[0m \x1b[35m100%\x1b[0m \x1b[36m0:00:00\x1b[0m \n\x1b[?25h" + expected = " \x1b[38;5;237m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\x1b[0m \x1b[36m-…\x1b[0m \x1b[?25l\r\x1b[2K \x1b[38;5;237m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\x1b[0m \x1b[36m-…\x1b[0m \r\x1b[2K\x1b[38;2;249;38;114m━━━━━━━━━━━\x1b[0m\x1b[38;2;249;38;114m╸\x1b[0m\x1b[38;5;237m━━━━━━━━━━━━━━━━━━━━━━━\x1b[0m \x1b[36m-:…\x1b[0m \r\x1b[2K\x1b[38;2;249;38;114m━━━━━━━━━━━━━━━━━━━━━━━\x1b[0m\x1b[38;5;237m╺\x1b[0m\x1b[38;5;237m━━━━━━━━━━━\x1b[0m \x1b[36m0:…\x1b[0m \r\x1b[2K\x1b[38;2;114;156;31m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\x1b[0m \x1b[36m0:…\x1b[0m \r\x1b[2K\x1b[38;2;114;156;31m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\x1b[0m \x1b[36m0:…\x1b[0m \n\x1b[?25h" assert result == expected with pytest.raises(ValueError): @@ -192,7 +192,7 @@ def test_columns() -> None: progress.refresh() result = console.file.getvalue() print(repr(result)) - expected = 'test foo \x1b[38;5;237m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\x1b[0m \x1b[36m-:--:--\x1b[0m \x1b[32m0 bytes\x1b[0m \x1b[32m10 bytes\x1b[0m \x1b[32m0/10 bytes\x1b[0m \x1b[31m?\x1b[0m \r\x1b[2Ktest foo \x1b[38;5;237m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\x1b[0m \x1b[36m-:--:--\x1b[0m \x1b[32m0 bytes\x1b[0m \x1b[32m10 bytes\x1b[0m \x1b[32m0/10 bytes\x1b[0m \x1b[31m?\x1b[0m \ntest bar \x1b[38;5;237m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\x1b[0m \x1b[36m-:--:--\x1b[0m \x1b[32m0 bytes\x1b[0m \x1b[32m7 bytes \x1b[0m \x1b[32m0/7 bytes \x1b[0m \x1b[31m?\x1b[0m \x1b[?25l\r\x1b[1A\x1b[2Ktest foo \x1b[38;5;237m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\x1b[0m \x1b[36m-:--:--\x1b[0m \x1b[32m0 bytes\x1b[0m \x1b[32m10 bytes\x1b[0m \x1b[32m0/10 bytes\x1b[0m \x1b[31m?\x1b[0m \ntest bar \x1b[38;5;237m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\x1b[0m \x1b[36m-:--:--\x1b[0m \x1b[32m0 bytes\x1b[0m \x1b[32m7 bytes \x1b[0m \x1b[32m0/7 bytes \x1b[0m \x1b[31m?\x1b[0m \r\x1b[1A\x1b[2K\x1b[2;36m[TIME]\x1b[0m\x1b[2;36m \x1b[0mhello \x1b[2mtest_progress.py:190\x1b[0m\x1b[2m \x1b[0m\ntest foo \x1b[38;5;237m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\x1b[0m \x1b[36m-:--:--\x1b[0m \x1b[32m0 bytes\x1b[0m \x1b[32m10 bytes\x1b[0m \x1b[32m0/10 bytes\x1b[0m \x1b[31m?\x1b[0m \ntest bar \x1b[38;5;237m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\x1b[0m \x1b[36m-:--:--\x1b[0m \x1b[32m0 bytes\x1b[0m \x1b[32m7 bytes \x1b[0m \x1b[32m0/7 bytes \x1b[0m \x1b[31m?\x1b[0m \r\x1b[1A\x1b[2Kworld\ntest foo \x1b[38;5;237m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\x1b[0m \x1b[36m-:--:--\x1b[0m \x1b[32m0 bytes\x1b[0m \x1b[32m10 bytes\x1b[0m \x1b[32m0/10 bytes\x1b[0m \x1b[31m?\x1b[0m \ntest bar \x1b[38;5;237m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\x1b[0m \x1b[36m-:--:--\x1b[0m \x1b[32m0 bytes\x1b[0m \x1b[32m7 bytes \x1b[0m \x1b[32m0/7 bytes \x1b[0m \x1b[31m?\x1b[0m \r\x1b[1A\x1b[2Ktest foo \x1b[38;2;114;156;31m━━━━━━━━━━━━━━━━━━━━━━\x1b[0m \x1b[36m0:00:00\x1b[0m \x1b[32m12 bytes\x1b[0m \x1b[32m10 bytes\x1b[0m \x1b[32m12/10 bytes\x1b[0m \x1b[31m1 byte/s \x1b[0m \ntest bar \x1b[38;2;114;156;31m━━━━━━━━━━━━━━━━━━━━━━\x1b[0m \x1b[36m0:00:00\x1b[0m \x1b[32m16 bytes\x1b[0m \x1b[32m7 bytes \x1b[0m \x1b[32m16/7 bytes \x1b[0m \x1b[31m2 bytes/s\x1b[0m \r\x1b[1A\x1b[2Ktest foo \x1b[38;2;114;156;31m━━━━━━━━━━━━━━━━━━━━━━\x1b[0m \x1b[36m0:00:00\x1b[0m \x1b[32m12 bytes\x1b[0m \x1b[32m10 bytes\x1b[0m \x1b[32m12/10 bytes\x1b[0m \x1b[31m1 byte/s \x1b[0m \ntest bar \x1b[38;2;114;156;31m━━━━━━━━━━━━━━━━━━━━━━\x1b[0m \x1b[36m0:00:00\x1b[0m \x1b[32m16 bytes\x1b[0m \x1b[32m7 bytes \x1b[0m \x1b[32m16/7 bytes \x1b[0m \x1b[31m2 bytes/s\x1b[0m \n\x1b[?25h' + expected = "test foo \x1b[38;5;237m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\x1b[0m \x1b[36m-:--:--\x1b[0m \x1b[32m0 bytes\x1b[0m \x1b[32m10 bytes\x1b[0m \x1b[32m0/10 bytes\x1b[0m \x1b[31m?\x1b[0m \r\x1b[2Ktest foo \x1b[38;5;237m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\x1b[0m \x1b[36m-:--:--\x1b[0m \x1b[32m0 bytes\x1b[0m \x1b[32m10 bytes\x1b[0m \x1b[32m0/10 bytes\x1b[0m \x1b[31m?\x1b[0m \ntest bar \x1b[38;5;237m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\x1b[0m \x1b[36m-:--:--\x1b[0m \x1b[32m0 bytes\x1b[0m \x1b[32m7 bytes \x1b[0m \x1b[32m0/7 bytes \x1b[0m \x1b[31m?\x1b[0m \x1b[?25l\r\x1b[1A\x1b[2Ktest foo \x1b[38;5;237m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\x1b[0m \x1b[36m-:--:--\x1b[0m \x1b[32m0 bytes\x1b[0m \x1b[32m10 bytes\x1b[0m \x1b[32m0/10 bytes\x1b[0m \x1b[31m?\x1b[0m \ntest bar \x1b[38;5;237m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\x1b[0m \x1b[36m-:--:--\x1b[0m \x1b[32m0 bytes\x1b[0m \x1b[32m7 bytes \x1b[0m \x1b[32m0/7 bytes \x1b[0m \x1b[31m?\x1b[0m \r\x1b[1A\x1b[2K\x1b[2;36m[TIME]\x1b[0m\x1b[2;36m \x1b[0mhello \x1b[2mtest_progress.py:190\x1b[0m\ntest foo \x1b[38;5;237m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\x1b[0m \x1b[36m-:--:--\x1b[0m \x1b[32m0 bytes\x1b[0m \x1b[32m10 bytes\x1b[0m \x1b[32m0/10 bytes\x1b[0m \x1b[31m?\x1b[0m \ntest bar \x1b[38;5;237m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\x1b[0m \x1b[36m-:--:--\x1b[0m \x1b[32m0 bytes\x1b[0m \x1b[32m7 bytes \x1b[0m \x1b[32m0/7 bytes \x1b[0m \x1b[31m?\x1b[0m \r\x1b[1A\x1b[2Kworld\ntest foo \x1b[38;5;237m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\x1b[0m \x1b[36m-:--:--\x1b[0m \x1b[32m0 bytes\x1b[0m \x1b[32m10 bytes\x1b[0m \x1b[32m0/10 bytes\x1b[0m \x1b[31m?\x1b[0m \ntest bar \x1b[38;5;237m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\x1b[0m \x1b[36m-:--:--\x1b[0m \x1b[32m0 bytes\x1b[0m \x1b[32m7 bytes \x1b[0m \x1b[32m0/7 bytes \x1b[0m \x1b[31m?\x1b[0m \r\x1b[1A\x1b[2Ktest foo \x1b[38;2;114;156;31m━━━━━━━━━━━━━━━━━━━━━━\x1b[0m \x1b[36m0:00:00\x1b[0m \x1b[32m12 bytes\x1b[0m \x1b[32m10 bytes\x1b[0m \x1b[32m12/10 bytes\x1b[0m \x1b[31m1 byte/s \x1b[0m \ntest bar \x1b[38;2;114;156;31m━━━━━━━━━━━━━━━━━━━━━━\x1b[0m \x1b[36m0:00:00\x1b[0m \x1b[32m16 bytes\x1b[0m \x1b[32m7 bytes \x1b[0m \x1b[32m16/7 bytes \x1b[0m \x1b[31m2 bytes/s\x1b[0m \r\x1b[1A\x1b[2Ktest foo \x1b[38;2;114;156;31m━━━━━━━━━━━━━━━━━━━━━━\x1b[0m \x1b[36m0:00:00\x1b[0m \x1b[32m12 bytes\x1b[0m \x1b[32m10 bytes\x1b[0m \x1b[32m12/10 bytes\x1b[0m \x1b[31m1 byte/s \x1b[0m \ntest bar \x1b[38;2;114;156;31m━━━━━━━━━━━━━━━━━━━━━━\x1b[0m \x1b[36m0:00:00\x1b[0m \x1b[32m16 bytes\x1b[0m \x1b[32m7 bytes \x1b[0m \x1b[32m16/7 bytes \x1b[0m \x1b[31m2 bytes/s\x1b[0m \n\x1b[?25h" assert result == expected diff --git a/tests/test_text.py b/tests/test_text.py index 34cc6cbd..3ba8052f 100644 --- a/tests/test_text.py +++ b/tests/test_text.py @@ -313,8 +313,18 @@ def test_right_crop(): assert test._spans == [Span(0, 3, "red")] -def test_wrap_4(): +def test_wrap_3(): test = Text("foo bar baz") + lines = test.wrap(Console(), 3) + print(repr(lines)) + assert len(lines) == 3 + assert lines[0] == Text("foo") + assert lines[1] == Text("bar") + assert lines[2] == Text("baz") + + +def test_wrap_4(): + test = Text("foo bar baz", justify="left") lines = test.wrap(Console(), 4) assert len(lines) == 3 assert lines[0] == Text("foo ") @@ -322,17 +332,8 @@ def test_wrap_4(): assert lines[2] == Text("baz ") -def test_wrap_3(): - test = Text("foo bar baz") - lines = test.wrap(Console(), 3) - assert len(lines) == 3 - assert lines[0] == Text("foo") - assert lines[1] == Text("bar") - assert lines[2] == Text("baz") - - def test_wrap_long(): - test = Text("abracadabra") + test = Text("abracadabra", justify="left") lines = test.wrap(Console(), 4) assert len(lines) == 3 assert lines[0] == Text("abra") @@ -341,7 +342,7 @@ def test_wrap_long(): def test_wrap_long_words(): - test = Text("X 123456789") + test = Text("X 123456789", justify="left") lines = test.wrap(Console(), 4) assert len(lines) == 3 @@ -358,7 +359,7 @@ def test_fit(): def test_wrap_tabs(): - test = Text("foo\tbar",) + test = Text("foo\tbar", justify="left") lines = test.wrap(Console(), 4) assert len(lines) == 2 assert str(lines[0]) == "foo "