Coverage for apio/commands/apio_info.py: 90%

138 statements  

« prev     ^ index     » next       coverage.py v7.11.0, created at 2025-11-06 10:20 +0000

1# -*- coding: utf-8 -*- 

2# -- This file is part of the Apio project 

3# -- (C) 2016-2024 FPGAwars 

4# -- Authors 

5# -- * Jesús Arroyo (2016-2019) 

6# -- * Juan Gonzalez (obijuan) (2019-2024) 

7# -- License GPLv2 

8"""Implementation of 'apio info' command""" 

9 

10from typing import List 

11import click 

12from rich.table import Table 

13from rich.text import Text 

14from rich import box 

15from rich.color import ANSI_COLOR_NAMES 

16from apio.common.apio_styles import BORDER, EMPH1, EMPH3, INFO 

17from apio.utils import util 

18from apio.apio_context import ( 

19 ApioContext, 

20 PackagesPolicy, 

21 ProjectPolicy, 

22 RemoteConfigPolicy, 

23) 

24from apio.utils.cmd_util import ApioGroup, ApioSubgroup, ApioCommand 

25from apio.common.apio_themes import THEMES_TABLE, THEME_LIGHT 

26from apio.profile import get_datetime_stamp, days_between_datetime_stamps 

27from apio.common.apio_console import ( 

28 PADDING, 

29 cout, 

30 cstyle, 

31 ctable, 

32 get_theme, 

33 configure, 

34) 

35 

36 

37# ------ apio info system 

38 

39 

40def construct_remote_config_status_str(apio_ctx: ApioContext) -> str: 

41 """Query the apio profile and construct a short string indicating the 

42 status of the cached remote config.""" 

43 config = apio_ctx.profile.remote_config 

44 metadata = config.get("metadata", {}) 

45 timestamp_now = get_datetime_stamp() 

46 config_status = [] 

47 # -- Handle the case of a having a cached config. 

48 if config: 48 ↛ 64line 48 didn't jump to line 64 because the condition on line 48 was always true

49 config_days = days_between_datetime_stamps( 

50 metadata.get("loaded-at", ""), timestamp_now, None 

51 ) 

52 # -- Determine cache age in days, if possible. 

53 if config_days is not None: 53 ↛ 58line 53 didn't jump to line 58 because the condition on line 53 was always true

54 config_status.append( 

55 f"Cached {util.plurality(config_days, 'day')} ago" 

56 ) 

57 else: 

58 config_status.append("Cached") 

59 # -- Indicate if there is a sign of a failed refresh attempt. 

60 if "refresh-failure-on" in metadata: 60 ↛ 61line 60 didn't jump to line 61 because the condition on line 60 was never true

61 config_status.append("refresh failed.") 

62 # -- Handle the case of not having a cached config. 

63 else: 

64 config_status.append("Not cached") 

65 

66 # -- Concatenate and return. 

67 config_status = ", ".join(config_status) 

68 return config_status 

69 

70 

71# -- Text in the rich-text format of the python rich library. 

72APIO_INFO_SYSTEM_HELP = """ 

73The command 'apio info system' provides general information about your \ 

74system and Apio installation, which is useful for diagnosing Apio \ 

75installation issues. 

76 

77Examples:[code] 

78 apio info system # System info.[/code] 

79 

80[b][Advanced][/b] The default location of the Apio home directory, \ 

81where apio saves preferences and packages, is in the '.apio' directory \ 

82under the user home directory but can be changed using the system \ 

83environment variable 'APIO_HOME'. 

84""" 

85 

86 

87@click.command( 

88 name="system", 

89 cls=ApioCommand, 

90 short_help="Show system information.", 

91 help=APIO_INFO_SYSTEM_HELP, 

92) 

93def _system_cli(): 

94 """Implements the 'apio info system' command.""" 

95 

96 # -- Create the apio context. We use 'cached_ok' to cause the config 

