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

1 import sys

2 from typing import TYPE_CHECKING, Iterable, List

3

4 if sys.version_info >= (3, 8):

5 from typing import Literal

6 else:

7 from typing_extensions import Literal # pragma: no cover

8

9

10 from ._loop import loop_last

11

12 if TYPE_CHECKING:

13 from rich.console import ConsoleOptions

14

15

16 class Box:

17 """Defines characters to render boxes.

18

19 ┌─┬┐ top

20 │ ││ head

21 ├─┼┤ head_row

22 │ ││ mid

23 ├─┼┤ row

24 ├─┼┤ foot_row

25 │ ││ foot

26 └─┴┘ bottom

27

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 """

32

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)

48

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 )

66

67 def __repr__(self) -> str:

68 return "Box(...)"

69

70 def __str__(self) -> str:

71 return self._box

72

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.

75

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.

80

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

90

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.

94

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)

100

101 def get_top(self, widths: Iterable[int]) -> str:

102 """Get the top of a simple box.

103

104 Args:

105 widths (List[int]): Widths of columns.

106

107 Returns:

108 str: A string of box characters.

109 """

110

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)

120

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.

128

129 Args:

130 width (List[int]): Widths of columns.

131

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'")

157

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)

169

170 def get_bottom(self, widths: Iterable[int]) -> str:

171 """Get the bottom of a simple box.

172

173 Args:

174 widths (List[int]): Widths of columns.

175

176 Returns:

177 str: A string of box characters.

178 """

179

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)

189

190

191# fmt: off

192 ASCII: Box = Box(

193 "+--+\n"

194 "| ||\n"

195 "|-+|\n"

196 "| ||\n"

197 "|-+|\n"

198 "|-+|\n"

199 "| ||\n"

200 "+--+\n",

201 ascii=True,

202)

203

204 ASCII2: Box = Box(

205 "+-++\n"

206 "| ||\n"

207 "+-++\n"

208 "| ||\n"

209 "+-++\n"

210 "+-++\n"

211 "| ||\n"

212 "+-++\n",

213 ascii=True,

214)

215

216 ASCII_DOUBLE_HEAD: Box = Box(

217 "+-++\n"

218 "| ||\n"

219 "+=++\n"

220 "| ||\n"

221 "+-++\n"

222 "+-++\n"

223 "| ||\n"

224 "+-++\n",

225 ascii=True,

226)

227

228 SQUARE: Box = Box(

229 "┌─┬┐\n"

230 "│ ││\n"

231 "├─┼┤\n"

232 "│ ││\n"

233 "├─┼┤\n"

234 "├─┼┤\n"

235 "│ ││\n"

236 "└─┴┘\n"

237)

238

239 SQUARE_DOUBLE_HEAD: Box = Box(

240 "┌─┬┐\n"

241 "│ ││\n"

242 "╞═╪╡\n"

243 "│ ││\n"

244 "├─┼┤\n"

245 "├─┼┤\n"

246 "│ ││\n"

247 "└─┴┘\n"

248)

249

250 MINIMAL: Box = Box(

251 " ╷ \n"

252 " │ \n"

253 "╶─┼╴\n"

254 " │ \n"

255 "╶─┼╴\n"

256 "╶─┼╴\n"

257 " │ \n"

258 " ╵ \n"

259)

260

261

262 MINIMAL_HEAVY_HEAD: Box = Box(

263 " ╷ \n"

264 " │ \n"

265 "╺━┿╸\n"

266 " │ \n"

267 "╶─┼╴\n"

268 "╶─┼╴\n"

269 " │ \n"

270 " ╵ \n"

271)

272

273 MINIMAL_DOUBLE_HEAD: Box = Box(

274 " ╷ \n"

275 " │ \n"

276 " ═╪ \n"

277 " │ \n"

278 " ─┼ \n"

279 " ─┼ \n"

280 " │ \n"

281 " ╵ \n"

282)

283

284

285 SIMPLE: Box = Box(

286 " \n"

287 " \n"

288 " ── \n"

289 " \n"

290 " \n"

291 " ── \n"

292 " \n"

293 " \n"

294)

295

296 SIMPLE_HEAD: Box = Box(

297 " \n"

298 " \n"

299 " ── \n"

300 " \n"

301 " \n"

302 " \n"

303 " \n"

304 " \n"

305)

306

307

308 SIMPLE_HEAVY: Box = Box(

309 " \n"

310 " \n"

311 " ━━ \n"

312 " \n"

313 " \n"

314 " ━━ \n"

315 " \n"

316 " \n"

317)

318

319

320 HORIZONTALS: Box = Box(

321 " ── \n"

322 " \n"

323 " ── \n"

324 " \n"

325 " ── \n"

326 " ── \n"

327 " \n"

328 " ── \n"

329)

330

