edq.config.argparser

  1import argparse
  2import os
  3import typing
  4
  5import edq.config.constants
  6import edq.config.load
  7import edq.config.settings
  8import edq.util.serial
  9
 10def set_cli_args(
 11        parser: argparse.ArgumentParser,
 12        extra_state: typing.Dict[str, typing.Any],
 13        **kwargs: typing.Any,
 14        ) -> None:
 15    """
 16    Set common CLI arguments for configuration.
 17    """
 18
 19    group = parser.add_argument_group('config options')
 20
 21    group.add_argument('--config', dest = edq.config.constants.CONFIG_OPTIONS_KEY, metavar = "<KEY>=<VALUE>",
 22        action = 'append', type = str, default = [],
 23        help = ('Set a configuration option from the command-line.'
 24            + ' Specify options as <key>=<value> pairs.'
 25            + ' This flag can be specified multiple times.'
 26            + ' The options are applied in the order provided and later options override earlier ones.'
 27            + ' Will override options form all config files.')
 28    )
 29
 30    group.add_argument('--config-file', dest = edq.config.constants.CONFIG_PATHS_KEY,
 31        action = 'append', type = str, default = [],
 32        help = ('Load config options from a JSON file.'
 33            + ' This flag can be specified multiple times.'
 34            + ' Files are applied in the order provided and later files override earlier ones.'
 35            + ' Will override options form both global and local config files.')
 36    )
 37
 38    group.add_argument('--config-global', dest = edq.config.constants.GLOBAL_CONFIG_KEY,
 39        action = 'store', type = str, default = os.path.join(edq.config.settings.get_global_dir(), edq.config.settings.get_config_filename()),
 40        help = 'Set the default global config file path (default: %(default)s).',
 41    )
 42
 43    group.add_argument('--encryption-key', dest = edq.config.constants.CONFIG_ENCRYPTION_KEY,
 44        action = 'store', type = str, default = None,
 45        help = 'Encryption key to use for configuration secrets (e.g., passwords and tokens).',
 46    )
 47
 48    _attach_epilogue(parser)
 49
 50def _attach_epilogue(parser: argparse.ArgumentParser) -> None:
 51    """
 52    Construct an epilogue detailing configuration information and attach it to the parser's epilogue.
 53    The epilogue will be created from the current config load order.
 54    Each source spec will be asked to create their portion of the epilogue.
 55    """
 56
 57    load_order = edq.config.settings.get_load_order()
 58    if (len(load_order) == 0):
 59        return
 60
 61    lines = [
 62        'Configuration is loaded into this program using a "tiered" system.',
 63        'This means that configuration options will be loaded from various sources in a pre-defined order.',
 64        'If the same value is read from multiple sources, then the **last** read value overrides any previous values.',
 65        'If you want to see where your configuration values are being loaded from, see the config list command with the `--show-origin` flag.',
 66        '',
 67        'Below is the order that configurations are read for this program:'
 68    ]
 69
 70    indent = ' ' * edq.config.constants.DEFAULT_INDENT
 71
 72    for (step_index, spec) in enumerate(load_order):
 73        spec_lines = spec.get_help_lines()
 74        if ((spec_lines is None) or (len(spec_lines) == 0)):
 75            continue
 76
 77        for i in range(len(spec_lines)):  # pylint: disable=consider-using-enumerate
 78            # Add in a step number for this first line, and an indentation for all other lines..
 79            if (i == 0):
 80                spec_lines[i] = f"{step_index + 1}) {spec_lines[i]}"
 81            else:
 82                spec_lines[i] = indent + spec_lines[i]
 83
 84        # Add another indentation level (for all lines).
 85        spec_lines = [indent + line for line in spec_lines]
 86
 87        if (step_index != 0):
 88            lines.append('')
 89
 90        lines += spec_lines
 91
 92    # Prepare any existing epilogue.
 93    if (parser.epilog is None):
 94        parser.epilog = ''
 95    elif (len(parser.epilog) > 0):
 96        parser.epilog += "\n\n"
 97
 98    lines = [indent + line for line in lines]
 99    parser.epilog += "CONFIGURATION\n\n" + "\n".join(lines)
