Source code for gfw.common.cli.option
"""CLI option wrapper for argparse.
This module provides a convenient wrapper around argparse to define command-line interface
options using the :class:`Option` class. It supports automatic argument registration,
default values, and custom types, including boolean flags.
"""
import argparse
from functools import cached_property
from typing import Any, Callable
[docs]
class Option:
"""Represents a CLI option.
Provides a declarative interface to define command-line options that can be
added to an :class:`argparse.ArgumentParser` instance via its
:meth:`~argparse.ArgumentParser.add_argument` method.
Args:
*flags:
One or more command-line flag strings for this option.
These are passed directly to argparse's :meth:`~argparse.ArgumentParser.add_argument`.
The first long flag (if any) is used to derive the internal name,
which is also used as the destination (:meth:`dest`) when parsing.
Three examples:
.. code-block:: python
Option("-c", "--config-file", ...) # Short and long form
Option("--verbose", ...) # Long form only
Option("-v", ...) # Short form only
type:
A callable that converts the command-line string to the desired Python type.
Typically, a built-in type like ``str``, ``int``, ``float``, or ``bool``.
default:
The default value to use if the option is not provided. This should match
the specified type, although no enforcement is currently done.
required:
We capture the required flag so we can validate it not only against command-line
parameters but also against the provided config file.
**kwargs:
Additional keyword arguments passed directly to
:meth:`~argparse.ArgumentParser.add_argument`.
"""
def __init__(
self,
*flags: str,
type: Callable[..., Any],
default: Any = None,
required: bool = False,
**kwargs: Any,
) -> None:
"""Initializes an Option instance."""
self.flags = flags
self.type = type
self.default = default
self.required = required
self.kwargs = kwargs
if required and default is not None:
raise argparse.ArgumentTypeError(
f"You cannot set a default in a required argument: {self.dest}"
)
@cached_property
def dest(self) -> str:
"""Returns the internal variable name used by argparse for this option.
Uses the last long flag (e.g., ``--config-file``) if present, or the first
flag as a fallback. Dashes are converted to underscores for compatibility
with argparse's variable naming.
"""
first_flag = next(iter(self.flags)).lstrip("-")
last_long_flag = None
for f in self.flags:
if f.startswith("--"):
last_long_flag = f.lstrip("-")
return (last_long_flag or first_flag).replace("-", "_")