CLI

Common CLI Modules

This module includes common CLI methods and parser extension

Attributes

BOLD module-attribute

BOLD = '\x1b[1m'

GRN module-attribute

GRN = '\x1b[1;32m'

NC module-attribute

NC = '\x1b[0m'

RED module-attribute

RED = '\x1b[1;31m'

YLW module-attribute

YLW = '\x1b[1;33m'

cli module-attribute

cli = CommonCLI()

Classes

CLIArgumentParser

CLIArgumentParser(subparser)
Source code in fedbiomed/common/cli.py
def __init__(self, subparser: argparse.ArgumentParser):

    self._subparser = subparser
    # Parser that is going to be add using subparser
    self._parser = None

Functions

default
default(args=None)

Default function for subparser command

Source code in fedbiomed/common/cli.py
def default(self, args: argparse.Namespace = None) -> None:
    """Default function for subparser command"""

    self._parser.print_help()

    return None

CommonCLI

CommonCLI()
Source code in fedbiomed/common/cli.py
def __init__(self) -> None:
    self._parser: argparse.ArgumentParser = argparse.ArgumentParser(
        prog="fedbiomed_run", formatter_class=argparse.RawTextHelpFormatter
    )

    self._subparsers = self._parser.add_subparsers()
    self._certificate_manager: CertificateManager = CertificateManager()
    self._environ = None
    self._description: str = ""
    self._args = None

    # Initialize configuration parser
    self.configuration_parser = ConfigurationParser(self._subparsers)

Attributes

arguments property
arguments

Gets global parser arguments

Returns:

Type Description
Namespace

Parser arguments

configuration_parser instance-attribute
configuration_parser = ConfigurationParser(_subparsers)
description property writable
description

Gets description of CLI

Returns:

Type Description
str

Description (Intro) for the CLI

parser property
parser

Gets parser for CLI

Returns:

Type Description
ArgumentParser

Main argument parser object

subparsers property
subparsers

Gets subparsers of common cli

Returns:

Type Description

Subparsers of CLI parser

Functions

config_action staticmethod
config_action(this, component)

Returns CLI argument action for config file name

Source code in fedbiomed/common/cli.py
@staticmethod
def config_action(this: "CommonCLI", component: ComponentType):
    """Returns CLI argument action for config file name"""
    return ConfigNameAction
error staticmethod
error(message)

Prints given error message

Parameters:

Name Type Description Default
message str

Error message

required
Source code in fedbiomed/common/cli.py
@staticmethod
def error(message: str) -> None:
    """Prints given error message

    Args:
        message: Error message
    """
    print(f"{RED}ERROR:{NC}")
    print(f"{BOLD}{message}{NC}")
    logger.critical(message)
    sys.exit(1)
initialize
initialize()

Initializes parser classes and common parser for child clisses.

This parser classes will be added by child classes.

Source code in fedbiomed/common/cli.py
def initialize(self):
    """Initializes parser classes and common parser for child clisses.

    This parser classes will be added by child classes.
    """

    for arg_parser in self._arg_parsers_classes:
        p = arg_parser(self._subparsers)
        p.initialize()
        self._arg_parsers.update({arg_parser.__name__: p})

    self.initialize_certificate_parser()
initialize_certificate_parser
initialize_certificate_parser()

Common arguments

Source code in fedbiomed/common/cli.py
def initialize_certificate_parser(self):
    """Common arguments"""

    # Add certificate sub parser (sub-command)
    certificate_parser = self._subparsers.add_parser(
        "certificate",
        help="Command to manage certificates in node and researcher components. "
        "Please see 'certificate --help' for more information.",
        prog="fedbiomed_run [ node | researcher ] [--config [CONFIG_FILE]] certificate",
    )

    def print_help(args):
        certificate_parser.print_help()

    certificate_parser.set_defaults(func=print_help)

    # Create sub parser under `certificate` command
    certificate_sub_parsers = certificate_parser.add_subparsers(
        description="Commands that can be used with the option `certificate`",
        title="Subcommands",
    )

    register_parser = certificate_sub_parsers.add_parser(
        "register",
        help="Register certificate of specified party. Please run 'fedbiomed_run "
            "[COMPONENT SPECIFICATION] certificate register --help'",
    )  # command register

    list_parser = certificate_sub_parsers.add_parser(
        "list", help="Lists registered certificates"
    )  # command list
    delete_parser = certificate_sub_parsers.add_parser(
        "delete", help="Deletes specified certificate from database"
    )  # command delete

    # Command `certificate generate`
    generate = certificate_sub_parsers.add_parser(
        "generate",
        help="Generates certificate for given component/party if files don't exist yet. "
        "Overwrites existing certificate file if '--force' option is given. "
        "Uses an alternate directory if '--path DIRECTORY' is given",
    )

    # Command `certificate generate`
    prepare = certificate_sub_parsers.add_parser(
        "registration-instructions",
        help="Prepares certificate of current component to send other FL participant"
            "through trusted channel.",
    )

    register_parser.set_defaults(func=self._register_certificate)
    list_parser.set_defaults(func=self._list_certificates)
    delete_parser.set_defaults(func=self._delete_certificate)
    generate.set_defaults(func=self._generate_certificate)
    prepare.set_defaults(func=self._prepare_certificate_for_registration)

    # Add arguments
    register_parser.add_argument(
        "-pk",
        "--public-key",
        metavar="PUBLIC_KEY",
        type=str,
        nargs="?",
        required=True,
        help="Certificate/key that will be registered",
    )

    register_parser.add_argument(
        "-pi",
        "--party-id",
        metavar="PUBLIC_ID",
        type=str,
        nargs="?",
        required=True,
        help="ID of the party to which the certificate is to be registered (component ID).",
    )

    register_parser.add_argument(
        "--upsert",
        action="store_true",
        help="Updates if certificate of given party id is already existing.",
    )

    generate.add_argument(
        "--path",
        type=str,
        nargs="?",
        required=False,
        help="The path where certificates will be saved. By default it will overwrite"
            "existing certificate.",
    )

    generate.add_argument(
        "-f",
        "--force",
        action="store_true",
        help="Forces to overwrite certificate files",
    )