97 # -- to be loaded so we can report it. 

98 apio_ctx = ApioContext( 

99 project_policy=ProjectPolicy.NO_PROJECT, 

100 remote_config_policy=RemoteConfigPolicy.CACHED_OK, 

101 packages_policy=PackagesPolicy.ENSURE_PACKAGES, 

102 ) 

103 

104 # -- Define the table. 

105 table = Table( 

106 show_header=True, 

107 show_lines=True, 

108 padding=PADDING, 

109 box=box.SQUARE, 

110 border_style=BORDER, 

111 title="Apio System Information", 

112 title_justify="left", 

113 ) 

114 

115 table.add_column("ITEM", no_wrap=True, min_width=20) 

116 table.add_column("VALUE", no_wrap=True, style=EMPH1) 

117 

118 # -- Add rows 

119 table.add_row("Apio version", util.get_apio_version()) 

120 table.add_row("Python version", util.get_python_version()) 

121 table.add_row("Platform id", apio_ctx.platform_id) 

122 table.add_row( 

123 "Apio Python package", str(util.get_path_in_apio_package("")) 

124 ) 

125 table.add_row("Apio home dir", str(apio_ctx.apio_home_dir)) 

126 table.add_row("Apio packages dir", str(apio_ctx.apio_packages_dir)) 

127 table.add_row("Remote config URL", apio_ctx.profile.remote_config_url) 

128 table.add_row( 

129 "Remote config status", construct_remote_config_status_str(apio_ctx) 

130 ) 

131 table.add_row( 

132 "Veriable formatter", 

133 str(apio_ctx.apio_packages_dir / "verible/bin/verible-verilog-format"), 

134 ) 

135 table.add_row( 

136 "Veriable language server", 

137 str(apio_ctx.apio_packages_dir / "verible/bin/verible-verilog-ls"), 

138 ) 

139 

140 # -- Render the table. 

141 cout() 

142 ctable(table) 

143 cout( 

144 "To force a remote config refresh, run 'apio packages update'.", 

145 style=INFO, 

146 ) 

147 

148 

149# ------ apio info platforms 

150 

151# -- Text in the rich-text format of the python rich library. 

152APIO_INFO_PLATFORMS_HELP = """ 

153The command 'apio info platforms' lists the platform IDs supported by Apio, \ 

154with the effective platform ID of your system highlighted. 

155 

156Examples:[code] 

157 apio info platforms # List supported platform ids.[/code] 

158 

159The automatic platform ID detection of Apio can be overridden by \ 

160defining a different platform ID using the APIO_PLATFORM environment variable. 

161""" 

162 

163 

164@click.command( 

165 name="platforms", 

166 cls=ApioCommand, 

167 short_help="Supported platforms.", 

168 help=APIO_INFO_PLATFORMS_HELP, 

169) 

170def _platforms_cli(): 

171 """Implements the 'apio info platforms' command.""" 

172 

173 # Create the apio context. 

174 apio_ctx = ApioContext( 

175 project_policy=ProjectPolicy.NO_PROJECT, 

176 remote_config_policy=RemoteConfigPolicy.CACHED_OK, 

177 packages_policy=PackagesPolicy.ENSURE_PACKAGES, 

178 ) 

179 

180 # -- Define the table. 

181 table = Table( 

182 show_header=True, 

183 show_lines=True, 

184 padding=PADDING, 

185 box=box.SQUARE, 

186 border_style=BORDER, 

187 title="Apio Supported Platforms", 

188 title_justify="left", 

189 ) 

190 

191 table.add_column(" PLATFORM ID", no_wrap=True) 

192 table.add_column("TYPE", no_wrap=True) 

193 table.add_column("VARIANT", no_wrap=True) 

194 

195 # -- Add rows. 

196 for platform_id, platform_info in apio_ctx.platforms.items(): 

197 platform_type = platform_info.get("type") 

198 platform_variant = platform_info.get("variant") 

199 

