Team Ai
Datasetpublic

codekingpro/portable-devtools

sourceHugging Faceupdated 5mo agoView on Hugging Face
1likes15kdownloads
align.py321 linesDownload Raw Back to rich
1from itertools import chain2from typing import TYPE_CHECKING, Iterable, Optional, Literal3 4from .constrain import Constrain5from .jupyter import JupyterMixin6from .measure import Measurement7from .segment import Segment8from .style import StyleType9 10if TYPE_CHECKING:11    from .console import Console, ConsoleOptions, RenderableType, RenderResult12 13AlignMethod = Literal["left", "center", "right"]14VerticalAlignMethod = Literal["top", "middle", "bottom"]15 16 17class Align(JupyterMixin):18    """Align a renderable by adding spaces if necessary.19 20    Args:21        renderable (RenderableType): A console renderable.22        align (AlignMethod): One of "left", "center", or "right""23        style (StyleType, optional): An optional style to apply to the background.24        vertical (Optional[VerticalAlignMethod], optional): Optional vertical align, one of "top", "middle", or "bottom". Defaults to None.25        pad (bool, optional): Pad the right with spaces. Defaults to True.26        width (int, optional): Restrict contents to given width, or None to use default width. Defaults to None.27        height (int, optional): Set height of align renderable, or None to fit to contents. Defaults to None.28 29    Raises:30        ValueError: if ``align`` is not one of the expected values.31 32    Example:33        .. code-block:: python34 35            from rich.console import Console36            from rich.align import Align37            from rich.panel import Panel38 39            console = Console()40            # Create a panel 20 characters wide41            p = Panel("Hello, [b]World[/b]!", style="on green", width=20)42 43            # Renders the panel centered in the terminal44            console.print(Align(p, align="center"))45    """46 47    def __init__(48        self,49        renderable: "RenderableType",50        align: AlignMethod = "left",51        style: Optional[StyleType] = None,52        *,53        vertical: Optional[VerticalAlignMethod] = None,54        pad: bool = True,55        width: Optional[int] = None,56        height: Optional[int] = None,57    ) -> None:58        if align not in ("left", "center", "right"):59            raise ValueError(60                f'invalid value for align, expected "left", "center", or "right" (not {align!r})'61            )62        if vertical is not None and vertical not in ("top", "middle", "bottom"):63            raise ValueError(64                f'invalid value for vertical, expected "top", "middle", or "bottom" (not {vertical!r})'65            )66        self.renderable = renderable67        self.align = align68        self.style = style69        self.vertical = vertical70        self.pad = pad71        self.width = width72        self.height = height73 74    def __repr__(self) -> str:75        return f"Align({self.renderable!r}, {self.align!r})"76 77    @classmethod78    def left(79        cls,80        renderable: "RenderableType",81        style: Optional[StyleType] = None,82        *,83        vertical: Optional[VerticalAlignMethod] = None,84        pad: bool = True,85        width: Optional[int] = None,86        height: Optional[int] = None,87    ) -> "Align":88        """Align a renderable to the left."""89        return cls(90            renderable,91            "left",92            style=style,93            vertical=vertical,94            pad=pad,95            width=width,96            height=height,97        )98 99    @classmethod100    def center(101        cls,102        renderable: "RenderableType",103        style: Optional[StyleType] = None,104        *,105        vertical: Optional[VerticalAlignMethod] = None,106        pad: bool = True,107        width: Optional[int] = None,108        height: Optional[int] = None,109    ) -> "Align":110        """Align a renderable to the center."""111        return cls(112            renderable,113            "center",114            style=style,115            vertical=vertical,116            pad=pad,117            width=width,118            height=height,119        )120 121    @classmethod122    def right(123        cls,124        renderable: "RenderableType",125        style: Optional[StyleType] = None,126        *,127        vertical: Optional[VerticalAlignMethod] = None,128        pad: bool = True,129        width: Optional[int] = None,130        height: Optional[int] = None,131    ) -> "Align":132        """Align a renderable to the right."""133        return cls(134            renderable,135            "right",136            style=style,137            vertical=vertical,138            pad=pad,139            width=width,140            height=height,141        )142 143    def __rich_console__(144        self, console: "Console", options: "ConsoleOptions"145    ) -> "RenderResult":146        align = self.align147        width = console.measure(self.renderable, options=options).maximum148        rendered = console.render(149            Constrain(150                self.renderable, width if self.width is None else min(width, self.width)151            ),152            options.update(height=None),153        )154        lines = list(Segment.split_lines(rendered))155        width, height = Segment.get_shape(lines)156        lines = Segment.set_shape(lines, width, height)157        new_line = Segment.line()158        excess_space = options.max_width - width159        style = console.get_style(self.style) if self.style is not None else None160 161        def generate_segments() -> Iterable[Segment]:162            if excess_space <= 0:163                # Exact fit164                for line in lines:165                    yield from line166                    yield new_line167 168            elif align == "left":169                # Pad on the right170                pad = Segment(" " * excess_space, style) if self.pad else None171                for line in lines:172                    yield from line173                    if pad:174                        yield pad175                    yield new_line176 177            elif align == "center":178                # Pad left and right179                left = excess_space // 2180                pad = Segment(" " * left, style)181                pad_right = (182                    Segment(" " * (excess_space - left), style) if self.pad else None183                )184                for line in lines:185                    if left:186                        yield pad187                    yield from line188                    if pad_right:189                        yield pad_right190                    yield new_line191 192            elif align == "right":193                # Padding on left194                pad = Segment(" " * excess_space, style)195                for line in lines:196                    yield pad197                    yield from line198                    yield new_line199 200        blank_line = (201            Segment(f"{' ' * (self.width or options.max_width)}\n", style)202            if self.pad203            else Segment("\n")204        )205 206        def blank_lines(count: int) -> Iterable[Segment]:207            if count > 0:208                for _ in range(count):209                    yield blank_line210 211        vertical_height = self.height or options.height212        iter_segments: Iterable[Segment]213        if self.vertical and vertical_height is not None:214            if self.vertical == "top":215                bottom_space = vertical_height - height216                iter_segments = chain(generate_segments(), blank_lines(bottom_space))217            elif self.vertical == "middle":218                top_space = (vertical_height - height) // 2219                bottom_space = vertical_height - top_space - height220                iter_segments = chain(221                    blank_lines(top_space),222                    generate_segments(),223                    blank_lines(bottom_space),224                )225            else:  #  self.vertical == "bottom":226                top_space = vertical_height - height227                iter_segments = chain(blank_lines(top_space), generate_segments())228        else:229            iter_segments = generate_segments()230        if self.style:231            style = console.get_style(self.style)232            iter_segments = Segment.apply_style(iter_segments, style)233        yield from iter_segments234 235    def __rich_measure__(236        self, console: "Console", options: "ConsoleOptions"237    ) -> Measurement:238        measurement = Measurement.get(console, options, self.renderable)239        return measurement240 241 242class VerticalCenter(JupyterMixin):243    """Vertically aligns a renderable.244 245    Warn:246        This class is deprecated and may be removed in a future version. Use Align class with247        `vertical="middle"`.248 249    Args:250        renderable (RenderableType): A renderable object.251        style (StyleType, optional): An optional style to apply to the background. Defaults to None.252    """253 254    def __init__(255        self,256        renderable: "RenderableType",257        style: Optional[StyleType] = None,258    ) -> None:259        self.renderable = renderable260        self.style = style261 262    def __repr__(self) -> str:263        return f"VerticalCenter({self.renderable!r})"264 265    def __rich_console__(266        self, console: "Console", options: "ConsoleOptions"267    ) -> "RenderResult":268        style = console.get_style(self.style) if self.style is not None else None269        lines = console.render_lines(270            self.renderable, options.update(height=None), pad=False271        )272        width, _height = Segment.get_shape(lines)273        new_line = Segment.line()274        height = options.height or options.size.height275        top_space = (height - len(lines)) // 2276        bottom_space = height - top_space - len(lines)277        blank_line = Segment(f"{' ' * width}", style)278 279        def blank_lines(count: int) -> Iterable[Segment]:280            for _ in range(count):281                yield blank_line282                yield new_line283 284        if top_space > 0:285            yield from blank_lines(top_space)286        for line in lines:287            yield from line288            yield new_line289        if bottom_space > 0:290            yield from blank_lines(bottom_space)291 292    def __rich_measure__(293        self, console: "Console", options: "ConsoleOptions"294    ) -> Measurement:295        measurement = Measurement.get(console, options, self.renderable)296        return measurement297 298 299if __name__ == "__main__":  # pragma: no cover300    from rich.console import Console, Group301    from rich.highlighter import ReprHighlighter302    from rich.panel import Panel303 304    highlighter = ReprHighlighter()305    console = Console()306 307    panel = Panel(308        Group(309            Align.left(highlighter("align='left'")),310            Align.center(highlighter("align='center'")),311            Align.right(highlighter("align='right'")),312        ),313        width=60,314        style="on dark_blue",315        title="Align",316    )317 318    console.print(319        Align.center(panel, vertical="middle", style="on red", height=console.height)320    )321 
codekingpro/portable-devtools · Team Ai