• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

nbiotcloud / ucdp / 19150419608

06 Nov 2025 09:27PM UTC coverage: 90.58% (+0.02%) from 90.564%
19150419608

push

github

web-flow
Merge pull request #133 from nbiotcloud/fix/traceback

fix traceback

10 of 14 new or added lines in 2 files covered. (71.43%)

4683 of 5170 relevant lines covered (90.58%)

10.86 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

66.47
/src/ucdp/cli.py
1
#
2
# MIT License
3
#
4
# Copyright (c) 2024-2025 nbiotcloud
5
#
6
# Permission is hereby granted, free of charge, to any person obtaining a copy
7
# of this software and associated documentation files (the "Software"), to deal
8
# in the Software without restriction, including without limitation the rights
9
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10
# copies of the Software, and to permit persons to whom the Software is
11
# furnished to do so, subject to the following conditions:
12
#
13
# The above copyright notice and this permission notice shall be included in all
14
# copies or substantial portions of the Software.
15
#
16
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22
# SOFTWARE.
23
#
24

25
"""Command Line Interface."""
26

27
import logging
12✔
28
import sys
12✔
29
from collections import defaultdict
12✔
30
from collections.abc import Iterable
12✔
31
from logging import StreamHandler
12✔
32
from pathlib import Path
12✔
33
from typing import Literal
12✔
34

35
import click
12✔
36
from click_bash42_completion import patch
12✔
37
from pydantic import BaseModel, ConfigDict
12✔
38
from rich import box
12✔
39
from rich.console import Console
12✔
40
from rich.logging import RichHandler
12✔
41
from rich.pretty import pprint
12✔
42
from rich.table import Table
12✔
43

44
from ._cligroup import MainGroup
12✔
45
from ._logging import HasErrorHandler
12✔
46
from .cache import CACHE
12✔
47
from .cliutil import (
12✔
48
    Exit,
49
    PathType,
50
    arg_template_filepaths,
51
    arg_top,
52
    arg_tops,
53
    auto_path,
54
    defines2data,
55
    opt_check,
56
    opt_clean,
57
    opt_create,
58
    opt_defines,
59
    opt_dry_run,
60
    opt_file,
61
    opt_filelist,
62
    opt_filepath,
63
    opt_local,
64
    opt_maxlevel,
65
    opt_maxworkers,
66
    opt_path,
67
    opt_show_diff,
68
    opt_tag,
69
    opt_target,
70
    opt_topsfile,
71
    read_file,
72
)
73
from .consts import PATH
12✔
74
from .create import TB_MAP, TYPE_CHOICES, CreateInfo
12✔
75
from .create import create as create_
12✔
76
from .fileset import FileSet
12✔
77
from .finder import find
12✔
78
from .generate import Generator, clean, get_makolator, render_generate, render_inplace
12✔
79
from .iterutil import namefilter
12✔
80
from .loader import load
12✔
81
from .modfilelist import iter_modfilelists
12✔
82
from .modtopref import PAT_TOPMODREF, TopModRef
12✔
83
from .pathutil import absolute, relative
12✔
84
from .top import Top
12✔
85
from .util import LOGGER, guess_path
12✔
86

87
patch()
12✔
88

89

90
_LOGLEVELMAP = {
12✔
91
    0: logging.WARNING,
92
    1: logging.INFO,
93
    2: logging.DEBUG,
94
}
95

96

97
class Ctx(BaseModel):
12✔
98
    """Command Line Context."""
99

100
    model_config = ConfigDict(
12✔
101
        arbitrary_types_allowed=True,
102
    )
103

104
    console: Console
12✔
105
    has_error_handler: HasErrorHandler | None = None
12✔
106

107
    verbose: int = 0
12✔
108
    no_cache: bool = False
12✔
109
    no_color: bool | None = None
12✔
110

111
    @staticmethod
12✔
112
    def create(no_color: bool | None = None, **kwargs) -> "Ctx":
12✔
113
        """Create."""
114
        console = Console(log_time=False, log_path=False, no_color=no_color)
12✔
115
        has_error_handler = HasErrorHandler()
12✔
116
        return Ctx(console=console, has_error_handler=has_error_handler, no_color=no_color, **kwargs)
12✔
117

118
    def __enter__(self):
12✔
119
        # Logging
120
        level = _LOGLEVELMAP.get(self.verbose, logging.DEBUG)
12✔
121
        if not self.no_color:
12✔
122
            handler = RichHandler(
12✔
123
                show_time=False,
124
                show_path=False,
125
                rich_tracebacks=True,
126
                console=Console(stderr=True, no_color=self.no_color),
127
            )
128
            format_ = "%(message)s"
12✔
129
        else:
130
            handler = StreamHandler(stream=sys.stderr)
×
131
            format_ = "%(levelname)s %(message)s"
×
132
        handlers = [handler, self.has_error_handler]
12✔
133
        logging.basicConfig(level=level, format=format_, handlers=handlers)
12✔
134

135
        # Cache
136
        if self.no_cache:
12✔
137
            CACHE.disable()
×
138

139
        return self
12✔
140

141
    def __exit__(self, exc_type, exc_value, tb):
12✔
142
        pass
12✔
143

144

145
@click.group(cls=MainGroup, context_settings={"help_option_names": ["-h", "--help"]})
12✔
146
@click.option("-v", "--verbose", count=True, help="Increase Verbosity.")
12✔
147
@click.option("-C", "--no-cache", is_flag=True, help="Disable Caching.")
12✔
148
@click.option("--no-color", is_flag=True, help="Disable Coloring.", envvar="UCDP_NO_COLOR")
12✔
149
@click.version_option()
12✔
150
@click.pass_context
12✔
151
def ucdp(ctx, verbose=0, no_cache=False, no_color=False):
12✔
152
    """Unified Chip Design Platform."""
153
    ctx.obj = ctx.with_resource(Ctx.create(verbose=verbose, no_cache=no_cache, no_color=no_color))
12✔
154

155

156
pass_ctx = click.make_pass_decorator(Ctx)
12✔
157

158

159
@ucdp.result_callback()
12✔
160
@pass_ctx
12✔
161
def _end(ctx: Ctx, *args, **kwargs):
12✔
162
    if ctx.has_error_handler.has_errors:
12✔
NEW
163
        raise Exit(ctx.console, "FAILED")
×
164

165

166
def get_group(help=None):  # pragma: no cover
167
    """Create Command Group."""
168

169
    @click.group(help=help)
170
    @click.pass_context
171
    def group(ctx):
172
        ctx.obj = Ctx(console=Console(log_time=False, log_path=False))
173

174
    return group
175

176

177
def load_top(ctx: Ctx, top: str | TopModRef, paths: Iterable[str | Path], quiet: bool = False) -> Top:
12✔
178
    """Load Top Module."""
179
    lpaths = [Path(path) for path in paths]
×
180
    # Check if top seems to be some kind of file path
181
    topmodref = TopModRef.cast(guess_path(top) or top) if isinstance(top, str) else top
×
182
    if quiet:
×
183
        return load(topmodref, paths=lpaths)
×
184
    with ctx.console.status(f"Loading '{topmodref!s}'"):
×
185
        result = load(topmodref, paths=lpaths)
×
186
    ctx.console.log(f"'{topmodref!s}' checked.")
×
187
    return result
×
188

189

190
@ucdp.command(
12✔
191
    help=f"""
192
Load Data Model and Check.
193

194
TOP: Top Module. {PAT_TOPMODREF}. Environment Variable 'UCDP_TOP'
195
"""
196
)
197
@arg_top
12✔
198
@opt_path
12✔
199
@click.option("--stat", default=False, is_flag=True, help="Show Statistics.")
12✔
200
@pass_ctx
12✔
201
def check(ctx, top, path, stat=False):
12✔
202
    """Check."""
203
    top = load_top(ctx, top, path)
×
204
    if stat:
×
205
        print("Statistics:")
×
206
        for name, value in top.get_stat().items():
×
207
            print(f"  {name}: {value}")
×
208

209

210
@ucdp.command(
12✔
211
    help=f"""
212
Load Data Model and Generate Files.
213

214
TOP: Top Module. {PAT_TOPMODREF}. Environment Variable 'UCDP_TOP'
215
"""
216
)
217
@arg_tops
12✔
218
@opt_path
12✔
219
@opt_filelist
12✔
220
@opt_target
12✔
221
@opt_show_diff
12✔
222
@opt_maxworkers
12✔
223
@opt_defines
12✔
224
@opt_local
12✔
225
@opt_topsfile
12✔
226
@opt_check
12✔
227
@opt_create
12✔
228
@opt_clean
12✔
229
@pass_ctx
12✔
230
def gen(
12✔
231
    ctx,
232
    tops,
233
    path,
234
    filelist,
235
    target=None,
236
    show_diff=False,
237
    maxworkers=None,
238
    define=None,
239
    local=None,
240
    check=False,
241
    create=False,
242
    clean=False,
243
    tops_file=None,
244
):
245
    """Generate."""