200 # -- Mark the current platform. 

201 if platform_id == apio_ctx.platform_id: 

202 style = EMPH3 

203 marker = "* " 

204 else: 

205 style = None 

206 marker = " " 

207 

208 table.add_row( 

209 f"{marker}{platform_id}", 

210 platform_type, 

211 platform_variant, 

212 style=style, 

213 ) 

214 

215 # -- Render the table. 

216 cout() 

217 ctable(table) 

218 

219 

220# ------ apio info colors 

221 

222# -- Text in the rich-text format of the python rich library. 

223APIO_INFO_COLORS_HELP = """ 

224The command 'apio info colors' shows how ansi colors are rendered on \ 

225the platform, and is typically used to diagnose color related issues. 

226 

227The command shows the themes colors even if the current theme is 'no-colors'. 

228 

229Examples:[code] 

230 apio info colors # Rich library output (default) 

231 apio inf col -p # Using shortcuts.[/code] 

232""" 

233 

234 

235@click.command( 

236 name="colors", 

237 cls=ApioCommand, 

238 short_help="Colors table.", 

239 help=APIO_INFO_COLORS_HELP, 

240) 

241def _colors_cli(): 

242 """Implements the 'apio info colors' command.""" 

243 

244 # -- This initializes the output console. 

245 ApioContext( 

246 project_policy=ProjectPolicy.NO_PROJECT, 

247 remote_config_policy=RemoteConfigPolicy.CACHED_OK, 

248 packages_policy=PackagesPolicy.ENSURE_PACKAGES, 

249 ) 

250 

251 # -- Print title. 

252 cout("", "ANSI Colors", "") 

253 

254 # -- Create a reversed num->name map 

255 lookup = {} 

256 for name, num in ANSI_COLOR_NAMES.items(): 

257 assert 0 <= num <= 255 

258 lookup[num] = name 

259 

260 # -- Make sure the current theme supports colors, otherwise they will 

261 # -- suppressed 

262 if get_theme().colors_enabled: 262 ↛ 265line 262 didn't jump to line 265 because the condition on line 262 was always true

263 saved_theme_name = None 

264 else: 

265 saved_theme_name = get_theme().name 

266 configure(theme_name=THEME_LIGHT.name) 

267 

268 # -- Print the table. 

269 num_rows = 64 

270 num_cols = 4 

271 for row in range(num_rows): 

272 values = [] 

273 for col in range(num_cols): 

274 num = row + (col * num_rows) 

275 name = lookup.get(num, None) 

276 if name is None: 

277 # -- No color name. 

278 values.append(" " * 24) 

279 else: 

280 # -- Color name is available. 

281 # -- Note that the color names and styling is always done by 

282 # -- the rich library regardless of the choice of output. 

283 s = f"{num:3} {name:20}" 

284 values.append(cstyle(s, style=name)) 

285 

286 # -- Construct the line. 

287 line = " ".join(values) 

288 

289 # -- Output the line. 

290 cout(line) 

291 

292 cout() 

293 

294 # -- Restore the original theme. 

295 if saved_theme_name: 295 ↛ 296line 295 didn't jump to line 296 because the condition on line 295 was never true

296 configure(theme_name=saved_theme_name) 

297 

298 

299# ------ apio info themes 

300 

301# -- Text in the rich-text format of the python rich library. 

302APIO_INFO_THEMES_HELP = """ 

303The command 'apio info themes' shows the colors of the Apio themes. It can \ 

304be used to select the theme that works the best for you. Type \ 

305'apio preferences -h' for information on our to select a theme. 

306 

307The command shows colors even if the current theme is 'no-colors'. 

308 

309[code] 

310Examples: 

311 apio info themes # Show themes colors 

312 apio inf col -p # Using shortcuts.[/code] 

313""" 

314 

315 

316@click.command( 

317 name="themes", 

318 cls=ApioCommand, 

319 short_help="Show apio themes.", 

320 help=APIO_INFO_THEMES_HELP, 

321) 

