Skip to content

Commit

Permalink
Add new base and special classes for CLI
Browse files Browse the repository at this point in the history
  • Loading branch information
led02 committed Feb 26, 2024
1 parent abeb11a commit 7d5af3e
Showing 1 changed file with 124 additions and 154 deletions.
278 changes: 124 additions & 154 deletions src/hermes/commands/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,172 +8,142 @@
"""
This module provides the main entry point for the HERMES command line application.
"""
import pathlib
import argparse
import sys
import os
from os import access, R_OK
from importlib import metadata
import logging

from hermes.commands.workflow import (
clean,
harvest,
process,
curate,
deposit,
postprocess,
)
from hermes import config
from hermes.config import configure, init_logging


def log_header(header, summary=None):
_log = config.getLogger("cli")

dist = metadata.distribution("hermes")
meta = dist.metadata

if header is None:
title = f"{dist.name} workflow ({dist.version})"

_log.info(title)
_log.info("=" * len(title))
_log.info("")

if "Summary" in meta:
_log.info("%s", meta["Summary"])
_log.info("")

else:
_log.info("%s", header)
if summary:
_log.info("%s", summary)
_log.info("")


def _is_valid_file(parser, f):
p = pathlib.Path(f)
if not p.exists():
parser.error(f"The file {f} does not exist!")
if p.is_dir():
parser.error(f"{f} is a directory, not a file!")
if not access(f, R_OK):
parser.error(f"The file {f} is not readable!")
return pathlib.Path(f)


def init(config_path: pathlib.Path, working_path: pathlib.Path, subcommand: str):
# Get the user provided working dir from the --path option or default to current working directory.

configure(config_path.absolute(), working_path)
init_logging()
log_header(None)

audit_log = logging.getLogger("audit")
audit_log.info("# Running Hermes")
audit_log.info("Running Hermes command line in: %s", working_path)
audit_log.debug("")
audit_log.debug("Invoked `%s`", subcommand)


