Team Ai
Datasetpublic

codekingpro/portable-devtools

sourceHugging Faceupdated 5mo agoView on Hugging Face
1likes14kdownloads
box.py481 linesDownload Raw Back to rich
1import sys2from typing import TYPE_CHECKING, Iterable, List3 4if sys.version_info >= (3, 8):5    from typing import Literal6else:7    from typing_extensions import Literal  # pragma: no cover8 9 10from ._loop import loop_last11 12if TYPE_CHECKING:13    from rich.console import ConsoleOptions14 15 16class Box:17    """Defines characters to render boxes.18 19    ┌─┬┐ top20    │ ││ head21    ├─┼┤ head_row22    │ ││ mid23    ├─┼┤ row24    ├─┼┤ foot_row25    │ ││ foot26    └─┴┘ bottom27 28    Args:29        box (str): Characters making up box.30        ascii (bool, optional): True if this box uses ascii characters only. Default is False.31    """32 33    def __init__(self, box: str, *, ascii: bool = False) -> None:34        self._box = box35        self.ascii = ascii36        line1, line2, line3, line4, line5, line6, line7, line8 = box.splitlines()37        # top38        self.top_left, self.top, self.top_divider, self.top_right = iter(line1)39        # head40        self.head_left, _, self.head_vertical, self.head_right = iter(line2)41        # head_row42        (43            self.head_row_left,44            self.head_row_horizontal,45            self.head_row_cross,46            self.head_row_right,47        ) = iter(line3)48 49        # mid50        self.mid_left, _, self.mid_vertical, self.mid_right = iter(line4)51        # row52        self.row_left, self.row_horizontal, self.row_cross, self.row_right = iter(line5)53        # foot_row54        (55            self.foot_row_left,56            self.foot_row_horizontal,57            self.foot_row_cross,58            self.foot_row_right,59        ) = iter(line6)60        # foot61        self.foot_left, _, self.foot_vertical, self.foot_right = iter(line7)62        # bottom63        self.bottom_left, self.bottom, self.bottom_divider, self.bottom_right = iter(64            line865        )66 67    def __repr__(self) -> str:68        return "Box(...)"69 70    def __str__(self) -> str:71        return self._box72 73    def substitute(self, options: "ConsoleOptions", safe: bool = True) -> "Box":74        """Substitute this box for another if it won't render due to platform issues.75 76        Args:77            options (ConsoleOptions): Console options used in rendering.78            safe (bool, optional): Substitute this for another Box if there are known problems79                displaying on the platform (currently only relevant on Windows). Default is True.80 81        Returns:82            Box: A different Box or the same Box.83        """84        box = self85        if options.legacy_windows and safe:86            box = LEGACY_WINDOWS_SUBSTITUTIONS.get(box, box)87        if options.ascii_only and not box.ascii:88            box = ASCII89        return box90 91    def get_plain_headed_box(self) -> "Box":92        """If this box uses special characters for the borders of the header, then93        return the equivalent box that does not.94 95        Returns:96            Box: The most similar Box that doesn't use header-specific box characters.97                If the current Box already satisfies this criterion, then it's returned.98        """99        return PLAIN_HEADED_SUBSTITUTIONS.get(self, self)100 101    def get_top(self, widths: Iterable[int]) -> str:102        """Get the top of a simple box.103 104        Args:105            widths (List[int]): Widths of columns.106 107        Returns:108            str: A string of box characters.109        """110 111        parts: List[str] = []112        append = parts.append113        append(self.top_left)114        for last, width in loop_last(widths):115            append(self.top * width)116            if not last:117                append(self.top_divider)118        append(self.top_right)119        return "".join(parts)120 121    def get_row(122        self,123        widths: Iterable[int],124        level: Literal["head", "row", "foot", "mid"] = "row",125        edge: bool = True,126    ) -> str:127        """Get the top of a simple box.128 129        Args:130            width (List[int]): Widths of columns.131 132        Returns:133            str: A string of box characters.134        """135        if level == "head":136            left = self.head_row_left137            horizontal = self.head_row_horizontal138            cross = self.head_row_cross139            right = self.head_row_right140        elif level == "row":141            left = self.row_left142            horizontal = self.row_horizontal143            cross = self.row_cross144            right = self.row_right145        elif level == "mid":146            left = self.mid_left147            horizontal = " "148            cross = self.mid_vertical149            right = self.mid_right150        elif level == "foot":151            left = self.foot_row_left152            horizontal = self.foot_row_horizontal153            cross = self.foot_row_cross154            right = self.foot_row_right155        else:156            raise ValueError("level must be 'head', 'row' or 'foot'")157 158        parts: List[str] = []159        append = parts.append160        if edge:161            append(left)162        for last, width in loop_last(widths):163            append(horizontal * width)164            if not last:165                append(cross)166        if edge:167            append(right)168        return "".join(parts)169 170    def get_bottom(self, widths: Iterable[int]) -> str:171        """Get the bottom of a simple box.172 173        Args:174            widths (List[int]): Widths of columns.175 176        Returns:177            str: A string of box characters.178        """179 180        parts: List[str] = []181        append = parts.append182        append(self.bottom_left)183        for last, width in loop_last(widths):184            append(self.bottom * width)185            if not last:186                append(self.bottom_divider)187        append(self.bottom_right)188        return "".join(parts)189 190 191# fmt: off192ASCII: Box = Box(193    "+--+\n"194    "| ||\n"195    "|-+|\n"196    "| ||\n"197    "|-+|\n"198    "|-+|\n"199    "| ||\n"200    "+--+\n",201    ascii=True,202)203 204ASCII2: Box = Box(205    "+-++\n"206    "| ||\n"207    "+-++\n"208    "| ||\n"209    "+-++\n"210    "+-++\n"211    "| ||\n"212    "+-++\n",213    ascii=True,214)215 216ASCII_DOUBLE_HEAD: Box = Box(217    "+-++\n"218    "| ||\n"219    "+=++\n"220    "| ||\n"221    "+-++\n"222    "+-++\n"223    "| ||\n"224    "+-++\n",225    ascii=True,226)227 228SQUARE: Box = Box(229    "┌─┬┐\n"230    "│ ││\n"231    "├─┼┤\n"232    "│ ││\n"233    "├─┼┤\n"234    "├─┼┤\n"235    "│ ││\n"236    "└─┴┘\n"237)238 239SQUARE_DOUBLE_HEAD: Box = Box(240    "┌─┬┐\n"241    "│ ││\n"242    "╞═╪╡\n"243    "│ ││\n"244    "├─┼┤\n"245    "├─┼┤\n"246    "│ ││\n"247    "└─┴┘\n"248)249 250MINIMAL: Box = Box(251    "  ╷ \n"252    "  │ \n"253    "╶─┼╴\n"254    "  │ \n"255    "╶─┼╴\n"256    "╶─┼╴\n"257    "  │ \n"258    "  ╵ \n"259)260 261 262MINIMAL_HEAVY_HEAD: Box = Box(263    "  ╷ \n"264    "  │ \n"265    "╺━┿╸\n"266    "  │ \n"267    "╶─┼╴\n"268    "╶─┼╴\n"269    "  │ \n"270    "  ╵ \n"271)272 273MINIMAL_DOUBLE_HEAD: Box = Box(274    "  ╷ \n"275    "  │ \n"276    " ═╪ \n"277    "  │ \n"278    " ─┼ \n"279    " ─┼ \n"280    "  │ \n"281    "  ╵ \n"282)283 284 285SIMPLE: Box = Box(286    "    \n"287    "    \n"288    " ── \n"289    "    \n"290    "    \n"291    " ── \n"292    "    \n"293    "    \n"294)295 296SIMPLE_HEAD: Box = Box(297    "    \n"298    "    \n"299    " ── \n"300    "    \n"301    "    \n"302    "    \n"303    "    \n"304    "    \n"305)306 307 308SIMPLE_HEAVY: Box = Box(309    "    \n"310    "    \n"311    " ━━ \n"312    "    \n"313    "    \n"314    " ━━ \n"315    "    \n"316    "    \n"317)318 319 320HORIZONTALS: Box = Box(321    " ── \n"322    "    \n"323    " ── \n"324    "    \n"325    " ── \n"326    " ── \n"327    "    \n"328    " ── \n"329)330 331ROUNDED: Box = Box(332    "╭─┬╮\n"333    "│ ││\n"334    "├─┼┤\n"335    "│ ││\n"336    "├─┼┤\n"337    "├─┼┤\n"338    "│ ││\n"339    "╰─┴╯\n"340)341 342HEAVY: Box = Box(343    "┏━┳┓\n"344    "┃ ┃┃\n"345    "┣━╋┫\n"346    "┃ ┃┃\n"347    "┣━╋┫\n"348    "┣━╋┫\n"349    "┃ ┃┃\n"350    "┗━┻┛\n"351)352 353HEAVY_EDGE: Box = Box(354    "┏━┯┓\n"355    "┃ │┃\n"356    "┠─┼┨\n"357    "┃ │┃\n"358    "┠─┼┨\n"359    "┠─┼┨\n"360    "┃ │┃\n"361    "┗━┷┛\n"362)363 364HEAVY_HEAD: Box = Box(365    "┏━┳┓\n"366    "┃ ┃┃\n"367    "┡━╇┩\n"368    "│ ││\n"369    "├─┼┤\n"370    "├─┼┤\n"371    "│ ││\n"372    "└─┴┘\n"373)374 375DOUBLE: Box = Box(376    "╔═╦╗\n"377    "║ ║║\n"378    "╠═╬╣\n"379    "║ ║║\n"380    "╠═╬╣\n"381    "╠═╬╣\n"382    "║ ║║\n"383    "╚═╩╝\n"384)385 386DOUBLE_EDGE: Box = Box(387    "╔═╤╗\n"388    "║ │║\n"389    "╟─┼╢\n"390    "║ │║\n"391    "╟─┼╢\n"392    "╟─┼╢\n"393    "║ │║\n"394    "╚═╧╝\n"395)396 397MARKDOWN: Box = Box(398    "    \n"399    "| ||\n"400    "|-||\n"401    "| ||\n"402    "|-||\n"403    "|-||\n"404    "| ||\n"405    "    \n",406    ascii=True,407)408# fmt: on409 410# Map Boxes that don't render with raster fonts on to equivalent that do411LEGACY_WINDOWS_SUBSTITUTIONS = {412    ROUNDED: SQUARE,413    MINIMAL_HEAVY_HEAD: MINIMAL,414    SIMPLE_HEAVY: SIMPLE,415    HEAVY: SQUARE,416    HEAVY_EDGE: SQUARE,417    HEAVY_HEAD: SQUARE,418}419 420# Map headed boxes to their headerless equivalents421PLAIN_HEADED_SUBSTITUTIONS = {422    HEAVY_HEAD: SQUARE,423    SQUARE_DOUBLE_HEAD: SQUARE,424    MINIMAL_DOUBLE_HEAD: MINIMAL,425    MINIMAL_HEAVY_HEAD: MINIMAL,426    ASCII_DOUBLE_HEAD: ASCII2,427}428 429 430if __name__ == "__main__":  # pragma: no cover431    from rich.columns import Columns432    from rich.panel import Panel433 434    from . import box as box435    from .console import Console436    from .table import Table437    from .text import Text438 439    console = Console(record=True)440 441    BOXES = [442        "ASCII",443        "ASCII2",444        "ASCII_DOUBLE_HEAD",445        "SQUARE",446        "SQUARE_DOUBLE_HEAD",447        "MINIMAL",448        "MINIMAL_HEAVY_HEAD",449        "MINIMAL_DOUBLE_HEAD",450        "SIMPLE",451        "SIMPLE_HEAD",452        "SIMPLE_HEAVY",453        "HORIZONTALS",454        "ROUNDED",455        "HEAVY",456        "HEAVY_EDGE",457        "HEAVY_HEAD",458        "DOUBLE",459        "DOUBLE_EDGE",460        "MARKDOWN",461    ]462 463    console.print(Panel("[bold green]Box Constants", style="green"), justify="center")464    console.print()465 466    columns = Columns(expand=True, padding=2)467    for box_name in sorted(BOXES):468        table = Table(469            show_footer=True, style="dim", border_style="not dim", expand=True470        )471        table.add_column("Header 1", "Footer 1")472        table.add_column("Header 2", "Footer 2")473        table.add_row("Cell", "Cell")474        table.add_row("Cell", "Cell")475        table.box = getattr(box, box_name)476        table.title = Text(f"box.{box_name}", style="magenta")477        columns.add_renderable(table)478    console.print(columns)479 480    # console.save_svg("box.svg")481 
codekingpro/portable-devtools · Team Ai