Source code for geoips.commandline.geoips_run

# # # This source code is subject to the license referenced at
# # # https://github.com/NRLMMD-GEOIPS.

"""GeoIPS CLI "run" command.

Runs the appropriate script based on the args provided.
"""

from os.path import abspath

from colorama import Fore, Style

import geoips_yaml_utils as yaml

from geoips.commandline.args import add_args
from geoips.commandline.run_procflow import main
from geoips.commandline.geoips_command import (
    GeoipsCommand,
    GeoipsExecutableCommand,
    GeoipsWorkflowCommand,
)
from geoips.filenames.base_paths import PATHS
from geoips.interfaces import procflows, workflows
from geoips.pydantic_models.v1.workflows import WorkflowPluginModel
from geoips.utils.context_managers import import_optional_dependencies

data_fusion_installed = False

with import_optional_dependencies(loglevel="info"):
    """Attempt to import data_fusion_args from Data Fusion."""
    # NOTE: loglevel is set to 'info' here so we don't get the constant output of
    # 'Failed to import data_fusion.commandline at /path/to/geoips/geoips/commandline/geoips_run.py:19. If you need it, install it.' # NOQA
    # every time we use the CLI. If a user needs the CLI, I'm assuming they'll know to
    # install it.
    try:
        from data_fusion.commandline.args import add_args as data_fusion_add_args
    except ModuleNotFoundError as e:
        try:
            # 'data_fusion' is not an allowed name for the data_fusion package on pypi.
            # We have temporarily pushed it to pypi as geoips_data_fusion.
            # This change allows geoips run to use the pypi packaged
            # version of the data_fusion file, which is named
            # 'geoips_data_fusion'
            from geoips_data_fusion.commandline.args import (
                add_args as data_fusion_add_args,
            )
        except ModuleNotFoundError:
            raise e
    data_fusion_installed = True


