From 04f425e921b7b4747a7900ac39e0ddcb8085cc2b Mon Sep 17 00:00:00 2001 From: Will McGugan Date: Sun, 26 Jul 2020 18:20:45 +0100 Subject: [PATCH 1/9] newline fix --- rich/__main__.py | 2 +- rich/console.py | 1 + rich/markdown.py | 2 +- rich/syntax.py | 2 + rich/text.py | 15 ++++--- tests/test_markdown.py | 89 ++++++++++++++++++++++++++++++++++++++-- tests/test_rich_print.py | 18 +++++++- 7 files changed, 118 insertions(+), 11 deletions(-) diff --git a/rich/__main__.py b/rich/__main__.py index 461d722b0..86d3a3bb2 100644 --- a/rich/__main__.py +++ b/rich/__main__.py @@ -75,7 +75,7 @@ def make_test_card() -> Table: "Text", RenderGroup( Text.from_markup( - """Word wrap text. Justify [green]left[/], [yellow]center[/], [blue]right[/] or [red]full[/].\n\n""" + """Word wrap text. Justify [green]left[/], [yellow]center[/], [blue]right[/] or [red]full[/].\n""" ), lorem_table, ), diff --git a/rich/console.py b/rich/console.py index ee5870b8c..cdd285aa5 100644 --- a/rich/console.py +++ b/rich/console.py @@ -784,6 +784,7 @@ def check_text() -> None: append_text(_highlighter(str(renderable))) check_text() + return renderables def rule( diff --git a/rich/markdown.py b/rich/markdown.py index d00de0d2f..75b938123 100644 --- a/rich/markdown.py +++ b/rich/markdown.py @@ -156,7 +156,7 @@ def __rich_console__( else: # Styled text for h2 and beyond if self.level == 2: - yield Text("\n") + yield Text("") yield text diff --git a/rich/syntax.py b/rich/syntax.py index b60e97360..3e8a1b9a2 100644 --- a/rich/syntax.py +++ b/rich/syntax.py @@ -229,6 +229,8 @@ def __rich_console__( if self.dedent: code = textwrap.dedent(code) text = self._highlight(self.lexer_name) + if text.plain.endswith("\n"): + text.plain = text.plain[:-1] if not self.line_numbers: if self.code_width is None: yield text diff --git a/rich/text.py b/rich/text.py index d625d3346..31d9e2547 100644 --- a/rich/text.py +++ b/rich/text.py @@ -758,7 +758,13 @@ def copy_styles(self, text: "Text") -> None: """ self._spans.extend(text._spans) - def split(self, separator="\n", *, include_separator: bool = False) -> Lines: + def split( + self, + separator="\n", + *, + include_separator: bool = False, + allow_blank: bool = False, + ) -> Lines: r"""Split rich text in to lines, preserving styles. Args: @@ -772,7 +778,7 @@ def split(self, separator="\n", *, include_separator: bool = False) -> Lines: text = self.plain if separator not in text: return Lines([self.copy()]) - if text.endswith(separator): + if not allow_blank and text.endswith(separator): text = text[: -len(separator)] offsets: List[int] = [] append = offsets.append @@ -886,8 +892,8 @@ def wrap( "OverflowMethod", overflow or self.overflow or DEFAULT_OVERFLOW ) no_wrap = pick_bool(no_wrap, self.no_wrap, False) - lines: Lines = Lines() - for line in self.split(): + lines = Lines() + for line in self.split(allow_blank=True): if "\t" in line: line = line.tabs_to_spaces(tab_size) if no_wrap: @@ -904,7 +910,6 @@ def wrap( for line in new_lines: line.truncate(width, overflow=wrap_overflow) lines.extend(new_lines) - return lines def fit(self, width: int) -> Lines: diff --git a/tests/test_markdown.py b/tests/test_markdown.py index da853810b..2df349968 100644 --- a/tests/test_markdown.py +++ b/tests/test_markdown.py @@ -1,13 +1,96 @@ # coding=utf-8 -from ._markdown import MARKDOWN +MARKDOWN = """Heading +======= -from .render import render +Sub-heading +----------- -from rich.console import Console +### Heading + +#### H4 Heading + +##### H5 Heading + +###### H6 Heading + + +Paragraphs are separated +by a blank line. + +Two spaces at the end of a line +produces a line break. + +Text attributes _italic_, +**bold**, `monospace`. + +Horizontal rule: + +--- + +Bullet list: + + * apples + * oranges + * pears + +Numbered list: + + 1. lather + 2. rinse + 3. repeat + +An [example](http://example.com). + +> Markdown uses email-style > characters for blockquoting. +> +> Lorem ipsum + +![progress](https://github.com/willmcgugan/rich/raw/master/imgs/progress.gif) + + +``` +a=1 +``` + +```python +import this +``` + +```somelang +foobar +``` + +""" + +import io +import re + +from rich.console import Console, RenderableType +from rich.console import Console, RenderableType from rich.markdown import Markdown +re_link_ids = re.compile(r"id=[\d\.\-]*?;.*?\x1b") + + +def replace_link_ids(render: str) -> str: + """Link IDs have a random ID and system path which is a problem for + reproducible tests. + + """ + return re_link_ids.sub("id=0;foo\x1b", render) + + +def render(renderable: RenderableType) -> str: + console = Console( + width=100, file=io.StringIO(), color_system="truecolor", legacy_windows=False + ) + console.print(renderable) + output = replace_link_ids(console.file.getvalue()) + return output + + def test_markdown_render(): markdown = Markdown(MARKDOWN) rendered_markdown = render(markdown) diff --git a/tests/test_rich_print.py b/tests/test_rich_print.py index 81e2a382a..7fe1af7ad 100644 --- a/tests/test_rich_print.py +++ b/tests/test_rich_print.py @@ -16,6 +16,22 @@ def test_rich_print(): try: console.file = output rich.print("foo") - assert output.getvalue() == "foo\n" + rich.print("foo\n") + rich.print("foo\n\n") + assert output.getvalue() == "foo\nfoo\n\nfoo\n\n\n" + finally: + console.file = backup_file + + +def test_rich_print_X(): + console = rich.get_console() + output = io.StringIO() + backup_file = console.file + try: + console.file = output + rich.print("foo") + rich.print("fooX") + rich.print("fooXX") + assert output.getvalue() == "foo\nfooX\nfooXX\n" finally: console.file = backup_file From 2e8f5dee961229f0a725be1eca229c1a30c84793 Mon Sep 17 00:00:00 2001 From: Will McGugan Date: Sun, 26 Jul 2020 18:21:54 +0100 Subject: [PATCH 2/9] test --- tests/_markdown.py | 62 -------------------- tests/test_markdown.py | 1 - tests/test_markdown_no_hyperlinks.py | 88 +++++++++++++++++++++++++++- 3 files changed, 85 insertions(+), 66 deletions(-) delete mode 100644 tests/_markdown.py diff --git a/tests/_markdown.py b/tests/_markdown.py deleted file mode 100644 index a089f7c2e..000000000 --- a/tests/_markdown.py +++ /dev/null @@ -1,62 +0,0 @@ -MARKDOWN = """Heading -======= - -Sub-heading ------------ - -### Heading - -#### H4 Heading - -##### H5 Heading - -###### H6 Heading - - -Paragraphs are separated -by a blank line. - -Two spaces at the end of a line -produces a line break. - -Text attributes _italic_, -**bold**, `monospace`. - -Horizontal rule: - ---- - -Bullet list: - - * apples - * oranges - * pears - -Numbered list: - - 1. lather - 2. rinse - 3. repeat - -An [example](http://example.com). - -> Markdown uses email-style > characters for blockquoting. -> -> Lorem ipsum - -![progress](https://github.com/willmcgugan/rich/raw/master/imgs/progress.gif) - - -``` -a=1 -``` - -```python -import this -``` - -```somelang -foobar -``` - -""" diff --git a/tests/test_markdown.py b/tests/test_markdown.py index 2df349968..4eb3fd237 100644 --- a/tests/test_markdown.py +++ b/tests/test_markdown.py @@ -66,7 +66,6 @@ import io import re -from rich.console import Console, RenderableType from rich.console import Console, RenderableType from rich.markdown import Markdown diff --git a/tests/test_markdown_no_hyperlinks.py b/tests/test_markdown_no_hyperlinks.py index be0b48373..8ce671855 100644 --- a/tests/test_markdown_no_hyperlinks.py +++ b/tests/test_markdown_no_hyperlinks.py @@ -1,13 +1,95 @@ # coding=utf-8 -from ._markdown import MARKDOWN +MARKDOWN = """Heading +======= -from .render import render +Sub-heading +----------- -from rich.console import Console +### Heading + +#### H4 Heading + +##### H5 Heading + +###### H6 Heading + + +Paragraphs are separated +by a blank line. + +Two spaces at the end of a line +produces a line break. + +Text attributes _italic_, +**bold**, `monospace`. + +Horizontal rule: + +--- + +Bullet list: + + * apples + * oranges + * pears + +Numbered list: + + 1. lather + 2. rinse + 3. repeat + +An [example](http://example.com). + +> Markdown uses email-style > characters for blockquoting. +> +> Lorem ipsum + +![progress](https://github.com/willmcgugan/rich/raw/master/imgs/progress.gif) + + +``` +a=1 +``` + +```python +import this +``` + +```somelang +foobar +``` + +""" + +import io +import re + +from rich.console import Console, RenderableType from rich.markdown import Markdown +re_link_ids = re.compile(r"id=[\d\.\-]*?;.*?\x1b") + + +def replace_link_ids(render: str) -> str: + """Link IDs have a random ID and system path which is a problem for + reproducible tests. + + """ + return re_link_ids.sub("id=0;foo\x1b", render) + + +def render(renderable: RenderableType) -> str: + console = Console( + width=100, file=io.StringIO(), color_system="truecolor", legacy_windows=False + ) + console.print(renderable) + output = replace_link_ids(console.file.getvalue()) + return output + + def test_markdown_render(): markdown = Markdown(MARKDOWN, hyperlinks=False) rendered_markdown = render(markdown) From 7a5f5a32cfa730d02343372e3105c291e0b59476 Mon Sep 17 00:00:00 2001 From: Will McGugan Date: Sun, 26 Jul 2020 18:23:06 +0100 Subject: [PATCH 3/9] test --- tests/test_rich_print.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/test_rich_print.py b/tests/test_rich_print.py index 7fe1af7ad..9fc611e24 100644 --- a/tests/test_rich_print.py +++ b/tests/test_rich_print.py @@ -15,10 +15,10 @@ def test_rich_print(): backup_file = console.file try: console.file = output - rich.print("foo") + rich.print("foo", "bar") rich.print("foo\n") rich.print("foo\n\n") - assert output.getvalue() == "foo\nfoo\n\nfoo\n\n\n" + assert output.getvalue() == "foo bar\nfoo\n\nfoo\n\n\n" finally: console.file = backup_file From 113ac6605d68af5567da9c59e195970e799c95b6 Mon Sep 17 00:00:00 2001 From: Will McGugan Date: Sun, 26 Jul 2020 18:25:43 +0100 Subject: [PATCH 4/9] version bump --- CHANGELOG.md | 6 ++++++ pyproject.toml | 2 +- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f5b08c8c9..c8ea741ff 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,12 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [4.2.0] - 2020-07-26 + +### Fixed + +- Fixed missing new lines https://github.com/willmcgugan/rich/issues/178 + ## [4.1.0] - 2020-07-26 ### Changed diff --git a/pyproject.toml b/pyproject.toml index e62ecef51..51055de4b 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -2,7 +2,7 @@ name = "rich" homepage = "https://github.com/willmcgugan/rich" documentation = "https://rich.readthedocs.io/en/latest/" -version = "4.1.0" +version = "4.2.0" description = "Render rich text, tables, progress bars, syntax highlighting, markdown and more to the terminal" authors = ["Will McGugan "] license = "MIT" From 220ac27f27595796661751e5b1cc68c2c432a7c9 Mon Sep 17 00:00:00 2001 From: Will McGugan Date: Sun, 26 Jul 2020 18:29:21 +0100 Subject: [PATCH 5/9] tweak GH actions --- .github/workflows/pythonpackage.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/pythonpackage.yml b/.github/workflows/pythonpackage.yml index 618557ff8..de443c064 100644 --- a/.github/workflows/pythonpackage.yml +++ b/.github/workflows/pythonpackage.yml @@ -1,12 +1,12 @@ name: Test Rich module -on: [push, pull_request] +on: [pull_request] jobs: build: runs-on: ${{ matrix.os }} strategy: - max-parallel: 4 + # max-parallel: 4 matrix: os: [ubuntu-latest, windows-latest, macos-latest] python-version: [3.6, 3.7, 3.8] From fcc3c2bd65cdda386b283ef9d6c8c1e62fb57f43 Mon Sep 17 00:00:00 2001 From: Will McGugan Date: Sun, 26 Jul 2020 18:35:06 +0100 Subject: [PATCH 6/9] tidy yml --- .github/workflows/pythonpackage.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/pythonpackage.yml b/.github/workflows/pythonpackage.yml index de443c064..70eaaef7c 100644 --- a/.github/workflows/pythonpackage.yml +++ b/.github/workflows/pythonpackage.yml @@ -6,7 +6,6 @@ jobs: build: runs-on: ${{ matrix.os }} strategy: - # max-parallel: 4 matrix: os: [ubuntu-latest, windows-latest, macos-latest] python-version: [3.6, 3.7, 3.8] From e5b9fd8b7d047a51c7674493c1e55f57983b704a Mon Sep 17 00:00:00 2001 From: Will McGugan Date: Mon, 27 Jul 2020 17:21:03 +0100 Subject: [PATCH 7/9] 16 color fixes --- CHANGELOG.md | 5 ++- rich/_palettes.py | 36 +++++++++++++++--- rich/color.py | 30 +++++++++------ rich/console.py | 22 +++++++---- rich/markdown.py | 4 +- rich/palette.py | 8 ++-- rich/progress.py | 13 ++++--- tests/_card_render.py | 2 +- tests/test_card.py | 28 +++++++++++--- tests/test_color.py | 26 +++++++++---- tests/test_console.py | 15 +++++++- tests/test_log.py | 2 +- tests/test_markdown.py | 2 +- tests/test_markdown_no_hyperlinks.py | 2 +- tests/test_progress.py | 56 ++++++++++++++++++++++++++++ tests/test_rule.py | 6 ++- 16 files changed, 201 insertions(+), 56 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c8ea741ff..6a062181e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,11 +5,14 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). -## [4.2.0] - 2020-07-26 +## [4.2.0] - 2020-07-27 ### Fixed - Fixed missing new lines https://github.com/willmcgugan/rich/issues/178 +- Fixed Progress.track https://github.com/willmcgugan/rich/issues/184 +- Remove control codes from exported text https://github.com/willmcgugan/rich/issues/181 +- Implemented auto-detection and color rendition of 16-color mode ## [4.1.0] - 2020-07-26 diff --git a/rich/_palettes.py b/rich/_palettes.py index 8dfe6f4e8..d562d821d 100644 --- a/rich/_palettes.py +++ b/rich/_palettes.py @@ -2,19 +2,42 @@ # The standard ansi colors +WINDOWS_PALETTE = Palette( + [ + (0, 0, 0), + (128, 0, 0), + (0, 128, 0), + (128, 128, 0), + (0, 0, 128), + (128, 0, 128), + (0, 128, 128), + (192, 192, 192), + ] +) + +# # The standard ansi colors (including bright variants) STANDARD_PALETTE = Palette( [ (0, 0, 0), - (255, 0, 0), - (0, 255, 0), - (255, 255, 0), - (0, 0, 255), - (255, 0, 255), - (0, 255, 255), + (170, 0, 0), + (0, 170, 0), + (170, 85, 0), + (0, 0, 170), + (170, 0, 170), + (0, 170, 170), + (170, 170, 170), + (85, 85, 85), + (255, 85, 85), + (85, 255, 85), + (255, 255, 85), + (85, 85, 255), + (255, 85, 255), + (85, 255, 255), (255, 255, 255), ] ) + # The 256 color palette EIGHT_BIT_PALETTE = Palette( [ @@ -276,3 +299,4 @@ (238, 238, 238), ] ) + diff --git a/rich/color.py b/rich/color.py index c6b2a6cc6..ab5e29135 100644 --- a/rich/color.py +++ b/rich/color.py @@ -1,11 +1,11 @@ +import platform import re from colorsys import rgb_to_hls from enum import IntEnum from functools import lru_cache -import platform -from typing import NamedTuple, Optional, Tuple, TYPE_CHECKING +from typing import TYPE_CHECKING, NamedTuple, Optional, Tuple -from ._palettes import STANDARD_PALETTE, EIGHT_BIT_PALETTE +from ._palettes import EIGHT_BIT_PALETTE, STANDARD_PALETTE, WINDOWS_PALETTE from .color_triplet import ColorTriplet from .terminal_theme import DEFAULT_TERMINAL_THEME @@ -297,10 +297,7 @@ def get_truecolor( return self.triplet elif self.type == ColorType.EIGHT_BIT: assert self.number is not None - if self.number <= 15: - return theme.ansi_colors[self.number] - else: - return EIGHT_BIT_PALETTE[self.number] + return EIGHT_BIT_PALETTE[self.number] elif self.type == ColorType.STANDARD: assert self.number is not None return theme.ansi_colors[self.number] @@ -347,7 +344,7 @@ def parse(cls, color: str) -> "Color": color, type=( ColorType.STANDARD - if named_color_number < 8 + if named_color_number < 16 else ColorType.EIGHT_BIT ), number=named_color_number, @@ -362,7 +359,11 @@ def parse(cls, color: str) -> "Color": number = int(color_8) if number > 255: raise ColorParseError(f"8bit colors must be <= 255 in {color!r}") - return cls(color, ColorType.EIGHT_BIT, number=number) + return cls( + color, + ColorType.STANDARD if number < 16 else ColorType.EIGHT_BIT, + number=number, + ) elif color_24: triplet = ColorTriplet( @@ -387,11 +388,17 @@ def get_ansi_codes(self, foreground: bool = True) -> Tuple[str, ...]: if _type == ColorType.DEFAULT: return ("39" if foreground else "49",) - elif _type in (ColorType.STANDARD, ColorType.WINDOWS): + elif _type == ColorType.WINDOWS: number = self.number assert number is not None return (str(30 + number if foreground else 40 + number),) + elif _type == ColorType.STANDARD: + number = self.number + assert number is not None + fore, back = (30, 40) if number < 8 else (82, 92) + return (str(fore + number if foreground else back + number),) + elif _type == ColorType.EIGHT_BIT: assert self.number is not None return ("38" if foreground else "48", "5", str(self.number)) @@ -410,6 +417,7 @@ def downgrade(self, system: ColorSystem) -> "Color": # Convert to 8-bit color from truecolor color if system == ColorSystem.EIGHT_BIT and self.system == ColorSystem.TRUECOLOR: assert self.triplet is not None + red, green, blue = self.triplet.normalized _h, l, s = rgb_to_hls(red, green, blue) @@ -453,7 +461,7 @@ def downgrade(self, system: ColorSystem) -> "Color": return Color(self.name, ColorType.WINDOWS, number=self.number - 8) triplet = ColorTriplet(*EIGHT_BIT_PALETTE[self.number]) - color_number = STANDARD_PALETTE.match(triplet) + color_number = WINDOWS_PALETTE.match(triplet) return Color(self.name, ColorType.WINDOWS, number=color_number) return self diff --git a/rich/console.py b/rich/console.py index cdd285aa5..2141686de 100644 --- a/rich/console.py +++ b/rich/console.py @@ -73,6 +73,8 @@ """ +_TERM_COLORS = {"256color": ColorSystem.EIGHT_BIT, "16color": ColorSystem.STANDARD} + @dataclass class ConsoleOptions: @@ -406,11 +408,12 @@ def _detect_color_system(self) -> Optional[ColorSystem]: ) else: color_term = self._environ.get("COLORTERM", "").strip().lower() - return ( - ColorSystem.TRUECOLOR - if color_term in ("truecolor", "24bit") - else ColorSystem.EIGHT_BIT - ) + if color_term in ("truecolor", "24bit"): + return ColorSystem.TRUECOLOR + term = self._environ.get("TERM", "").strip().lower() + _term_name, _hyphen, colors = term.partition("-") + color_system = _TERM_COLORS.get(colors, ColorSystem.STANDARD) + return color_system def _enter_buffer(self) -> None: """Enter in to a buffer context, and buffer all output.""" @@ -1047,7 +1050,7 @@ def export_text(self, *, clear: bool = True, styles: bool = False) -> str: Args: clear (bool, optional): Set to ``True`` to clear the record buffer after exporting. - styles (bool, optional): If ``True``, ansi style codes will be included. ``False`` for plain text. + styles (bool, optional): If ``True``, ansi escape codes will be included. ``False`` for plain text. Defaults to ``False``. Returns: @@ -1065,7 +1068,11 @@ def export_text(self, *, clear: bool = True, styles: bool = False) -> str: for text, style, _ in self._record_buffer ) else: - text = "".join(text for text, _, _ in self._record_buffer) + text = "".join( + segment.text + for segment in self._record_buffer + if not segment.is_control + ) if clear: del self._record_buffer[:] return text @@ -1197,6 +1204,7 @@ def save_html( if __name__ == "__main__": # pragma: no cover console = Console() + console.log( "JSONRPC [i]request[/i]", 5, diff --git a/rich/markdown.py b/rich/markdown.py index 75b938123..352dfdf3d 100644 --- a/rich/markdown.py +++ b/rich/markdown.py @@ -180,7 +180,9 @@ def __rich_console__( ) -> RenderResult: code = str(self.text).rstrip() syntax = Panel( - Syntax(code, self.lexer_name, theme=self.theme), style="dim", box=box.SQUARE + Syntax(code, self.lexer_name, theme=self.theme), + border_style="dim", + box=box.SQUARE, ) yield syntax diff --git a/rich/palette.py b/rich/palette.py index 5fb7ff96f..3faa23fa9 100644 --- a/rich/palette.py +++ b/rich/palette.py @@ -28,9 +28,9 @@ def match(self, color: Tuple[int, int, int]) -> int: red1, green1, blue1 = color _sqrt = sqrt - def get_color_distance(color: Tuple[int, int, int]) -> float: + def get_color_distance(index: int) -> float: """Get the distance to a color.""" - red2, green2, blue2 = color + red2, green2, blue2 = self._colors[index] red_mean = int((red1 + red2) / 2) red = red1 - red2 green = green1 - green2 @@ -41,7 +41,5 @@ def get_color_distance(color: Tuple[int, int, int]) -> float: + (((767 - red_mean) * blue * blue) >> 8) ) - min_index, _min_color = min( - enumerate(self._colors), key=lambda _color: get_color_distance(_color[1]), - ) + min_index = min(range(len(self._colors)), key=get_color_distance) return min_index diff --git a/rich/progress.py b/rich/progress.py index 3df1cb51d..70083b267 100644 --- a/rich/progress.py +++ b/rich/progress.py @@ -55,7 +55,10 @@ def iter_track( - values: Iterable[ProgressType], total: int, update_period: float = 0.05 + values: Iterable[ProgressType], + total: int, + update_period: float = 0.05, + get_time: Callable[[], float] = None, ) -> Iterable[Iterable[ProgressType]]: """Break a sequence in to chunks based on time. @@ -71,7 +74,7 @@ def iter_track( yield [value] return - get_time = perf_counter + get_time = get_time or perf_counter period_size = 1.0 def gen_values( @@ -87,7 +90,7 @@ def gen_values( value_count = 0 while value_count < total: - _count = int(period_size) + _count = min(int(period_size), total - value_count) start_time = get_time() yield gen_values(iter_values, _count) time_taken = get_time() - start_time @@ -649,7 +652,7 @@ def stop(self) -> None: self._refresh_thread = None if self.transient: self.console.control(self._live_render.restore_cursor()) - if self.ipy_widget is not None and self.transient: + if self.ipy_widget is not None and self.transient: # pragma: no cover self.ipy_widget.clear_output() self.ipy_widget.close() @@ -707,7 +710,7 @@ def track( advance_total += 1 advance(task_id, advance_total) if not self.auto_refresh: - progress.refresh() + self.refresh() def start_task(self, task_id: TaskID) -> None: """Start a task. diff --git a/tests/_card_render.py b/tests/_card_render.py index 3810fb60b..320a7e7af 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[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 \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 \x1b[0m \x1b[34multricies pretium a\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[33m a at justo. \x1b[0m \x1b[34m at justo. Maecenas\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 Maecenas luctus \x1b[0m \x1b[34m luctus velit et\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 velit et auctor \x1b[0m \x1b[34m auctor maximus.\x1b[0m \x1b[31mauctor\x1b[0m\x1b[31m \x1b[0m\x1b[31mmaximus.\x1b[0m \n \x1b[32mDonec faucibus vel \x1b[0m \x1b[33m maximus. Donec \x1b[0m \x1b[34m Donec faucibus vel\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[33mfaucibus vel arcu \x1b[0m \x1b[34m arcu id pretium.\x1b[0m \x1b[31marcu id pretium.\x1b[0m \n \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 \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;35mProduction Budget\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 ┡━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━┩ \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 │ $275,000,000 │ $393,151,347 │ \n │ │ Story │ │ │ \n │\x1b[2m \x1b[0m\x1b[2mDec 15, 2017\x1b[0m\x1b[2m \x1b[0m│ Star Wars Ep. VIII: │ $262,000,000 │ \x1b[1m$1,332,539,889\x1b[0m │ \n │ │ The 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' +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 \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 \x1b[0m \x1b[34multricies pretium a\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[33m a at justo. \x1b[0m \x1b[34m at justo. Maecenas\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 Maecenas luctus \x1b[0m \x1b[34m luctus velit et\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 velit et auctor \x1b[0m \x1b[34m auctor maximus.\x1b[0m \x1b[31mauctor\x1b[0m\x1b[31m \x1b[0m\x1b[31mmaximus.\x1b[0m \n \x1b[32mDonec faucibus vel \x1b[0m \x1b[33m maximus. Donec \x1b[0m \x1b[34m Donec faucibus vel\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[33mfaucibus vel arcu \x1b[0m \x1b[34m arcu id pretium.\x1b[0m \x1b[31marcu id pretium.\x1b[0m \n \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 \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;35mProduction Budget\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 ┡━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━┩ \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 │ $275,000,000 │ $393,151,347 │ \n │ │ Story │ │ │ \n │\x1b[2m \x1b[0m\x1b[2mDec 15, 2017\x1b[0m\x1b[2m \x1b[0m│ Star Wars Ep. VIII: │ $262,000,000 │ \x1b[1m$1,332,539,889\x1b[0m │ \n │ │ The 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[97;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_card.py b/tests/test_card.py index 38e723a15..41581fda6 100644 --- a/tests/test_card.py +++ b/tests/test_card.py @@ -1,11 +1,29 @@ +import io +import re + +from rich.console import Console, RenderableType from rich.__main__ import make_test_card -from .render import render +from ._card_render import expected + +re_link_ids = re.compile(r"id=[\d\.\-]*?;.*?\x1b") + + +def replace_link_ids(render: str) -> str: + """Link IDs have a random ID and system path which is a problem for + reproducible tests. + + """ + return re_link_ids.sub("id=0;foo\x1b", render) + -try: - from ._card_render import expected -except ImportError: - expected = None +def render(renderable: RenderableType) -> str: + console = Console( + width=100, file=io.StringIO(), color_system="truecolor", legacy_windows=False + ) + console.print(renderable) + output = replace_link_ids(console.file.getvalue()) + return output def test_card_render(): diff --git a/tests/test_color.py b/tests/test_color.py index 3a9cc32f1..b691a7260 100644 --- a/tests/test_color.py +++ b/tests/test_color.py @@ -27,6 +27,10 @@ def test_system() -> None: assert Color.parse("#ff0000").system == ColorSystem.TRUECOLOR +def test_windows() -> None: + assert Color("red", ColorType.WINDOWS, number=1).get_ansi_codes() == ("31",) + + def test_truecolor() -> None: assert Color.parse("#ff0000").get_truecolor() == ColorTriplet(255, 0, 0) assert Color.parse("red").get_truecolor() == ColorTriplet(128, 0, 0) @@ -37,16 +41,14 @@ def test_truecolor() -> None: 255, 255, 255 ) assert Color("red", ColorType.WINDOWS, number=1).get_truecolor() == ColorTriplet( - 255, 0, 0 + 170, 0, 0 ) def test_parse_success() -> None: assert Color.parse("default") == Color("default", ColorType.DEFAULT, None, None) assert Color.parse("red") == Color("red", ColorType.STANDARD, 1, None) - assert Color.parse("bright_red") == Color( - "bright_red", ColorType.EIGHT_BIT, 9, None - ) + assert Color.parse("bright_red") == Color("bright_red", ColorType.STANDARD, 9, None) assert Color.parse("yellow4") == Color("yellow4", ColorType.EIGHT_BIT, 106, None) assert Color.parse("100") == Color("100", ColorType.EIGHT_BIT, 100, None) assert Color.parse("#112233") == Color( @@ -87,15 +89,15 @@ def test_get_ansi_codes() -> None: assert Color.parse("default").get_ansi_codes(False) == ("49",) assert Color.parse("red").get_ansi_codes() == ("31",) assert Color.parse("red").get_ansi_codes(False) == ("41",) - assert Color.parse("1").get_ansi_codes() == ("38", "5", "1") - assert Color.parse("1").get_ansi_codes(False) == ("48", "5", "1") + assert Color.parse("1").get_ansi_codes() == ("31",) + assert Color.parse("1").get_ansi_codes(False) == ("41",) assert Color.parse("#ff0000").get_ansi_codes() == ("38", "2", "255", "0", "0") assert Color.parse("#ff0000").get_ansi_codes(False) == ("48", "2", "255", "0", "0") def test_downgrade() -> None: - assert Color.parse("9").downgrade(0) == Color("9", ColorType.EIGHT_BIT, 9, None) + assert Color.parse("9").downgrade(0) == Color("9", ColorType.STANDARD, 9, None) assert Color.parse("#000000").downgrade(ColorSystem.EIGHT_BIT) == Color( "#000000", ColorType.EIGHT_BIT, 16, None @@ -118,7 +120,11 @@ def test_downgrade() -> None: ) assert Color.parse("9").downgrade(ColorSystem.STANDARD) == Color( - "9", ColorType.STANDARD, 1, None + "9", ColorType.STANDARD, 9, None + ) + + assert Color.parse("20").downgrade(ColorSystem.STANDARD) == Color( + "20", ColorType.STANDARD, 4, None ) assert Color.parse("red").downgrade(ColorSystem.WINDOWS) == Color( @@ -137,6 +143,10 @@ def test_downgrade() -> None: "255", ColorType.WINDOWS, 7, None ) + assert Color.parse("#00ff00").downgrade(ColorSystem.STANDARD) == Color( + "#00ff00", ColorType.STANDARD, 2, None + ) + def test_parse_rgb_hex() -> None: assert parse_rgb_hex("aabbcc") == ColorTriplet(0xAA, 0xBB, 0xCC) diff --git a/tests/test_console.py b/tests/test_console.py index 358f2680e..065da1651 100644 --- a/tests/test_console.py +++ b/tests/test_console.py @@ -4,7 +4,7 @@ import pytest -from rich.color import ColorSystem +from rich.color import Color, ColorSystem from rich.console import Console, ConsoleOptions from rich import errors from rich.panel import Panel @@ -24,6 +24,19 @@ def test_dumb_terminal(): assert height == 25 +def test_16color_terminal(): + console = Console(force_terminal=True, _environ={"TERM": "xterm-16color"}) + assert console.color_system == "standard" + + +def test_truecolor_terminal(): + console = Console( + force_terminal=True, + _environ={"COLORTERM": "truecolor", "TERM": "xterm-16color"}, + ) + assert console.color_system == "truecolor" + + def test_console_options_update(): options = ConsoleOptions( min_width=10, max_width=20, is_terminal=False, encoding="utf-8" diff --git a/tests/test_log.py b/tests/test_log.py index 1948e8332..edb3fee20 100644 --- a/tests/test_log.py +++ b/tests/test_log.py @@ -27,7 +27,7 @@ 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]8;id=0;foo\x1b\\\x1b[2mtest_log.py\x1b[0m\x1b]8;;\x1b\\\x1b[2m:24\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]8;id=0;foo\x1b\\\x1b[2mtest_log.py\x1b[0m\x1b]8;;\x1b\\\x1b[2m:25\x1b[0m\n \x1b[34m╭─\x1b[0m\x1b[34m───────────────────── \x1b[0m\x1b[3;34mlocals\x1b[0m\x1b[34m ─────────────────────\x1b[0m\x1b[34m─╮\x1b[0m \n \x1b[34m│\x1b[0m \x1b[3;33mconsole\x1b[0m\x1b[31m =\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;95mconsole\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]8;id=0;foo\x1b\\\x1b[2mtest_log.py\x1b[0m\x1b]8;;\x1b\\\x1b[2m:24\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]8;id=0;foo\x1b\\\x1b[2mtest_log.py\x1b[0m\x1b]8;;\x1b\\\x1b[2m:25\x1b[0m\n \x1b[34m╭─\x1b[0m\x1b[34m───────────────────── \x1b[0m\x1b[3;34mlocals\x1b[0m\x1b[34m ─────────────────────\x1b[0m\x1b[34m─╮\x1b[0m \n \x1b[34m│\x1b[0m \x1b[3;33mconsole\x1b[0m\x1b[31m =\x1b[0m \x1b[1m<\x1b[0m\x1b[1;95mconsole\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 diff --git a/tests/test_markdown.py b/tests/test_markdown.py index 4eb3fd237..7f7ad420b 100644 --- a/tests/test_markdown.py +++ b/tests/test_markdown.py @@ -93,7 +93,7 @@ def render(renderable: RenderableType) -> str: def test_markdown_render(): markdown = Markdown(MARKDOWN) rendered_markdown = render(markdown) - expected = "╔══════════════════════════════════════════════════════════════════════════════════════════════════╗\n║ \x1b[1mHeading\x1b[0m ║\n╚══════════════════════════════════════════════════════════════════════════════════════════════════╝\n\n\n \x1b[1;4mSub-heading\x1b[0m \n\n \x1b[1mHeading\x1b[0m \n\n \x1b[1;2mH4 Heading\x1b[0m \n\n \x1b[4mH5 Heading\x1b[0m \n\n \x1b[3mH6 Heading\x1b[0m \n\nParagraphs are separated by a blank line. \n\nTwo spaces at the end of a line \nproduces a line break. \n\nText attributes \x1b[3mitalic\x1b[0m, \x1b[1mbold\x1b[0m, \x1b[38;5;15;40mmonospace\x1b[0m. \n\nHorizontal rule: \n\n\x1b[33m────────────────────────────────────────────────────────────────────────────────────────────────────\x1b[0m\nBullet list: \n\n\x1b[1;33m • \x1b[0mapples \n\x1b[1;33m • \x1b[0moranges \n\x1b[1;33m • \x1b[0mpears \n\nNumbered list: \n\n\x1b[1;33m 1 \x1b[0mlather \n\x1b[1;33m 2 \x1b[0mrinse \n\x1b[1;33m 3 \x1b[0mrepeat \n\nAn \x1b]8;id=0;foo\x1b\\\x1b[38;5;12mexample\x1b[0m\x1b]8;;\x1b\\. \n\n\x1b[35m▌ \x1b[0m\x1b[35mMarkdown uses email-style > characters for blockquoting.\x1b[0m\x1b[35m \x1b[0m\n\x1b[35m▌ \x1b[0m\x1b[35mLorem ipsum\x1b[0m\x1b[35m \x1b[0m\n\n🌆 \x1b]8;id=0;foo\x1b\\progress\x1b]8;;\x1b\\ \n\n\x1b[2m┌──────────────────────────────────────────────────────────────────────────────────────────────────┐\x1b[0m\n\x1b[2m│\x1b[0m\x1b[2;38;2;248;248;242;48;2;39;40;34ma=1 \x1b[0m\x1b[2m│\x1b[0m\n\x1b[2m└──────────────────────────────────────────────────────────────────────────────────────────────────┘\x1b[0m\n\n\x1b[2m┌──────────────────────────────────────────────────────────────────────────────────────────────────┐\x1b[0m\n\x1b[2m│\x1b[0m\x1b[2;38;2;249;38;114;48;2;39;40;34mimport\x1b[0m\x1b[2;38;2;248;248;242;48;2;39;40;34m \x1b[0m\x1b[2;38;2;248;248;242;48;2;39;40;34mthis\x1b[0m\x1b[2;38;2;248;248;242;48;2;39;40;34m \x1b[0m\x1b[2m│\x1b[0m\n\x1b[2m└──────────────────────────────────────────────────────────────────────────────────────────────────┘\x1b[0m\n\n\x1b[2m┌──────────────────────────────────────────────────────────────────────────────────────────────────┐\x1b[0m\n\x1b[2m│\x1b[0m\x1b[2;38;2;248;248;242;48;2;39;40;34mfoobar \x1b[0m\x1b[2m│\x1b[0m\n\x1b[2m└──────────────────────────────────────────────────────────────────────────────────────────────────┘\x1b[0m\n" + expected = "╔══════════════════════════════════════════════════════════════════════════════════════════════════╗\n║ \x1b[1mHeading\x1b[0m ║\n╚══════════════════════════════════════════════════════════════════════════════════════════════════╝\n\n\n \x1b[1;4mSub-heading\x1b[0m \n\n \x1b[1mHeading\x1b[0m \n\n \x1b[1;2mH4 Heading\x1b[0m \n\n \x1b[4mH5 Heading\x1b[0m \n\n \x1b[3mH6 Heading\x1b[0m \n\nParagraphs are separated by a blank line. \n\nTwo spaces at the end of a line \nproduces a line break. \n\nText attributes \x1b[3mitalic\x1b[0m, \x1b[1mbold\x1b[0m, \x1b[97;40mmonospace\x1b[0m. \n\nHorizontal rule: \n\n\x1b[33m────────────────────────────────────────────────────────────────────────────────────────────────────\x1b[0m\nBullet list: \n\n\x1b[1;33m • \x1b[0mapples \n\x1b[1;33m • \x1b[0moranges \n\x1b[1;33m • \x1b[0mpears \n\nNumbered list: \n\n\x1b[1;33m 1 \x1b[0mlather \n\x1b[1;33m 2 \x1b[0mrinse \n\x1b[1;33m 3 \x1b[0mrepeat \n\nAn \x1b]8;id=0;foo\x1b\\\x1b[94mexample\x1b[0m\x1b]8;;\x1b\\. \n\n\x1b[35m▌ \x1b[0m\x1b[35mMarkdown uses email-style > characters for blockquoting.\x1b[0m\x1b[35m \x1b[0m\n\x1b[35m▌ \x1b[0m\x1b[35mLorem ipsum\x1b[0m\x1b[35m \x1b[0m\n\n🌆 \x1b]8;id=0;foo\x1b\\progress\x1b]8;;\x1b\\ \n\n\x1b[2m┌──────────────────────────────────────────────────────────────────────────────────────────────────┐\x1b[0m\n\x1b[2m│\x1b[0m\x1b[38;2;248;248;242;48;2;39;40;34ma=1 \x1b[0m\x1b[2m│\x1b[0m\n\x1b[2m└──────────────────────────────────────────────────────────────────────────────────────────────────┘\x1b[0m\n\n\x1b[2m┌──────────────────────────────────────────────────────────────────────────────────────────────────┐\x1b[0m\n\x1b[2m│\x1b[0m\x1b[38;2;249;38;114;48;2;39;40;34mimport\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;34mthis\x1b[0m\x1b[38;2;248;248;242;48;2;39;40;34m \x1b[0m\x1b[2m│\x1b[0m\n\x1b[2m└──────────────────────────────────────────────────────────────────────────────────────────────────┘\x1b[0m\n\n\x1b[2m┌──────────────────────────────────────────────────────────────────────────────────────────────────┐\x1b[0m\n\x1b[2m│\x1b[0m\x1b[38;2;248;248;242;48;2;39;40;34mfoobar \x1b[0m\x1b[2m│\x1b[0m\n\x1b[2m└──────────────────────────────────────────────────────────────────────────────────────────────────┘\x1b[0m\n" assert rendered_markdown == expected diff --git a/tests/test_markdown_no_hyperlinks.py b/tests/test_markdown_no_hyperlinks.py index 8ce671855..feb350c44 100644 --- a/tests/test_markdown_no_hyperlinks.py +++ b/tests/test_markdown_no_hyperlinks.py @@ -93,7 +93,7 @@ def render(renderable: RenderableType) -> str: def test_markdown_render(): markdown = Markdown(MARKDOWN, hyperlinks=False) rendered_markdown = render(markdown) - expected = "╔══════════════════════════════════════════════════════════════════════════════════════════════════╗\n║ \x1b[1mHeading\x1b[0m ║\n╚══════════════════════════════════════════════════════════════════════════════════════════════════╝\n\n\n \x1b[1;4mSub-heading\x1b[0m \n\n \x1b[1mHeading\x1b[0m \n\n \x1b[1;2mH4 Heading\x1b[0m \n\n \x1b[4mH5 Heading\x1b[0m \n\n \x1b[3mH6 Heading\x1b[0m \n\nParagraphs are separated by a blank line. \n\nTwo spaces at the end of a line \nproduces a line break. \n\nText attributes \x1b[3mitalic\x1b[0m, \x1b[1mbold\x1b[0m, \x1b[38;5;15;40mmonospace\x1b[0m. \n\nHorizontal rule: \n\n\x1b[33m────────────────────────────────────────────────────────────────────────────────────────────────────\x1b[0m\nBullet list: \n\n\x1b[1;33m • \x1b[0mapples \n\x1b[1;33m • \x1b[0moranges \n\x1b[1;33m • \x1b[0mpears \n\nNumbered list: \n\n\x1b[1;33m 1 \x1b[0mlather \n\x1b[1;33m 2 \x1b[0mrinse \n\x1b[1;33m 3 \x1b[0mrepeat \n\nAn \x1b[38;5;12mexample\x1b[0m (\x1b[4;34mhttp://example.com\x1b[0m). \n\n\x1b[35m▌ \x1b[0m\x1b[35mMarkdown uses email-style > characters for blockquoting.\x1b[0m\x1b[35m \x1b[0m\n\x1b[35m▌ \x1b[0m\x1b[35mLorem ipsum\x1b[0m\x1b[35m \x1b[0m\n\n🌆 progress \n\n\x1b[2m┌──────────────────────────────────────────────────────────────────────────────────────────────────┐\x1b[0m\n\x1b[2m│\x1b[0m\x1b[2;38;2;248;248;242;48;2;39;40;34ma=1 \x1b[0m\x1b[2m│\x1b[0m\n\x1b[2m└──────────────────────────────────────────────────────────────────────────────────────────────────┘\x1b[0m\n\n\x1b[2m┌──────────────────────────────────────────────────────────────────────────────────────────────────┐\x1b[0m\n\x1b[2m│\x1b[0m\x1b[2;38;2;249;38;114;48;2;39;40;34mimport\x1b[0m\x1b[2;38;2;248;248;242;48;2;39;40;34m \x1b[0m\x1b[2;38;2;248;248;242;48;2;39;40;34mthis\x1b[0m\x1b[2;38;2;248;248;242;48;2;39;40;34m \x1b[0m\x1b[2m│\x1b[0m\n\x1b[2m└──────────────────────────────────────────────────────────────────────────────────────────────────┘\x1b[0m\n\n\x1b[2m┌──────────────────────────────────────────────────────────────────────────────────────────────────┐\x1b[0m\n\x1b[2m│\x1b[0m\x1b[2;38;2;248;248;242;48;2;39;40;34mfoobar \x1b[0m\x1b[2m│\x1b[0m\n\x1b[2m└──────────────────────────────────────────────────────────────────────────────────────────────────┘\x1b[0m\n" + expected = "╔══════════════════════════════════════════════════════════════════════════════════════════════════╗\n║ \x1b[1mHeading\x1b[0m ║\n╚══════════════════════════════════════════════════════════════════════════════════════════════════╝\n\n\n \x1b[1;4mSub-heading\x1b[0m \n\n \x1b[1mHeading\x1b[0m \n\n \x1b[1;2mH4 Heading\x1b[0m \n\n \x1b[4mH5 Heading\x1b[0m \n\n \x1b[3mH6 Heading\x1b[0m \n\nParagraphs are separated by a blank line. \n\nTwo spaces at the end of a line \nproduces a line break. \n\nText attributes \x1b[3mitalic\x1b[0m, \x1b[1mbold\x1b[0m, \x1b[97;40mmonospace\x1b[0m. \n\nHorizontal rule: \n\n\x1b[33m────────────────────────────────────────────────────────────────────────────────────────────────────\x1b[0m\nBullet list: \n\n\x1b[1;33m • \x1b[0mapples \n\x1b[1;33m • \x1b[0moranges \n\x1b[1;33m • \x1b[0mpears \n\nNumbered list: \n\n\x1b[1;33m 1 \x1b[0mlather \n\x1b[1;33m 2 \x1b[0mrinse \n\x1b[1;33m 3 \x1b[0mrepeat \n\nAn \x1b[94mexample\x1b[0m (\x1b[4;34mhttp://example.com\x1b[0m). \n\n\x1b[35m▌ \x1b[0m\x1b[35mMarkdown uses email-style > characters for blockquoting.\x1b[0m\x1b[35m \x1b[0m\n\x1b[35m▌ \x1b[0m\x1b[35mLorem ipsum\x1b[0m\x1b[35m \x1b[0m\n\n🌆 progress \n\n\x1b[2m┌──────────────────────────────────────────────────────────────────────────────────────────────────┐\x1b[0m\n\x1b[2m│\x1b[0m\x1b[38;2;248;248;242;48;2;39;40;34ma=1 \x1b[0m\x1b[2m│\x1b[0m\n\x1b[2m└──────────────────────────────────────────────────────────────────────────────────────────────────┘\x1b[0m\n\n\x1b[2m┌──────────────────────────────────────────────────────────────────────────────────────────────────┐\x1b[0m\n\x1b[2m│\x1b[0m\x1b[38;2;249;38;114;48;2;39;40;34mimport\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;34mthis\x1b[0m\x1b[38;2;248;248;242;48;2;39;40;34m \x1b[0m\x1b[2m│\x1b[0m\n\x1b[2m└──────────────────────────────────────────────────────────────────────────────────────────────────┘\x1b[0m\n\n\x1b[2m┌──────────────────────────────────────────────────────────────────────────────────────────────────┐\x1b[0m\n\x1b[2m│\x1b[0m\x1b[38;2;248;248;242;48;2;39;40;34mfoobar \x1b[0m\x1b[2m│\x1b[0m\n\x1b[2m└──────────────────────────────────────────────────────────────────────────────────────────────────┘\x1b[0m\n" assert rendered_markdown == expected diff --git a/tests/test_progress.py b/tests/test_progress.py index d7a205f95..962dcf7fd 100644 --- a/tests/test_progress.py +++ b/tests/test_progress.py @@ -11,6 +11,7 @@ from rich.progress import ( BarColumn, FileSizeColumn, + iter_track, TotalFileSizeColumn, DownloadColumn, TransferSpeedColumn, @@ -43,6 +44,36 @@ def tick(self, advance: float = 1) -> None: self.time += advance +def test_iter_track(): + mock_clock = MockClock(auto=False) + result = [] + for values in iter_track( + range(1000), update_period=0.1, total=100, get_time=mock_clock + ): + chunk = [] + for n in values: + mock_clock.tick(0.01) + chunk.append(n) + result.append(chunk) + expected = [ + [0], + [1], + [2, 3], + [4, 5, 6], + [7, 8, 9, 10, 11], + [12, 13, 14, 15, 16, 17, 18], + [19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29], + [30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40], + [41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51], + [52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62], + [63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73], + [74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84], + [85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95], + [96, 97, 98, 99], + ] + assert result == expected + + def test_bar_columns(): bar_column = BarColumn(100) assert bar_column.bar_width == 100 @@ -185,6 +216,31 @@ def test_track() -> None: pass +def test_progress_track() -> None: + console = Console( + file=io.StringIO(), + force_terminal=True, + width=60, + color_system="truecolor", + legacy_windows=False, + ) + progress = Progress( + console=console, auto_refresh=False, get_time=MockClock(auto=True) + ) + test = ["foo", "bar", "baz"] + expected_values = iter(test) + for value in progress.track(test, description="test"): + assert value == next(expected_values) + result = console.file.getvalue() + print(repr(result)) + expected = "\x1b[?25ltest \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;2;249;38;114m╸\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" + assert result == expected + + with pytest.raises(ValueError): + for n in progress.track(5): + pass + + def test_columns() -> None: console = Console( diff --git a/tests/test_rule.py b/tests/test_rule.py index 1988a2a5a..22c53ab37 100644 --- a/tests/test_rule.py +++ b/tests/test_rule.py @@ -15,8 +15,10 @@ def test_rule(): console.rule("foo") console.rule(Text("foo", style="bold")) console.rule("foobarbazeggfoobarbazegg") - expected = "\x1b[38;5;10m────────────────\x1b[0m\n\x1b[38;5;10m───── \x1b[0mfoo\x1b[38;5;10m ──────\x1b[0m\n\x1b[38;5;10m───── \x1b[0m\x1b[1mfoo\x1b[0m\x1b[38;5;10m ──────\x1b[0m\n\x1b[38;5;10m─ \x1b[0mfoobarbazeg…\x1b[38;5;10m ─\x1b[0m\n" - assert console.file.getvalue() == expected + expected = "\x1b[92m────────────────\x1b[0m\n\x1b[92m───── \x1b[0mfoo\x1b[92m ──────\x1b[0m\n\x1b[92m───── \x1b[0m\x1b[1mfoo\x1b[0m\x1b[92m ──────\x1b[0m\n\x1b[92m─ \x1b[0mfoobarbazeg…\x1b[92m ─\x1b[0m\n" + result = console.file.getvalue() + print(repr(result)) + assert result == expected def test_rule_cjk(): From eb2f0d5277c159d8693636ec60c79c5442fd2e43 Mon Sep 17 00:00:00 2001 From: Will McGugan Date: Mon, 27 Jul 2020 17:27:50 +0100 Subject: [PATCH 8/9] windows test fix --- docs/source/reference/emoji.rst | 4 ++-- rich/text.py | 2 ++ tests/test_console.py | 7 +++++-- 3 files changed, 9 insertions(+), 4 deletions(-) diff --git a/docs/source/reference/emoji.rst b/docs/source/reference/emoji.rst index bcaa66138..59ca4da6e 100644 --- a/docs/source/reference/emoji.rst +++ b/docs/source/reference/emoji.rst @@ -1,5 +1,5 @@ -rich.box -======== +rich.emoji +========== .. automodule:: rich.emoji :members: Emoji diff --git a/rich/text.py b/rich/text.py index 31d9e2547..625f78f62 100644 --- a/rich/text.py +++ b/rich/text.py @@ -769,6 +769,8 @@ def split( Args: separator (str, optional): String to split on. Defaults to "\n". + include_separator (bool, optional): Include the separator in the lines. Defaults to False. + allow_blank (bool, optional): Return a blank line if the text ends with a separator. Defaults to False. Returns: List[RichText]: A list of rich text, one per line of the original. diff --git a/tests/test_console.py b/tests/test_console.py index 065da1651..4111f08fa 100644 --- a/tests/test_console.py +++ b/tests/test_console.py @@ -25,13 +25,16 @@ def test_dumb_terminal(): def test_16color_terminal(): - console = Console(force_terminal=True, _environ={"TERM": "xterm-16color"}) - assert console.color_system == "standard" + console = Console( + force_terminal=True, _environ={"TERM": "xterm-16color"}, legacy_windows=False + ) + assert console.color_system in ("windows", "standard") def test_truecolor_terminal(): console = Console( force_terminal=True, + legacy_windows=False, _environ={"COLORTERM": "truecolor", "TERM": "xterm-16color"}, ) assert console.color_system == "truecolor" From dbdade899bab3aaf63f5394a9aa274933ff4c9d6 Mon Sep 17 00:00:00 2001 From: Will McGugan Date: Mon, 27 Jul 2020 17:33:51 +0100 Subject: [PATCH 9/9] skip test on windows --- .github/workflows/pythonpackage.yml | 2 +- tests/test_console.py | 5 ++++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/.github/workflows/pythonpackage.yml b/.github/workflows/pythonpackage.yml index 70eaaef7c..9b6305e6b 100644 --- a/.github/workflows/pythonpackage.yml +++ b/.github/workflows/pythonpackage.yml @@ -7,7 +7,7 @@ jobs: runs-on: ${{ matrix.os }} strategy: matrix: - os: [ubuntu-latest, windows-latest, macos-latest] + os: [windows-latest, ubuntu-latest, macos-latest] python-version: [3.6, 3.7, 3.8] steps: diff --git a/tests/test_console.py b/tests/test_console.py index 4111f08fa..6b7c019ba 100644 --- a/tests/test_console.py +++ b/tests/test_console.py @@ -1,5 +1,6 @@ import io import os +import sys import tempfile import pytest @@ -24,13 +25,15 @@ def test_dumb_terminal(): assert height == 25 +@pytest.mark.skipif(sys.platform == "win32", reason="does not run on windows") def test_16color_terminal(): console = Console( force_terminal=True, _environ={"TERM": "xterm-16color"}, legacy_windows=False ) - assert console.color_system in ("windows", "standard") + assert console.color_system == "standard" +@pytest.mark.skipif(sys.platform == "win32", reason="does not run on windows") def test_truecolor_terminal(): console = Console( force_terminal=True,