Coverage for /run/media/veganeco/water/status600.com/pypi/reptilian_climates/modules_series_2/ships/fields/gardens_pip/rich/table.py: 23%

1 from dataclasses import dataclass, field, replace

2 from typing import (

3 TYPE_CHECKING,

4 Dict,

5 Iterable,

6 List,

7 NamedTuple,

8 Optional,

9 Sequence,

10 Tuple,

11 Union,

12)

13

14 from . import box, errors

15 from ._loop import loop_first_last, loop_last

16 from ._pick import pick_bool

17 from ._ratio import ratio_distribute, ratio_reduce

18 from .align import VerticalAlignMethod

19 from .jupyter import JupyterMixin

20 from .measure import Measurement

21 from .padding import Padding, PaddingDimensions

22 from .protocol import is_renderable

23 from .segment import Segment

24 from .style import Style, StyleType

25 from .text import Text, TextType

26

27 if TYPE_CHECKING:

28 from .console import (

29 Console,

30 ConsoleOptions,

31 JustifyMethod,

32 OverflowMethod,

33 RenderableType,

34 RenderResult,

35 )

36

37

38@dataclass

39 class 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] = None

95 """Optional[int]: Width of the column, or ``None`` (default) to auto calculate width."""

96

97 min_width: Optional[int] = None

98 """Optional[int]: Minimum width of column, or ``None`` for no minimum. Defaults to None."""

99

100 max_width: Optional[int] = None

101 """Optional[int]: Maximum width of column, or ``None`` for no maximum. Defaults to None."""

102

103 ratio: Optional[int] = None

104 """Optional[int]: Ratio to use when calculating column width, or ``None`` (default) to adapt to column contents."""

105

106 no_wrap: bool = False

107 """bool: Prevent wrapping of text within the column. Defaults to ``False``."""

108

109 _index: int = 0

