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

SystemRDL / PeakRDL / 4497728222

pending completion
4497728222

push

github

Alex Mykyta
Add link to community plugins

207 of 214 branches covered (96.73%)

Branch coverage included in aggregate %.

545 of 549 relevant lines covered (99.27%)

5.89 hits per line

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

100.0
/src/peakrdl/main.py
1
import argparse
6✔
2
import sys
6✔
3
import os
6✔
4
import shlex
6✔
5
from typing import TYPE_CHECKING, List, Dict, Optional, Set
6✔
6

7
from systemrdl import RDLCompileError
6✔
8

9
from .__about__ import __version__
6✔
10
from .config.loader import load_cfg, AppConfig
6✔
11
from .plugins.exporter import get_exporter_plugins
6✔
12
from .plugins.importer import get_importer_plugins
6✔
13
from .cmd.dump import Dump
6✔
14
from .cmd.list_globals import ListGlobals
6✔
15
from .cmd.preprocess import Preprocess
6✔
16

17

18
if TYPE_CHECKING:
19
    from .subcommand import Subcommand
20

21

22
DESCRIPTION = """
6✔
23
PeakRDL is a control & status register model automation toolchain.
24

25
For help about a specific subcommand, try:
26
    peakrdl <command> --help
27

28
For more documentation, visit https://peakrdl.readthedocs.io
29

30
"""
31

32
class SubcommandHelpFormatter(argparse.RawDescriptionHelpFormatter):
6✔
33
    def _format_action(self, action): # type: ignore
6✔
34
        parts = super(argparse.RawDescriptionHelpFormatter, self)._format_action(action)
6✔
35
        if action.nargs == argparse.PARSER:
6✔
36
            parts = "\n".join(parts.split("\n")[1:])
6✔
37
        return parts
6✔
38

39

40
class ReportPluginsImpl(argparse.Action):
6✔
41
    CFG = None # type: AppConfig
6✔
42

43
    def __call__ (self, parser, namespace, values, option_string = None): # type: ignore
6✔
44
        exporters = get_exporter_plugins(self.CFG)
6✔
45
        importers = get_importer_plugins(self.CFG)
6✔
46

47
        print("importers:")
6✔
48
        for importer in importers:
6✔
49
            print(f"\t{importer.plugin_info}")
6✔
50
        print("exporters:")
6✔
51
        for exporter in exporters:
6✔
52
            print(f"\t{exporter.plugin_info}")
6✔
53
        sys.exit(0)
6✔
54

55

56
def get_file_args(path: str) -> List[str]:
6✔
57
    if not os.path.exists(path):
6✔
58
        print(f"error: file not found: {path}", file=sys.stderr)
6✔
59
        sys.exit(1)
6✔
60

61
    with open(path, "r", encoding='utf-8') as f:
6✔
62
        return shlex.split(f.read(), comments=True)
6✔
63

64

65
def expand_file_args(argv: List[str], _pathlist: Optional[Set[str]] = None) -> List[str]:
6✔
66
    if _pathlist is None:
6✔
67
        _pathlist = set()
6✔
68

69
    new_argv = []
6✔
70
    argv_iter = iter(argv)
6✔
71
    for arg in argv_iter:
6✔
72
        if arg == "-f":
6✔
73
            try:
6✔
74
                path = next(argv_iter)
6✔
75
            except StopIteration:
6✔
76
                print("error: argument -f: expected FILE", file=sys.stderr)
6✔
77
                sys.exit(1)
6✔
78

79
            if path in _pathlist:
6✔
80
                print(f"error: circular reference in -f files: '{path}' was already opened", file=sys.stderr)
6✔
81
                sys.exit(1)
6✔
82
            _pathlist.add(path)
6✔
83
            file_args = get_file_args(path)
6✔
84
            file_args = expand_file_args(file_args, _pathlist)
6✔
85
            _pathlist.remove(path)
6✔
86
            new_argv.extend(file_args)
6✔
87
        else:
88
            new_argv.append(arg)
6✔
89
    return new_argv
6✔
90

91

92
def main() -> None:
6✔
93
    # manually expand any -f argfiles first
94
    argv = expand_file_args(sys.argv[1:])
6✔
95

96
    cfg = load_cfg(argv)
6✔
97

98
    # Collect all importers and initialize them with the config
99
    importers = get_importer_plugins(cfg)
6✔
100
    for importer in importers:
6✔
101
        importer._load_cfg(cfg)
6✔
102

103
    # Collect all subcommands
104
    subcommands = [
6✔
105
        Dump(),
106
        ListGlobals(),
107
        Preprocess(),
108
    ] # type: List[Subcommand]
109
    subcommands += get_exporter_plugins(cfg)
6✔
110
    for subcommand in subcommands:
6✔
111
        subcommand._load_cfg(cfg)
6✔
112

113
    # Check for duplicate subcommands
114
    sc_dict = {} # type: Dict[str, Subcommand]
6✔
115
    for sc in subcommands:
6✔
116
        if sc.name in sc_dict:
6✔
117
            raise RuntimeError(f"More than one exporter plugin was registered with the same name '{sc.name}': \n\t{sc_dict[sc.name]}\n\t{sc}")
118
        sc_dict[sc.name] = sc
6✔
119

120
    # Initialize top-level arg parser
121
    class ReportPlugins(ReportPluginsImpl):
6✔
122
        CFG = cfg
6✔
123
    parser = argparse.ArgumentParser(
6✔
124
        description=DESCRIPTION,
125
        formatter_class=SubcommandHelpFormatter,
126
    )
127
    parser.add_argument("--version", action="version", version=__version__)
6✔
128
    parser.add_argument(
6✔
129
        "--plugins", action=ReportPlugins, nargs=0,
130
        help="Report the PeakRDL plugins, their versions, then exit"
131
    )
132

133
    # Add dummy -f and cfg flags. Not actually used as these are already
134
    # expanded earlier manually
135
    parser.add_argument(
6✔
136
        '-f',
137
        metavar="FILE",
138
        dest="argfile",
139
        help="Specify a file containing more command line arguments"
140
    )
141
    parser.add_argument(
6✔
142
        '--peakrdl-cfg',
143
        metavar="CFG",
144
        dest="peakrdl_cfg",
145
        help="Specify a PeakRDL configuration TOML file"
146
    )
147

148
    # Initialize subcommand arg parsers
149
    subgroup = parser.add_subparsers(
6✔
150
        title="subcommands",
151
        metavar="<subcommand>",
152
    )
153
    for subcommand in subcommands:
6✔
154
        subcommand._init_subparser(subgroup, importers)
6✔
155

156
    # Process command-line args
157
    options = parser.parse_args(argv)
6✔
158
    if not hasattr(options, 'subcommand'):
6✔
159
        parser.print_usage()
6✔
160
        print(f"{parser.prog}: error the following arguments are required: <subcommand>")
6✔
161
        sys.exit(1)
6✔
162

163
    # Run subcommand!
164
    try:
6✔
165
        options.subcommand.main(importers, options)
6✔
166
    except RDLCompileError:
6✔
167
        sys.exit(1)
6✔
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

© 2025 Coveralls, Inc