Coverage for apio/commands/apio_examples.py: 87%
76 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 examples' command"""
10import sys
11import re
12from pathlib import Path
13from typing import List, Any, Optional
14import click
15from rich.table import Table
16from rich import box
17from apio.common.apio_console import cerror
18from apio.common import apio_console
19from apio.common.apio_console import cout, ctable
20from apio.common.apio_styles import INFO, BORDER, EMPH1
21from apio.managers.examples import Examples, ExampleInfo
22from apio.commands import options
23from apio.apio_context import (
24 ApioContext,
25 PackagesPolicy,
26 ProjectPolicy,
27 RemoteConfigPolicy,
28)
29from apio.utils import util
30from apio.utils.cmd_util import ApioGroup, ApioSubgroup, ApioCommand
33# ---- apio examples list
36# -- Text in the rich-text format of the python rich library.
37APIO_EXAMPLES_LIST_HELP = """
38The command 'apio examples list' lists the available Apio project examples \
39that you can use.
41Examples:[code]
42 apio examples list # List all examples
43 apio examples list -v # More verbose output.
44 apio examples list | grep alhambra-ii # Show alhambra-ii examples.
45 apio examples list | grep -i blink # Show blinking examples.[/code]
46"""
49def examples_sort_key(entry: ExampleInfo) -> Any:
50 """A key for sorting the fpga entries in our preferred order."""
51 return (util.fpga_arch_sort_key(entry.fpga_arch), entry.name)
54def list_examples(apio_ctx: ApioContext, verbose: bool) -> None:
55 """Print all the examples available. Return a process exit
56 code, 0 if ok, non zero otherwise."""
58 # -- Get list of examples.
59 entries: List[ExampleInfo] = Examples(apio_ctx).get_examples_infos()
61 # -- Sort boards by case insensitive board id.
62 entries.sort(key=examples_sort_key)
64 # -- Define the table.
65 table = Table(
66 show_header=True,
67 show_lines=False,
68 box=box.SQUARE,
69 border_style=BORDER,
70 title="Apio Examples",
71 title_justify="left",
72 )
74 # -- Add columns.
75 table.add_column("BOARD/EXAMPLE", no_wrap=True, style=EMPH1)
76 table.add_column("ARCH", no_wrap=True)
77 if verbose: 77 ↛ 78line 77 didn't jump to line 78 because the condition on line 77 was never true
78 table.add_column("PART-NUM", no_wrap=True)
79 table.add_column("SIZE", no_wrap=True)
80 table.add_column(
81 "DESCRIPTION",
82 no_wrap=True,
83 max_width=40 if verbose else 70, # Limit in verbose mode.
84 )
86 # -- Add rows.
87 last_arch = None
88 for entry in entries:
89 # -- Separation before each architecture group, unless piped out.
90 if last_arch != entry.fpga_arch and apio_console.is_terminal():
91 table.add_section()
92 last_arch = entry.fpga_arch
94 # -- Collect row's values.
95 values = []
96 values.append(entry.name)
97 values.append(entry.fpga_arch)
98 if verbose: 98 ↛ 99line 98 didn't jump to line 99 because the condition on line 98 was never true
99 values.append(entry.fpga_part_num)
100 values.append(entry.fpga_size)
101 values.append(entry.description)
103 # -- Append the row
104 table.add_row(*values)
106 # -- Render the table.
107 cout()
108 ctable(table)
110 # -- Print summary.
111 if apio_console.is_terminal(): 111 ↛ exitline 111 didn't return from function 'list_examples' because the condition on line 111 was always true
112 cout(f"Total of {util.plurality(entries, 'example')}")
113 if not verbose: 113 ↛ exitline 113 didn't return from function 'list_examples' because the condition on line 113 was always true
114 cout(
115 "Run 'apio examples list -v' for additional columns.",
116 style=INFO,
117 )
120@click.command(
121 name="list",
122 cls=ApioCommand,
123 short_help="List the available apio examples.",
124 help=APIO_EXAMPLES_LIST_HELP,
125)
126@options.verbose_option
127def _list_cli(verbose: bool):
128 """Implements the 'apio examples list' command group."""
130 # -- Create the apio context.
131 apio_ctx = ApioContext(
132 project_policy=ProjectPolicy.NO_PROJECT,
133 remote_config_policy=RemoteConfigPolicy.CACHED_OK,
134 packages_policy=PackagesPolicy.ENSURE_PACKAGES,
135 )
137 # --List all available examples.
138 list_examples(apio_ctx, verbose)
141# ---- apio examples fetch
143# -- Text in the rich-text format of the python rich library.
144APIO_EXAMPLES_FETCH_HELP = """
145The command 'apio examples fetch' fetches a single examples or all the \
146examples of a board. The destination directory is either the current \
147directory or the directory specified with '--dst' and it should be empty \
148and non existing.
150Examples:[code]
151 apio examples fetch alhambra-ii/ledon # Single example
152 apio examples fetch alhambra-ii # All board's examples
153 apio examples fetch alhambra-ii -d work # Explicit destination
155"""
158@click.command(
159 name="fetch",
160 cls=ApioCommand,
161 short_help="Fetch the files of an example.",
162 help=APIO_EXAMPLES_FETCH_HELP,
163)
164@click.argument("example", metavar="EXAMPLE", nargs=1, required=True)
165@options.dst_option_gen(short_help="Set a different destination directory.")
166def _fetch_cli(
167 # Arguments
168 example: str,
169 # Options
170 dst: Optional[Path],
171):
172 """Implements the 'apio examples fetch' command."""
174 # -- Create the apio context.
175 apio_ctx = ApioContext(
176 project_policy=ProjectPolicy.NO_PROJECT,
177 remote_config_policy=RemoteConfigPolicy.CACHED_OK,
178 packages_policy=PackagesPolicy.ENSURE_PACKAGES,
179 )
181 # -- Create the examples manager.
182 examples = Examples(apio_ctx)
184 # -- Determine the destination directory.
185 dst_dir_path = util.user_directory_or_cwd(
186 dst, description="Destination", must_exist=False
187 )
189 # Parse the argument as board or board/example
190 pattern = r"^([a-zA-Z0-9-]+)(?:[/]([a-zA-Z0-9-]+))?$"
191 match = re.match(pattern, example)
192 if not match: 192 ↛ 193line 192 didn't jump to line 193 because the condition on line 192 was never true
193 cerror(f"Invalid example specification '{example}.")
194 cout(
195 "Expecting board-id or board/example-name, e.g. "
196 "'alhambra-ii' or 'alhambra-ii/blinky.",
197 style=INFO,
198 )
199 sys.exit(1)
200 board_id: str = match.group(1)
201 example_name: Optional[str] = match.group(2)
203 if example_name:
204 # -- Copy the files of a single example.
205 examples.copy_example_files(example, dst_dir_path)
206 else:
207 # -- Copy the directories of the board's examples.
208 examples.copy_board_examples(board_id, dst_dir_path)
211# ---- apio examples
213# -- Text in the rich-text format of the python rich library.
214APIO_EXAMPLES_HELP = """
215The command group 'apio examples' provides subcommands for listing and \
216fetching Apio provided examples. Each example is a self contained \
217mini project that can be built and uploaded to an FPGA board.
218"""
221# -- We have only a single group with the title 'Subcommands'.
222SUBGROUPS = [
223 ApioSubgroup(
224 "Subcommands",
225 [
226 _list_cli,
227 _fetch_cli,
228 ],
229 )
230]
233@click.command(
234 name="examples",
235 cls=ApioGroup,
236 subgroups=SUBGROUPS,
237 short_help="List and fetch apio examples.",
238 help=APIO_EXAMPLES_HELP,
239)
240def cli():
241 """Implements the 'apio examples' command group."""
243 # pass