322def _themes_cli(): 

323 """Implements the 'apio info colors' command.""" 

324 

325 # -- This initializes the output console. 

326 ApioContext( 

327 project_policy=ProjectPolicy.NO_PROJECT, 

328 remote_config_policy=RemoteConfigPolicy.CACHED_OK, 

329 packages_policy=PackagesPolicy.ENSURE_PACKAGES, 

330 ) 

331 

332 # -- Collect the list of apio list names. 

333 style_names = set() 

334 for theme_info in THEMES_TABLE.values(): 

335 style_names.update(list(theme_info.styles.keys())) 

336 style_names = sorted(list(style_names), key=str.lower) 

337 

338 # -- Define the table. 

339 table = Table( 

340 show_header=True, 

341 show_lines=True, 

342 padding=PADDING, 

343 box=box.SQUARE, 

344 border_style=BORDER, 

345 title="Apio Themes Style Colors", 

346 title_justify="left", 

347 ) 

348 

349 # -- Get selected theme 

350 selected_theme = get_theme() 

351 selected_theme_name = selected_theme.name 

352 

353 # -- Add the table columns, one per theme. 

354 for theme_name, theme in THEMES_TABLE.items(): 

355 assert theme_name == theme.name 

356 column_name = theme_name.upper() 

357 if theme_name == selected_theme_name: 

358 column_name = f"*{column_name}*" 

359 table.add_column(column_name, no_wrap=True, justify="center") 

360 

361 # -- Append the table rows 

362 for style_name in style_names: 

363 row_values: List[Text] = [] 

364 for theme_name, theme_info in THEMES_TABLE.items(): 

365 # Get style 

366 colors_enabled = theme_info.colors_enabled 

367 if colors_enabled: 

368 if style_name not in theme_info.styles: 368 ↛ 369line 368 didn't jump to line 369 because the condition on line 368 was never true

369 styled_text = Text("---") 

370 else: 

371 styled_text = Text( 

372 style_name, style=theme_info.styles[style_name] 

373 ) 

374 else: 

375 styled_text = Text(style_name) 

376 

377 # -- Apply the style 

378 row_values.append(styled_text) 

379 

380 table.add_row(*row_values) 

381 

382 # -- Make sure the current theme supports colors, otherwise they will 

383 # -- suppressed 

384 if get_theme().colors_enabled: 384 ↛ 387line 384 didn't jump to line 387 because the condition on line 384 was always true

385 saved_theme_name = None 

386 else: 

387 saved_theme_name = get_theme().name 

388 configure(theme_name=THEME_LIGHT.name) 

389 

390 # -- Render the table. 

391 cout() 

392 ctable(table) 

393 

394 if saved_theme_name: 394 ↛ 395line 394 didn't jump to line 395 because the condition on line 394 was never true

395 configure(theme_name=saved_theme_name) 

396 

397 cout("To change your theme use 'apio preferences -t ...'", style=INFO) 

398 cout() 

399 

400 

401# ------ apio info 

402 

403# -- Text in the rich-text format of the python rich library. 

404APIO_INFO_HELP = """ 

405The command group 'apio info' contains subcommands that provide \ 

406additional information about Apio and your system. 

407""" 

408 

409# -- We have only a single group with the title 'Subcommands'. 

410SUBGROUPS = [ 

411 ApioSubgroup( 

412 "Subcommands", 

413 [ 

414 _system_cli, 

415 _platforms_cli, 

416 _colors_cli, 

417 _themes_cli, 

418 ], 

419 ), 

420] 

421 

422 

423@click.command( 

424 name="info", 

425 cls=ApioGroup, 

426 subgroups=SUBGROUPS, 

427 short_help="Apio's info and info.", 

428 help=APIO_INFO_HELP, 

429) 

430def cli(): 

431 """Implements the 'apio info' command group.""" 

432 

433 # pass