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'
Classes
CLIArgumentParser
CLIArgumentParser(subparser, parser=None)
Source code in fedbiomed/common/cli.py
def __init__(self, subparser: argparse.ArgumentParser, parser = None):
self._subparser = subparser
# Parser that is going to be add using subparser
self._parser = None
self._main_parser = parser
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", formatter_class=argparse.RawTextHelpFormatter
)
self._subparsers = self._parser.add_subparsers()
self._certificate_manager: CertificateManager = CertificateManager()
self._description: str = ""
self._args = None
# Initialize configuration parser
self.configuration_parser = ComponentParser(self._subparsers)
Attributes
arguments property
arguments
config instance-attribute
config
configuration_parser instance-attribute
configuration_parser = ComponentParser(_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 ComponentDirectoryAction
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 classes.
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 classes.
This parser classes will be added by child classes.
"""
self._parser.add_argument(
"-y",
action="store_true"
)
for arg_parser in self._arg_parsers_classes:
p = arg_parser(self._subparsers, self._parser)
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 [ node | researcher ] [--path [COMPONENT_DIRECTORY]] 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' "
"[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. "
"Uses an alternate directory if '--path DIRECTORY' is given."
" If files already exist, overwrite existing certificate.\n"
"Certificate are here refering to the public certificate and its associated private key "
"(the latter should remain secret and not shared to other parties)."
)
# 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 to the RESEARCHER|NODE component, in which certificate will be saved."
" By default it will overwrite existing certificate.",
)
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()
self.initialize_version()
initialize_version
initialize_version()
Initializes argument parser for common options.
Source code in fedbiomed/common/cli.py
def initialize_version(self):
"""Initializes argument parser for common options."""
self._parser.add_argument(
"--version",
"-v",
action='version',
version=str(__version__),
help="Print software version",
)
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()
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}")
ComponentDirectoryAction
ComponentDirectoryAction(*args, **kwargs)
Action for the argument config
This action class gets the config file name and set config object before executing any command.
Source code in fedbiomed/common/cli.py
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
# Sets config 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._create_config(self.default)
super().__init__(*args, **kwargs)
Functions
set_component abstractmethod
set_component(component_dir)
Implements configuration import
Parameters:
Name | Type | Description | Default |
---|---|---|---|
component_dir | str | Name of the config file for the component | required |
Source code in fedbiomed/common/cli.py
@abstractmethod
def set_component(self, component_dir: str) -> None:
"""Implements configuration import
Args:
component_dir: Name of the config file for the component
"""
ComponentParser
ComponentParser(subparser, parser=None)
Bases: CLIArgumentParser
Instantiates configuration parser
Source code in fedbiomed/common/cli.py
def __init__(self, subparser: argparse.ArgumentParser, parser = None):
self._subparser = subparser
# Parser that is going to be add using subparser
self._parser = None
self._main_parser = parser
Functions
create
create(args)
CLI Handler for creating configuration file and assets for given component
Source code in fedbiomed/common/cli.py
def create(self, args):
"""CLI Handler for creating configuration file and assets for given component
"""
if not args.path:
if args.component.lower() == "researcher":
component_path = os.path.join(os.getcwd(), DEFAULT_RESEARCHER_NAME)
else:
component_path = os.path.join(os.getcwd(), DEFAULT_NODE_NAME)
else:
component_path = args.path
# Researcher specific case ----------------------------------------------------
# This is a special case since researcher import
if args.component is None:
CommonCLI.error("Error: bad command line syntax")
if args.component.lower() == "researcher":
if DEFAULT_RESEARCHER_NAME in component_path and \
os.path.isdir(component_path) and \
not docker_special_case(component_path):
if not args.exist_ok:
CommonCLI.error(
f"Default component is already existing. In the directory {component_path} "
"please remove existing one to re-initiate"
)
sys.exit(1)
else:
CommonCLI.success(
"Component is already existing. Using existing component."
)
sys.exit(0)
else:
self._get_component_instance(component_path, args.component)
return
else:
component = self._get_component_instance(component_path, args.component)
# Overwrite force configuration file
if component.is_component_existing(component_path):
if not args.exist_ok:
CommonCLI.error(
f"Component is already existing in the directory `{component_path}`. To ignore "
"this error please execute component creation using `--exist-ok`"
)
else:
CommonCLI.success(
"Component is already exsiting. Using existing component."
)
return
component.initiate(component_path)
CommonCLI.success(f"Component has been initialized in {component_path}")
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(
"component",
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(
"-p",
"--path",
action=UniqueStore,
metavar="COMPONENT_PATH",
type=str,
nargs="?",
required=False,
help="Path to specificy where Fed-BioMed component will be intialized.",
)
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
component_sub_parsers = self._parser.add_subparsers()
create = component_sub_parsers.add_parser(
"create",
parents=[common_parser],
help="Creates component folder for the specified component if it does not exist. "
"If the component folder exists, leave it unchanged",
)
create.add_argument(
"-eo",
"--exist-ok",
action="store_true",
help="Creates configuration only if there isn't an existing one",
)
create.set_defaults(func=self.create)