initialize_magic_dev_environment_parsers
initialize_magic_dev_environment_parsers()

Initializes argument parser for the option to create development environment.

Source code in fedbiomed/common/cli.py
def initialize_magic_dev_environment_parsers(self) -> None:
    """Initializes argument parser for the option to create development environment."""
    magic = self._subparsers.add_parser(
        "certificate-dev-setup",
        description="Prepares development environment by registering certificates"
            "of each component created in a single clone of Fed-BioMed. Parses configuration "
            "files ends with '.ini' that are created in 'etc' directory. This setup requires "
            "to have one 'researcher' and at least 2 nodes.",
        help="Prepares development environment by registering certificates of each "
            "component created in a single clone of Fed-BioMed.",
    )
    magic.set_defaults(func=self._create_magic_dev_environment)
initialize_optional
initialize_optional()

Initializes optional subparser

Optional subparsers are not going to be visible for the CLI that are inherited from CommonCLI class as long as intialize_optional method is not executed.

Source code in fedbiomed/common/cli.py
def initialize_optional(self):
    """Initializes optional subparser

    Optional subparsers are not going to be visible for the CLI that are
    inherited from CommonCLI class as long as `intialize_optional` method
    is not executed.
    """

    self.configuration_parser.initialize()
    self.initialize_magic_dev_environment_parsers()
parse_args
parse_args(args_=None)

Parse arguments after adding the arguments

Attention

Please make sure this method is called after all necessary arguments are set
Source code in fedbiomed/common/cli.py
def parse_args(self, args_=None):
    """Parse arguments after adding the arguments

    !!! warning "Attention"
            Please make sure this method is called after all necessary arguments are set
    """
    args, unknown_args = self._parser.parse_known_args(args_)
    if hasattr(args, "func"):
        specs = get_method_spec(args.func)
        if specs:
            # If default function has 2 arguments
            if len(specs) > 1:
                return args.func(args, unknown_args)

            # Run parser_args to raise error for unrecognized arguments
            if unknown_args:
                args = self._parser.parse_args(args_)
            args.func(args)
        else:
            # Raise for unrecognized arguments
            if unknown_args:
                self._parser.parse_args(args_)
            args.func()
    else:
        self._parser.print_help()
set_environ
set_environ(environ)

Sets environ object

Source code in fedbiomed/common/cli.py
def set_environ(self, environ):
    """Sets environ object"""
    self._environ = environ
success staticmethod
success(message)

Prints given message with success tag

Parameters:

Name Type Description Default
message str

Message to print as successful operation

required
Source code in fedbiomed/common/cli.py
@staticmethod
def success(message: str) -> None:
    """Prints given message with success tag

    Args:
        message: Message to print as successful operation
    """
    print(f"{GRN}Operation successful! {NC}")
    print(f"{BOLD}{message}{NC}")

ConfigNameAction

ConfigNameAction(*args, **kwargs)

Bases: ABC, Action

Action for the argument config

This action class gets the config file name and set environ object before executing any command.

Source code in fedbiomed/common/cli.py
def __init__(self, *args, **kwargs):
    super().__init__(*args, **kwargs)

    # Sets environ by default if option string for config is not present.
    # The default is defined by the argument parser.
    if (
        not set(self.option_strings).intersection(set(sys.argv))
        and not set(["--help", "-h"]).intersection(set(sys.argv))
        and len(sys.argv) > 2
    ):
        self.set_environ(self.default)

Functions

import_environ abstractmethod
import_environ()

Implements environ import

Returns:

Type Description
Environ

Environ object

Source code in fedbiomed/common/cli.py
@abstractmethod
def import_environ(self) -> "Environ":
    """Implements environ import


    Returns:
        Environ object
    """
