CoolFace
Apppublic

Aluode/PerceptionLabPortable

sourceHugging Faceupdated 9mo agoView on Hugging Face
0likes
box.py475 linesDownload Raw Back to rich
1from typing import TYPE_CHECKING, Iterable, List, Literal2 3 4from ._loop import loop_last5 6if TYPE_CHECKING:7    from pip._vendor.rich.console import ConsoleOptions8 9 10class Box:11    """Defines characters to render boxes.12 13    ┌─┬┐ top14    │ ││ head15    ├─┼┤ head_row16    │ ││ mid17    ├─┼┤ row18    ├─┼┤ foot_row19    │ ││ foot20    └─┴┘ bottom21 22    Args:23        box (str): Characters making up box.24        ascii (bool, optional): True if this box uses ascii characters only. Default is False.25    """26 27    def __init__(self, box: str, *, ascii: bool = False) -> None:28        self._box = box29        self.ascii = ascii30        line1, line2, line3, line4, line5, line6, line7, line8 = box.splitlines()31        # top32        self.top_left, self.top, self.top_divider, self.top_right = iter(line1)33        # head34        self.head_left, _, self.head_vertical, self.head_right = iter(line2)35        # head_row36        (37            self.head_row_left,38            self.head_row_horizontal,39            self.head_row_cross,40            self.head_row_right,41        ) = iter(line3)42 43        # mid44        self.mid_left, _, self.mid_vertical, self.mid_right = iter(line4)45        # row46        self.row_left, self.row_horizontal, self.row_cross, self.row_right = iter(line5)47        # foot_row48        (49            self.foot_row_left,50            self.foot_row_horizontal,51            self.foot_row_cross,52            self.foot_row_right,53        ) = iter(line6)54        # foot55        self.foot_left, _, self.foot_vertical, self.foot_right = iter(line7)56        # bottom57        self.bottom_left, self.bottom, self.bottom_divider, self.bottom_right = iter(58            line859        )60 61    def __repr__(self) -> str:62        return "Box(...)"63 64    def __str__(self) -> str:65        return self._box66 67    def substitute(self, options: "ConsoleOptions", safe: bool = True) -> "Box":68        """Substitute this box for another if it won't render due to platform issues.69 70        Args:71            options (ConsoleOptions): Console options used in rendering.72            safe (bool, optional): Substitute this for another Box if there are known problems73                displaying on the platform (currently only relevant on Windows). Default is True.74 75        Returns:76            Box: A different Box or the same Box.77        """78        box = self79        if options.legacy_windows and safe:80            box = LEGACY_WINDOWS_SUBSTITUTIONS.get(box, box)81        if options.ascii_only and not box.ascii:82            box = ASCII83        return box84 85    def get_plain_headed_box(self) -> "Box":86        """If this box uses special characters for the borders of the header, then87        return the equivalent box that does not.88 89        Returns:90            Box: The most similar Box that doesn't use header-specific box characters.91                If the current Box already satisfies this criterion, then it's returned.92        """93        return PLAIN_HEADED_SUBSTITUTIONS.get(self, self)94 95    def get_top(self, widths: Iterable[int]) -> str:96        """Get the top of a simple box.97 98        Args:99            widths (List[int]): Widths of columns.100 101        Returns:102            str: A string of box characters.103        """104 105        parts: List[str] = []106        append = parts.append107        append(self.top_left)108        for last, width in loop_last(widths):109            append(self.top * width)110            if not last:111                append(self.top_divider)112        append(self.top_right)113        return "".join(parts)114 115    def get_row(116        self,117        widths: Iterable[int],118        level: Literal["head", "row", "foot", "mid"] = "row",119        edge: bool = True,120    ) -> str:121        """Get the top of a simple box.122 123        Args:124            width (List[int]): Widths of columns.125 126        Returns:127            str: A string of box characters.128        """129        if level == "head":130            left = self.head_row_left131            horizontal = self.head_row_horizontal132            cross = self.head_row_cross133            right = self.head_row_right134        elif level == "row":135            left = self.row_left136            horizontal = self.row_horizontal137            cross = self.row_cross138            right = self.row_right139        elif level == "mid":140            left = self.mid_left141            horizontal = " "142            cross = self.mid_vertical143            right = self.mid_right144        elif level == "foot":145            left = self.foot_row_left146            horizontal = self.foot_row_horizontal147            cross = self.foot_row_cross148            right = self.foot_row_right149        else:150            raise ValueError("level must be 'head', 'row' or 'foot'")151 152        parts: List[str] = []153        append = parts.append154        if edge:155            append(left)156        for last, width in loop_last(widths):157            append(horizontal * width)158            if not last:159                append(cross)160        if edge:161            append(right)162        return "".join(parts)163 164    def get_bottom(self, widths: Iterable[int]) -> str:165        """Get the bottom of a simple box.166 167        Args:168            widths (List[int]): Widths of columns.169 170        Returns:171            str: A string of box characters.172        """173 174        parts: List[str] = []175        append = parts.append176        append(self.bottom_left)177        for last, width in loop_last(widths):178            append(self.bottom * width)179            if not last:180                append(self.bottom_divider)181        append(self.bottom_right)182        return "".join(parts)183 184 185# fmt: off186ASCII: Box = Box(187    "+--+\n"188    "| ||\n"189    "|-+|\n"190    "| ||\n"191    "|-+|\n"192    "|-+|\n"193    "| ||\n"194    "+--+\n",195    ascii=True,196)197 198ASCII2: Box = Box(199    "+-++\n"200    "| ||\n"201    "+-++\n"202    "| ||\n"203    "+-++\n"204    "+-++\n"205    "| ||\n"206    "+-++\n",207    ascii=True,208)209 210ASCII_DOUBLE_HEAD: Box = Box(211    "+-++\n"212    "| ||\n"213    "+=++\n"214    "| ||\n"215    "+-++\n"216    "+-++\n"217    "| ||\n"218    "+-++\n",219    ascii=True,220)221 222SQUARE: Box = Box(223    "┌─┬┐\n"224    "│ ││\n"225    "├─┼┤\n"226    "│ ││\n"227    "├─┼┤\n"228    "├─┼┤\n"229    "│ ││\n"230    "└─┴┘\n"231)232 233SQUARE_DOUBLE_HEAD: Box = Box(234    "┌─┬┐\n"235    "│ ││\n"236    "╞═╪╡\n"237    "│ ││\n"238    "├─┼┤\n"239    "├─┼┤\n"240    "│ ││\n"241    "└─┴┘\n"242)243 244MINIMAL: Box = Box(245    "  ╷ \n"246    "  │ \n"247    "╶─┼╴\n"248    "  │ \n"249    "╶─┼╴\n"250    "╶─┼╴\n"251    "  │ \n"252    "  ╵ \n"253)254 255 256MINIMAL_HEAVY_HEAD: Box = Box(257    "  ╷ \n"258    "  │ \n"259    "╺━┿╸\n"260    "  │ \n"261    "╶─┼╴\n"262    "╶─┼╴\n"263    "  │ \n"264    "  ╵ \n"265)266 267MINIMAL_DOUBLE_HEAD: Box = Box(268    "  ╷ \n"269    "  │ \n"270    " ═╪ \n"271    "  │ \n"272    " ─┼ \n"273    " ─┼ \n"274    "  │ \n"275    "  ╵ \n"276)277 278 279SIMPLE: Box = Box(280    "    \n"281    "    \n"282    " ── \n"283    "    \n"284    "    \n"285    " ── \n"286    "    \n"287    "    \n"288)289 290SIMPLE_HEAD: Box = Box(291    "    \n"292    "    \n"293    " ── \n"294    "    \n"295    "    \n"296    "    \n"297    "    \n"298    "    \n"299)300 301 302SIMPLE_HEAVY: Box = Box(303    "    \n"304    "    \n"305    " ━━ \n"306    "    \n"307    "    \n"308    " ━━ \n"309    "    \n"310    "    \n"311)312 313 314HORIZONTALS: Box = Box(315    " ── \n"316    "    \n"317    " ── \n"318    "    \n"319    " ── \n"320    " ── \n"321    "    \n"322    " ── \n"323)324 325ROUNDED: Box = Box(326    "╭─┬╮\n"327    "│ ││\n"328    "├─┼┤\n"329    "│ ││\n"330    "├─┼┤\n"331    "├─┼┤\n"332    "│ ││\n"333    "╰─┴╯\n"334)335 336HEAVY: Box = Box(337    "┏━┳┓\n"338    "┃ ┃┃\n"339    "┣━╋┫\n"340    "┃ ┃┃\n"341    "┣━╋┫\n"342    "┣━╋┫\n"343    "┃ ┃┃\n"344    "┗━┻┛\n"345)346 347HEAVY_EDGE: Box = Box(348    "┏━┯┓\n"349    "┃ │┃\n"350    "┠─┼┨\n"351    "┃ │┃\n"352    "┠─┼┨\n"353    "┠─┼┨\n"354    "┃ │┃\n"355    "┗━┷┛\n"356)357 358HEAVY_HEAD: Box = Box(359    "┏━┳┓\n"360    "┃ ┃┃\n"361    "┡━╇┩\n"362    "│ ││\n"363    "├─┼┤\n"364    "├─┼┤\n"365    "│ ││\n"366    "└─┴┘\n"367)368 369DOUBLE: Box = Box(370    "╔═╦╗\n"371    "║ ║║\n"372    "╠═╬╣\n"373    "║ ║║\n"374    "╠═╬╣\n"375    "╠═╬╣\n"376    "║ ║║\n"377    "╚═╩╝\n"378)379 380DOUBLE_EDGE: Box = Box(381    "╔═╤╗\n"382    "║ │║\n"383    "╟─┼╢\n"384    "║ │║\n"385    "╟─┼╢\n"386    "╟─┼╢\n"387    "║ │║\n"388    "╚═╧╝\n"389)390 391MARKDOWN: Box = Box(392    "    \n"393    "| ||\n"394    "|-||\n"395    "| ||\n"396    "|-||\n"397    "|-||\n"398    "| ||\n"399    "    \n",400    ascii=True,401)402# fmt: on403 404# Map Boxes that don't render with raster fonts on to equivalent that do405LEGACY_WINDOWS_SUBSTITUTIONS = {406    ROUNDED: SQUARE,407    MINIMAL_HEAVY_HEAD: MINIMAL,408    SIMPLE_HEAVY: SIMPLE,409    HEAVY: SQUARE,410    HEAVY_EDGE: SQUARE,411    HEAVY_HEAD: SQUARE,412}413 414# Map headed boxes to their headerless equivalents415PLAIN_HEADED_SUBSTITUTIONS = {416    HEAVY_HEAD: SQUARE,417    SQUARE_DOUBLE_HEAD: SQUARE,418    MINIMAL_DOUBLE_HEAD: MINIMAL,419    MINIMAL_HEAVY_HEAD: MINIMAL,420    ASCII_DOUBLE_HEAD: ASCII2,421}422 423 424if __name__ == "__main__":  # pragma: no cover425    from pip._vendor.rich.columns import Columns426    from pip._vendor.rich.panel import Panel427 428    from . import box as box429    from .console import Console430    from .table import Table431    from .text import Text432 433    console = Console(record=True)434 435    BOXES = [436        "ASCII",437        "ASCII2",438        "ASCII_DOUBLE_HEAD",439        "SQUARE",440        "SQUARE_DOUBLE_HEAD",441        "MINIMAL",442        "MINIMAL_HEAVY_HEAD",443        "MINIMAL_DOUBLE_HEAD",444        "SIMPLE",445        "SIMPLE_HEAD",446        "SIMPLE_HEAVY",447        "HORIZONTALS",448        "ROUNDED",449        "HEAVY",450        "HEAVY_EDGE",451        "HEAVY_HEAD",452        "DOUBLE",453        "DOUBLE_EDGE",454        "MARKDOWN",455    ]456 457    console.print(Panel("[bold green]Box Constants", style="green"), justify="center")458    console.print()459 460    columns = Columns(expand=True, padding=2)461    for box_name in sorted(BOXES):462        table = Table(463            show_footer=True, style="dim", border_style="not dim", expand=True464        )465        table.add_column("Header 1", "Footer 1")466        table.add_column("Header 2", "Footer 2")467        table.add_row("Cell", "Cell")468        table.add_row("Cell", "Cell")469        table.box = getattr(box, box_name)470        table.title = Text(f"box.{box_name}", style="magenta")471        columns.add_renderable(table)472    console.print(columns)473 474    # console.save_svg("box.svg")475 
Aluode/PerceptionLabPortable · CoolFace