Source code for socon.core.management.subcommand

import sys

from typing import Any, List, Optional, Type

from socon.core.exceptions import CommandNotFound, ImproperlyConfigured
from socon.core.management import get_commands
from socon.core.management.base import (
    BaseCommand,
    CommandManager,
    Config,
    handle_default_options,
)


class SubcommandManager(CommandManager):
    name = "subcommands"
    lookup_module = "management.commands.subcommands"

    def get_usage_message(self, prog_name: str) -> List[str]:
        return ["List of available subcommands:"]


[docs]class Subcommand(BaseCommand, abstract=True): """ Base class for every commands carry subcommands. A subcommand can by of any type (ProjectCommand or BaseCommand). Each subcommand must inherit from this class and set the `subcommand_manager` attribute """ # The name of the manager that holds the subcommands subcommand_manager: Optional[str] = None def __init_subclass__(cls, **kwargs: Any) -> None: super().__init_subclass__(**kwargs) # If the base class has not been linked to a registry, we raise # an error as we won't be able to register the subclass if cls.subcommand_manager is None: raise ImproperlyConfigured( "{} class must link a subcommand manager".format(cls.__name__) ) def get_subcommands(self) -> Type[CommandManager]: """Get the list of subcommands for the specific project""" return get_commands(self.subcommand_manager) def get_subcommand(self, subcommand: str, argv: list) -> Type[BaseCommand]: subcommands = self.get_subcommands() try: command = subcommands.search_command(subcommand) return command() except CommandNotFound: sys.stderr.write("Error: Unknown subcommand '{}'\n".format(subcommand)) self.print_help(argv[0], f"{argv[1]} SUBCOMMAND") sys.exit(1) def print_help(self, prog_name, subcommand): commands = self.get_subcommands() usage = commands.get_commands_usage(prog_name, show_registry=False) super().print_help(prog_name, subcommand) sys.stdout.write("{}\n\n".format(usage)) def parse_args(self, argv: tuple) -> Config: """Parse command line arguments for subcommands""" self.argv = argv subcommand = None if len(argv) > 2 and not argv[2].startswith("-"): subcommand = argv[2] command = self.get_subcommand(subcommand, argv) parser = command.create_parser(argv[0], "{} {}".format(argv[1], subcommand)) parse_argv = argv[3:] else: parser = self.create_parser(argv[0], argv[1]) parse_argv = argv[2:] command = self if subcommand is None: self.print_help(argv[0], f"{argv[1]} SUBCOMMAND") sys.exit(0) extras_args = [] if command.keep_extras_args: options, extras_args = parser.parse_known_args(parse_argv) else: options = parser.parse_args(parse_argv) handle_default_options(options) # Set the subcommand in the options here as we don't want to add # the subcommand as a positional arg. This is to avoid wrong help # message when we run the subcommand. setattr(options, "subcommand", subcommand) # Save the subcommand for later use in the handle method. This is to avoid # to seach again for the command and initialize it with no config. #42 self.__subcommand = command # Create a config object that will store all the options return command.set_config(options, extras_args) def handle(self, config: Config): """Execute the subcommand""" return self.__subcommand.execute(config)