110 """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 @property

119 def cells(self) -> Iterable["RenderableType"]:

120 """Get all cells in the column, not including header."""

121 yield from self._cells

122

123 @property

124 def flexible(self) -> bool:

125 """Check if this column is flexible."""

126 return self.ratio is not None

127

128

129@dataclass

130 class Row:

131 """Information regarding a row."""

132

133 style: Optional[StyleType] = None

134 """Style to apply to row."""

135

136 end_section: bool = False

137 """Indicated end of section, which will force a line beneath the row."""

138

139

140 class _Cell(NamedTuple):

141 """A single cell in a table."""

142

143 style: StyleType

144 """Style to apply to cell."""

145 renderable: "RenderableType"

146 """Cell renderable."""

147 vertical: VerticalAlignMethod

148 """Cell vertical alignment."""

149

150

151 class 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 = title

218 self.caption = caption

219 self.width = width

220 self.min_width = min_width

221 self.box = box

222 self.safe_box = safe_box

223 self._padding = Padding.unpack(padding)

224 self.pad_edge = pad_edge

225 self._expand = expand

226 self.show_header = show_header

227 self.show_footer = show_footer

228 self.show_edge = show_edge

229 self.show_lines = show_lines

230 self.leading = leading

231 self.collapse_padding = collapse_padding

232 self.style = style

233 self.header_style = header_style or ""

234 self.footer_style = footer_style or ""

235 self.border_style = border_style

236 self.title_style = title_style

237 self.caption_style = caption_style

238 self.title_justify: "JustifyMethod" = title_justify

239 self.caption_justify: "JustifyMethod" = caption_justify

240 self.highlight = highlight

241 self.row_styles: Sequence[StyleType] = list(row_styles or [])

242 append_column = self.columns.append

243 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 @classmethod

251 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 @property

284 def expand(self) -> bool:

285 """Setting a non-None self.width implies expand."""

286 return self._expand or self.width is not None

287

288 @expand.setter

289 def expand(self, expand: bool) -> None:

290 """Set expand."""

291 self._expand = expand

292

293 @property

294 def _extra_width(self) -> int:

295 """Get extra width to add to cell content."""

296 width = 0

297 if self.box and self.show_edge:

298 width += 2

299 if self.box:

300 width += len(self.columns) - 1

301 return width

302

303 @property

304 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].style

314 if row_style is not None:

315 style += console.get_style(row_style)

316 return style

317

318 def __rich_measure__(

319 self, console: "Console", options: "ConsoleOptions"

320 ) -> Measurement:

321 max_width = options.max_width

322 if self.width is not None:

323 max_width = self.width

324 if max_width < 0:

325 return Measurement(0, 0)

326

327 extra_width = self._extra_width

328 max_width = sum(

329 self._calculate_column_widths(

330 console, options.update_width(max_width - extra_width)

331 )

332 )

333 _measure_column = self._measure_column

334

335 measurements = [

336 _measure_column(console, options.update_width(max_width), column)

337 for column in self.columns

338 ]

339 minimum_width = (

340 sum(measurement.minimum for measurement in measurements) + extra_width

341 )

342 maximum_width = (

343 sum(measurement.maximum for measurement in measurements) + extra_width

344 if (self.width is None)

345 else self.width

346 )

347 measurement = Measurement(minimum_width, maximum_width)

348 measurement = measurement.clamp(self.min_width)

349 return measurement

350

351 @property

352 def padding(self) -> Tuple[int, int, int, int]:

353 """Get cell padding."""

354 return self._padding

355

356 @padding.setter

357 def padding(self, padding: PaddingDimensions) -> "Table":

358 """Set cell padding."""

359 self._padding = Padding.unpack(padding)

360 return self

361

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.columns

441 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 = True

469

470 def __rich_console__(

471 self, console: "Console", options: "ConsoleOptions"

472 ) -> "RenderResult":

473 if not self.columns:

474 yield Segment("\n")

475 return

476

477 max_width = options.max_width

478 if self.width is not None:

479 max_width = self.width

480

481 extra_width = self._extra_width

482 widths = self._calculate_column_widths(

483 console, options.update_width(max_width - extra_width)

484 )

485 table_width = sum(widths) + extra_width

486

487 render_options = options.update(

488 width=table_width, highlight=self.highlight, height=None

489 )

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 text

498 )

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_width

522 columns = self.columns

523 width_ranges = [

524 self._measure_column(console, options, column) for column in columns

525 ]

526 widths = [_range.maximum or 1 for _range in width_ranges]

527 get_padding_width = self._get_padding_width

528 extra_width = self._extra_width

529 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.maximum

534 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 columns

539 if column.flexible

540 ]

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 evenly

557 if table_width > max_width:

558 excess_width = table_width - max_width

559 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_width

573 if self.min_width is None

574 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 widths

580

581 @classmethod

582 def _collapse_widths(

583 cls, widths: List[int], wrapable: List[bool], max_width: int

584 ) -> 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_width

597 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_wrap

601 )

602 second_max_column = max(

603 width if allow_wrap and width != max_column else 0

604 for width, allow_wrap in zip(widths, wrapable)

605 )

606 column_difference = max_column - second_max_column

607 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 break

613 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_width

618 return widths

619

620 def _get_cells(

621 self, console: "Console", column_index: int, column: Column

622 ) -> Iterable[_Cell]:

623 """Get all the cells with padding and optional header."""

624

625 collapse_padding = self.collapse_padding

626 pad_edge = self.pad_edge

627 padding = self.padding

628 any_padding = any(padding)

629

630 first_column = column_index == 0

631 last_column = column_index == len(self.columns) - 1

632

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 cached

639 top, right, bottom, left = padding

640

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 = 0

650 if last_column:

651 right = 0

652 if first_row:

653 top = 0

654 if last_row:

655 bottom = 0

656 _padding = (top, right, bottom, left)

657 _padding_cache[(first_row, last_row)] = _padding

658 return _padding

659

660 raw_cells: List[Tuple[StyleType, "RenderableType"]] = []

661 _append = raw_cells.append

662 get_style = console.get_style

663 if self.show_header:

664 header_style = get_style(self.header_style or "") + get_style(

665 column.header_style

666 )

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_style

674 )

675 _append((footer_style, column.footer))

676

677 if any_padding:

678 _Padding = Padding

679 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.padding

696 if self.collapse_padding:

697 if column_index > 0:

698 pad_left = max(0, pad_left - pad_right)

699 return pad_left + pad_right

700

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_width

710 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 column

717 return Measurement(

718 column.width + padding_width, column.width + padding_width

719 ).with_maximum(max_width)

720 # Flexible column, we need to measure contents

721 min_widths: List[int] = []

722 max_widths: List[int] = []

723 append_min = min_widths.append

724 append_max = max_widths.append

725 get_render_width = Measurement.get

726 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 measurement

740

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.box

757 else None

758 )

759 _box = _box.get_plain_headed_box() if _box and not self.show_header else _box

760

761 new_line = Segment.line()

762

763 columns = self.columns

764 show_header = self.show_header

765 show_footer = self.show_footer

766 show_edge = self.show_edge

767 show_lines = self.show_lines

768 leading = self.leading

769

770 _Segment = Segment

771 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_line

792 else:

793 box_segments = []

794

795 get_row_style = self.get_row_style

796 get_style = console.get_style

797

798 for index, (first, last, row_cell) in enumerate(loop_first_last(row_cells)):

799 header_row = first and show_header

800 footer_row = last and show_footer

801 row = (

802 self.rows[index - show_header]

803 if (not header_row and not footer_row)

804 else None

805 )

806 max_height = 1

807 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_style

867 )

868 yield new_line

869 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 background

872 divider = (

873 _divider

874 if _divider.text.strip()

875 else _Segment(

876 _divider.text, row_style.background_style + _divider.style

877 )

878 )

879 for line_no in range(max_height):

880 if show_edge:

881 yield left

882 for last_cell, rendered_cell in loop_last(cells):

883 yield from rendered_cell[line_no]

884 if not last_cell:

885 yield divider

886 if show_edge:

887 yield right

888 yield new_line

889 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_line

894 if _box and first and show_header:

895 yield _Segment(

896 _box.get_row(widths, "head", edge=show_edge), border_style

897 )

898 yield new_line

899 end_section = row and row.end_section

900 if _box and (show_lines or leading or end_section):

901 if (

902 not last

903 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_style

914 )

915 yield new_line

916

917 if _box and show_edge:

918 yield _Segment(_box.get_bottom(widths), border_style)

919 yield new_line

920

921

922 if __name__ == "__main__": # pragma: no cover

923 from rich.console import Console

924 from rich.highlighter import ReprHighlighter

925 from rich.table import Table as Table

926

927 from ._timer import timer

928

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=True

938 )

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 = True

972 header("expand=True")

973 console.print(table)

974

975 table.width = 50

976 header("width=50")

977

978 console.print(table, justify="center")

979

980 table.width = None

981 table.expand = False

982 table.row_styles = ["dim", "none"]

983 header("row_styles=['dim', 'none']")

984

985 console.print(table, justify="center")

986

987 table.width = None

988 table.expand = False

989 table.row_styles = ["dim", "none"]

990 table.leading = 1

991 header("leading=1, row_styles=['dim', 'none']")

992 console.print(table, justify="center")

993

994 table.width = None

995 table.expand = False

996 table.row_styles = ["dim", "none"]

997 table.show_lines = True

998 table.leading = 0

999 header("show_lines=True, row_styles=['dim', 'none']")

1000 console.print(table, justify="center")