331 ROUNDED: Box = Box(

332 "╭─┬╮\n"

333 "│ ││\n"

334 "├─┼┤\n"

335 "│ ││\n"

336 "├─┼┤\n"

337 "├─┼┤\n"

338 "│ ││\n"

339 "╰─┴╯\n"

340)

341

342 HEAVY: Box = Box(

343 "┏━┳┓\n"

344 "┃ ┃┃\n"

345 "┣━╋┫\n"

346 "┃ ┃┃\n"

347 "┣━╋┫\n"

348 "┣━╋┫\n"

349 "┃ ┃┃\n"

350 "┗━┻┛\n"

351)

352

353 HEAVY_EDGE: Box = Box(

354 "┏━┯┓\n"

355 "┃ │┃\n"

356 "┠─┼┨\n"

357 "┃ │┃\n"

358 "┠─┼┨\n"

359 "┠─┼┨\n"

360 "┃ │┃\n"

361 "┗━┷┛\n"

362)

363

364 HEAVY_HEAD: Box = Box(

365 "┏━┳┓\n"

366 "┃ ┃┃\n"

367 "┡━╇┩\n"

368 "│ ││\n"

369 "├─┼┤\n"

370 "├─┼┤\n"

371 "│ ││\n"

372 "└─┴┘\n"

373)

374

375 DOUBLE: Box = Box(

376 "╔═╦╗\n"

377 "║ ║║\n"

378 "╠═╬╣\n"

379 "║ ║║\n"

380 "╠═╬╣\n"

381 "╠═╬╣\n"

382 "║ ║║\n"

383 "╚═╩╝\n"

384)

385

386 DOUBLE_EDGE: Box = Box(

387 "╔═╤╗\n"

388 "║ │║\n"

389 "╟─┼╢\n"

390 "║ │║\n"

391 "╟─┼╢\n"

392 "╟─┼╢\n"

393 "║ │║\n"

394 "╚═╧╝\n"

395)

396

397 MARKDOWN: Box = Box(

398 " \n"

399 "| ||\n"

400 "|-||\n"

401 "| ||\n"

402 "|-||\n"

403 "|-||\n"

404 "| ||\n"

405 " \n",

406 ascii=True,

407)

408# fmt: on

409

410# Map Boxes that don't render with raster fonts on to equivalent that do

411 LEGACY_WINDOWS_SUBSTITUTIONS = {

412 ROUNDED: SQUARE,

413 MINIMAL_HEAVY_HEAD: MINIMAL,

414 SIMPLE_HEAVY: SIMPLE,

415 HEAVY: SQUARE,

416 HEAVY_EDGE: SQUARE,

417 HEAVY_HEAD: SQUARE,

418}

419

420# Map headed boxes to their headerless equivalents

421 PLAIN_HEADED_SUBSTITUTIONS = {

422 HEAVY_HEAD: SQUARE,

423 SQUARE_DOUBLE_HEAD: SQUARE,

424 MINIMAL_DOUBLE_HEAD: MINIMAL,

425 MINIMAL_HEAVY_HEAD: MINIMAL,

426 ASCII_DOUBLE_HEAD: ASCII2,

427}

428

429

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

431 from rich.columns import Columns

432 from rich.panel import Panel

433

434 from . import box as box

435 from .console import Console

436 from .table import Table

437 from .text import Text

438

439 console = Console(record=True)

440

441 BOXES = [

442 "ASCII",

443 "ASCII2",

444 "ASCII_DOUBLE_HEAD",

445 "SQUARE",

446 "SQUARE_DOUBLE_HEAD",

447 "MINIMAL",

448 "MINIMAL_HEAVY_HEAD",

449 "MINIMAL_DOUBLE_HEAD",

450 "SIMPLE",

451 "SIMPLE_HEAD",

452 "SIMPLE_HEAVY",

453 "HORIZONTALS",

454 "ROUNDED",

455 "HEAVY",

456 "HEAVY_EDGE",

457 "HEAVY_HEAD",

458 "DOUBLE",

459 "DOUBLE_EDGE",

460 "MARKDOWN",

461 ]

462

463 console.print(Panel("[bold green]Box Constants", style="green"), justify="center")

464 console.print()

465

466 columns = Columns(expand=True, padding=2)

467 for box_name in sorted(BOXES):

468 table = Table(

469 show_footer=True, style="dim", border_style="not dim", expand=True

470 )

471 table.add_column("Header 1", "Footer 1")

472 table.add_column("Header 2", "Footer 2")

473 table.add_row("Cell", "Cell")

474 table.add_row("Cell", "Cell")

475 table.box = getattr(box, box_name)

476 table.title = Text(f"box.{box_name}", style="magenta")

477 columns.add_renderable(table)

478 console.print(columns)

479

480 # console.save_svg("box.svg")