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
« 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"""
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)
37# ------ apio info system
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")
66 # -- Concatenate and return.
67 config_status = ", ".join(config_status)
68 return config_status
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.
77Examples:[code]
78 apio info system # System info.[/code]
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"""
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."""
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 )
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 )
115 table.add_column("ITEM", no_wrap=True, min_width=20)
116 table.add_column("VALUE", no_wrap=True, style=EMPH1)
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 )
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 )
149# ------ apio info platforms
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.
156Examples:[code]
157 apio info platforms # List supported platform ids.[/code]
159The automatic platform ID detection of Apio can be overridden by \
160defining a different platform ID using the APIO_PLATFORM environment variable.
161"""
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."""
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 )
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 )
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)
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")
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 = " "
208 table.add_row(
209 f"{marker}{platform_id}",
210 platform_type,
211 platform_variant,
212 style=style,
213 )
215 # -- Render the table.
216 cout()
217 ctable(table)
220# ------ apio info colors
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.
227The command shows the themes colors even if the current theme is 'no-colors'.
229Examples:[code]
230 apio info colors # Rich library output (default)
231 apio inf col -p # Using shortcuts.[/code]
232"""
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."""
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 )
251 # -- Print title.
252 cout("", "ANSI Colors", "")
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
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)
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))
286 # -- Construct the line.
287 line = " ".join(values)
289 # -- Output the line.
290 cout(line)
292 cout()
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)
299# ------ apio info themes
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.
307The command shows colors even if the current theme is 'no-colors'.
309[code]
310Examples:
311 apio info themes # Show themes colors
312 apio inf col -p # Using shortcuts.[/code]
313"""
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."""
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 )
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)
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 )
349 # -- Get selected theme
350 selected_theme = get_theme()
351 selected_theme_name = selected_theme.name
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")
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)
377 # -- Apply the style
378 row_values.append(styled_text)
380 table.add_row(*row_values)
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)
390 # -- Render the table.
391 cout()
392 ctable(table)
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)
397 cout("To change your theme use 'apio preferences -t ...'", style=INFO)
398 cout()
401# ------ apio info
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"""
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]
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."""
433 # pass