246
    tops = list(tops)
×
247
    for filepath in tops_file or []:
×
248
        tops.extend(read_file(filepath))
×
249
    makolator = get_makolator(show_diff=show_diff, paths=path, create=create)
×
250
    data = defines2data(define)
×
251
    filelist = filelist or ["*"]
×
252
    with Generator(makolator=makolator, maxworkers=maxworkers, check=check) as generator:
×
253
        for info in find(path, patterns=tuple(tops), local=local, is_top=True):
×
254
            try:
×
255
                top = load_top(ctx, info.topmodref, path)
×
256
            except Exception as exc:
×
257
                LOGGER.warning(f"Cannot load '{info.topmodref}'")
×
258
                LOGGER.warning(str(exc))
×
259
                LOGGER.warning(f"Debug with 'ucdp check {info.topmodref}'")
×
260
                continue
×
261
            for item in filelist:
×
262
                generator.generate(top, item, target=target, data=data, clean=clean)
×
263

264

265
@ucdp.command(
12✔
266
    help=f"""
267
Load Data Model and Render Template and Create File.
268

269
TOP: Top Module. {PAT_TOPMODREF}. Environment Variable 'UCDP_TOP'
270

271
TEMPLATE_FILEPATHS: Templates to render. Environment Variable 'UCDP_TEMPLATE_FILEPATHS'
272
                    Templates in `templates` folders are found automatically.
273

274
GENFILE: Generated File.
275
"""
276
)
277
@arg_top
12✔
278
@opt_path
12✔
279
@arg_template_filepaths
12✔
280
@click.argument("genfile", type=PathType, shell_complete=auto_path, nargs=1)
12✔
281
@opt_show_diff
12✔
282
@opt_defines
12✔
283
@opt_create
12✔
284
@pass_ctx
12✔
285
def rendergen(ctx, top, path, template_filepaths, genfile, show_diff=False, define=None, create=False):
12✔
286
    """Render Generate."""
287
    top = load_top(ctx, top, path)
×
288
    makolator = get_makolator(show_diff=show_diff, paths=path, create=create)
×
289
    data = defines2data(define)
×
290
    render_generate(top, template_filepaths, genfile=genfile, makolator=makolator, data=data)
×
291

292

293
@ucdp.command(
12✔
294
    help=f"""
295
Load Data Model and Render Template and Update File.
296

297
TOP: Top Module. {PAT_TOPMODREF}. Environment Variable 'UCDP_TOP'
298

299
TEMPLATE_FILEPATHS: Templates to render. Environment Variable 'UCDP_TEMPLATE_FILEPATHS'
300
                    Templates in `templates` folders are found automatically.
301

302
INPLACEFILE: Inplace File.
303
"""
304
)
305
@arg_top
12✔
306
@opt_path
12✔
307
@arg_template_filepaths
12✔
308
@click.argument("inplacefile", type=PathType, shell_complete=auto_path, nargs=1)
12✔
309
@opt_show_diff
12✔
310
@opt_defines
12✔
311
@opt_create
12✔
312
@click.option("--ignore_unknown", "-i", default=False, is_flag=True, help="Ignore Unknown Placeholder.")
12✔
313
@pass_ctx
12✔
314
def renderinplace(
12✔
315
    ctx, top, path, template_filepaths, inplacefile, show_diff=False, define=None, ignore_unknown=False, create=False
316
):
317
    """Render Inplace."""
318
    top = load_top(ctx, top, path)
×
319
    makolator = get_makolator(show_diff=show_diff, paths=path, create=create)
×
320
    data = defines2data(define)
×
321
    render_inplace(
×
322
        top,
323
        template_filepaths,
324
        inplacefile=inplacefile,
325
        makolator=makolator,
326
        data=data,
327
        ignore_unknown=ignore_unknown,
328
    )
329

330

331
@ucdp.command(
12✔
332
    help=f"""
333
Load Data Model and REMOVE Generated Files.
334

335
TOP: Top Module. {PAT_TOPMODREF}. Environment Variable 'UCDP_TOP'
336
"""
337
)
338
@arg_top
12✔
339
@opt_path
12✔
340
@opt_filelist
12✔
341
@opt_target
12✔
342
@opt_show_diff
12✔
343
@opt_dry_run
12✔
344
@opt_maxworkers
12✔
345
@pass_ctx
12✔
346
def cleangen(ctx, top, path, filelist, target=None, show_diff=False, maxworkers=None, dry_run=False):
12✔
347
    """Clean Generated Files."""