[docs]class GeoipsRunConfigBased(GeoipsExecutableCommand): """Run Command for executing the config based process-workflow (procflow).""" name = "config_based" command_classes = []
[docs] def add_arguments(self): """Add arguments to the run-subparser for the 'run config_based' Command.""" add_args(parser=self.parser, legacy=self.legacy)
def __call__(self, args): """Run the provided GeoIPS command. Parameters ---------- args: Namespace() - The argument namespace to parse through """ if args.procflow is None and self.legacy: err_str = ( "Deprecated, Legacy 'run_procflow' call was used and --procflow " "flag wasn't specified. Please either specify which procflow is " "being executed via '--procflow <procflow_name>' or use the " "supported procflow call 'geoips run <procflow_name>'" ) self.parser.error(err_str) elif args.procflow is None: # If None, set to 'config_based'. We don't want users to have to specify # what procflow will be used as it is specified in # 'geoips run config_based'. args.procflow = "config_based" main(ARGS=args)
[docs]class GeoipsRunDataFusion(GeoipsExecutableCommand): """Run Command for executing the data fusion process-workflow (procflow).""" name = "data_fusion" command_classes = []
[docs] def add_arguments(self): """Add arguments to the run-subparser for the 'run data_fusion' Command.""" if data_fusion_installed: data_fusion_add_args(parser=self.parser, legacy=self.legacy)
def __call__(self, args): """Run the provided GeoIPS command. Parameters ---------- args: Namespace() - The argument namespace to parse through """ if data_fusion_installed: if args.procflow is None and self.legacy: err_str = ( "Deprecated, Legacy 'data_fusion_procflow' call was used and " "--procflow flag wasn't specified. Please either specify which " "procflow is being executed via '--procflow <procflow_name>' or " "use the supported procflow call 'geoips run <procflow_name>'" ) self.parser.error(err_str) elif args.procflow is None: # If None, set to 'data_fusion'. We don't want users to have to specify # what procflow will be used as it is specified in # 'geoips run data_fusion'. args.procflow = "data_fusion" main(ARGS=args) else: print( "Data fusion is not installed. If you want to run this type of " "functionality, please clone and install " "https://github.com/NRLMMD-GEOIPS/data_fusion.git" )
[docs]class GeoipsRunOrderBased(GeoipsWorkflowCommand): """Run command for executing an order based process-workflow (procflow). Makes use of workflow plugins and additional commandline arguments that single source would use. """ name = "order_based" command_classes = [] warning_with_color = ( Fore.YELLOW + "\nNote: " + Style.RESET_ALL + "`geoips run order_based` (Order-Based Processing) is the recommended " + "processing path in GeoIPS 2.0. It is still stabilizing, so some interfaces " + "may change between releases.\n" ) warning_no_color = ( "\nNote: `geoips run order_based` (Order-Based Processing) is the recommended " "processing path in GeoIPS 2.0. It is still stabilizing, so some interfaces " "may change between releases.\n" )
[docs] def dict_type(self, value): """Ensure an dictionary-based override can be cast as a dictionary. This is used to validate the input of -S, -K, and -G flags. Parameters ---------- value: str The full global override string for a geoips run order_based command. """ try: return yaml.safe_load(value) except Exception as e: raise self.parser.error(f"Invalid dictionary input: {value}") from e
[docs] def global_override_type(self, value: str): """Ensure an override string fits the following format. Expected Format --------------- '<global_variable_name>=<some_value>' Parameters ---------- value: str The full global override string for a geoips run order_based command. Returns ------- override_dict: dict The validated contents of an override string in a dictionary. """ try: override = workflows.global_override_type(value) except ValueError: raise self.parser.error( f"Invalid format '{value}'. Expected '<global_variable_name>=<value>'" ) return override
[docs] def kind_override_type(self, value: str): """Ensure an override string fits the following format. Expected Format --------------- '<kind>.<argument_name>=<some_value>' Parameters ---------- value: str The full kind override string for a geoips run order_based command. Returns ------- override_dict: dict The validated contents of an override string in a dictionary. """ try: override = workflows.kind_override_type(value) except ValueError: raise self.parser.error( f"Invalid format '{value}'. Expected '<kind>.<argument_name>=<value>'" ) return override
[docs] def step_override_type(self, value: str): """Ensure an override string fits the following format. Expected Format --------------- '<step_id>.<string1>.<optional_string2>.<optional_string3>...=<some_value>' Parameters ---------- value: str The full step override string for a geoips run order_based command. Returns ------- override_dict: dict The validated contents of an override string in a dictionary. """ try: override = workflows.step_override_type(value) except ValueError: raise self.parser.error( f"Invalid format '{value}'. Expected '<step_id>.<...>=<value>'" ) return override
[docs] def add_arguments(self): """Add arguments to the run-subparser for the 'run order-based' command.""" # Required arguments self.parser.add_argument( "workflow", type=self.workflow_type, help=( "Workflow instance. Can be the name of a registered workflow plugin, " "a .json or .yaml path to an unregistered workflow plugin, or a " "dictionary that will be literally evaluated as a workflow." ), ) self.parser.add_argument( "filenames", nargs="+", type=abspath, help="""Fully qualified paths to data files to be processed.""", ) # dict-based override arguments self.parser.add_argument( "-S", "--step-override-dict", default={}, type=self.dict_type, help=( "One or more step overrides to apply to your workflow. In a dictionary " "format. See geoips.pydantic_models.v1.workflows for more info on the " "correct format." ), ) self.parser.add_argument( "-K", "--kind-override-dict", default={}, type=self.dict_type, help=( "One or more kind overrides to apply to your workflow. In a dictionary " "format. See geoips.pydantic_models.v1.workflows for more info on the " "correct format." ), ) self.parser.add_argument( "-G", "--global-override-dict", default={}, type=self.dict_type, help=( "One or more global overrides to apply to your workflow. In a " "dictionary format. See geoips.pydantic_models.v1.workflows for more " "info on the correct format." ), ) # string-based override arguments self.parser.add_argument( "-s", "--step-override-strings", default=[], type=self.step_override_type, action="append", help=( "Step override string to apply to your workflow. An " "override string should take on the following format:\n " "'<step_id>.<string1>.<optional_string2>...<argument>=<some_value>'" ), ) self.parser.add_argument( "-k", "--kind-override-strings", default=[], type=self.kind_override_type, action="append", help=( "Kind override string to apply to your workflow. An " "override string should take on the following format:\n " "'<kind>.<argument_name>=<some_value>'" ), ) self.parser.add_argument( "-g", "--global-override-strings", default=[], type=self.global_override_type, action="append", help=( "Global override string to apply to your workflow. An " "override string should take on the following format:\n " "'<global_variable_name>=<some_value>'" ), ) self.parser.add_argument( "-wt", "--write-tokens", default=False, action="store_true", help=( "Write tokens for the output of every step in a workflow. Used for " "quick comparison against known token outputs." ), )
# Turning off all additional procflow args for this command. We want this # command to have a limited set of arguments to start. # add_args(parser=self.parser, legacy=self.legacy) def _apply_overrides(self, workflow, args): """Override a workflow via dictionary and string overrides. Parameters ---------- workflow: WorkflowPlugin-like - The workflow to override. args: Namespace() - The input argument namespace. Returns ------- workflow: WorkflowPlugin-like - The overridden workflow. """ s_override_dict = args.step_override_dict k_override_dict = args.kind_override_dict g_override_dict = args.global_override_dict s_override_strings = args.step_override_strings k_override_strings = args.kind_override_strings g_override_strings = args.global_override_strings # apply dict-based overrides if any( [ s_override_dict, k_override_dict, g_override_dict, ] ): workflow = workflows._override_workflow_dict_format( workflow, goverrides=g_override_dict, koverrides=k_override_dict, soverrides=s_override_dict, ) WorkflowPluginModel(**workflow, is_registered=False) # apply string-based overrides if any( [ s_override_strings, k_override_strings, g_override_strings, ] ): workflow = workflows._override_workflow_string_format( workflow, goverrides=g_override_strings, koverrides=k_override_strings, soverrides=s_override_strings, ) WorkflowPluginModel(**workflow, is_registered=False) return workflow def __call__(self, args): """Run the provided GeoIPS command. In specific, run a GeoIPS order based process-workflow (procflow) to produce some output. Parameters ---------- args: Namespace() - The argument namespace to parse through. """ workflow = args.workflow workflow = self._apply_overrides(workflow, args) obp = procflows.get_plugin("order_based") obp(workflow_spec=workflow, filenames=args.filenames, command_line_args=args) if PATHS["NO_COLOR"]: print(self.warning_no_color) else: print(self.warning_with_color)
[docs]class GeoipsRunSingleSource(GeoipsExecutableCommand): """Run Command for executing the single source process-workflow (procflow).""" name = "single_source" command_classes = []
[docs] def add_arguments(self): """Add arguments to the run-subparser for the 'run single_source' Command.""" add_args(parser=self.parser, legacy=self.legacy)
def __call__(self, args): """Run the provided GeoIPS command. Parameters ---------- args: Namespace() - The argument namespace to parse through """ if args.procflow is None and self.legacy: err_str = ( "Deprecated, Legacy 'run_procflow' call was used and --procflow " "flag wasn't specified. Please either specify which procflow is " "being executed via '--procflow <procflow_name>' or use the " "supported procflow call 'geoips run <procflow_name>'" ) self.parser.error(err_str) elif args.procflow is None: # If None, set to 'single_source'. We don't want users to have to specify # what procflow will be used as it is specified in # 'geoips run single_source'. args.procflow = "single_source" main(ARGS=args)
[docs]class GeoipsRun(GeoipsCommand): """Run Command for running process-workflows (procflows).""" name = "run" command_classes = [ GeoipsRunDataFusion, GeoipsRunConfigBased, GeoipsRunOrderBased, GeoipsRunSingleSource, ]