def parser(input_args=None):
    """
    Command line parser for *sick*.
    """

    parser = argparse.ArgumentParser(
        description="sick, the spectroscopic inference crank", epilog="Use "
            "'sick-model COMMAND -h' for information on a specific command."
            " Documentation and examples available at "
            "https://github.com/andycasey/sick")

    # Create a parent subparser.
    parent_parser = argparse.ArgumentParser(add_help=False)
    parent_parser.add_argument(
        "-v", "--verbose", dest="verbose", action="store_true", default=False,
        help="Vebose logging mode.")
    parent_parser.add_argument(
        "--clobber", dest="clobber", action="store_true", default=False, 
        help="Overwrite existing files if they already exist.")
    parent_parser.add_argument(
        "--debug", dest="debug", action="store_true", default=False,
        help="Enable debug mode. Any suppressed exception will be re-raised.")
    parent_parser.add_argument(
        "-o", "--output_dir", dest="output_dir", nargs="?", type=str,
        help="Directory for the files that will be created. If not given, this"
        " defaults to the current working directory.", default=os.getcwd())

    # Create subparsers.
    subparsers = parser.add_subparsers(title="command", dest="command",
        description="Specify the action to perform.")

    # Create parser for the aggregate command
    aggregate_parser = subparsers.add_parser(
        "aggregate", parents=[parent_parser],
        help="Aggregate many result files into a single tabular FITS file.")
    aggregate_parser.add_argument("output_filename", type=str, nargs=1,
        help="Output filename to aggregate results into.")
    aggregate_parser.add_argument("result_filenames", nargs="+",
        help="The YAML result filenames to combine.")
    aggregate_parser.set_defaults(func=aggregate)

    # Create parser for the estimate command
    estimate_parser = subparsers.add_parser(
        "estimate", parents=[parent_parser],
        help="Compute a point estimate of the model parameters given the data.")
    estimate_parser.add_argument(
        "model", type=str,
        help="The model filename in YAML-style formatting.")
    estimate_parser.add_argument(
        "spectrum_filenames", nargs="+",
        help="Filenames of (observed) spectroscopic data.")
    estimate_parser.add_argument(
        "--filename-prefix", "-p", dest="filename_prefix",
        type=str, help="The filename prefix to use for the output files.")
    estimate_parser.add_argument(
        "--no-plots", dest="plotting", action="store_false", default=True,
        help="Disable plotting.")
    estimate_parser.add_argument(
        "--plot-format", "-pf", dest="plot_format", action="store", type=str, 
        default="png", help="Format for output plots (default: %(default)s)")
    estimate_parser.add_argument(
        "-r", dest="read_from_filename", action="store", type=bool,
        default=False, help="Read spectrum paths from a filename.")
    estimate_parser.set_defaults(func=estimate)

    # Create parser for the optimise command
    optimise_parser = subparsers.add_parser(
        "optimise", parents=[parent_parser],
        help="Optimise the model parameters, given the data.")
    optimise_parser.add_argument(
        "model", type=str,
        help="The model filename in YAML-style formatting.")
    optimise_parser.add_argument(
        "spectrum_filenames", nargs="+",
        help="Filenames of (observed) spectroscopic data.")
    optimise_parser.add_argument(
        "--filename-prefix", "-p", dest="filename_prefix", type=str,
        help="The filename prefix to use for the output files.")
    optimise_parser.add_argument(
        "--no-plots", dest="plotting", action="store_false", default=True,
        help="Disable plotting.")
    optimise_parser.add_argument(
        "--plot-format", "-pf", dest="plot_format", action="store", type=str,
        default="png", help="Format for output plots (default: %(default)s)")
    optimise_parser.add_argument(
        "-r", dest="read_from_filename", action="store", type=bool,
        default=False, help="Read spectrum paths from a filename.")
    optimise_parser.set_defaults(func=optimise)

    # Create parser for the infer command
    infer_parser = subparsers.add_parser(
        "infer", parents=[parent_parser],
        help="Infer the model parameters, given the data.")
    infer_parser.add_argument(
        "model", type=str,
        help="The model filename in YAML-style formatting.")
    infer_parser.add_argument(
        "spectrum_filenames", nargs="+",
        help="Filenames of (observed) spectroscopic data.")
    infer_parser.add_argument(
        "--filename-prefix", "-p", dest="filename_prefix", type=str,
        help="The filename prefix to use for the output files.")
    infer_parser.add_argument(
        "--no-chains", dest="save_chain_files", action="store_false",
        default=True, help="Do not save the chains to disk.", )
    infer_parser.add_argument(
        "--no-plots", dest="plotting", action="store_false", default=True,
        help="Disable plotting.")
    infer_parser.add_argument(
        "--plot-format", "-pf", dest="plot_format", action="store", type=str,
        default="png", help="Format for output plots (default: %(default)s)")
    infer_parser.add_argument(
        "-r", dest="read_from_filename", action="store", type=bool,
        default=False, help="Read spectrum paths from a filename.")
    infer_parser.set_defaults(func=infer)

    args = parser.parse_args(input_args)
    logger.setLevel(logging.DEBUG if args.verbose else logging.INFO)

    # Check plot formats.
    if args.command.lower() in ("estimate", "optimise", "infer") \
    and args.plotting:

        import matplotlib.pyplot as plt
        fig = plt.figure()
        available = fig.canvas.get_supported_filetypes().keys()
        plt.close(fig)

        if args.plot_format.lower() not in available:
            raise ValueError("plotting format {0} is unavailable: Options are:"\
                " {1}".format(args.plot_format.lower(), ", ".join(available)))

    # Create a default filename prefix based on the input filename arguments
    if args.command.lower() in ("estimate", "optimise", "infer") \
    and args.filename_prefix is None \
    and not args.read_from_filename:
        args.filename_prefix = _default_output_prefix(args.spectrum_filenames)

        handler = logging.FileHandler("{}.log".format(
            os.path.join(args.output_dir, args.filename_prefix)))
        formatter = logging.Formatter("%(asctime)s [%(levelname)s] %(message)s")
        handler.setFormatter(formatter)
        logger.addHandler(handler)

    else:
        args.filename_prefix = ""

    return args