348
    top = load_top(ctx, top, path)
×
349
    makolator = get_makolator(show_diff=show_diff, paths=path)
×
350
    for item in filelist or ["*"]:
×
351
        clean(top, item, target=target, makolator=makolator, maxworkers=maxworkers, dry_run=dry_run)
×
352

353

354
@ucdp.command(
12✔
355
    help=f"""
356
Load Data Model and Generate File List.
357

358
TOP: Top Module. {PAT_TOPMODREF}. Environment Variable 'UCDP_TOP'
359
"""
360
)
361
@arg_top
12✔
362
@opt_path
12✔
363
@opt_filelist
12✔
364
@opt_target
12✔
365
@opt_file
12✔
366
@pass_ctx
12✔
367
def filelist(ctx, top, path, filelist, target=None, file=None):
12✔
368
    """File List."""
369
    # Load quiet, otherwise stdout is messed-up
370
    top = load_top(ctx, top, path, quiet=True)
×
371
    for item in filelist or ["*"]:
×
372
        fileset = FileSet.from_mod(top.mod, item, target=target)
×
373
        for line in fileset:
×
374
            print(line, file=file)
×
375

376

377
@ucdp.command(
12✔
378
    help=f"""
379
Load Data Model and Show File Information
380

381
TOP: Top Module. {PAT_TOPMODREF}. Environment Variable 'UCDP_TOP'
382
"""
383
)
384
@arg_top
12✔
385
@opt_path
12✔
386
@opt_filelist
12✔
387
@opt_target
12✔
388
@opt_maxlevel
12✔
389
@click.option("--minimal", "-m", default=False, is_flag=True, help="Skip defaults.")
12✔
390
@opt_file
12✔
391
@pass_ctx
12✔
392
def fileinfo(ctx, top, path, filelist, target=None, maxlevel=None, minimal=False, file=None):
12✔
393
    """File List."""
394
    # Load quiet, otherwise stdout is messed-up
395
    top = load_top(ctx, top, path, quiet=True)
×
396
    console = Console(file=file) if file else ctx.console
×
397
    for item in filelist or ["*"]:
×
398
        data = defaultdict(list)
×
399
        for mod, modfilelist in iter_modfilelists(top.mod, item, target=target, maxlevel=maxlevel):
×
400
            data[str(mod)].append(modfilelist.model_dump(exclude_defaults=minimal))
×
401
        pprint(dict(data), indent_guides=False, console=console)
×
402

403

404
@ucdp.command(
12✔
405
    help="""
406
              List Available Data Models.
407

408
              PATTERN: Limit list to these modules only.
409

410
              Examples:
411

412
                ucdp ls
413

414
                ucdp ls -n
415

416
                ucdp ls glbl_lib*
417
              """
418
)
419
@arg_tops
12✔
420
@opt_path
12✔
421
@click.option("--names", "-n", default=False, is_flag=True, help="Just print names")
12✔
422
@click.option("--top/--no-top", "-t/-T", default=None, is_flag=True, help="List loadable top modules only.")
12✔
423
@click.option("--tb/--no-tb", "-b/-B", default=None, is_flag=True, help="List testbench modules only.")
12✔
424
@click.option("--generic-tb", "-g", default=False, is_flag=True, help="List Generic Testbench modules only.")
12✔
425
@opt_local
12✔
426
@click.option("--base", "-A", default=False, is_flag=True, help="Show Base Classes.")
12✔
427
@click.option("--filepath", "-f", default=False, is_flag=True, help="Show File Path.")
12✔
428
@click.option("--abs-filepath", "-F", default=False, is_flag=True, help="Show Absolute File Path.")
12✔
429
@opt_tag
12✔
430
@pass_ctx
12✔
431
def ls(  # noqa: C901
12✔
432
    ctx,
433
    path=None,
434
    tops=None,
435
    names=False,
436
    top=None,
437
    tb=None,
438
    local=None,
439
    generic_tb=False,
440
    tag=None,
441
    base=False,
442
    filepath=False,
443
    abs_filepath=False,
444
):
445
    """List Modules."""
446
    with ctx.console.status("Searching"):
×
447
        infos = find(path, patterns=tops or ["*"], local=local)
