rpdelaney/dumbpw

View on GitHub
dumbpw/cli.py

Summary

Maintainability
A
1 hr
Test Coverage
"""Create a command-line interface entrypoint."""

import fileinput
import sys
from typing import NoReturn

import click
import deal


deal.activate()

from .constants import (  # noqa: E402
    DEFAULT_BLOCKS,
    PASSWORD_LENGTH_MAX,
    PASSWORD_LENGTH_MIN,
)
from .engine import search  # noqa: E402
from .exceptions import DumbValueError  # noqa: E402


@deal.has("io", "global", "stderr", "stdout")
@deal.raises(SystemExit, RuntimeError)
@click.command(
    no_args_is_help=True,
)
@click.version_option()
@click.option(
    "--min-uppercase",
    type=int,
    default=1,
    help="The minimum number of uppercase characters.",
)
@click.option(
    "--min-lowercase",
    type=int,
    default=1,
    help="The minimum number of lowercase characters.",
)
@click.option(
    "--min-digits",
    type=int,
    default=1,
    help="The minimum number of digit characters.",
)
@click.option(
    "--min-specials",
    type=int,
    default=0,
    help="The minimum number of special characters.",
)
@click.option(
    "--blocklist",
    type=str,
    default=DEFAULT_BLOCKS,
    show_default=True,
    help="Characters that may not be in the password.",
)
@click.option(
    "--allow-repeating/--reject-repeating",
    help="Allow or reject repeating characters in the password.",
    default=False,
    show_default=True,
)
@click.argument(
    "length",
    type=click.IntRange(
        min=PASSWORD_LENGTH_MIN,
        max=PASSWORD_LENGTH_MAX,
    ),
)
@click.option(
    "--specials",
    envvar="DUMBPW_SPECIALS",
    help=(
        "Non-alphanumeric characters that may be in the password. "
        "Pass '-' to read from standard input."
    ),
    default="",
)
def cli(  # noqa: PLR0913
    *,
    length: int,
    min_uppercase: int,
    min_lowercase: int,
    min_digits: int,
    min_specials: int,
    specials: str,
    blocklist: str,
    allow_repeating: bool,
) -> NoReturn:
    """A dumb password generator."""  # noqa: D401
    if specials == "-":
        specials = "".join(char for char in fileinput.input(files="-")).strip()

    try:
        try_password = search(
            length=length,
            min_uppercase=min_uppercase,
            min_lowercase=min_lowercase,
            min_digits=min_digits,
            min_specials=min_specials,
            specials=specials,
            blocklist=blocklist,
            allow_repeating=allow_repeating,
        )
    except DumbValueError as ve:
        print(ve, file=sys.stderr)
        sys.exit(1)

    print(try_password)

    sys.exit(0)