Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.8/site-packages/rich/box.py: 44%
107 statements
« prev ^ index » next coverage.py v7.0.1, created at 2022-12-25 06:11 +0000
« prev ^ index » next coverage.py v7.0.1, created at 2022-12-25 06:11 +0000
1import sys
2from typing import TYPE_CHECKING, Iterable, List
4if sys.version_info >= (3, 8):
5 from typing import Literal
6else:
7 from typing_extensions import Literal # pragma: no cover
10from ._loop import loop_last
12if TYPE_CHECKING:
13 from rich.console import ConsoleOptions
16class Box:
17 """Defines characters to render boxes.
19 ┌─┬┐ top
20 │ ││ head
21 ├─┼┤ head_row
22 │ ││ mid
23 ├─┼┤ row
24 ├─┼┤ foot_row
25 │ ││ foot
26 └─┴┘ bottom
28 Args:
29 box (str): Characters making up box.
30 ascii (bool, optional): True if this box uses ascii characters only. Default is False.
31 """
33 def __init__(self, box: str, *, ascii: bool = False) -> None:
34 self._box = box
35 self.ascii = ascii
36 line1, line2, line3, line4, line5, line6, line7, line8 = box.splitlines()
37 # top
38 self.top_left, self.top, self.top_divider, self.top_right = iter(line1)
39 # head
40 self.head_left, _, self.head_vertical, self.head_right = iter(line2)
41 # head_row
42 (
43 self.head_row_left,
44 self.head_row_horizontal,
45 self.head_row_cross,
46 self.head_row_right,
47 ) = iter(line3)
49 # mid
50 self.mid_left, _, self.mid_vertical, self.mid_right = iter(line4)
51 # row
52 self.row_left, self.row_horizontal, self.row_cross, self.row_right = iter(line5)
53 # foot_row
54 (
55 self.foot_row_left,
56 self.foot_row_horizontal,
57 self.foot_row_cross,
58 self.foot_row_right,
59 ) = iter(line6)
60 # foot
61 self.foot_left, _, self.foot_vertical, self.foot_right = iter(line7)
62 # bottom
63 self.bottom_left, self.bottom, self.bottom_divider, self.bottom_right = iter(
64 line8
65 )
67 def __repr__(self) -> str:
68 return "Box(...)"
70 def __str__(self) -> str:
71 return self._box
73 def substitute(self, options: "ConsoleOptions", safe: bool = True) -> "Box":
74 """Substitute this box for another if it won't render due to platform issues.
76 Args:
77 options (ConsoleOptions): Console options used in rendering.
78 safe (bool, optional): Substitute this for another Box if there are known problems
79 displaying on the platform (currently only relevant on Windows). Default is True.
81 Returns:
82 Box: A different Box or the same Box.
83 """
84 box = self
85 if options.legacy_windows and safe:
86 box = LEGACY_WINDOWS_SUBSTITUTIONS.get(box, box)
87 if options.ascii_only and not box.ascii:
88 box = ASCII
89 return box
91 def get_plain_headed_box(self) -> "Box":
92 """If this box uses special characters for the borders of the header, then
93 return the equivalent box that does not.
95 Returns:
96 Box: The most similar Box that doesn't use header-specific box characters.
97 If the current Box already satisfies this criterion, then it's returned.
98 """
99 return PLAIN_HEADED_SUBSTITUTIONS.get(self, self)
101 def get_top(self, widths: Iterable[int]) -> str:
102 """Get the top of a simple box.
104 Args:
105 widths (List[int]): Widths of columns.
107 Returns:
108 str: A string of box characters.
109 """
111 parts: List[str] = []
112 append = parts.append
113 append(self.top_left)
114 for last, width in loop_last(widths):
115 append(self.top * width)
116 if not last:
117 append(self.top_divider)
118 append(self.top_right)
119 return "".join(parts)
121 def get_row(
122 self,
123 widths: Iterable[int],
124 level: Literal["head", "row", "foot", "mid"] = "row",
125 edge: bool = True,
126 ) -> str:
127 """Get the top of a simple box.
129 Args:
130 width (List[int]): Widths of columns.
132 Returns:
133 str: A string of box characters.
134 """
135 if level == "head":
136 left = self.head_row_left
137 horizontal = self.head_row_horizontal
138 cross = self.head_row_cross
139 right = self.head_row_right
140 elif level == "row":
141 left = self.row_left
142 horizontal = self.row_horizontal
143 cross = self.row_cross
144 right = self.row_right
145 elif level == "mid":
146 left = self.mid_left
147 horizontal = " "
148 cross = self.mid_vertical
149 right = self.mid_right
150 elif level == "foot":
151 left = self.foot_row_left
152 horizontal = self.foot_row_horizontal
153 cross = self.foot_row_cross
154 right = self.foot_row_right
155 else:
156 raise ValueError("level must be 'head', 'row' or 'foot'")
158 parts: List[str] = []
159 append = parts.append
160 if edge:
161 append(left)
162 for last, width in loop_last(widths):
163 append(horizontal * width)
164 if not last:
165 append(cross)
166 if edge:
167 append(right)
168 return "".join(parts)
170 def get_bottom(self, widths: Iterable[int]) -> str:
171 """Get the bottom of a simple box.
173 Args:
174 widths (List[int]): Widths of columns.
176 Returns:
177 str: A string of box characters.
178 """
180 parts: List[str] = []
181 append = parts.append
182 append(self.bottom_left)
183 for last, width in loop_last(widths):
184 append(self.bottom * width)
185 if not last:
186 append(self.bottom_divider)
187 append(self.bottom_right)
188 return "".join(parts)
191ASCII: Box = Box(
192 """\
193+--+
194| ||
195|-+|
196| ||
197|-+|
198|-+|
199| ||
200+--+
201""",
202 ascii=True,
203)
205ASCII2: Box = Box(
206 """\
207+-++
208| ||
209+-++
210| ||
211+-++
212+-++
213| ||
214+-++
215""",
216 ascii=True,
217)
219ASCII_DOUBLE_HEAD: Box = Box(
220 """\
221+-++
222| ||
223+=++
224| ||
225+-++
226+-++
227| ||
228+-++
229""",
230 ascii=True,
231)
233SQUARE: Box = Box(
234 """\
235┌─┬┐
236│ ││
237├─┼┤
238│ ││
239├─┼┤
240├─┼┤
241│ ││
242└─┴┘
243"""
244)
246SQUARE_DOUBLE_HEAD: Box = Box(
247 """\
248┌─┬┐
249│ ││
250╞═╪╡
251│ ││
252├─┼┤
253├─┼┤
254│ ││
255└─┴┘
256"""
257)
259MINIMAL: Box = Box(
260 """\
261 ╷
262 │
263╶─┼╴
264 │
265╶─┼╴
266╶─┼╴
267 │
268 ╵
269"""
270)
273MINIMAL_HEAVY_HEAD: Box = Box(
274 """\
275 ╷
276 │
277╺━┿╸
278 │
279╶─┼╴
280╶─┼╴
281 │
282 ╵
283"""
284)
286MINIMAL_DOUBLE_HEAD: Box = Box(
287 """\
288 ╷
289 │
290 ═╪
291 │
292 ─┼
293 ─┼
294 │
295 ╵
296"""
297)
300SIMPLE: Box = Box(
301 """\
304 ──
307 ──
310"""
311)
313SIMPLE_HEAD: Box = Box(
314 """\
317 ──
323"""
324)
327SIMPLE_HEAVY: Box = Box(
328 """\
331 ━━
334 ━━
337"""
338)
341HORIZONTALS: Box = Box(
342 """\
343 ──
345 ──
347 ──
348 ──
350 ──
351"""
352)
354ROUNDED: Box = Box(
355 """\
356╭─┬╮
357│ ││
358├─┼┤
359│ ││
360├─┼┤
361├─┼┤
362│ ││
363╰─┴╯
364"""
365)
367HEAVY: Box = Box(
368 """\
369┏━┳┓
370┃ ┃┃
371┣━╋┫
372┃ ┃┃
373┣━╋┫
374┣━╋┫
375┃ ┃┃
376┗━┻┛
377"""
378)
380HEAVY_EDGE: Box = Box(
381 """\
382┏━┯┓
383┃ │┃
384┠─┼┨
385┃ │┃
386┠─┼┨
387┠─┼┨
388┃ │┃
389┗━┷┛
390"""
391)
393HEAVY_HEAD: Box = Box(
394 """\
395┏━┳┓
396┃ ┃┃
397┡━╇┩
398│ ││
399├─┼┤
400├─┼┤
401│ ││
402└─┴┘
403"""
404)
406DOUBLE: Box = Box(
407 """\
408╔═╦╗
409║ ║║
410╠═╬╣
411║ ║║
412╠═╬╣
413╠═╬╣
414║ ║║
415╚═╩╝
416"""
417)
419DOUBLE_EDGE: Box = Box(
420 """\
421╔═╤╗
422║ │║
423╟─┼╢
424║ │║
425╟─┼╢
426╟─┼╢
427║ │║
428╚═╧╝
429"""
430)
432MARKDOWN: Box = Box(
433 """\
435| ||
436|-||
437| ||
438|-||
439|-||
440| ||
442""",
443 ascii=True,
444)
446# Map Boxes that don't render with raster fonts on to equivalent that do
447LEGACY_WINDOWS_SUBSTITUTIONS = {
448 ROUNDED: SQUARE,
449 MINIMAL_HEAVY_HEAD: MINIMAL,
450 SIMPLE_HEAVY: SIMPLE,
451 HEAVY: SQUARE,
452 HEAVY_EDGE: SQUARE,
453 HEAVY_HEAD: SQUARE,
454}
456# Map headed boxes to their headerless equivalents
457PLAIN_HEADED_SUBSTITUTIONS = {
458 HEAVY_HEAD: SQUARE,
459 SQUARE_DOUBLE_HEAD: SQUARE,
460 MINIMAL_DOUBLE_HEAD: MINIMAL,
461 MINIMAL_HEAVY_HEAD: MINIMAL,
462 ASCII_DOUBLE_HEAD: ASCII2,
463}
466if __name__ == "__main__": # pragma: no cover
468 from rich.columns import Columns
469 from rich.panel import Panel
471 from . import box as box
472 from .console import Console
473 from .table import Table
474 from .text import Text
476 console = Console(record=True)
478 BOXES = [
479 "ASCII",
480 "ASCII2",
481 "ASCII_DOUBLE_HEAD",
482 "SQUARE",
483 "SQUARE_DOUBLE_HEAD",
484 "MINIMAL",
485 "MINIMAL_HEAVY_HEAD",
486 "MINIMAL_DOUBLE_HEAD",
487 "SIMPLE",
488 "SIMPLE_HEAD",
489 "SIMPLE_HEAVY",
490 "HORIZONTALS",
491 "ROUNDED",
492 "HEAVY",
493 "HEAVY_EDGE",
494 "HEAVY_HEAD",
495 "DOUBLE",
496 "DOUBLE_EDGE",
497 "MARKDOWN",
498 ]
500 console.print(Panel("[bold green]Box Constants", style="green"), justify="center")
501 console.print()
503 columns = Columns(expand=True, padding=2)
504 for box_name in sorted(BOXES):
505 table = Table(
506 show_footer=True, style="dim", border_style="not dim", expand=True
507 )
508 table.add_column("Header 1", "Footer 1")
509 table.add_column("Header 2", "Footer 2")
510 table.add_row("Cell", "Cell")
511 table.add_row("Cell", "Cell")
512 table.box = getattr(box, box_name)
513 table.title = Text(f"box.{box_name}", style="magenta")
514 columns.add_renderable(table)
515 console.print(columns)
517 # console.save_svg("box.svg")