painless-software / python-cli-test-helpers

Useful helpers for writing tests for your Python CLI program.
https://python-cli-test-helpers.readthedocs.io
GNU General Public License v3.0
28 stars 4 forks source link

Demonstrate `argparse` advanced features (example project) #54

Open bittner opened 4 months ago

bittner commented 4 months ago

Python's argparse module is very well documented, but it's still difficult to get started and implement more advanced use cases, later.

Hence, the example project in this repository, while staying simple and concise, should showcase the most handy, often-used features of argparse. One of those features is the argument help text, which can accommodate default values and other configured properties of the argument. Others may be using the type (and argument validation and transformation using custom type functions), and the epilog.

Examples

from argparse import ArgumentParser, ArgumentTypeError
from enum import Enum, auto

class Color(Enum):
    WHITE = auto()
    BLACK = auto()
    RED = auto()
    GREEN = auto()
    BLUE = auto()

    def __str__(self):
        return self.name

    @classmethod
    def from_string(cls, value):
        try:
            return cls[value.upper()]
        except KeyError:
            raise ArgumentTypeError(f"Invalid color: {value}")

parser = ArgumentParser()
parser.add_argument(
    "--color",
    type=Color.from_string,
    choices=list(Color),
    default=Color.WHITE,
    help="Pick from %(choices)s (default: %(default)s)",
)
import os
from argparse import ArgumentParser, ArgumentTypeError

TRUEISH = ["1", "on", "yes", "true"]
FALSEISH = ["0", "off", "no", "false"]
BOOLISH = TRUEISH + FALSEISH

def boolean(text, validate=True, default=False):
    val = text.strip().lower()
    if validate and val not in BOOLISH:
        raise ArgumentTypeError(f"Invalid boolean value: {val}")
    return val in TRUEISH if default is False else val not in FALSEISH

parser = ArgumentParser()
parser.add_argument(
    "--activate",
    type=boolean,
    default=os.environ.get("ACTIVATE", False),
    help="Activate the feature (env: ACTIVATE, default: %(default)s)",
)
parser.add_argument(
    "--debug",
    action="store_true",
    default=boolean(os.environ.get("DEBUG", False), validate=False),
    help="Turn debug logging on (env: DEBUG)",
)
from argparse import ArgumentParser, ArgumentTypeError
from pathlib import Path

def file_exists(filename):
    """Validates file existence and expands a file path."""
    if not Path(filename).exists():
        raise ArgumentTypeError(f"File does not exist: {filename}")
    if not Path(filename).is_file():
        raise ArgumentTypeError(f"Not a regular file: {filename}")
    return Path(filename).resolve()

parser = ArgumentParser()
parser.add_argument("existing_file", type=file_exists)