Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.8/site-packages/pip/_vendor/rich/columns.py: 16%
98 statements
« prev ^ index » next coverage.py v7.2.7, created at 2023-06-07 06:48 +0000
« prev ^ index » next coverage.py v7.2.7, created at 2023-06-07 06:48 +0000
1from collections import defaultdict
2from itertools import chain
3from operator import itemgetter
4from typing import Dict, Iterable, List, Optional, Tuple
6from .align import Align, AlignMethod
7from .console import Console, ConsoleOptions, RenderableType, RenderResult
8from .constrain import Constrain
9from .measure import Measurement
10from .padding import Padding, PaddingDimensions
11from .table import Table
12from .text import TextType
13from .jupyter import JupyterMixin
16class Columns(JupyterMixin):
17 """Display renderables in neat columns.
19 Args:
20 renderables (Iterable[RenderableType]): Any number of Rich renderables (including str).
21 width (int, optional): The desired width of the columns, or None to auto detect. Defaults to None.
22 padding (PaddingDimensions, optional): Optional padding around cells. Defaults to (0, 1).
23 expand (bool, optional): Expand columns to full width. Defaults to False.
24 equal (bool, optional): Arrange in to equal sized columns. Defaults to False.
25 column_first (bool, optional): Align items from top to bottom (rather than left to right). Defaults to False.
26 right_to_left (bool, optional): Start column from right hand side. Defaults to False.
27 align (str, optional): Align value ("left", "right", or "center") or None for default. Defaults to None.
28 title (TextType, optional): Optional title for Columns.
29 """
31 def __init__(
32 self,
33 renderables: Optional[Iterable[RenderableType]] = None,
34 padding: PaddingDimensions = (0, 1),
35 *,
36 width: Optional[int] = None,
37 expand: bool = False,
38 equal: bool = False,
39 column_first: bool = False,
40 right_to_left: bool = False,
41 align: Optional[AlignMethod] = None,
42 title: Optional[TextType] = None,
43 ) -> None:
44 self.renderables = list(renderables or [])
45 self.width = width
46 self.padding = padding
47 self.expand = expand
48 self.equal = equal
49 self.column_first = column_first
50 self.right_to_left = right_to_left
51 self.align: Optional[AlignMethod] = align
52 self.title = title
54 def add_renderable(self, renderable: RenderableType) -> None:
55 """Add a renderable to the columns.
57 Args:
58 renderable (RenderableType): Any renderable object.
59 """
60 self.renderables.append(renderable)
62 def __rich_console__(
63 self, console: Console, options: ConsoleOptions
64 ) -> RenderResult:
65 render_str = console.render_str
66 renderables = [
67 render_str(renderable) if isinstance(renderable, str) else renderable
68 for renderable in self.renderables
69 ]
70 if not renderables:
71 return
72 _top, right, _bottom, left = Padding.unpack(self.padding)
73 width_padding = max(left, right)
74 max_width = options.max_width
75 widths: Dict[int, int] = defaultdict(int)
76 column_count = len(renderables)
78 get_measurement = Measurement.get
79 renderable_widths = [
80 get_measurement(console, options, renderable).maximum
81 for renderable in renderables
82 ]
83 if self.equal:
84 renderable_widths = [max(renderable_widths)] * len(renderable_widths)
86 def iter_renderables(
87 column_count: int,
88 ) -> Iterable[Tuple[int, Optional[RenderableType]]]:
89 item_count = len(renderables)
90 if self.column_first:
91 width_renderables = list(zip(renderable_widths, renderables))
93 column_lengths: List[int] = [item_count // column_count] * column_count
94 for col_no in range(item_count % column_count):
95 column_lengths[col_no] += 1
97 row_count = (item_count + column_count - 1) // column_count
98 cells = [[-1] * column_count for _ in range(row_count)]
99 row = col = 0
100 for index in range(item_count):
101 cells[row][col] = index
102 column_lengths[col] -= 1
103 if column_lengths[col]:
104 row += 1
105 else:
106 col += 1
107 row = 0
108 for index in chain.from_iterable(cells):
109 if index == -1:
110 break
111 yield width_renderables[index]
112 else:
113 yield from zip(renderable_widths, renderables)
114 # Pad odd elements with spaces
115 if item_count % column_count:
116 for _ in range(column_count - (item_count % column_count)):
117 yield 0, None
119 table = Table.grid(padding=self.padding, collapse_padding=True, pad_edge=False)
120 table.expand = self.expand
121 table.title = self.title
123 if self.width is not None:
124 column_count = (max_width) // (self.width + width_padding)
125 for _ in range(column_count):
126 table.add_column(width=self.width)
127 else:
128 while column_count > 1:
129 widths.clear()
130 column_no = 0
131 for renderable_width, _ in iter_renderables(column_count):
132 widths[column_no] = max(widths[column_no], renderable_width)
133 total_width = sum(widths.values()) + width_padding * (
134 len(widths) - 1
135 )
136 if total_width > max_width:
137 column_count = len(widths) - 1
138 break
139 else:
140 column_no = (column_no + 1) % column_count
141 else:
142 break
144 get_renderable = itemgetter(1)
145 _renderables = [
146 get_renderable(_renderable)
147 for _renderable in iter_renderables(column_count)
148 ]
149 if self.equal:
150 _renderables = [
151 None
152 if renderable is None
153 else Constrain(renderable, renderable_widths[0])
154 for renderable in _renderables
155 ]
156 if self.align:
157 align = self.align
158 _Align = Align
159 _renderables = [
160 None if renderable is None else _Align(renderable, align)
161 for renderable in _renderables
162 ]
164 right_to_left = self.right_to_left
165 add_row = table.add_row
166 for start in range(0, len(_renderables), column_count):
167 row = _renderables[start : start + column_count]
168 if right_to_left:
169 row = row[::-1]
170 add_row(*row)
171 yield table
174if __name__ == "__main__": # pragma: no cover
175 import os
177 console = Console()
179 files = [f"{i} {s}" for i, s in enumerate(sorted(os.listdir()))]
180 columns = Columns(files, padding=(0, 1), expand=False, equal=False)
181 console.print(columns)
182 console.rule()
183 columns.column_first = True
184 console.print(columns)
185 columns.right_to_left = True
186 console.rule()
187 console.print(columns)