100
101def add_config_location_argument_group(parser: argparse.ArgumentParser) -> None:
102    """ Add the configuration location argument group to the parser. """
103
104    group = parser.add_argument_group("config location options").add_mutually_exclusive_group()
105
106    group.add_argument('--local',
107        action = 'store_true', dest = 'scope_local',
108        help = ("Target config option(s) in a local config file.")
109    )
110
111    group.add_argument('--project',
112        action = 'store_true', dest = 'scope_project',
113        help = ("Target config option(s) in a project config file.")
114    )
115
116    group.add_argument('--global',
117        action = 'store_true', dest = 'scope_global',
118        help =  ("Target config option(s) in the global config file."),
119    )
120
121    group.add_argument('--file', metavar = "<FILE>",
122        action = 'store', type = str, default = None, dest = 'scope_file',
123        help = ("Target config option(s) in a specified config file.")
124    )
125
126def load_config_into_args(
127        parser: argparse.ArgumentParser,
128        args: argparse.Namespace,
129        extra_state: typing.Dict[str, typing.Any],
130        cli_arg_config_map: typing.Union[typing.Dict[str, str], None] = None,
131        serialization_context: typing.Union[edq.util.serial.SerializationContext, None] = None,
132        **kwargs: typing.Any,
133        ) -> None:
134    """
135    Take in args from a parser that was passed to set_cli_args(),
136    and get the tired configuration with the appropriate parameters, and attache it to args.
137
138    Arguments that appear on the CLI as flags (e.g. `--foo bar`) can be copied over to the config options via `cli_arg_config_map`.
139    The keys of `cli_arg_config_map` represent attributes in the CLI arguments (`args`),
140    while the values represent the desired config name this argument should be set as.
141    For example, a `cli_arg_config_map` of `{'foo': 'baz'}` will make the CLI argument `--foo bar`
142    be equivalent to `--config baz=bar`.
143    """
144
145    if (cli_arg_config_map is None):
146        cli_arg_config_map = {}
147
148    for (cli_key, config_key) in cli_arg_config_map.items():
149        value = getattr(args, cli_key, None)
150        if (value is not None):
151            getattr(args, edq.config.constants.CONFIG_OPTIONS_KEY).append(f"{config_key}={value}")
152
153    default_values = {}
154    for action in parser._actions:
155        key = getattr(action, 'dest', None)
156        if (key is None):
157            continue
158
159        default_values[str(key)] = getattr(action, 'default', None)
160
161    config_info = edq.config.load.get_tiered_config(
162        cli_arguments = args,
163        cli_default_values = default_values,
164        serialization_context = serialization_context,
165    )
166    setattr(args, "_config_info", config_info)
def set_cli_args( parser: argparse.ArgumentParser, extra_state: Dict[str, Any], **kwargs: Any) -> None:
11def set_cli_args(
12        parser: argparse.ArgumentParser,
13        extra_state: typing.Dict[str, typing.Any],
14        **kwargs: typing.Any,
15        ) -> None:
16    """
17    Set common CLI arguments for configuration.
18    """
19
20    group = parser.add_argument_group('config options')
21
22    group.add_argument('--config', dest = edq.config.constants.CONFIG_OPTIONS_KEY, metavar = "<KEY>=<VALUE>",
23        action = 'append', type = str, default = [],
24        help = ('Set a configuration option from the command-line.'
25            + ' Specify options as <key>=<value> pairs.'
26            + ' This flag can be specified multiple times.'
27            + ' The options are applied in the order provided and later options override earlier ones.'
28            + ' Will override options form all config files.')
29    )
30
31    group.add_argument('--config-file', dest = edq.config.constants.CONFIG_PATHS_KEY,
32        action = 'append', type = str, default = [],
33        help = ('Load config options from a JSON file.'
34            + ' This flag can be specified multiple times.'
35            + ' Files are applied in the order provided and later files override earlier ones.'
36            + ' Will override options form both global and local config files.')
37    )
38
39    group.add_argument('--config-global', dest = edq.config.constants.GLOBAL_CONFIG_KEY,
40        action = 'store', type = str, default = os.path.join(edq.config.settings.get_global_dir(), edq.config.settings.get_config_filename()),
41        help = 'Set the default global config file path (default: %(default)s).',
42    )
43
44    group.add_argument('--encryption-key', dest = edq.config.constants.CONFIG_ENCRYPTION_KEY,
45        action = 'store', type = str, default = None,
46        help = 'Encryption key to use for configuration secrets (e.g., passwords and tokens).',
47    )
48
49    _attach_epilogue(parser)

