Skip to content

confit.cli

Cli [source]

Command line application for Confit commands that:

  • validates a command parameters before executing it
  • accepts a configuration file describing the parameters
  • automatically instantiates parameters given a dictionary when type hinted
Source code in confit/cli.py
79
80
81
82
83
84
85
def __init__(self, *args: Any, **kwargs: Any):
    self.commands = {}
    self.subcommands = []
    self.typer_args = args
    self.typer_kwargs = kwargs
    self.typer_cli = None
    self.typer_warning_emitted = False

add_subcommands [source]

Add another Confit CLI as a group or flattened command entries

Source code in confit/cli.py
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
def add_subcommands(
    self,
    cli: "Cli",
    *,
    name: Optional[str] = None,
    help: Optional[str] = None,
    short_help: Optional[str] = None,
    hidden: bool = False,
    deprecated: bool = False,
) -> None:
    """Add another Confit CLI as a group or flattened command entries"""
    if not isinstance(cli, Cli):
        raise TypeError("add_subcommands expects a Confit Cli instance")
    self.subcommands.append(
        {
            "cli": cli,
            "name": name,
            "help": help,
            "short_help": short_help,
            "hidden": hidden,
            "deprecated": deprecated,
        }
    )
    self.typer_cli = None

add_typer [source]

Add Confit subcommands through the deprecated Typer compatible name

Source code in confit/cli.py
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
def add_typer(
    self,
    cli: "Cli",
    *,
    name: Optional[str] = None,
    help: Optional[str] = None,
    short_help: Optional[str] = None,
    hidden: bool = False,
    deprecated: bool = False,
) -> None:
    """Add Confit subcommands through the deprecated Typer compatible name"""
    warnings.warn(
        "Cli.add_typer() is deprecated, use Cli.add_subcommands() instead.",
        VisibleDeprecationWarning,
        stacklevel=2,
    )
    self.add_subcommands(
        cli,
        name=name,
        help=help,
        short_help=short_help,
        hidden=hidden,
        deprecated=deprecated,
    )

command_entries [source]

Return the command and group entries used by dispatch and help

Source code in confit/cli.py
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
def command_entries(self):
    """Return the command and group entries used by dispatch and help"""
    entries = {
        name: {"kind": "command", "cli": self, "command": command}
        for name, command in self.commands.items()
    }
    for group in self.subcommands:
        if group["name"] is None:
            entries.update(group["cli"].command_entries())
        else:
            entries[group["name"]] = {
                "kind": "group",
                "cli": group["cli"],
                "group": group,
            }
    return entries

format_commands_help [source]

Render the visible commands and groups for the current CLI level

Source code in confit/cli.py
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
def format_commands_help(self):
    """Render the visible commands and groups for the current CLI level"""
    lines = ["Commands:"]
    for name, entry in self.command_entries().items():
        if entry["kind"] == "command":
            options = entry["command"]["typer_options"]
        else:
            options = entry["group"]
        if options["hidden"]:
            continue
        description = options.get("short_help") or options.get("help")
        suffix = " (deprecated)" if options["deprecated"] else ""
        if description:
            description = inspect.cleandoc(description).splitlines()[0]
            lines.append(f"  {name}{suffix}  {description}")
        else:
            lines.append(f"  {name}{suffix}")
    return "\n".join(lines)

get_typer_cli [source]

Build and cache the Typer representation read by external Typer apps

Source code in confit/cli.py
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
def get_typer_cli(self):
    """Build and cache the Typer representation read by external Typer apps"""
    if self.typer_cli is not None:
        return self.typer_cli
    try:
        typer = importlib.import_module("typer")
    except ImportError as e:
        raise ImportError(
            "Typer interoperability requires the application to install "
            "and declare a dependency on 'typer'."
        ) from e

    if not self.typer_warning_emitted:
        warnings.warn(
            "Adapting a Confit Cli for Typer is deprecated and will be removed "
            "in the next major Confit release.",
            VisibleDeprecationWarning,
            stacklevel=3,
        )
        self.typer_warning_emitted = True

    typer_cli = typer.Typer(*self.typer_args, **self.typer_kwargs)
    for name, command in self.commands.items():
        options = dict(command["typer_options"])
        options["context_settings"] = {
            **(options["context_settings"] or {}),
            "ignore_unknown_options": True,
            "allow_extra_args": True,
        }

        def create_callback(command_name, command_record):
            def callback(
                ctx: typer.Context,
                config: Optional[List[Path]] = None,
            ):
                return self.run_command(
                    command_name,
                    command_record,
                    config,
                    list(ctx.args),
                )

            return callback

        typer_cli.command(name=name, **options)(create_callback(name, command))

    for group in self.subcommands:
        typer_cli.add_typer(
            group["cli"],
            name=group["name"],
            help=group["help"],
            short_help=group["short_help"],
            hidden=group["hidden"],
            deprecated=group["deprecated"],
        )
    self.typer_cli = typer_cli
    return typer_cli

parse_overrides [source]

Parse the overrides from the command line into a dictionary of key value pairs.

Parameters

PARAMETER DESCRIPTION
args

The arguments to parse

TYPE: List[str]

RETURNS DESCRIPTION
Dict[str, Any]

The parsed overrides as a dictionary

Source code in confit/cli.py
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
def parse_overrides(args: List[str]) -> Dict[str, Any]:
    """
    Parse the overrides from the command line into a dictionary
    of key value pairs.

    Parameters
    ----------
    args: List[str]
        The arguments to parse

    Returns
    -------
    Dict[str, Any]
        The parsed overrides as a dictionary
    """
    result = {}
    while args:
        opt = args.pop(0)
        err = f"Invalid config override '{opt}'"
        if opt.startswith("--"):
            opt = opt.replace("--", "")
            if "=" in opt:
                opt, value = opt.split("=", 1)
            else:
                if not args or args[0].startswith("--"):
                    value = "true"
                else:
                    value = args.pop(0)
            opt = opt.replace("-", "_")
            result[opt] = loads(value)
        else:
            print(f"{err}: doesn't support shorthands")
            exit(1)
    return result