Team Ai
Datasetpublic

codekingpro/portable-devtools

sourceHugging Faceupdated 5mo agoView on Hugging Face
1likes14kdownloads
table.py1001 linesDownload Raw Back to rich
1from dataclasses import dataclass, field, replace2from typing import (3    TYPE_CHECKING,4    Dict,5    Iterable,6    List,7    NamedTuple,8    Optional,9    Sequence,10    Tuple,11    Union,12)13 14from . import box, errors15from ._loop import loop_first_last, loop_last16from ._pick import pick_bool17from ._ratio import ratio_distribute, ratio_reduce18from .align import VerticalAlignMethod19from .jupyter import JupyterMixin20from .measure import Measurement21from .padding import Padding, PaddingDimensions22from .protocol import is_renderable23from .segment import Segment24from .style import Style, StyleType25from .text import Text, TextType26 27if TYPE_CHECKING:28    from .console import (29        Console,30        ConsoleOptions,31        JustifyMethod,32        OverflowMethod,33        RenderableType,34        RenderResult,35    )36 37 38@dataclass39class Column:40    """Defines a column within a ~Table.41 42    Args:43        title (Union[str, Text], optional): The title of the table rendered at the top. Defaults to None.44        caption (Union[str, Text], optional): The table caption rendered below. Defaults to None.45        width (int, optional): The width in characters of the table, or ``None`` to automatically fit. Defaults to None.46        min_width (Optional[int], optional): The minimum width of the table, or ``None`` for no minimum. Defaults to None.47        box (box.Box, optional): One of the constants in box.py used to draw the edges (see :ref:`appendix_box`), or ``None`` for no box lines. Defaults to box.HEAVY_HEAD.48        safe_box (Optional[bool], optional): Disable box characters that don't display on windows legacy terminal with *raster* fonts. Defaults to True.49        padding (PaddingDimensions, optional): Padding for cells (top, right, bottom, left). Defaults to (0, 1).50        collapse_padding (bool, optional): Enable collapsing of padding around cells. Defaults to False.51        pad_edge (bool, optional): Enable padding of edge cells. Defaults to True.52        expand (bool, optional): Expand the table to fit the available space if ``True``, otherwise the table width will be auto-calculated. Defaults to False.53        show_header (bool, optional): Show a header row. Defaults to True.54        show_footer (bool, optional): Show a footer row. Defaults to False.55        show_edge (bool, optional): Draw a box around the outside of the table. Defaults to True.56        show_lines (bool, optional): Draw lines between every row. Defaults to False.57        leading (bool, optional): Number of blank lines between rows (precludes ``show_lines``). Defaults to 0.58        style (Union[str, Style], optional): Default style for the table. Defaults to "none".59        row_styles (List[Union, str], optional): Optional list of row styles, if more than one style is given then the styles will alternate. Defaults to None.60        header_style (Union[str, Style], optional): Style of the header. Defaults to "table.header".61        footer_style (Union[str, Style], optional): Style of the footer. Defaults to "table.footer".62        border_style (Union[str, Style], optional): Style of the border. Defaults to None.63        title_style (Union[str, Style], optional): Style of the title. Defaults to None.64        caption_style (Union[str, Style], optional): Style of the caption. Defaults to None.65        title_justify (str, optional): Justify method for title. Defaults to "center".66        caption_justify (str, optional): Justify method for caption. Defaults to "center".67        highlight (bool, optional): Highlight cell contents (if str). Defaults to False.68    """69 70    header: "RenderableType" = ""71    """RenderableType: Renderable for the header (typically a string)"""72 73    footer: "RenderableType" = ""74    """RenderableType: Renderable for the footer (typically a string)"""75 76    header_style: StyleType = ""77    """StyleType: The style of the header."""78 79    footer_style: StyleType = ""80    """StyleType: The style of the footer."""81 82    style: StyleType = ""83    """StyleType: The style of the column."""84 85    justify: "JustifyMethod" = "left"86    """str: How to justify text within the column ("left", "center", "right", or "full")"""87 88    vertical: "VerticalAlignMethod" = "top"89    """str: How to vertically align content ("top", "middle", or "bottom")"""90 91    overflow: "OverflowMethod" = "ellipsis"92    """str: Overflow method."""93 94    width: Optional[int] = None95    """Optional[int]: Width of the column, or ``None`` (default) to auto calculate width."""96 97    min_width: Optional[int] = None98    """Optional[int]: Minimum width of column, or ``None`` for no minimum. Defaults to None."""99 100    max_width: Optional[int] = None101    """Optional[int]: Maximum width of column, or ``None`` for no maximum. Defaults to None."""102 103    ratio: Optional[int] = None104    """Optional[int]: Ratio to use when calculating column width, or ``None`` (default) to adapt to column contents."""105 106    no_wrap: bool = False107    """bool: Prevent wrapping of text within the column. Defaults to ``False``."""108 109    _index: int = 0110    """Index of column."""111 112    _cells: List["RenderableType"] = field(default_factory=list)113 114    def copy(self) -> "Column":115        """Return a copy of this Column."""116        return replace(self, _cells=[])117 118    @property119    def cells(self) -> Iterable["RenderableType"]:120        """Get all cells in the column, not including header."""121        yield from self._cells122 123    @property124    def flexible(self) -> bool:125        """Check if this column is flexible."""126        return self.ratio is not None127 128 129@dataclass130class Row:131    """Information regarding a row."""132 133    style: Optional[StyleType] = None134    """Style to apply to row."""135 136    end_section: bool = False137    """Indicated end of section, which will force a line beneath the row."""138 139 140class _Cell(NamedTuple):141    """A single cell in a table."""142 143    style: StyleType144    """Style to apply to cell."""145    renderable: "RenderableType"146    """Cell renderable."""147    vertical: VerticalAlignMethod148    """Cell vertical alignment."""149 150 151class Table(JupyterMixin):152    """A console renderable to draw a table.153 154    Args:155        *headers (Union[Column, str]): Column headers, either as a string, or :class:`~rich.table.Column` instance.156        title (Union[str, Text], optional): The title of the table rendered at the top. Defaults to None.157        caption (Union[str, Text], optional): The table caption rendered below. Defaults to None.158        width (int, optional): The width in characters of the table, or ``None`` to automatically fit. Defaults to None.159        min_width (Optional[int], optional): The minimum width of the table, or ``None`` for no minimum. Defaults to None.160        box (box.Box, optional): One of the constants in box.py used to draw the edges (see :ref:`appendix_box`), or ``None`` for no box lines. Defaults to box.HEAVY_HEAD.161        safe_box (Optional[bool], optional): Disable box characters that don't display on windows legacy terminal with *raster* fonts. Defaults to True.162        padding (PaddingDimensions, optional): Padding for cells (top, right, bottom, left). Defaults to (0, 1).163        collapse_padding (bool, optional): Enable collapsing of padding around cells. Defaults to False.164        pad_edge (bool, optional): Enable padding of edge cells. Defaults to True.165        expand (bool, optional): Expand the table to fit the available space if ``True``, otherwise the table width will be auto-calculated. Defaults to False.166        show_header (bool, optional): Show a header row. Defaults to True.167        show_footer (bool, optional): Show a footer row. Defaults to False.168        show_edge (bool, optional): Draw a box around the outside of the table. Defaults to True.169        show_lines (bool, optional): Draw lines between every row. Defaults to False.170        leading (bool, optional): Number of blank lines between rows (precludes ``show_lines``). Defaults to 0.171        style (Union[str, Style], optional): Default style for the table. Defaults to "none".172        row_styles (List[Union, str], optional): Optional list of row styles, if more than one style is given then the styles will alternate. Defaults to None.173        header_style (Union[str, Style], optional): Style of the header. Defaults to "table.header".174        footer_style (Union[str, Style], optional): Style of the footer. Defaults to "table.footer".175        border_style (Union[str, Style], optional): Style of the border. Defaults to None.176        title_style (Union[str, Style], optional): Style of the title. Defaults to None.177        caption_style (Union[str, Style], optional): Style of the caption. Defaults to None.178        title_justify (str, optional): Justify method for title. Defaults to "center".179        caption_justify (str, optional): Justify method for caption. Defaults to "center".180        highlight (bool, optional): Highlight cell contents (if str). Defaults to False.181    """182 183    columns: List[Column]184    rows: List[Row]185 186    def __init__(187        self,188        *headers: Union[Column, str],189        title: Optional[TextType] = None,190        caption: Optional[TextType] = None,191        width: Optional[int] = None,192        min_width: Optional[int] = None,193        box: Optional[box.Box] = box.HEAVY_HEAD,194        safe_box: Optional[bool] = None,195        padding: PaddingDimensions = (0, 1),196        collapse_padding: bool = False,197        pad_edge: bool = True,198        expand: bool = False,199        show_header: bool = True,200        show_footer: bool = False,201        show_edge: bool = True,202        show_lines: bool = False,203        leading: int = 0,204        style: StyleType = "none",205        row_styles: Optional[Iterable[StyleType]] = None,206        header_style: Optional[StyleType] = "table.header",207        footer_style: Optional[StyleType] = "table.footer",208        border_style: Optional[StyleType] = None,209        title_style: Optional[StyleType] = None,210        caption_style: Optional[StyleType] = None,211        title_justify: "JustifyMethod" = "center",212        caption_justify: "JustifyMethod" = "center",213        highlight: bool = False,214    ) -> None:215        self.columns: List[Column] = []216        self.rows: List[Row] = []217        self.title = title218        self.caption = caption219        self.width = width220        self.min_width = min_width221        self.box = box222        self.safe_box = safe_box223        self._padding = Padding.unpack(padding)224        self.pad_edge = pad_edge225        self._expand = expand226        self.show_header = show_header227        self.show_footer = show_footer228        self.show_edge = show_edge229        self.show_lines = show_lines230        self.leading = leading231        self.collapse_padding = collapse_padding232        self.style = style233        self.header_style = header_style or ""234        self.footer_style = footer_style or ""235        self.border_style = border_style236        self.title_style = title_style237        self.caption_style = caption_style238        self.title_justify: "JustifyMethod" = title_justify239        self.caption_justify: "JustifyMethod" = caption_justify240        self.highlight = highlight241        self.row_styles: Sequence[StyleType] = list(row_styles or [])242        append_column = self.columns.append243        for header in headers:244            if isinstance(header, str):245                self.add_column(header=header)246            else:247                header._index = len(self.columns)248                append_column(header)249 250    @classmethod251    def grid(252        cls,253        *headers: Union[Column, str],254        padding: PaddingDimensions = 0,255        collapse_padding: bool = True,256        pad_edge: bool = False,257        expand: bool = False,258    ) -> "Table":259        """Get a table with no lines, headers, or footer.260 261        Args:262            *headers (Union[Column, str]): Column headers, either as a string, or :class:`~rich.table.Column` instance.263            padding (PaddingDimensions, optional): Get padding around cells. Defaults to 0.264            collapse_padding (bool, optional): Enable collapsing of padding around cells. Defaults to True.265            pad_edge (bool, optional): Enable padding around edges of table. Defaults to False.266            expand (bool, optional): Expand the table to fit the available space if ``True``, otherwise the table width will be auto-calculated. Defaults to False.267 268        Returns:269            Table: A table instance.270        """271        return cls(272            *headers,273            box=None,274            padding=padding,275            collapse_padding=collapse_padding,276            show_header=False,277            show_footer=False,278            show_edge=False,279            pad_edge=pad_edge,280            expand=expand,281        )282 283    @property284    def expand(self) -> bool:285        """Setting a non-None self.width implies expand."""286        return self._expand or self.width is not None287 288    @expand.setter289    def expand(self, expand: bool) -> None:290        """Set expand."""291        self._expand = expand292 293    @property294    def _extra_width(self) -> int:295        """Get extra width to add to cell content."""296        width = 0297        if self.box and self.show_edge:298            width += 2299        if self.box:300            width += len(self.columns) - 1301        return width302 303    @property304    def row_count(self) -> int:305        """Get the current number of rows."""306        return len(self.rows)307 308    def get_row_style(self, console: "Console", index: int) -> StyleType:309        """Get the current row style."""310        style = Style.null()311        if self.row_styles:312            style += console.get_style(self.row_styles[index % len(self.row_styles)])313        row_style = self.rows[index].style314        if row_style is not None:315            style += console.get_style(row_style)316        return style317 318    def __rich_measure__(319        self, console: "Console", options: "ConsoleOptions"320    ) -> Measurement:321        max_width = options.max_width322        if self.width is not None:323            max_width = self.width324        if max_width < 0:325            return Measurement(0, 0)326 327        extra_width = self._extra_width328        max_width = sum(329            self._calculate_column_widths(330                console, options.update_width(max_width - extra_width)331            )332        )333        _measure_column = self._measure_column334 335        measurements = [336            _measure_column(console, options.update_width(max_width), column)337            for column in self.columns338        ]339        minimum_width = (340            sum(measurement.minimum for measurement in measurements) + extra_width341        )342        maximum_width = (343            sum(measurement.maximum for measurement in measurements) + extra_width344            if (self.width is None)345            else self.width346        )347        measurement = Measurement(minimum_width, maximum_width)348        measurement = measurement.clamp(self.min_width)349        return measurement350 351    @property352    def padding(self) -> Tuple[int, int, int, int]:353        """Get cell padding."""354        return self._padding355 356    @padding.setter357    def padding(self, padding: PaddingDimensions) -> "Table":358        """Set cell padding."""359        self._padding = Padding.unpack(padding)360        return self361 362    def add_column(363        self,364        header: "RenderableType" = "",365        footer: "RenderableType" = "",366        *,367        header_style: Optional[StyleType] = None,368        footer_style: Optional[StyleType] = None,369        style: Optional[StyleType] = None,370        justify: "JustifyMethod" = "left",371        vertical: "VerticalAlignMethod" = "top",372        overflow: "OverflowMethod" = "ellipsis",373        width: Optional[int] = None,374        min_width: Optional[int] = None,375        max_width: Optional[int] = None,376        ratio: Optional[int] = None,377        no_wrap: bool = False,378    ) -> None:379        """Add a column to the table.380 381        Args:382            header (RenderableType, optional): Text or renderable for the header.383                Defaults to "".384            footer (RenderableType, optional): Text or renderable for the footer.385                Defaults to "".386            header_style (Union[str, Style], optional): Style for the header, or None for default. Defaults to None.387            footer_style (Union[str, Style], optional): Style for the footer, or None for default. Defaults to None.388            style (Union[str, Style], optional): Style for the column cells, or None for default. Defaults to None.389            justify (JustifyMethod, optional): Alignment for cells. Defaults to "left".390            vertical (VerticalAlignMethod, optional): Vertical alignment, one of "top", "middle", or "bottom". Defaults to "top".391            overflow (OverflowMethod): Overflow method: "crop", "fold", "ellipsis". Defaults to "ellipsis".392            width (int, optional): Desired width of column in characters, or None to fit to contents. Defaults to None.393            min_width (Optional[int], optional): Minimum width of column, or ``None`` for no minimum. Defaults to None.394            max_width (Optional[int], optional): Maximum width of column, or ``None`` for no maximum. Defaults to None.395            ratio (int, optional): Flexible ratio for the column (requires ``Table.expand`` or ``Table.width``). Defaults to None.396            no_wrap (bool, optional): Set to ``True`` to disable wrapping of this column.397        """398 399        column = Column(400            _index=len(self.columns),401            header=header,402            footer=footer,403            header_style=header_style or "",404            footer_style=footer_style or "",405            style=style or "",406            justify=justify,407            vertical=vertical,408            overflow=overflow,409            width=width,410            min_width=min_width,411            max_width=max_width,412            ratio=ratio,413            no_wrap=no_wrap,414        )415        self.columns.append(column)416 417    def add_row(418        self,419        *renderables: Optional["RenderableType"],420        style: Optional[StyleType] = None,421        end_section: bool = False,422    ) -> None:423        """Add a row of renderables.424 425        Args:426            *renderables (None or renderable): Each cell in a row must be a renderable object (including str),427                or ``None`` for a blank cell.428            style (StyleType, optional): An optional style to apply to the entire row. Defaults to None.429            end_section (bool, optional): End a section and draw a line. Defaults to False.430 431        Raises:432            errors.NotRenderableError: If you add something that can't be rendered.433        """434 435        def add_cell(column: Column, renderable: "RenderableType") -> None:436            column._cells.append(renderable)437 438        cell_renderables: List[Optional["RenderableType"]] = list(renderables)439 440        columns = self.columns441        if len(cell_renderables) < len(columns):442            cell_renderables = [443                *cell_renderables,444                *[None] * (len(columns) - len(cell_renderables)),445            ]446        for index, renderable in enumerate(cell_renderables):447            if index == len(columns):448                column = Column(_index=index)449                for _ in self.rows:450                    add_cell(column, Text(""))451                self.columns.append(column)452            else:453                column = columns[index]454            if renderable is None:455                add_cell(column, "")456            elif is_renderable(renderable):457                add_cell(column, renderable)458            else:459                raise errors.NotRenderableError(460                    f"unable to render {type(renderable).__name__}; a string or other renderable object is required"461                )462        self.rows.append(Row(style=style, end_section=end_section))463 464    def add_section(self) -> None:465        """Add a new section (draw a line after current row)."""466 467        if self.rows:468            self.rows[-1].end_section = True469 470    def __rich_console__(471        self, console: "Console", options: "ConsoleOptions"472    ) -> "RenderResult":473        if not self.columns:474            yield Segment("\n")475            return476 477        max_width = options.max_width478        if self.width is not None:479            max_width = self.width480 481        extra_width = self._extra_width482        widths = self._calculate_column_widths(483            console, options.update_width(max_width - extra_width)484        )485        table_width = sum(widths) + extra_width486 487        render_options = options.update(488            width=table_width, highlight=self.highlight, height=None489        )490 491        def render_annotation(492            text: TextType, style: StyleType, justify: "JustifyMethod" = "center"493        ) -> "RenderResult":494            render_text = (495                console.render_str(text, style=style, highlight=False)496                if isinstance(text, str)497                else text498            )499            return console.render(500                render_text, options=render_options.update(justify=justify)501            )502 503        if self.title:504            yield from render_annotation(505                self.title,506                style=Style.pick_first(self.title_style, "table.title"),507                justify=self.title_justify,508            )509        yield from self._render(console, render_options, widths)510        if self.caption:511            yield from render_annotation(512                self.caption,513                style=Style.pick_first(self.caption_style, "table.caption"),514                justify=self.caption_justify,515            )516 517    def _calculate_column_widths(518        self, console: "Console", options: "ConsoleOptions"519    ) -> List[int]:520        """Calculate the widths of each column, including padding, not including borders."""521        max_width = options.max_width522        columns = self.columns523        width_ranges = [524            self._measure_column(console, options, column) for column in columns525        ]526        widths = [_range.maximum or 1 for _range in width_ranges]527        get_padding_width = self._get_padding_width528        extra_width = self._extra_width529        if self.expand:530            ratios = [col.ratio or 0 for col in columns if col.flexible]531            if any(ratios):532                fixed_widths = [533                    0 if column.flexible else _range.maximum534                    for _range, column in zip(width_ranges, columns)535                ]536                flex_minimum = [537                    (column.width or 1) + get_padding_width(column._index)538                    for column in columns539                    if column.flexible540                ]541                flexible_width = max_width - sum(fixed_widths)542                flex_widths = ratio_distribute(flexible_width, ratios, flex_minimum)543                iter_flex_widths = iter(flex_widths)544                for index, column in enumerate(columns):545                    if column.flexible:546                        widths[index] = fixed_widths[index] + next(iter_flex_widths)547        table_width = sum(widths)548 549        if table_width > max_width:550            widths = self._collapse_widths(551                widths,552                [(column.width is None and not column.no_wrap) for column in columns],553                max_width,554            )555            table_width = sum(widths)556            # last resort, reduce columns evenly557            if table_width > max_width:558                excess_width = table_width - max_width559                widths = ratio_reduce(excess_width, [1] * len(widths), widths, widths)560                table_width = sum(widths)561 562            width_ranges = [563                self._measure_column(console, options.update_width(width), column)564                for width, column in zip(widths, columns)565            ]566            widths = [_range.maximum or 0 for _range in width_ranges]567 568        if (table_width < max_width and self.expand) or (569            self.min_width is not None and table_width < (self.min_width - extra_width)570        ):571            _max_width = (572                max_width573                if self.min_width is None574                else min(self.min_width - extra_width, max_width)575            )576            pad_widths = ratio_distribute(_max_width - table_width, widths)577            widths = [_width + pad for _width, pad in zip(widths, pad_widths)]578 579        return widths580 581    @classmethod582    def _collapse_widths(583        cls, widths: List[int], wrapable: List[bool], max_width: int584    ) -> List[int]:585        """Reduce widths so that the total is under max_width.586 587        Args:588            widths (List[int]): List of widths.589            wrapable (List[bool]): List of booleans that indicate if a column may shrink.590            max_width (int): Maximum width to reduce to.591 592        Returns:593            List[int]: A new list of widths.594        """595        total_width = sum(widths)596        excess_width = total_width - max_width597        if any(wrapable):598            while total_width and excess_width > 0:599                max_column = max(600                    width for width, allow_wrap in zip(widths, wrapable) if allow_wrap601                )602                second_max_column = max(603                    width if allow_wrap and width != max_column else 0604                    for width, allow_wrap in zip(widths, wrapable)605                )606                column_difference = max_column - second_max_column607                ratios = [608                    (1 if (width == max_column and allow_wrap) else 0)609                    for width, allow_wrap in zip(widths, wrapable)610                ]611                if not any(ratios) or not column_difference:612                    break613                max_reduce = [min(excess_width, column_difference)] * len(widths)614                widths = ratio_reduce(excess_width, ratios, max_reduce, widths)615 616                total_width = sum(widths)617                excess_width = total_width - max_width618        return widths619 620    def _get_cells(621        self, console: "Console", column_index: int, column: Column622    ) -> Iterable[_Cell]:623        """Get all the cells with padding and optional header."""624 625        collapse_padding = self.collapse_padding626        pad_edge = self.pad_edge627        padding = self.padding628        any_padding = any(padding)629 630        first_column = column_index == 0631        last_column = column_index == len(self.columns) - 1632 633        _padding_cache: Dict[Tuple[bool, bool], Tuple[int, int, int, int]] = {}634 635        def get_padding(first_row: bool, last_row: bool) -> Tuple[int, int, int, int]:636            cached = _padding_cache.get((first_row, last_row))637            if cached:638                return cached639            top, right, bottom, left = padding640 641            if collapse_padding:642                if not first_column:643                    left = max(0, left - right)644                if not last_row:645                    bottom = max(0, top - bottom)646 647            if not pad_edge:648                if first_column:649                    left = 0650                if last_column:651                    right = 0652                if first_row:653                    top = 0654                if last_row:655                    bottom = 0656            _padding = (top, right, bottom, left)657            _padding_cache[(first_row, last_row)] = _padding658            return _padding659 660        raw_cells: List[Tuple[StyleType, "RenderableType"]] = []661        _append = raw_cells.append662        get_style = console.get_style663        if self.show_header:664            header_style = get_style(self.header_style or "") + get_style(665                column.header_style666            )667            _append((header_style, column.header))668        cell_style = get_style(column.style or "")669        for cell in column.cells:670            _append((cell_style, cell))671        if self.show_footer:672            footer_style = get_style(self.footer_style or "") + get_style(673                column.footer_style674            )675            _append((footer_style, column.footer))676 677        if any_padding:678            _Padding = Padding679            for first, last, (style, renderable) in loop_first_last(raw_cells):680                yield _Cell(681                    style,682                    _Padding(renderable, get_padding(first, last)),683                    getattr(renderable, "vertical", None) or column.vertical,684                )685        else:686            for style, renderable in raw_cells:687                yield _Cell(688                    style,689                    renderable,690                    getattr(renderable, "vertical", None) or column.vertical,691                )692 693    def _get_padding_width(self, column_index: int) -> int:694        """Get extra width from padding."""695        _, pad_right, _, pad_left = self.padding696        if self.collapse_padding:697            if column_index > 0:698                pad_left = max(0, pad_left - pad_right)699        return pad_left + pad_right700 701    def _measure_column(702        self,703        console: "Console",704        options: "ConsoleOptions",705        column: Column,706    ) -> Measurement:707        """Get the minimum and maximum width of the column."""708 709        max_width = options.max_width710        if max_width < 1:711            return Measurement(0, 0)712 713        padding_width = self._get_padding_width(column._index)714 715        if column.width is not None:716            # Fixed width column717            return Measurement(718                column.width + padding_width, column.width + padding_width719            ).with_maximum(max_width)720        # Flexible column, we need to measure contents721        min_widths: List[int] = []722        max_widths: List[int] = []723        append_min = min_widths.append724        append_max = max_widths.append725        get_render_width = Measurement.get726        for cell in self._get_cells(console, column._index, column):727            _min, _max = get_render_width(console, options, cell.renderable)728            append_min(_min)729            append_max(_max)730 731        measurement = Measurement(732            max(min_widths) if min_widths else 1,733            max(max_widths) if max_widths else max_width,734        ).with_maximum(max_width)735        measurement = measurement.clamp(736            None if column.min_width is None else column.min_width + padding_width,737            None if column.max_width is None else column.max_width + padding_width,738        )739        return measurement740 741    def _render(742        self, console: "Console", options: "ConsoleOptions", widths: List[int]743    ) -> "RenderResult":744        table_style = console.get_style(self.style or "")745 746        border_style = table_style + console.get_style(self.border_style or "")747        _column_cells = (748            self._get_cells(console, column_index, column)749            for column_index, column in enumerate(self.columns)750        )751        row_cells: List[Tuple[_Cell, ...]] = list(zip(*_column_cells))752        _box = (753            self.box.substitute(754                options, safe=pick_bool(self.safe_box, console.safe_box)755            )756            if self.box757            else None758        )759        _box = _box.get_plain_headed_box() if _box and not self.show_header else _box760 761        new_line = Segment.line()762 763        columns = self.columns764        show_header = self.show_header765        show_footer = self.show_footer766        show_edge = self.show_edge767        show_lines = self.show_lines768        leading = self.leading769 770        _Segment = Segment771        if _box:772            box_segments = [773                (774                    _Segment(_box.head_left, border_style),775                    _Segment(_box.head_right, border_style),776                    _Segment(_box.head_vertical, border_style),777                ),778                (779                    _Segment(_box.foot_left, border_style),780                    _Segment(_box.foot_right, border_style),781                    _Segment(_box.foot_vertical, border_style),782                ),783                (784                    _Segment(_box.mid_left, border_style),785                    _Segment(_box.mid_right, border_style),786                    _Segment(_box.mid_vertical, border_style),787                ),788            ]789            if show_edge:790                yield _Segment(_box.get_top(widths), border_style)791                yield new_line792        else:793            box_segments = []794 795        get_row_style = self.get_row_style796        get_style = console.get_style797 798        for index, (first, last, row_cell) in enumerate(loop_first_last(row_cells)):799            header_row = first and show_header800            footer_row = last and show_footer801            row = (802                self.rows[index - show_header]803                if (not header_row and not footer_row)804                else None805            )806            max_height = 1807            cells: List[List[List[Segment]]] = []808            if header_row or footer_row:809                row_style = Style.null()810            else:811                row_style = get_style(812                    get_row_style(console, index - 1 if show_header else index)813                )814            for width, cell, column in zip(widths, row_cell, columns):815                render_options = options.update(816                    width=width,817                    justify=column.justify,818                    no_wrap=column.no_wrap,819                    overflow=column.overflow,820                    height=None,821                )822                lines = console.render_lines(823                    cell.renderable,824                    render_options,825                    style=get_style(cell.style) + row_style,826                )827                max_height = max(max_height, len(lines))828                cells.append(lines)829 830            row_height = max(len(cell) for cell in cells)831 832            def align_cell(833                cell: List[List[Segment]],834                vertical: "VerticalAlignMethod",835                width: int,836                style: Style,837            ) -> List[List[Segment]]:838                if header_row:839                    vertical = "bottom"840                elif footer_row:841                    vertical = "top"842 843                if vertical == "top":844                    return _Segment.align_top(cell, width, row_height, style)845                elif vertical == "middle":846                    return _Segment.align_middle(cell, width, row_height, style)847                return _Segment.align_bottom(cell, width, row_height, style)848 849            cells[:] = [850                _Segment.set_shape(851                    align_cell(852                        cell,853                        _cell.vertical,854                        width,855                        get_style(_cell.style) + row_style,856                    ),857                    width,858                    max_height,859                )860                for width, _cell, cell, column in zip(widths, row_cell, cells, columns)861            ]862 863            if _box:864                if last and show_footer:865                    yield _Segment(866                        _box.get_row(widths, "foot", edge=show_edge), border_style867                    )868                    yield new_line869                left, right, _divider = box_segments[0 if first else (2 if last else 1)]870 871                # If the column divider is whitespace also style it with the row background872                divider = (873                    _divider874                    if _divider.text.strip()875                    else _Segment(876                        _divider.text, row_style.background_style + _divider.style877                    )878                )879                for line_no in range(max_height):880                    if show_edge:881                        yield left882                    for last_cell, rendered_cell in loop_last(cells):883                        yield from rendered_cell[line_no]884                        if not last_cell:885                            yield divider886                    if show_edge:887                        yield right888                    yield new_line889            else:890                for line_no in range(max_height):891                    for rendered_cell in cells:892                        yield from rendered_cell[line_no]893                    yield new_line894            if _box and first and show_header:895                yield _Segment(896                    _box.get_row(widths, "head", edge=show_edge), border_style897                )898                yield new_line899            end_section = row and row.end_section900            if _box and (show_lines or leading or end_section):901                if (902                    not last903                    and not (show_footer and index >= len(row_cells) - 2)904                    and not (show_header and header_row)905                ):906                    if leading:907                        yield _Segment(908                            _box.get_row(widths, "mid", edge=show_edge) * leading,909                            border_style,910                        )911                    else:912                        yield _Segment(913                            _box.get_row(widths, "row", edge=show_edge), border_style914                        )915                    yield new_line916 917        if _box and show_edge:918            yield _Segment(_box.get_bottom(widths), border_style)919            yield new_line920 921 922if __name__ == "__main__":  # pragma: no cover923    from rich.console import Console924    from rich.highlighter import ReprHighlighter925    from rich.table import Table as Table926 927    from ._timer import timer928 929    with timer("Table render"):930        table = Table(931            title="Star Wars Movies",932            caption="Rich example table",933            caption_justify="right",934        )935 936        table.add_column(937            "Released", header_style="bright_cyan", style="cyan", no_wrap=True938        )939        table.add_column("Title", style="magenta")940        table.add_column("Box Office", justify="right", style="green")941 942        table.add_row(943            "Dec 20, 2019",944            "Star Wars: The Rise of Skywalker",945            "$952,110,690",946        )947        table.add_row("May 25, 2018", "Solo: A Star Wars Story", "$393,151,347")948        table.add_row(949            "Dec 15, 2017",950            "Star Wars Ep. V111: The Last Jedi",951            "$1,332,539,889",952            style="on black",953            end_section=True,954        )955        table.add_row(956            "Dec 16, 2016",957            "Rogue One: A Star Wars Story",958            "$1,332,439,889",959        )960 961        def header(text: str) -> None:962            console.print()963            console.rule(highlight(text))964            console.print()965 966        console = Console()967        highlight = ReprHighlighter()968        header("Example Table")969        console.print(table, justify="center")970 971        table.expand = True972        header("expand=True")973        console.print(table)974 975        table.width = 50976        header("width=50")977 978        console.print(table, justify="center")979 980        table.width = None981        table.expand = False982        table.row_styles = ["dim", "none"]983        header("row_styles=['dim', 'none']")984 985        console.print(table, justify="center")986 987        table.width = None988        table.expand = False989        table.row_styles = ["dim", "none"]990        table.leading = 1991        header("leading=1, row_styles=['dim', 'none']")992        console.print(table, justify="center")993 994        table.width = None995        table.expand = False996        table.row_styles = ["dim", "none"]997        table.show_lines = True998        table.leading = 0999        header("show_lines=True, row_styles=['dim', 'none']")1000        console.print(table, justify="center")1001 
codekingpro/portable-devtools · Team Ai