def main(*args, **kwargs) -> None:
"""
HERMES
import pathlib


This command runs the HERMES workflow or parts of it.
class HermesCommand:
""" Base class for a HERMES workflow command.
:cvar NAME: The name of the sub-command that is defined here.
"""
parser = argparse.ArgumentParser(
prog="hermes",
description="This command runs HERMES workflow steps.",
formatter_class=argparse.ArgumentDefaultsHelpFormatter,
)

# Arguments
parser.add_argument(
"--path", default=pathlib.Path("../"), help="Working path", type=pathlib.Path
)
parser.add_argument(
"--config",
default=pathlib.Path("hermes.toml"),
help="Configuration file in TOML format",
type=pathlib.Path,
)
NAME: str = None

subparsers = parser.add_subparsers(
help="Available subcommands", required=True, dest="subcommand"
)
def __init__(self, parser: argparse.ArgumentParser):
""" Initialize a new instance of any HERMES command.
# Subcommand clean
parser_clean = subparsers.add_parser("clean", help="Removes cached data")
parser_clean.set_defaults(func=clean)
:param parser: The command line parser used for reading command line arguments.
"""

# Subcommand harvest
parser_harvest = subparsers.add_parser(
"harvest", help="Harvest metadata from configured sources"
)
parser_harvest.set_defaults(func=harvest)
self.parser = parser

# Subcommand process
parser_process = subparsers.add_parser(
"process", help="Process the harvested metadata into a unified model"
)
parser_process.set_defaults(func=process)
@classmethod
def init_common_parser(cls, parser: argparse.ArgumentParser) -> None:
""" Initialize the common command line arguments available for all HERMES sub-commands.
# Subcommand curate
parser_curate = subparsers.add_parser(
"curate",
help="Resolve issues and conflicts in the processed metadata to create a curated set of metadata",
)
parser_curate.set_defaults(func=curate)
:param parser: The base command line parser used as entry point when reading command line arguments.
"""

# Subcommand deposit
parser_deposit = subparsers.add_parser(
"deposit",
help="Deposit the curated metadata and any artifacts in the configured target(s)",
)
parser_deposit.set_defaults(func=deposit)
parser_deposit.add_argument(
"--initial",
action="store_false",
default=False,
help="Allow initial deposition if no previous version exists in target repository. "
"Otherwise only an existing, configured upstream record may be updated.",
)
parser_deposit.add_argument(
"--auth-token",
default=os.environ.get("HERMES_DEPOSITION_AUTH_TOKEN"),
help="Token used to authenticate the user with the target deposition platform; "
"can be passed on the command line or set in an environment variable 'HERMES_DEPOSITION_AUTH_TOKEN'",
)
parser_deposit.add_argument(
"--files",
"-f",
required=True,
type=lambda f: _is_valid_file(parser, f),
nargs="+",
help="Files to be uploaded on the target deposition platform",
)
parser.add_argument("--path", default=pathlib.Path("../"), type=pathlib.Path,
help="Working path")
parser.add_argument("--config", default=pathlib.Path("hermes.toml"), type=pathlib.Path,
help="Configuration file in TOML format")

# Subcommand curate
parser_postprocess = subparsers.add_parser(
"postprocess",
help="Postprocess the deposited metadata",
)
parser_postprocess.set_defaults(func=postprocess)
def init_command_parser(self, command_parser: argparse.ArgumentParser) -> None:
""" Initialize the command line arguments available for this specific HERMES sub-commands.
# Show the help string if no arguments are given
if len(sys.argv) == 1:
parser.print_help(sys.stderr)
sys.exit(1)
You should override this method to add your custom arguments to the command line parser of
the respective sub-command.
args = parser.parse_args()
:param command_parser: The command line sub-parser responsible for the HERMES sub-command.
"""

pass

def __call__(self, args: argparse.Namespace):
""" Execute the HERMES sub-command.
:param args: The namespace that was returned by the command line parser when reading the arguments.
"""

pass


class HermesHelpCommand(HermesCommand):
""" Show help page and exit. """

NAME = "help"

# Initialize workflow
init(args.config, args.path, args.subcommand)
def init_command_parser(self, command_parser: argparse.ArgumentParser) -> None:
command_parser.add_argument('subcommand', nargs='?', metavar="COMMAND",
help="The HERMES sub-command to get help for.")

# Call the configured function of the argument
if args.subcommand == "deposit":
args.func(args.path, args.config, args.initial, args.auth_token, args.files)
else:
args.func(args.path, args.config)
def __call__(self, args: argparse.Namespace) -> None:
if args.subcommand:
# When a sub-command is given, show its help page (i.e., by "running" the command with "-h" flag).
self.parser.parse_args([args.subcommand, '-h'])
else:
# Otherwise, simply show the general help and exit (cleanly).
self.parser.print_help()
self.parser.exit()


class HermesCleanCommand(HermesCommand):
""" Clean up caches from previous HERMES runs. """

NAME = "clean"


class HermesHarvestCommand(HermesCommand):
""" Harvest metadata from configured sources. """

NAME = "harvest"


class HermesProcessCommand(HermesCommand):
""" Process the collected metadata into a common dataset. """

NAME = "process"


class HermesCurateCommand(HermesCommand):
""" Curate the unified metadata before deposition. """

NAME = "curate"


class HermesDepositCommand(HermesCommand):
""" Deposit the curated metadata to repositories. """

NAME = "deposit"


class HermesPostprocessCommand(HermesCommand):
""" Post-process the published metadata after deposition. """

NAME = "postprocess"


def main() -> None:
"""
HERMES main entry point (i.e., run the CLI).
This command runs the selected HERMES sub-command.
"""
parser = argparse.ArgumentParser(
prog="hermes",
description="This command runs HERMES workflow steps.",
formatter_class=argparse.ArgumentDefaultsHelpFormatter,
)
HermesCommand.init_common_parser(parser)

# Register all sub-commands to a new sub-parser each.
subparsers = parser.add_subparsers(dest="subcommand", required=True,
help="Available subcommands")
for command in (
HermesHelpCommand(parser),
HermesCleanCommand(parser),
HermesHarvestCommand(parser),
HermesProcessCommand(parser),
HermesCurateCommand(parser),
HermesDepositCommand(parser),
HermesPostprocessCommand(parser),
):
command_parser = subparsers.add_parser(command.NAME, help=command.__doc__)
command.init_command_parser(command_parser)
command_parser.set_defaults(command=command)

# Actually parse the commands and execute the selected sub-command.
args = parser.parse_args()
args.command(args)

0 comments on commit 7d5af3e

Please sign in to comment.