set_environ
set_environ(config_file)

Sets environ

Parameters:

Name Type Description Default
config_file str

Name of the config file that is activate

required
Source code in fedbiomed/common/cli.py
def set_environ(self, config_file: str):
    """Sets environ

    Args:
      config_file: Name of the config file that is activate
    """

    print(f"\n# {GRN}Using configuration file:{NC} {BOLD}{config_file}{NC} #")
    os.environ["CONFIG_FILE"] = config_file

    environ = self.import_environ()

    os.environ[f"FEDBIOMED_ACTIVE_{self._component.name}_ID"] = environ["ID"]

    # Sets environ for the CLI. This implementation is required for
    # the common CLI option that are present in fedbiomed.common.cli.CommonCLI
    self._this.set_environ(environ)

    # this may be changed on command line or in the config_node.ini
    logger.setLevel("DEBUG")

ConfigurationParser

ConfigurationParser(subparser)

Bases: CLIArgumentParser

Instantiates configuration parser

Source code in fedbiomed/common/cli.py
def __init__(self, subparser: argparse.ArgumentParser):

    self._subparser = subparser
    # Parser that is going to be add using subparser
    self._parser = None

Functions

create
create(args)

CLI Handler for creating configuration file and assets for given component

This method doesn't yet concentrate all actions for creating configuration file for

given component. Since, environ will be imported through component CLI, configuration file will be automatically created. In future, it might be useful to generate configuration files.

Source code in fedbiomed/common/cli.py
def create(self, args):
    """CLI Handler for creating configuration file and assets for given component

    TODO: This method doesn't yet concentrate all actions for creating configuration file for
        given component. Since, `environ` will be imported through component CLI, configuration
        file will be automatically created. In future, it might be useful to generate configuration
        files.
    """

    config = self._create_config_instance(args.component, args.root, args.name)

    # Overwrite force configuration file
    if config.is_config_existing() and args.force:
        print("Overwriting existing configuration file")
        config.generate(force=True)

    # Use exisintg one (do nothing)
    elif config.is_config_existing() and not args.force:
        if not args.use_current:
            print(
                f'Configuration file "{config.path}" is alreay existing for name "{config.name}". '
                "Please use --force option to overwrite"
            )
            exit(101)
        # Generate wont do anything
        config.generate()
    else:
        logger.info(f'Generation new configuration file "{config.name}"')
        config.generate()
initialize
initialize()

Initializes argument parser for creating configuration file.

Source code in fedbiomed/common/cli.py
def initialize(self):
    """Initializes argument parser for creating configuration file."""

    self._parser = self._subparser.add_parser(
        "configuration",
        help="The helper for generating or updating component configuration files, see `configuration -h`"
        " for more details",
    )

    self._parser.set_defaults(func=self.default)

    # Common parser to register common arguments for create and refresh
    common_parser = argparse.ArgumentParser(add_help=False)
    common_parser.add_argument(
        "-r",
        "--root",
        metavar="ROOT_PATH_FEDBIOMED",
        type=str,
        nargs="?",
        default=None,
        help="Root directory for configuration and Fed-BioMed setup",
    )

    # Add arguments
    common_parser.add_argument(
        "-n",
        "--name",
        metavar="CONFIGURATION_FILE_NAME",
        type=str,
        nargs="?",
        required=False,
        help="Name of configuration file",
    )

    common_parser.add_argument(
        "-c",
        "--component",
        metavar="COMPONENT_TYPE[ NODE|RESEARCHER ]",
        type=str,
        nargs="?",
        required=True,
        help="Component type NODE or RESEARCHER",
    )

    # Create sub parser under `configuration` command
    configuration_sub_parsers = self._parser.add_subparsers()

    create = configuration_sub_parsers.add_parser(
        "create",
        parents=[common_parser],
        help="Creates configuration file for the specified component if it does not exist. "
        "If the configuration file exists, leave it unchanged",
    )

    refresh = configuration_sub_parsers.add_parser(
        "refresh",
        parents=[common_parser],
        help="Refreshes the configuration file by overwriting parameters without changing component ID",
    )

    create.add_argument(
        "-uc",
        "--use-current",
        action="store_true",
        help="Creates configuration only if there isn't an existing one",
    )

    create.add_argument(
        "-f", "--force", action="store_true", help="Force configuration create"
    )

    create.set_defaults(func=self.create)
    refresh.set_defaults(func=self.refresh)
refresh
refresh(args)

Refreshes configuration file

Source code in fedbiomed/common/cli.py
def refresh(self, args):
    """Refreshes configuration file"""

    config = self._create_config_instance(args.component, args.root, args.name)
    print(
        "Refreshing configuration file using current environment variables. This operation will overwrite"
        "existing configuration file without changing component id."
    )

    # Refresh
    config.refresh()
    print("Configuration has been updated!")

Functions