mirror of
https://github.com/borgmatic-collective/borgmatic.git
synced 2026-07-31 13:43:02 +02:00
Organizational refactoring (#303).
This commit is contained in:
@@ -291,6 +291,91 @@ def parse_arguments_for_actions(unparsed_arguments, action_parsers, global_parse
|
||||
OMITTED_FLAG_NAMES = {'match_archives', 'progress', 'stats', 'list'}
|
||||
|
||||
|
||||
def make_argument_description(schema, flag_name):
|
||||
'''
|
||||
Given a configuration schema dict and a flag name for it, extend the schema's description with
|
||||
an example or additional information as appropriate based on its type. Return the updated
|
||||
description for use in a command-line argument.
|
||||
'''
|
||||
description = schema.get('description')
|
||||
schema_type = schema.get('type')
|
||||
|
||||
if not description:
|
||||
return None
|
||||
|
||||
if schema_type == 'array':
|
||||
example_buffer = io.StringIO()
|
||||
yaml = ruamel.yaml.YAML(typ='safe')
|
||||
yaml.default_flow_style = True
|
||||
yaml.dump(schema.get('example'), example_buffer)
|
||||
|
||||
description += f' Example value: "{example_buffer.getvalue().strip()}"'
|
||||
|
||||
if '[0]' in flag_name:
|
||||
description += ' To specify a different list element, replace the "[0]" with another array index ("[1]", "[2]", etc.).'
|
||||
|
||||
description = description.replace('%', '%%')
|
||||
|
||||
|
||||
def add_array_element_arguments_from_schema(arguments_group, schema, unparsed_arguments, flag_name):
|
||||
'''
|
||||
Given an argparse._ArgumentGroup instance, a configuration schema dict, a sequence of unparsed
|
||||
argument strings, and a dotted flag name, convert the schema into corresponding command-line
|
||||
array element flags that correspond to the given unparsed arguments.
|
||||
|
||||
Here's the background. We want to support flags that can have arbitrary indices like:
|
||||
|
||||
--foo.bar[1].baz
|
||||
|
||||
But argparse doesn't support that natively because the index can be an arbitrary number. We
|
||||
won't let that stop us though, will we?
|
||||
|
||||
If the current flag name has an array component in it (e.g. a name with "[0]"), then make a
|
||||
pattern that would match the flag name regardless of the number that's in it. The idea is that
|
||||
we want to look for unparsed arguments that appear like the flag name, but instead of "[0]" they
|
||||
have, say, "[1]" or "[123]".
|
||||
|
||||
Next, we check each unparsed argument against that pattern. If one of them matches, add an
|
||||
argument flag for it to the argument parser group. Example:
|
||||
|
||||
Let's say flag_name is:
|
||||
|
||||
--foo.bar[0].baz
|
||||
|
||||
... then the regular expression pattern will be:
|
||||
|
||||
^--foo\.bar\[\d+\]\.baz
|
||||
|
||||
... and, if that matches an unparsed argument of:
|
||||
|
||||
--foo.bar[1].baz
|
||||
|
||||
... then an argument flag will get added equal to that unparsed argument. And the unparsed
|
||||
argument will match it when parsing is performed! In this manner, we're using the actual user
|
||||
CLI input to inform what exact flags we support!
|
||||
'''
|
||||
if '[0]' not in flag_name or '--help' in unparsed_arguments:
|
||||
return
|
||||
|
||||
pattern = re.compile(f'^--{flag_name.replace("[0]", r"\[\d+\]").replace(".", r"\.")}$')
|
||||
existing_flags = set(
|
||||
itertools.chain(
|
||||
*(group_action.option_strings for group_action in arguments_group._group_actions)
|
||||
)
|
||||
)
|
||||
|
||||
for unparsed in unparsed_arguments:
|
||||
unparsed_flag_name = unparsed.split('=', 1)[0]
|
||||
|
||||
if pattern.match(unparsed_flag_name) and unparsed_flag_name not in existing_flags:
|
||||
arguments_group.add_argument(
|
||||
unparsed_flag_name,
|
||||
type=argument_type,
|
||||
metavar=metavar,
|
||||
help=description,
|
||||
)
|
||||
|
||||
|
||||
def add_arguments_from_schema(arguments_group, schema, unparsed_arguments, names=None):
|
||||
'''
|
||||
Given an argparse._ArgumentGroup instance, a configuration schema dict, and a sequence of
|
||||
@@ -362,28 +447,14 @@ def add_arguments_from_schema(arguments_group, schema, unparsed_arguments, names
|
||||
)
|
||||
|
||||
flag_name = '.'.join(names)
|
||||
description = schema.get('description')
|
||||
metavar = names[-1].upper()
|
||||
|
||||
if description:
|
||||
if schema_type == 'array':
|
||||
example_buffer = io.StringIO()
|
||||
yaml = ruamel.yaml.YAML(typ='safe')
|
||||
yaml.default_flow_style = True
|
||||
yaml.dump(schema.get('example'), example_buffer)
|
||||
|
||||
description += f' Example value: "{example_buffer.getvalue().strip()}"'
|
||||
|
||||
if '[0]' in flag_name:
|
||||
description += ' To specify a different list element, replace the "[0]" with another array index ("[1]", "[2]", etc.).'
|
||||
|
||||
description = description.replace('%', '%%')
|
||||
|
||||
# These options already have corresponding flags on individual actions (like "create
|
||||
# Certain options already have corresponding flags on individual actions (like "create
|
||||
# --progress"), so don't bother adding them to the global flags.
|
||||
if flag_name in OMITTED_FLAG_NAMES:
|
||||
return
|
||||
|
||||
description = make_argument_description(schema, flag_name)
|
||||
argument_type = borgmatic.config.schema.parse_type(schema_type)
|
||||
full_flag_name = f"--{flag_name.replace('_', '-')}"
|
||||
|
||||
@@ -404,54 +475,7 @@ def add_arguments_from_schema(arguments_group, schema, unparsed_arguments, names
|
||||
help=description,
|
||||
)
|
||||
|
||||
# We want to support flags that can have arbitrary indices like:
|
||||
#
|
||||
# --foo.bar[1].baz
|
||||
#
|
||||
# But argparse doesn't support that natively because the index can be an arbitrary number. We
|
||||
# won't let that stop us though, will we? So, if the current flag name has an array component in
|
||||
# it (e.g. a name with "[0]"), then make a pattern that would match the flag name regardless of
|
||||
# the number that's in it. The idea is that we want to look for unparsed arguments that appear
|
||||
# like the flag name, but instead of "[0]" they have, say, "[1]" or "[123]".
|
||||
#
|
||||
# Next, we check each unparsed argument against that pattern. If one of them matches, add an
|
||||
# argument flag for it to the argument parser group. Example:
|
||||
#
|
||||
# Let's say flag_name is:
|
||||
#
|
||||
# --foo.bar[0].baz
|
||||
#
|
||||
# ... then the regular expression pattern will be:
|
||||
#
|
||||
# ^--foo\.bar\[\d+\]\.baz
|
||||
#
|
||||
# ... and, if that matches an unparsed argument of:
|
||||
#
|
||||
# --foo.bar[1].baz
|
||||
#
|
||||
# ... then an argument flag will get added equal to that unparsed argument. And the unparsed
|
||||
# argument will match it when parsing is performed! In this manner, we're using the actual user
|
||||
# CLI input to inform what exact flags we support!
|
||||
if '[0]' not in flag_name or '--help' in unparsed_arguments:
|
||||
return
|
||||
|
||||
pattern = re.compile(f'^--{flag_name.replace("[0]", r"\[\d+\]").replace(".", r"\.")}$')
|
||||
existing_flags = set(
|
||||
itertools.chain(
|
||||
*(group_action.option_strings for group_action in arguments_group._group_actions)
|
||||
)
|
||||
)
|
||||
|
||||
for unparsed in unparsed_arguments:
|
||||
unparsed_flag_name = unparsed.split('=', 1)[0]
|
||||
|
||||
if pattern.match(unparsed_flag_name) and unparsed_flag_name not in existing_flags:
|
||||
arguments_group.add_argument(
|
||||
unparsed_flag_name,
|
||||
type=argument_type,
|
||||
metavar=metavar,
|
||||
help=description,
|
||||
)
|
||||
add_array_element_arguments_from_schema(arguments_group, schema, unparsed_arguments, flag_name)
|
||||
|
||||
|
||||
def make_parsers(schema, unparsed_arguments):
|
||||
|
||||
@@ -14,13 +14,13 @@ def set_values(config, keys, value):
|
||||
|
||||
('foo', 'bar', 'baz')
|
||||
|
||||
This looks up "foo" in the given configuration. And within that value, it looks up "bar". And
|
||||
then within that value, it looks up "baz" and sets it to the given value. Another example:
|
||||
This looks up "foo" in the given configuration dict. And within that, it looks up "bar". And
|
||||
then within that, it looks up "baz" and sets it to the given value. Another example:
|
||||
|
||||
('mylist[0]', 'foo')
|
||||
|
||||
This looks for the zeroth element of "mylist" in the given configuration. And within that value,
|
||||
it looks up "foo" and sets it to the given value.
|
||||
This looks for the zeroth element of "mylist" in the given configuration. And within that, it
|
||||
looks up "foo" and sets it to the given value.
|
||||
'''
|
||||
if not keys:
|
||||
return
|
||||
|
||||
@@ -5,7 +5,7 @@ import itertools
|
||||
def get_properties(schema):
|
||||
'''
|
||||
Given a schema dict, return its properties. But if it's got sub-schemas with multiple different
|
||||
potential properties, returned their merged properties instead (interleaved so the first
|
||||
potential properties, return their merged properties instead (interleaved so the first
|
||||
properties of each sub-schema come first). The idea is that the user should see all possible
|
||||
options even if they're not all possible together.
|
||||
'''
|
||||
@@ -24,6 +24,11 @@ def get_properties(schema):
|
||||
|
||||
|
||||
def parse_type(schema_type):
|
||||
'''
|
||||
Given a schema type as a string, return the corresponding Python type.
|
||||
|
||||
Raise ValueError if the schema type is unknown.
|
||||
'''
|
||||
try:
|
||||
return {
|
||||
'string': str,
|
||||
|
||||
Reference in New Issue
Block a user