×
448
    if top is not None:
×
449
        infos = [info for info in infos if info.is_top == top]
×
450
    if tb is not None:
×
451
        infos = [info for info in infos if bool(info.tb) == tb]
×
452
    if generic_tb:
×
453
        infos = [info for info in infos if info.tb == "Generic"]
×
454
    if tag:
×
455
        filter_ = namefilter(tag)
×
456
        infos = [info for info in infos if any(filter_(tag) for tag in info.tags)]
×
457

458
    def fill_row(row, info):
×
459
        if base:
×
460
            row.append(info.modbasecls.__name__)
×
461
        if filepath:
×
462
            row.append(str(relative(info.filepath)))
×
463
        if abs_filepath:
×
464
            row.append(str(info.filepath))
×
465

466
    if names:
×
467
        for info in infos:
×
468
            row = [info.topmodref]
×
469
            fill_row(row, info)
×
470
            print(*row)
×
471
    else:
472
        table = Table(expand=filepath or abs_filepath, box=box.MARKDOWN)
×
473
        table.add_column("Reference")
×
474
        table.add_column("Top", justify="center")
×
475
        table.add_column("Tb ", justify="center")
×
476
        table.add_column("Tags")
×
477
        if base:
×
478
            table.add_column("Bases on", justify="right")
×
479
        if filepath:
×
480
            table.add_column("Filepath")
×
481
        if abs_filepath:
×
482
            table.add_column("Absolute Filepath")
×
483
        for info in infos:
×
484
            row = [
×
485
                str(info.topmodref),
486
                "X" if info.is_top else "",
487
                "X" if info.tb else "",
488
                ",".join(sorted(info.tags)),
489
            ]
490
            fill_row(row, info)
×
491
            table.add_row(*row)
×
492
        ctx.console.print(table)
×
493

494

495
@ucdp.command(
12✔
496
    help=f"""
497
Load Data Model and Module Information.
498

499
TOP: Top Module. {PAT_TOPMODREF}. Environment Variable 'UCDP_TOP'
500
"""
501
)
502
@arg_tops
12✔
503
@opt_path
12✔
504
@opt_local
12✔
505
@click.option("--top", "-t", default=None, is_flag=True, help="List loadable top modules only.")
12✔
506
@click.option("--sub", "-S", default=False, is_flag=True, help="Show Submodules.")
12✔
507
@pass_ctx
12✔
508
def modinfo(ctx, tops, path, local, top, sub):
12✔
509
    """Module Information."""
510
    sep = ""
×
511
    for info in find(path, patterns=tops, local=local, is_top=top):
×
512
        try:
×
513
            top = load_top(ctx, info.topmodref, path, quiet=True)
×
514
        except Exception as exc:
×
515
            LOGGER.warning(str(exc))
×
516
            continue
×
517
        print(sep + top.mod.get_info(sub=sub))
×
518
        sep = "\n\n"
×
519

520

521
@ucdp.command(
12✔
522
    help=f"""
523
Load Data Model and Show Module Overview.
524

525
TOP: Top Module. {PAT_TOPMODREF}. Environment Variable 'UCDP_TOP'
526
"""
527
)
528
@arg_top
12✔
529
@opt_path
12✔
530
@click.option("--minimal", "-m", default=False, is_flag=True, help="Skip modules without specific details")
12✔
531
@opt_filepath
12✔
532
@opt_tag
12✔
533
@pass_ctx
12✔
534
def overview(ctx, top, path, minimal=False, file=None, tag=None):
12✔
535
    """Overview."""
536
    # Load quiet, otherwise stdout is messed-up
537
    top = load_top(ctx, top, path, quiet=True)
×
538
    data = {"minimal": minimal, "tags": tag}
×
539
    render_generate(top, [PATH / "ucdp-templates" / "overview.txt.mako"], genfile=file, data=data, no_stat=True)
×
540

541

542
@ucdp.group(context_settings={"help_option_names": ["-h", "--help"]})
12✔
543
def info():
12✔
544
    """Information."""
545

546

547
@info.command()
12✔
548
@pass_ctx
12✔
549
def examples(ctx):
12✔
550
    """Path to Examples."""
551
    examples_path = Path(__file__).parent / "examples"
×
552
    print(str(examples_path))
×
553

554

555
@info.command()
12✔
556
@opt_path
12✔
557
@pass_ctx
12✔
558
def template_paths(ctx, path):
12✔
559
    """Template Paths."""
