Expose propertyless YAML objects from configuration (e.g. "constants") as command-line flags (#303).

This commit is contained in:
Dan Helfman
2025-04-01 21:05:44 -07:00
parent 017cbae4f9
commit affe7cdc1b
3 changed files with 69 additions and 12 deletions
+6 -4
View File
@@ -307,7 +307,7 @@ def make_argument_description(schema, flag_name):
if '[0]' in flag_name:
description += ' To specify a different list element, replace the "[0]" with another array index ("[1]", "[2]", etc.).'
if example and schema_type == 'array':
if example and schema_type in ('array', 'object'):
example_buffer = io.StringIO()
yaml = ruamel.yaml.YAML(typ='safe')
yaml.default_flow_style = True
@@ -453,13 +453,15 @@ def add_arguments_from_schema(arguments_group, schema, unparsed_arguments, names
if schema_type == 'object':
properties = schema.get('properties')
# If there are child properties, recurse for each one. But if there are no child properties,
# fall through so that a flag gets added below for the (empty) object.
if properties:
for name, child in properties.items():
add_arguments_from_schema(
arguments_group, child, unparsed_arguments, names + (name,)
)
return
return
# If this is an "array" type, recurse for each items type child option. Don't return yet so that
# a flag also gets added below for the array itself.
@@ -485,9 +487,9 @@ def add_arguments_from_schema(arguments_group, schema, unparsed_arguments, names
metavar = names[-1].upper()
description = make_argument_description(schema, flag_name)
# array=str instead of list here to support specifying a list as a YAML string on the
# The ...=str given here is to support specifying an object or an array as a YAML string on the
# command-line.
argument_type = borgmatic.config.schema.parse_type(schema_type, array=str)
argument_type = borgmatic.config.schema.parse_type(schema_type, object=str, array=str)
full_flag_name = f"--{flag_name.replace('_', '-')}"
# As a UX nicety, allow boolean options that have a default of false to have command-line flags
@@ -4,6 +4,22 @@ from flexmock import flexmock
from borgmatic.commands import arguments as module
def test_make_argument_description_with_object_adds_example():
assert (
module.make_argument_description(
schema={
'description': 'Thing.',
'type': 'object',
'example': {'bar': 'baz'},
},
flag_name='flag',
)
# Apparently different versions of ruamel.yaml serialize this
# differently.
in ('Thing. Example value: "bar: baz"' 'Thing. Example value: "{bar: baz}"')
)
def test_make_argument_description_with_array_adds_example():
assert (
module.make_argument_description(
+47 -8
View File
@@ -590,6 +590,42 @@ def test_make_argument_description_without_description_bails():
)
def test_make_argument_description_with_object_adds_example():
buffer = flexmock()
buffer.should_receive('getvalue').and_return('{foo: example}')
flexmock(module.io).should_receive('StringIO').and_return(buffer)
yaml = flexmock()
yaml.should_receive('dump')
flexmock(module.ruamel.yaml).should_receive('YAML').and_return(yaml)
assert (
module.make_argument_description(
schema={
'description': 'Thing.',
'type': 'object',
'example': {'foo': 'example'},
},
flag_name='flag',
)
== 'Thing. Example value: "{foo: example}"'
)
def test_make_argument_description_with_object_skips_missing_example():
flexmock(module.ruamel.yaml).should_receive('YAML').never()
assert (
module.make_argument_description(
schema={
'description': 'Thing.',
'type': 'object',
},
flag_name='flag',
)
== 'Thing.'
)
def test_make_argument_description_with_array_adds_example():
buffer = flexmock()
buffer.should_receive('getvalue').and_return('[example]')
@@ -612,9 +648,7 @@ def test_make_argument_description_with_array_adds_example():
def test_make_argument_description_with_array_skips_missing_example():
yaml = flexmock()
yaml.should_receive('dump').and_return('[example]')
flexmock(module.ruamel.yaml).should_receive('YAML').and_return(yaml)
flexmock(module.ruamel.yaml).should_receive('YAML').never()
assert (
module.make_argument_description(
@@ -951,12 +985,17 @@ def test_add_arguments_from_schema_with_empty_multi_type_raises():
)
def test_add_arguments_from_schema_with_propertyless_option_does_not_add_flag():
def test_add_arguments_from_schema_with_propertyless_option_adds_flag():
arguments_group = flexmock()
flexmock(module).should_receive('make_argument_description').never()
flexmock(module.borgmatic.config.schema).should_receive('parse_type').never()
arguments_group.should_receive('add_argument').never()
flexmock(module).should_receive('add_array_element_arguments').never()
flexmock(module).should_receive('make_argument_description').and_return('help')
flexmock(module.borgmatic.config.schema).should_receive('parse_type').and_return(str)
arguments_group.should_receive('add_argument').with_args(
'--foo',
type=str,
metavar='FOO',
help='help',
).once()
flexmock(module).should_receive('add_array_element_arguments')
module.add_arguments_from_schema(
arguments_group=arguments_group,