From 2f331818d009752fe3769b5edbc6ae90bec4575e Mon Sep 17 00:00:00 2001 From: Will McGugan Date: Thu, 8 Oct 2020 18:18:25 +0100 Subject: [PATCH] replaced get_safe_box --- CHANGELOG.md | 10 ++++++- rich/bar.py | 2 +- rich/box.py | 67 ++++++++++++++++++++++--------------------- rich/console.py | 3 ++ rich/panel.py | 8 ++---- rich/table.py | 11 ++++--- tests/test_box.py | 23 +++++++++++---- tests/test_console.py | 6 +++- 8 files changed, 79 insertions(+), 51 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5536a2e4..7a8c966f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,12 +5,20 @@ 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). -## [8.1.0] - unreleased +## [9.0.0] - unreleased ### Fixed - Progress download column now displays decimal units +### Added + +- Added legacy_windows to ConsoleOptions + +### Changed + +- Dropped box.get_safe_box function in favor of Box.substitute + ## [8.0.0] - 2020-10-03 ### Added diff --git a/rich/bar.py b/rich/bar.py index 624af053..b6aaa6e5 100644 --- a/rich/bar.py +++ b/rich/bar.py @@ -157,7 +157,7 @@ class Bar(JupyterMixin): ) -> RenderResult: width = min(self.width or options.max_width, options.max_width) - ascii = console.legacy_windows or options.ascii_only + ascii = options.legacy_windows or options.ascii_only if self.pulse: yield from self._render_pulse(console, width, ascii=ascii) return diff --git a/rich/box.py b/rich/box.py index 18be3e73..c4aad755 100644 --- a/rich/box.py +++ b/rich/box.py @@ -1,9 +1,13 @@ -from typing import Iterable, List, Optional, overload +from typing import Iterable, List, Optional, overload, TYPE_CHECKING from typing_extensions import Literal from ._loop import loop_last +if TYPE_CHECKING: + from rich.console import ConsoleOptions + + class Box: """Defines characters to render boxes. @@ -59,6 +63,23 @@ class Box: def __str__(self) -> str: return self._box + def substitute(self, options: "ConsoleOptions", safe: bool = True) -> "Box": + """Substitute this box for another if it won't render due to platform issues. + + Args: + safe (bool, optional): Substitute this for another Box if there are known problems + in displaying (currently only relevant on Windows). + + Returns: + [Box]: A different Box or the same Box. + """ + box = self + if options.legacy_windows and safe: + box = LEGACY_WINDOWS_SUBSTITUTIONS.get(box, box) + if options.ascii_only: + box = ASCII + return box + def get_top(self, widths: Iterable[int]) -> str: """Get the top of a simple box. @@ -204,6 +225,18 @@ SQUARE: Box = Box( """ ) +SQUARE_DOUBLE_HEAD: Box = Box( + """\ +┌─┬┐ +│ ││ +╞═╪╡ +│ ││ +├─┼┤ +├─┼┤ +│ ││ +└─┴┘ +""" +) MINIMAL: Box = Box( """\ @@ -389,37 +422,6 @@ LEGACY_WINDOWS_SUBSTITUTIONS = { } -@overload -def get_safe_box(box: None, legacy_windows: bool, ascii: bool = False) -> None: - ... - - -@overload -def get_safe_box(box: Box, legacy_windows: bool, ascii: bool = False) -> Box: - ... - - -def get_safe_box( - box: Optional[Box], legacy_windows: bool, ascii: bool = False -) -> Optional[Box]: - """Substitute Box constants that are unlikely to render in the terminal. - - Args: - box (Optional[Box]): A Box instance. - legacy_windows (bool): Enable legacy Windows. - ascii (bool, optional): Allow only ascii characters. - - Returns: - Optional[Box]: A Box instance (potentially a new instance). - """ - if box is not None: - if ascii and not box.ascii: - box = ASCII - elif legacy_windows: - box = LEGACY_WINDOWS_SUBSTITUTIONS.get(box, box) - return box - - if __name__ == "__main__": # pragma: no cover from rich.columns import Columns @@ -436,6 +438,7 @@ if __name__ == "__main__": # pragma: no cover "ASCII2", "ASCII_DOUBLE_HEAD", "SQUARE", + "SQUARE_DOUBLE_HEAD", "MINIMAL", "MINIMAL_HEAVY_HEAD", "MINIMAL_DOUBLE_HEAD", diff --git a/rich/console.py b/rich/console.py index 4735076f..eea1b301 100644 --- a/rich/console.py +++ b/rich/console.py @@ -83,6 +83,8 @@ _TERM_COLORS = {"256color": ColorSystem.EIGHT_BIT, "16color": ColorSystem.STANDA class ConsoleOptions: """Options for __rich_console__ method.""" + legacy_windows: bool + """legacy_windows: flat for legacy windows.""" min_width: int """Minimum width of renderable.""" max_width: int @@ -606,6 +608,7 @@ class Console: def options(self) -> ConsoleOptions: """Get default console options.""" return ConsoleOptions( + legacy_windows=self.legacy_windows, min_width=1, max_width=self.width, encoding=self.encoding, diff --git a/rich/panel.py b/rich/panel.py index 75be46f8..d49eb1ba 100644 --- a/rich/panel.py +++ b/rich/panel.py @@ -1,6 +1,6 @@ from typing import Optional, TYPE_CHECKING -from .box import get_safe_box, Box, SQUARE, ROUNDED +from .box import Box, ROUNDED from .align import AlignValues from .jupyter import JupyterMixin @@ -113,11 +113,7 @@ class Panel(JupyterMixin): width = options.max_width if self.width is None else self.width safe_box: bool = console.safe_box if self.safe_box is None else self.safe_box # type: ignore - box = ( - get_safe_box(self.box, console.legacy_windows, ascii=options.ascii_only) - if safe_box - else self.box - ) + box = self.box.substitute(options, safe=safe_box) title_text = self._title if title_text is not None: diff --git a/rich/table.py b/rich/table.py index 047afc21..b0ac2e0d 100644 --- a/rich/table.py +++ b/rich/table.py @@ -3,6 +3,7 @@ from typing import TYPE_CHECKING, Iterable, List, NamedTuple, Optional, Tuple, U from . import box, errors from ._loop import loop_first_last, loop_last +from ._pick import pick_bool from ._ratio import ratio_distribute, ratio_reduce from .jupyter import JupyterMixin from .measure import Measurement @@ -494,6 +495,7 @@ class Table(JupyterMixin): for width, allow_wrap in zip(widths, wrapable) ) except ValueError: + 1 / 0 second_max_column = 0 column_difference = max_column - second_max_column ratios = [ @@ -609,11 +611,12 @@ class Table(JupyterMixin): ) ) ) - safe_box: bool = console.safe_box if self.safe_box is None else self.safe_box # type: ignore _box = ( - box.get_safe_box(self.box, console.legacy_windows, ascii=options.ascii_only) - if safe_box - else self.box + self.box.substitute( + options, safe=pick_bool(self.safe_box, console.safe_box) + ) + if self.box + else None ) # _box = self.box diff --git a/tests/test_box.py b/tests/test_box.py index 3685e18e..d3a644ee 100644 --- a/tests/test_box.py +++ b/tests/test_box.py @@ -1,6 +1,7 @@ import pytest -from rich.box import get_safe_box, ASCII, DOUBLE, ROUNDED, HEAVY, SQUARE +from rich.console import ConsoleOptions +from rich.box import ASCII, DOUBLE, ROUNDED, HEAVY, SQUARE def test_str(): @@ -35,8 +36,18 @@ def test_get_bottom(): assert bottom == "┗━┻━━┻━━━┛" -def test_get_safe_box(): - assert get_safe_box(HEAVY, True) == SQUARE - assert get_safe_box(HEAVY, False) == HEAVY - assert get_safe_box(None, True) is None - assert get_safe_box(None, False) is None +def test_box_substitute(): + options = ConsoleOptions( + legacy_windows=True, + min_width=1, + max_width=100, + is_terminal=True, + encoding="utf-8", + ) + assert HEAVY.substitute(options) == SQUARE + + options.legacy_windows = False + assert HEAVY.substitute(options) == HEAVY + + options.encoding = "ascii" + assert HEAVY.substitute(options) == ASCII \ No newline at end of file diff --git a/tests/test_console.py b/tests/test_console.py index a6d2d333..3df8e7ce 100644 --- a/tests/test_console.py +++ b/tests/test_console.py @@ -43,7 +43,11 @@ def test_truecolor_terminal(): def test_console_options_update(): options = ConsoleOptions( - min_width=10, max_width=20, is_terminal=False, encoding="utf-8" + legacy_windows=False, + min_width=10, + max_width=20, + is_terminal=False, + encoding="utf-8", ) options1 = options.update(width=15) assert options1.min_width == 15 and options1.max_width == 15