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
configuration_parser instance-attribute
configuration_parser = ConfigurationParser(_subparsers)
description property
writable
description
parser property
parser
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)
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!")