560
    makolator = get_makolator(paths=path)
×
561
    for template_path in makolator.config.template_paths:
×
562
        print(str(template_path))
×
563

564

565
@ucdp.command(
12✔
566
    help="""
567
Create Datamodel Skeleton.
568
"""
569
)
570
@click.option("--module", "-m", prompt=True, help="Name of the Module")
12✔
571
@click.option("--library", "-l", default=absolute(Path()).name, prompt=True, help="Name of the Library")
12✔
572
@click.option("--regf/--no-regf", "-r/-R", default=True, help="Make use of a Register File")
12✔
573
@click.option("--descr", "-d", default="", help="Description")
12✔
574
@click.option("--flavour", "-F", type=click.Choice(TYPE_CHOICES, case_sensitive=False), help="Choose a Module Flavour")
12✔
575
@click.option("--tb/--no-tb", "-t/-T", default=None, help="Create testbench for design module")
12✔
576
@click.option("--force", "-f", is_flag=True, help="Overwrite existing files")
12✔
577
@pass_ctx
12✔
578
def create(
12✔
579
    ctx,
580
    module,
581
    library,
582
    regf,
583
    descr,
584
    flavour,
585
    tb,
586
    force,
587
):
588
    """Let The User Type In The Name And Library Of The File."""
589
    if flavour is None:
12✔
590
        flavour = prompt_flavour()
12✔
591
        ctx.console.print(f"\nYou choose the flavour [bold blue]{flavour}[/bold blue].\n")
12✔
592

593
    info = CreateInfo(module=module, library=library, regf=regf, descr=descr, flavour=flavour)
12✔
594

595
    if info.is_tb:
12✔
596
        if not module.endswith("_tb"):
12✔
597
            LOGGER.warning(f"Your testbench module name {module!r} does not end with '_tb'")
12✔
598

599
    else:
600
        if module.endswith("_tb"):
12✔
601
            LOGGER.warning(f"Your design module name {module!r} ends with '_tb'")
12✔
602
        if tb is None:
12✔
603
            answer = click.prompt(
12✔
604
                "Do you want to create a corresponding testbench? (y)es. (n)o.",
605
                type=click.Choice(["y", "n"]),
606
                default="y",
607
            )
608
            tb = answer == "y"
12✔
609
            ctx.console.print("")
12✔
610
        if tb:
12✔
611
            tbinfo = CreateInfo(module=f"{module}_tb", library=library, regf=regf, descr=descr, flavour=TB_MAP[flavour])
12✔
612
            create_(tbinfo, force)
12✔
613
    create_(info, force)
12✔
614

615

616
Type = Literal["AConfigurableMod", "AConfigurableTbMod", "AGenericTbMod", "AMod", "ATailoredMod", "ATbMod"]
12✔
617

618

619
def prompt_flavour() -> Type:
12✔
620
    """Let The User Choose The Type Of The File."""
621
    answer = click.prompt("Do you want to build a (d)esign or (t)estbench?", type=click.Choice(["d", "t"]), default="d")
12✔
622
    if answer == "d":
12✔
623
        answer = click.prompt(
12✔
624
            "Does your design vary more than what `parameter` can cover? (y)es. (n)o.", type=click.Choice(["y", "n"])
625
        )
626
        if answer == "y":
12✔
627
            answer = click.prompt(
12✔
628
                "Do you want to use a (c)onfig or (t) shall the parent module tailor the functionality?",
629
                type=click.Choice(["c", "t"]),
630
            )
631
            if answer == "c":
12✔
632
                flavour_ = "AConfigurableMod"
12✔
633

634
            else:
635
                flavour_ = "ATailoredMod"
12✔
636

637
        else:
638
            flavour_ = "AMod"
12✔
639

640
    else:
641
        answer = click.prompt(
12✔
642
            "Do you want to build a generic testbench which tests similar modules? (y)es. (n)o.",
643
            type=click.Choice(["y", "n"]),
644
        )
645
        if answer == "y":
12✔
646
            answer = click.prompt(
12✔
647
                "Do you want to automatically adapt your testbench to your (g) dut or use a (c)onfig?",
648
                type=click.Choice(["g", "c"]),
649
            )
650
            if answer == "c":
12✔
651
                flavour_ = "AConfigurableTbMod"
12✔
652

653
            else:
654
                flavour_ = "AGenericTbMod"
12✔
655

656
        else:
657
            flavour_ = "ATbMod"
12✔
658

659
    return flavour_
12✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc