Team Ai
Datasetpublic

codekingpro/portable-devtools

sourceHugging Faceupdated 5mo agoView on Hugging Face
1likes15kdownloads
table.py1016 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        show_header (bool, optional): Show a header row. Defaults to True.53        show_footer (bool, optional): Show a footer row. Defaults to False.54        show_edge (bool, optional): Draw a box around the outside of the table. Defaults to True.55        show_lines (bool, optional): Draw lines between every row. Defaults to False.56        leading (int, optional): Number of blank lines between rows (precludes ``show_lines``). Defaults to 0.57        style (Union[str, Style], optional): Default style for the table. Defaults to "none".58        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.59        header_style (Union[str, Style], optional): Style of the header. Defaults to "table.header".60        footer_style (Union[str, Style], optional): Style of the footer. Defaults to "table.footer".61        border_style (Union[str, Style], optional): Style of the border. Defaults to None.62        title_style (Union[str, Style], optional): Style of the title. Defaults to None.63        caption_style (Union[str, Style], optional): Style of the caption. Defaults to None.64        title_justify (str, optional): Justify method for title. Defaults to "center".65        caption_justify (str, optional): Justify method for caption. Defaults to "center".66        highlight (bool, optional): Highlight cell contents (if str). Defaults to False.67    """68 69    header: "RenderableType" = ""70    """RenderableType: Renderable for the header (typically a string)"""71 72    footer: "RenderableType" = ""73    """RenderableType: Renderable for the footer (typically a string)"""74 75    header_style: StyleType = ""76    """StyleType: The style of the header."""77 78    footer_style: StyleType = ""79    """StyleType: The style of the footer."""80 81    style: StyleType = ""82    """StyleType: The style of the column."""83 84    justify: "JustifyMethod" = "left"85    """str: How to justify text within the column ("left", "center", "right", or "full")"""86 87    vertical: "VerticalAlignMethod" = "top"88    """str: How to vertically align content ("top", "middle", or "bottom")"""89 90    overflow: "OverflowMethod" = "ellipsis"91    """str: Overflow method."""92 93    width: Optional[int] = None94    """Optional[int]: Width of the column, or ``None`` (default) to auto calculate width."""95 96    min_width: Optional[int] = None97    """Optional[int]: Minimum width of column, or ``None`` for no minimum. Defaults to None."""98 99    max_width: Optional[int] = None100    """Optional[int]: Maximum width of column, or ``None`` for no maximum. Defaults to None."""101 102    ratio: Optional[int] = None103    """Optional[int]: Ratio to use when calculating column width, or ``None`` (default) to adapt to column contents."""104 105    no_wrap: bool = False106    """bool: Prevent wrapping of text within the column. Defaults to ``False``."""107 108    highlight: bool = False109    """bool: Apply highlighter to column. Defaults to ``False``."""110 111    _index: int = 0112    """Index of column."""113 114    _cells: List["RenderableType"] = field(default_factory=list)115 116    def copy(self) -> "Column":117        """Return a copy of this Column."""118        return replace(self, _cells=[])119 120    @property121    def cells(self) -> Iterable["RenderableType"]:122        """Get all cells in the column, not including header."""123        yield from self._cells124 125    @property126    def flexible(self) -> bool:127        """Check if this column is flexible."""128        return self.ratio is not None129 130 131@dataclass132class Row:133    """Information regarding a row."""134 135    style: Optional[StyleType] = None136    """Style to apply to row."""137 138    end_section: bool = False139    """Indicated end of section, which will force a line beneath the row."""140 141 142class _Cell(NamedTuple):143    """A single cell in a table."""144 145    style: StyleType146    """Style to apply to cell."""147    renderable: "RenderableType"148    """Cell renderable."""149    vertical: VerticalAlignMethod150    """Cell vertical alignment."""151 152 153class Table(JupyterMixin):154    """A console renderable to draw a table.155 156    Args:157        *headers (Union[Column, str]): Column headers, either as a string, or :class:`~rich.table.Column` instance.158        title (Union[str, Text], optional): The title of the table rendered at the top. Defaults to None.159        caption (Union[str, Text], optional): The table caption rendered below. Defaults to None.160        width (int, optional): The width in characters of the table, or ``None`` to automatically fit. Defaults to None.161        min_width (Optional[int], optional): The minimum width of the table, or ``None`` for no minimum. Defaults to None.162        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.163        safe_box (Optional[bool], optional): Disable box characters that don't display on windows legacy terminal with *raster* fonts. Defaults to True.164        padding (PaddingDimensions, optional): Padding for cells (top, right, bottom, left). Defaults to (0, 1).165        collapse_padding (bool, optional): Enable collapsing of padding around cells. Defaults to False.166        pad_edge (bool, optional): Enable padding of edge cells. Defaults to True.167        expand (bool, optional): Expand the table to fit the available space if ``True``, otherwise the table width will be auto-calculated. Defaults to False.168        show_header (bool, optional): Show a header row. Defaults to True.169        show_footer (bool, optional): Show a footer row. Defaults to False.170        show_edge (bool, optional): Draw a box around the outside of the table. Defaults to True.171        show_lines (bool, optional): Draw lines between every row. Defaults to False.172        leading (int, optional): Number of blank lines between rows (precludes ``show_lines``). Defaults to 0.173        style (Union[str, Style], optional): Default style for the table. Defaults to "none".174        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.175        header_style (Union[str, Style], optional): Style of the header. Defaults to "table.header".176        footer_style (Union[str, Style], optional): Style of the footer. Defaults to "table.footer".177        border_style (Union[str, Style], optional): Style of the border. Defaults to None.178        title_style (Union[str, Style], optional): Style of the title. Defaults to None.179        caption_style (Union[str, Style], optional): Style of the caption. Defaults to None.180        title_justify (str, optional): Justify method for title. Defaults to "center".181        caption_justify (str, optional): Justify method for caption. Defaults to "center".182        highlight (bool, optional): Highlight cell contents (if str). Defaults to False.183    """184 185    columns: List[Column]186    rows: List[Row]187 188    def __init__(189        self,190        *headers: Union[Column, str],191        title: Optional[TextType] = None,192        caption: Optional[TextType] = None,193        width: Optional[int] = None,194        min_width: Optional[int] = None,195        box: Optional[box.Box] = box.HEAVY_HEAD,196        safe_box: Optional[bool] = None,197        padding: PaddingDimensions = (0, 1),198        collapse_padding: bool = False,199        pad_edge: bool = True,200        expand: bool = False,201        show_header: bool = True,202        show_footer: bool = False,203        show_edge: bool = True,204        show_lines: bool = False,205        leading: int = 0,206        style: StyleType = "none",207        row_styles: Optional[Iterable[StyleType]] = None,208        header_style: Optional[StyleType] = "table.header",209        footer_style: Optional[StyleType] = "table.footer",210        border_style: Optional[StyleType] = None,211        title_style: Optional[StyleType] = None,212        caption_style: Optional[StyleType] = None,213        title_justify: "JustifyMethod" = "center",214        caption_justify: "JustifyMethod" = "center",215        highlight: bool = False,216    ) -> None:217        self.columns: List[Column] = []218        self.rows: List[Row] = []219        self.title = title220        self.caption = caption221        self.width = width222        self.min_width = min_width223        self.box = box224        self.safe_box = safe_box225        self._padding = Padding.unpack(padding)226        self.pad_edge = pad_edge227        self._expand = expand228        self.show_header = show_header229        self.show_footer = show_footer230        self.show_edge = show_edge231        self.show_lines = show_lines232        self.leading = leading233        self.collapse_padding = collapse_padding234        self.style = style235        self.header_style = header_style or ""236        self.footer_style = footer_style or ""237        self.border_style = border_style238        self.title_style = title_style239        self.caption_style = caption_style240        self.title_justify: "JustifyMethod" = title_justify241        self.caption_justify: "JustifyMethod" = caption_justify242        self.highlight = highlight243        self.row_styles: Sequence[StyleType] = list(row_styles or [])244        append_column = self.columns.append245        for header in headers:246            if isinstance(header, str):247                self.add_column(header=header)248            else:249                header._index = len(self.columns)250                append_column(header)251 252    @classmethod253    def grid(254        cls,255        *headers: Union[Column, str],256        padding: PaddingDimensions = 0,257        collapse_padding: bool = True,258        pad_edge: bool = False,259        expand: bool = False,260    ) -> "Table":261        """Get a table with no lines, headers, or footer.262 263        Args:264            *headers (Union[Column, str]): Column headers, either as a string, or :class:`~rich.table.Column` instance.265            padding (PaddingDimensions, optional): Get padding around cells. Defaults to 0.266            collapse_padding (bool, optional): Enable collapsing of padding around cells. Defaults to True.267            pad_edge (bool, optional): Enable padding around edges of table. Defaults to False.268            expand (bool, optional): Expand the table to fit the available space if ``True``, otherwise the table width will be auto-calculated. Defaults to False.269 270        Returns:271            Table: A table instance.272        """273        return cls(274            *headers,275            box=None,276            padding=padding,277            collapse_padding=collapse_padding,278            show_header=False,279            show_footer=False,280            show_edge=False,281            pad_edge=pad_edge,282            expand=expand,283        )284 285    @property286    def expand(self) -> bool:287        """Setting a non-None self.width implies expand."""288        return self._expand or self.width is not None289 290    @expand.setter291    def expand(self, expand: bool) -> None:292        """Set expand."""293        self._expand = expand294 295    @property296    def _extra_width(self) -> int:297        """Get extra width to add to cell content."""298        width = 0299        if self.box and self.show_edge:300            width += 2301        if self.box:302            width += len(self.columns) - 1303        return width304 305    @property306    def row_count(self) -> int:307        """Get the current number of rows."""308        return len(self.rows)309 310    def get_row_style(self, console: "Console", index: int) -> StyleType:311        """Get the current row style."""312        style = Style.null()313        if self.row_styles:314            style += console.get_style(self.row_styles[index % len(self.row_styles)])315        row_style = self.rows[index].style316        if row_style is not None:317            style += console.get_style(row_style)318        return style319 320    def __rich_measure__(321        self, console: "Console", options: "ConsoleOptions"322    ) -> Measurement:323        max_width = options.max_width324        if self.width is not None:325            max_width = self.width326        if max_width < 0:327            return Measurement(0, 0)328 329        extra_width = self._extra_width330        max_width = sum(331            self._calculate_column_widths(332                console, options.update_width(max_width - extra_width)333            )334        )335        _measure_column = self._measure_column336 337        measurements = [338            _measure_column(console, options.update_width(max_width), column)339            for column in self.columns340        ]341        minimum_width = (342            sum(measurement.minimum for measurement in measurements) + extra_width343        )344        maximum_width = (345            sum(measurement.maximum for measurement in measurements) + extra_width346            if (self.width is None)347            else self.width348        )349        measurement = Measurement(minimum_width, maximum_width)350        measurement = measurement.clamp(self.min_width)351        return measurement352 353    @property354    def padding(self) -> Tuple[int, int, int, int]:355        """Get cell padding."""356        return self._padding357 358    @padding.setter359    def padding(self, padding: PaddingDimensions) -> "Table":360        """Set cell padding."""361        self._padding = Padding.unpack(padding)362        return self363 364    def add_column(365        self,366        header: "RenderableType" = "",367        footer: "RenderableType" = "",368        *,369        header_style: Optional[StyleType] = None,370        highlight: Optional[bool] = None,371        footer_style: Optional[StyleType] = None,372        style: Optional[StyleType] = None,373        justify: "JustifyMethod" = "left",374        vertical: "VerticalAlignMethod" = "top",375        overflow: "OverflowMethod" = "ellipsis",376        width: Optional[int] = None,377        min_width: Optional[int] = None,378        max_width: Optional[int] = None,379        ratio: Optional[int] = None,380        no_wrap: bool = False,381    ) -> None:382        """Add a column to the table.383 384        Args:385            header (RenderableType, optional): Text or renderable for the header.386                Defaults to "".387            footer (RenderableType, optional): Text or renderable for the footer.388                Defaults to "".389            header_style (Union[str, Style], optional): Style for the header, or None for default. Defaults to None.390            highlight (bool, optional): Whether to highlight the text. The default of None uses the value of the table (self) object.391            footer_style (Union[str, Style], optional): Style for the footer, or None for default. Defaults to None.392            style (Union[str, Style], optional): Style for the column cells, or None for default. Defaults to None.393            justify (JustifyMethod, optional): Alignment for cells. Defaults to "left".394            vertical (VerticalAlignMethod, optional): Vertical alignment, one of "top", "middle", or "bottom". Defaults to "top".395            overflow (OverflowMethod): Overflow method: "crop", "fold", "ellipsis". Defaults to "ellipsis".396            width (int, optional): Desired width of column in characters, or None to fit to contents. Defaults to None.397            min_width (Optional[int], optional): Minimum width of column, or ``None`` for no minimum. Defaults to None.398            max_width (Optional[int], optional): Maximum width of column, or ``None`` for no maximum. Defaults to None.399            ratio (int, optional): Flexible ratio for the column (requires ``Table.expand`` or ``Table.width``). Defaults to None.400            no_wrap (bool, optional): Set to ``True`` to disable wrapping of this column.401        """402 403        column = Column(404            _index=len(self.columns),405            header=header,406            footer=footer,407            header_style=header_style or "",408            highlight=highlight if highlight is not None else self.highlight,409            footer_style=footer_style or "",410            style=style or "",411            justify=justify,412            vertical=vertical,413            overflow=overflow,414            width=width,415            min_width=min_width,416            max_width=max_width,417            ratio=ratio,418            no_wrap=no_wrap,419        )420        self.columns.append(column)421 422    def add_row(423        self,424        *renderables: Optional["RenderableType"],425        style: Optional[StyleType] = None,426        end_section: bool = False,427    ) -> None:428        """Add a row of renderables.429 430        Args:431            *renderables (None or renderable): Each cell in a row must be a renderable object (including str),432                or ``None`` for a blank cell.433            style (StyleType, optional): An optional style to apply to the entire row. Defaults to None.434            end_section (bool, optional): End a section and draw a line. Defaults to False.435 436        Raises:437            errors.NotRenderableError: If you add something that can't be rendered.438        """439 440        def add_cell(column: Column, renderable: "RenderableType") -> None:441            column._cells.append(renderable)442 443        cell_renderables: List[Optional["RenderableType"]] = list(renderables)444 445        columns = self.columns446        if len(cell_renderables) < len(columns):447            cell_renderables = [448                *cell_renderables,449                *[None] * (len(columns) - len(cell_renderables)),450            ]451        for index, renderable in enumerate(cell_renderables):452            if index == len(columns):453                column = Column(_index=index, highlight=self.highlight)454                for _ in self.rows:455                    add_cell(column, Text(""))456                self.columns.append(column)457            else:458                column = columns[index]459            if renderable is None:460                add_cell(column, "")461            elif is_renderable(renderable):462                add_cell(column, renderable)463            else:464                raise errors.NotRenderableError(465                    f"unable to render {type(renderable).__name__}; a string or other renderable object is required"466                )467        self.rows.append(Row(style=style, end_section=end_section))468 469    def add_section(self) -> None:470        """Add a new section (draw a line after current row)."""471 472        if self.rows:473            self.rows[-1].end_section = True474 475    def __rich_console__(476        self, console: "Console", options: "ConsoleOptions"477    ) -> "RenderResult":478        if not self.columns:479            yield Segment("\n")480            return481 482        max_width = options.max_width483        if self.width is not None:484            max_width = self.width485 486        extra_width = self._extra_width487 488        widths = self._calculate_column_widths(489            console, options.update_width(max_width - extra_width)490        )491        table_width = sum(widths) + extra_width492 493        render_options = options.update(494            width=table_width, highlight=self.highlight, height=None495        )496 497        def render_annotation(498            text: TextType, style: StyleType, justify: "JustifyMethod" = "center"499        ) -> "RenderResult":500            render_text = (501                console.render_str(text, style=style, highlight=False)502                if isinstance(text, str)503                else text504            )505            return console.render(506                render_text, options=render_options.update(justify=justify)507            )508 509        if self.title:510            yield from render_annotation(511                self.title,512                style=Style.pick_first(self.title_style, "table.title"),513                justify=self.title_justify,514            )515        yield from self._render(console, render_options, widths)516        if self.caption:517            yield from render_annotation(518                self.caption,519                style=Style.pick_first(self.caption_style, "table.caption"),520                justify=self.caption_justify,521            )522 523    def _calculate_column_widths(524        self, console: "Console", options: "ConsoleOptions"525    ) -> List[int]:526        """Calculate the widths of each column, including padding, not including borders."""527        max_width = options.max_width528        columns = self.columns529        width_ranges = [530            self._measure_column(console, options, column) for column in columns531        ]532        widths = [_range.maximum or 1 for _range in width_ranges]533 534        get_padding_width = self._get_padding_width535        extra_width = self._extra_width536        if self.expand:537            ratios = [col.ratio or 0 for col in columns if col.flexible]538            if any(ratios):539                fixed_widths = [540                    0 if column.flexible else _range.maximum541                    for _range, column in zip(width_ranges, columns)542                ]543                flex_minimum = [544                    (column.width or 1) + get_padding_width(column._index)545                    for column in columns546                    if column.flexible547                ]548                flexible_width = max_width - sum(fixed_widths)549                flex_widths = ratio_distribute(flexible_width, ratios, flex_minimum)550                iter_flex_widths = iter(flex_widths)551                for index, column in enumerate(columns):552                    if column.flexible:553                        widths[index] = fixed_widths[index] + next(iter_flex_widths)554        table_width = sum(widths)555 556        if table_width > max_width:557            widths = self._collapse_widths(558                widths,559                [(column.width is None and not column.no_wrap) for column in columns],560                max_width,561            )562            table_width = sum(widths)563            # last resort, reduce columns evenly564            if table_width > max_width:565                excess_width = table_width - max_width566                widths = ratio_reduce(excess_width, [1] * len(widths), widths, widths)567                table_width = sum(widths)568 569            width_ranges = [570                self._measure_column(console, options.update_width(width), column)571                for width, column in zip(widths, columns)572            ]573            widths = [_range.maximum or 0 for _range in width_ranges]574 575        if (table_width < max_width and self.expand) or (576            self.min_width is not None and table_width < (self.min_width - extra_width)577        ):578            _max_width = (579                max_width580                if self.min_width is None581                else min(self.min_width - extra_width, max_width)582            )583            pad_widths = ratio_distribute(_max_width - table_width, widths)584            widths = [_width + pad for _width, pad in zip(widths, pad_widths)]585 586        return widths587 588    @classmethod589    def _collapse_widths(590        cls, widths: List[int], wrapable: List[bool], max_width: int591    ) -> List[int]:592        """Reduce widths so that the total is under max_width.593 594        Args:595            widths (List[int]): List of widths.596            wrapable (List[bool]): List of booleans that indicate if a column may shrink.597            max_width (int): Maximum width to reduce to.598 599        Returns:600            List[int]: A new list of widths.601        """602        total_width = sum(widths)603        excess_width = total_width - max_width604        if any(wrapable):605            while total_width and excess_width > 0:606                max_column = max(607                    width for width, allow_wrap in zip(widths, wrapable) if allow_wrap608                )609                second_max_column = max(610                    width if allow_wrap and width != max_column else 0611                    for width, allow_wrap in zip(widths, wrapable)612                )613                column_difference = max_column - second_max_column614                ratios = [615                    (1 if (width == max_column and allow_wrap) else 0)616                    for width, allow_wrap in zip(widths, wrapable)617                ]618                if not any(ratios) or not column_difference:619                    break620                max_reduce = [min(excess_width, column_difference)] * len(widths)621                widths = ratio_reduce(excess_width, ratios, max_reduce, widths)622 623                total_width = sum(widths)624                excess_width = total_width - max_width625        return widths626 627    def _get_cells(628        self, console: "Console", column_index: int, column: Column629    ) -> Iterable[_Cell]:630        """Get all the cells with padding and optional header."""631 632        collapse_padding = self.collapse_padding633        pad_edge = self.pad_edge634        padding = self.padding635        any_padding = any(padding)636 637        first_column = column_index == 0638        last_column = column_index == len(self.columns) - 1639 640        _padding_cache: Dict[Tuple[bool, bool], Tuple[int, int, int, int]] = {}641 642        def get_padding(first_row: bool, last_row: bool) -> Tuple[int, int, int, int]:643            cached = _padding_cache.get((first_row, last_row))644            if cached:645                return cached646            top, right, bottom, left = padding647 648            if collapse_padding:649                if not first_column:650                    left = max(0, left - right)651                if not last_row:652                    bottom = max(0, top - bottom)653 654            if not pad_edge:655                if first_column:656                    left = 0657                if last_column:658                    right = 0659                if first_row:660                    top = 0661                if last_row:662                    bottom = 0663            _padding = (top, right, bottom, left)664            _padding_cache[(first_row, last_row)] = _padding665            return _padding666 667        raw_cells: List[Tuple[StyleType, "RenderableType"]] = []668        _append = raw_cells.append669        get_style = console.get_style670        if self.show_header:671            header_style = get_style(self.header_style or "") + get_style(672                column.header_style673            )674            _append((header_style, column.header))675        cell_style = get_style(column.style or "")676        for cell in column.cells:677            _append((cell_style, cell))678        if self.show_footer:679            footer_style = get_style(self.footer_style or "") + get_style(680                column.footer_style681            )682            _append((footer_style, column.footer))683 684        if any_padding:685            _Padding = Padding686            for first, last, (style, renderable) in loop_first_last(raw_cells):687                yield _Cell(688                    style,689                    _Padding(renderable, get_padding(first, last)),690                    getattr(renderable, "vertical", None) or column.vertical,691                )692        else:693            for style, renderable in raw_cells:694                yield _Cell(695                    style,696                    renderable,697                    getattr(renderable, "vertical", None) or column.vertical,698                )699 700    def _get_padding_width(self, column_index: int) -> int:701        """Get extra width from padding."""702        _, pad_right, _, pad_left = self.padding703 704        if self.collapse_padding:705            pad_left = 0706            pad_right = abs(pad_left - pad_right)707 708        if not self.pad_edge:709            if column_index == 0:710                pad_left = 0711            if column_index == len(self.columns) - 1:712                pad_right = 0713 714        return pad_left + pad_right715 716    def _measure_column(717        self,718        console: "Console",719        options: "ConsoleOptions",720        column: Column,721    ) -> Measurement:722        """Get the minimum and maximum width of the column."""723 724        max_width = options.max_width725        if max_width < 1:726            return Measurement(0, 0)727 728        padding_width = self._get_padding_width(column._index)729        if column.width is not None:730            # Fixed width column731            return Measurement(732                column.width + padding_width, column.width + padding_width733            ).with_maximum(max_width)734        # Flexible column, we need to measure contents735        min_widths: List[int] = []736        max_widths: List[int] = []737        append_min = min_widths.append738        append_max = max_widths.append739        get_render_width = Measurement.get740        for cell in self._get_cells(console, column._index, column):741            _min, _max = get_render_width(console, options, cell.renderable)742            append_min(_min)743            append_max(_max)744 745        measurement = Measurement(746            max(min_widths) if min_widths else 1,747            max(max_widths) if max_widths else max_width,748        ).with_maximum(max_width)749        measurement = measurement.clamp(750            None if column.min_width is None else column.min_width + padding_width,751            None if column.max_width is None else column.max_width + padding_width,752        )753        return measurement754 755    def _render(756        self, console: "Console", options: "ConsoleOptions", widths: List[int]757    ) -> "RenderResult":758        table_style = console.get_style(self.style or "")759 760        border_style = table_style + console.get_style(self.border_style or "")761        _column_cells = (762            self._get_cells(console, column_index, column)763            for column_index, column in enumerate(self.columns)764        )765 766        row_cells: List[Tuple[_Cell, ...]] = list(zip(*_column_cells))767        _box = (768            self.box.substitute(769                options, safe=pick_bool(self.safe_box, console.safe_box)770            )771            if self.box772            else None773        )774        _box = _box.get_plain_headed_box() if _box and not self.show_header else _box775 776        new_line = Segment.line()777 778        columns = self.columns779        show_header = self.show_header780        show_footer = self.show_footer781        show_edge = self.show_edge782        show_lines = self.show_lines783        leading = self.leading784 785        _Segment = Segment786        if _box:787            box_segments = [788                (789                    _Segment(_box.head_left, border_style),790                    _Segment(_box.head_right, border_style),791                    _Segment(_box.head_vertical, border_style),792                ),793                (794                    _Segment(_box.mid_left, border_style),795                    _Segment(_box.mid_right, border_style),796                    _Segment(_box.mid_vertical, border_style),797                ),798                (799                    _Segment(_box.foot_left, border_style),800                    _Segment(_box.foot_right, border_style),801                    _Segment(_box.foot_vertical, border_style),802                ),803            ]804            if show_edge:805                yield _Segment(_box.get_top(widths), border_style)806                yield new_line807        else:808            box_segments = []809 810        get_row_style = self.get_row_style811        get_style = console.get_style812 813        for index, (first, last, row_cell) in enumerate(loop_first_last(row_cells)):814            header_row = first and show_header815            footer_row = last and show_footer816            row = (817                self.rows[index - show_header]818                if (not header_row and not footer_row)819                else None820            )821            max_height = 1822            cells: List[List[List[Segment]]] = []823            if header_row or footer_row:824                row_style = Style.null()825            else:826                row_style = get_style(827                    get_row_style(console, index - 1 if show_header else index)828                )829            for width, cell, column in zip(widths, row_cell, columns):830                render_options = options.update(831                    width=width,832                    justify=column.justify,833                    no_wrap=column.no_wrap,834                    overflow=column.overflow,835                    height=None,836                    highlight=column.highlight,837                )838                lines = console.render_lines(839                    cell.renderable,840                    render_options,841                    style=get_style(cell.style) + row_style,842                )843                max_height = max(max_height, len(lines))844                cells.append(lines)845 846            row_height = max(len(cell) for cell in cells)847 848            def align_cell(849                cell: List[List[Segment]],850                vertical: "VerticalAlignMethod",851                width: int,852                style: Style,853            ) -> List[List[Segment]]:854                if header_row:855                    vertical = "bottom"856                elif footer_row:857                    vertical = "top"858 859                if vertical == "top":860                    return _Segment.align_top(cell, width, row_height, style)861                elif vertical == "middle":862                    return _Segment.align_middle(cell, width, row_height, style)863                return _Segment.align_bottom(cell, width, row_height, style)864 865            cells[:] = [866                _Segment.set_shape(867                    align_cell(868                        cell,869                        _cell.vertical,870                        width,871                        get_style(_cell.style) + row_style,872                    ),873                    width,874                    max_height,875                )876                for width, _cell, cell, column in zip(widths, row_cell, cells, columns)877            ]878 879            if _box:880                if last and show_footer:881                    yield _Segment(882                        _box.get_row(widths, "foot", edge=show_edge), border_style883                    )884                    yield new_line885                left, right, _divider = box_segments[0 if first else (2 if last else 1)]886 887                # If the column divider is whitespace also style it with the row background888                divider = (889                    _divider890                    if _divider.text.strip()891                    else _Segment(892                        _divider.text, row_style.background_style + _divider.style893                    )894                )895                for line_no in range(max_height):896                    if show_edge:897                        yield left898                    for last_cell, rendered_cell in loop_last(cells):899                        yield from rendered_cell[line_no]900                        if not last_cell:901                            yield divider902                    if show_edge:903                        yield right904                    yield new_line905            else:906                for line_no in range(max_height):907                    for rendered_cell in cells:908                        yield from rendered_cell[line_no]909                    yield new_line910            if _box and first and show_header:911                yield _Segment(912                    _box.get_row(widths, "head", edge=show_edge), border_style913                )914                yield new_line915            end_section = row and row.end_section916            if _box and (show_lines or leading or end_section):917                if (918                    not last919                    and not (show_footer and index >= len(row_cells) - 2)920                    and not (show_header and header_row)921                ):922                    if leading:923                        yield _Segment(924                            _box.get_row(widths, "mid", edge=show_edge) * leading,925                            border_style,926                        )927                    else:928                        yield _Segment(929                            _box.get_row(widths, "row", edge=show_edge), border_style930                        )931                    yield new_line932 933        if _box and show_edge:934            yield _Segment(_box.get_bottom(widths), border_style)935            yield new_line936 937 938if __name__ == "__main__":  # pragma: no cover939    from rich.console import Console940    from rich.highlighter import ReprHighlighter941 942    from ._timer import timer943 944    with timer("Table render"):945        table = Table(946            title="Star Wars Movies",947            caption="Rich example table",948            caption_justify="right",949        )950 951        table.add_column(952            "Released", header_style="bright_cyan", style="cyan", no_wrap=True953        )954        table.add_column("Title", style="magenta")955        table.add_column("Box Office", justify="right", style="green")956 957        table.add_row(958            "Dec 20, 2019",959            "Star Wars: The Rise of Skywalker",960            "$952,110,690",961        )962        table.add_row("May 25, 2018", "Solo: A Star Wars Story", "$393,151,347")963        table.add_row(964            "Dec 15, 2017",965            "Star Wars Ep. V111: The Last Jedi",966            "$1,332,539,889",967            style="on black",968            end_section=True,969        )970        table.add_row(971            "Dec 16, 2016",972            "Rogue One: A Star Wars Story",973            "$1,332,439,889",974        )975 976        def header(text: str) -> None:977            console.print()978            console.rule(highlight(text))979            console.print()980 981        console = Console()982        highlight = ReprHighlighter()983        header("Example Table")984        console.print(table, justify="center")985 986        table.expand = True987        header("expand=True")988        console.print(table)989 990        table.width = 50991        header("width=50")992 993        console.print(table, justify="center")994 995        table.width = None996        table.expand = False997        table.row_styles = ["dim", "none"]998        header("row_styles=['dim', 'none']")999 1000        console.print(table, justify="center")1001 1002        table.width = None1003        table.expand = False1004        table.row_styles = ["dim", "none"]1005        table.leading = 11006        header("leading=1, row_styles=['dim', 'none']")1007        console.print(table, justify="center")1008 1009        table.width = None1010        table.expand = False1011        table.row_styles = ["dim", "none"]1012        table.show_lines = True1013        table.leading = 01014        header("show_lines=True, row_styles=['dim', 'none']")1015        console.print(table, justify="center")1016 
codekingpro/portable-devtools · Team Ai