Set common CLI arguments for configuration.

def add_config_location_argument_group(parser: argparse.ArgumentParser) -> None:
102def add_config_location_argument_group(parser: argparse.ArgumentParser) -> None:
103    """ Add the configuration location argument group to the parser. """
104
105    group = parser.add_argument_group("config location options").add_mutually_exclusive_group()
106
107    group.add_argument('--local',
108        action = 'store_true', dest = 'scope_local',
109        help = ("Target config option(s) in a local config file.")
110    )
111
112    group.add_argument('--project',
113        action = 'store_true', dest = 'scope_project',
114        help = ("Target config option(s) in a project config file.")
115    )
116
117    group.add_argument('--global',
118        action = 'store_true', dest = 'scope_global',
119        help =  ("Target config option(s) in the global config file."),
120    )
121
122    group.add_argument('--file', metavar = "<FILE>",
123        action = 'store', type = str, default = None, dest = 'scope_file',
124        help = ("Target config option(s) in a specified config file.")
125    )

Add the configuration location argument group to the parser.

def load_config_into_args( parser: argparse.ArgumentParser, args: argparse.Namespace, extra_state: Dict[str, Any], cli_arg_config_map: Optional[Dict[str, str]] = None, serialization_context: Optional[edq.util.common.SerializationContext] = None, **kwargs: Any) -> None:
127def load_config_into_args(
128        parser: argparse.ArgumentParser,
129        args: argparse.Namespace,
130        extra_state: typing.Dict[str, typing.Any],
131        cli_arg_config_map: typing.Union[typing.Dict[str, str], None] = None,
132        serialization_context: typing.Union[edq.util.serial.SerializationContext, None] = None,
133        **kwargs: typing.Any,
134        ) -> None:
135    """
136    Take in args from a parser that was passed to set_cli_args(),
137    and get the tired configuration with the appropriate parameters, and attache it to args.
138
139    Arguments that appear on the CLI as flags (e.g. `--foo bar`) can be copied over to the config options via `cli_arg_config_map`.
140    The keys of `cli_arg_config_map` represent attributes in the CLI arguments (`args`),
141    while the values represent the desired config name this argument should be set as.
142    For example, a `cli_arg_config_map` of `{'foo': 'baz'}` will make the CLI argument `--foo bar`
143    be equivalent to `--config baz=bar`.
144    """
145
146    if (cli_arg_config_map is None):
147        cli_arg_config_map = {}
148
149    for (cli_key, config_key) in cli_arg_config_map.items():
150        value = getattr(args, cli_key, None)
151        if (value is not None):
152            getattr(args, edq.config.constants.CONFIG_OPTIONS_KEY).append(f"{config_key}={value}")
153
154    default_values = {}
155    for action in parser._actions:
156        key = getattr(action, 'dest', None)
157        if (key is None):
158            continue
159
160        default_values[str(key)] = getattr(action, 'default', None)
161
162    config_info = edq.config.load.get_tiered_config(
163        cli_arguments = args,
164        cli_default_values = default_values,
165        serialization_context = serialization_context,
166    )
167    setattr(args, "_config_info", config_info)

Take in args from a parser that was passed to set_cli_args(), and get the tired configuration with the appropriate parameters, and attache it to args.

Arguments that appear on the CLI as flags (e.g. --foo bar) can be copied over to the config options via cli_arg_config_map. The keys of cli_arg_config_map represent attributes in the CLI arguments (args), while the values represent the desired config name this argument should be set as. For example, a cli_arg_config_map of {'foo': 'baz'} will make the CLI argument --foo bar be equivalent to --config baz=bar.