Skip to content
Snippets Groups Projects
null.py 5.75 KiB
#
# Copyright (C) 2021 Associated Universities, Inc. Washington DC, USA.
#
# This file is part of NRAO Workspaces.
#
# Workspaces is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# Workspaces is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with Workspaces.  If not, see <https://www.gnu.org/licenses/>.

import argparse
import importlib.metadata
import logging
import os
import random
import sys
import time

from . import __version__ as version

_DESCRIPTION = """Workspaces null executable, a status capture test of the Workspaces system. Version {}"""
ERRORS = {
    1: "ERROR: An error has been purposefully induced.",
    2: "ERROR: Unable to breach the mainframe.",
    3: "ERROR: Your standard run-of-the-mill error.",
    4: "ERROR: Very special and specific heisenbug-caused error that will take several days and several programmers to debug.",
    5: "ERROR: [insert hilarious error joke here]",
}

logger = logging.getLogger("null")
logger.setLevel(logging.INFO)
handler = logging.StreamHandler(stream=sys.stdout)


def change_to_error_handler():
    logger.removeHandler(handler)
    err_handler = logging.StreamHandler(stream=sys.stderr)
    logger.addHandler(err_handler)


def print_error():
    """
    Logs an error message to stderr.
    """
    change_to_error_handler()
    logger.error("ERROR: This is an error.")


def print_greeting():
    """
    Prints a friendly greeting to stdout
    """
    logger.info("Hello, world!")
    logger.debug("And goodbye, world...")


def exit_with_failure():
    """
    Exits with status code 1
    """
    change_to_error_handler()
    logger.error(ERRORS[1])
    sys.exit(1)


def exit_randomly():
    """
    Exits with a random status code between -50 and 50
    """
    change_to_error_handler()
    status_code = random.choice([*ERRORS.keys()])
    logger.error("Exiting with status code {}: {}".format(status_code, ERRORS[status_code]))
    sys.exit(status_code)


def take_nap():
    """
    Sleeps for 5 seconds
    """
    logger.debug("Going to sleep...")
    time.sleep(5)
    logger.debug("Waking up.")


def dump_core():
    """
    Makes a call to os.abort() which dumps the core
    """
    logger.debug("Aborting and dumping core...", stack_info=True)
    os.abort()


class Null:
    """
    Null executable that executes null functionality based on arguments given
    """

    def __init__(self, args: argparse.Namespace, verbose: bool):
        self.args = args
        if verbose:
            logger.setLevel(logging.DEBUG)
        self.args_to_funcs = {
            "print_error": print_error,
            "greeting": print_greeting,
            "exit_fail": exit_with_failure,
            "exit_random": exit_randomly,
            "nap": take_nap,
            "dump": dump_core,
        }

    def execute(self):
        """
        Executes command specified by CL arguments.

        :return:
        """
        for arg, val in vars(self.args).items():
            if val and arg in self.args_to_funcs:
                self.args_to_funcs[arg]()


def make_arg_parser() -> argparse.ArgumentParser:
    """
    Creates an argparse arguments parser with appropriate options

    :return: Said argument parser
    """
    parser = argparse.ArgumentParser(
        description=_DESCRIPTION.format(version),
        formatter_class=argparse.RawTextHelpFormatter,
    )
    parser.add_argument("--version", action="version", version=importlib.metadata.version("ssa-null"))
    options = parser.add_argument_group("options", "settings for altering program behavior")
    options.add_argument(
        "-v",
        "--verbose",
        action="store_true",
        required=False,
        dest="verbose",
        default=False,
        help="allow the program the gift of speech",
    )

    functions = parser.add_mutually_exclusive_group(required=False)
    functions.add_argument(
        "-pe",
        "--print-error",
        action="store_true",
        required=False,
        dest="print_error",
        default=False,
        help="print out aggressive message to stderr",
    )
    functions.add_argument(
        "-g",
        "--greeting",
        action="store_true",
        required=False,
        dest="greeting",
        default=False,
        help="print out a friendly greeting to stdout",
    )
    functions.add_argument(
        "-ef",
        "--exit-fail",
        action="store_true",
        required=False,
        dest="exit_fail",
        default=False,
        help="print error message and exit with status code -1",
    )
    functions.add_argument(
        "-er",
        "--exit-random",
        action="store_true",
        required=False,
        dest="exit_random",
        default=False,
        help="print error message and exit with random status code within [-50, 50]",
    )
    functions.add_argument(
        "-n",
        "--nap",
        action="store_true",
        required=False,
        dest="nap",
        default=False,
        help="take a short nap",
    )
    functions.add_argument(
        "-d",
        "--dump",
        action="store_true",
        required=False,
        dest="dump",
        default=False,
        help="abort program and dump core",
    )
    return parser


def main():
    arg_parser = make_arg_parser()
    args = arg_parser.parse_args()
    logger.addHandler(handler)

    executable = Null(args, args.verbose)
    executable.execute()


if __name__ == "__main__":
    main()