Source code for pyscaffold.cli

# -*- coding: utf-8 -*-
"""
Command-Line-Interface of PyScaffold
"""

import argparse
import logging
import os.path
import sys

from pkg_resources import parse_version

from . import __version__ as pyscaffold_version
from . import api, info, shell, templates, utils
from .exceptions import NoPyScaffoldProject
from .log import ReportFormatter, logger
from .utils import get_id


[docs]def add_default_args(parser): """Add the default options and arguments to the CLI parser. Args: parser (argparse.ArgumentParser): CLI parser object """ parser.add_argument(dest="project", help="project name", metavar="PROJECT") parser.add_argument( "-p", "--package", dest="package", required=False, help="package name (default: project name)", metavar="NAME", ) parser.add_argument( "-d", "--description", dest="description", required=False, help="package description", metavar="TEXT", ) license_choices = templates.licenses.keys() parser.add_argument( "-l", "--license", dest="license", choices=license_choices, required=False, help="package license like {choices} (default: {default})".format( choices=", ".join(license_choices), default="mit" ), metavar="LICENSE", ) parser.add_argument( "-u", "--url", dest="url", required=False, help="package url", metavar="URL" ) parser.add_argument( "-f", "--force", dest="force", action="store_true", default=False, help="force overwriting an existing directory", ) parser.add_argument( "-U", "--update", dest="update", action="store_true", default=False, help="update an existing project by replacing the most important files" " like setup.py etc. Use additionally --force to " "replace all scaffold files.", ) parser.add_argument( "-V", "--version", action="version", version="PyScaffold {ver}".format(ver=pyscaffold_version), ) parser.add_argument( "-v", "--verbose", action="store_const", const=logging.INFO, dest="log_level", help="show additional information about current actions", ) parser.add_argument( "-vv", "--very-verbose", action="store_const", const=logging.DEBUG, dest="log_level", help="show all available information about current actions", ) group = parser.add_mutually_exclusive_group() group.add_argument( "-P", "--pretend", dest="pretend", action="store_true", default=False, help="do not create project, but displays the log of all operations" " as if it had been created.", ) group.add_argument( "--list-actions", dest="command", action="store_const", const=list_actions, help="do not create project, but show a list of planned actions", )
[docs]def parse_args(args): """Parse command line parameters respecting extensions Args: args ([str]): command line parameters as list of strings Returns: dict: command line parameters """ from pkg_resources import iter_entry_points # create the argument parser parser = argparse.ArgumentParser( description="PyScaffold is a tool for easily putting up the scaffold " "of a Python project." ) parser.set_defaults(log_level=logging.WARNING, extensions=[], command=run_scaffold) add_default_args(parser) # load and instantiate extensions cli_extensions = [ extension.load()(extension.name) for extension in iter_entry_points("pyscaffold.cli") # TODO: sort in the same way the extensions are activated for # determinism ] # add a group for mutually exclusive external generators mutex_group = parser.add_mutually_exclusive_group() for extension in cli_extensions: if extension.mutually_exclusive: extension.augment_cli(mutex_group) else: extension.augment_cli(parser) # Parse options and transform argparse Namespace object into common dict opts = {k: v for k, v in vars(parser.parse_args(args)).items() if v is not None} return opts
[docs]def process_opts(opts): """Process and enrich command line arguments Args: opts (dict): dictionary of parameters Returns: dict: dictionary of parameters from command line arguments """ # When pretending the user surely wants to see the output if opts["pretend"]: opts["log_level"] = logging.INFO logger.reconfigure(opts) # In case of an update read and parse setup.cfg if opts["update"]: try: opts = info.project(opts) except Exception as e: raise NoPyScaffoldProject from e # Save cli params for later updating opts["cli_params"] = {"extensions": list(), "args": dict()} for extension in opts["extensions"]: opts["cli_params"]["extensions"].append(extension.name) if extension.args is not None: opts["cli_params"]["args"][extension.name] = extension.args # Strip (back)slash when added accidentally during update opts["project"] = opts["project"].rstrip(os.sep) # Remove options with None values so setdefault works opts = {k: v for k, v in opts.items() if v is not None} return opts
[docs]def run_scaffold(opts): """Actually scaffold the project, calling the python API Args: opts (dict): command line options as dictionary """ api.create_project(opts) if opts["update"] and not opts["force"]: note = ( "Update accomplished!\n" "Please check if your setup.cfg still complies with:\n" "https://pyscaffold.org/en/v{}/configuration.html" ) base_version = parse_version(pyscaffold_version).base_version print(note.format(base_version))
[docs]def list_actions(opts): """Do not create a project, just list actions considering extensions Args: opts (dict): command line options as dictionary """ actions = api.discover_actions(opts.get("extensions", [])) print("Planned Actions:") for action in actions: print(ReportFormatter.SPACING + get_id(action))
[docs]def main(args): """Main entry point for external applications Args: args ([str]): command line arguments """ utils.check_setuptools_version() opts = parse_args(args) opts = process_opts(opts) opts["command"](opts)
[docs]@shell.shell_command_error2exit_decorator @utils.exceptions2exit([RuntimeError]) def run(): """Entry point for console script""" main(sys.argv[1:])
if __name__ == "__main__": main(sys.argv[1:])