Break out main borgmatic arguments-parsing code into a separate file.
This commit is contained in:
parent
0ca43ef67a
commit
4bf3e906a1
4 changed files with 641 additions and 633 deletions
292
borgmatic/commands/arguments.py
Normal file
292
borgmatic/commands/arguments.py
Normal file
|
@ -0,0 +1,292 @@
|
||||||
|
import collections
|
||||||
|
from argparse import ArgumentParser
|
||||||
|
|
||||||
|
from borgmatic.config import collect
|
||||||
|
|
||||||
|
SUBPARSER_ALIASES = {
|
||||||
|
'init': ['--init', '-I'],
|
||||||
|
'prune': ['--prune', '-p'],
|
||||||
|
'create': ['--create', '-C'],
|
||||||
|
'check': ['--check', '-k'],
|
||||||
|
'extract': ['--extract', '-x'],
|
||||||
|
'list': ['--list', '-l'],
|
||||||
|
'info': ['--info', '-i'],
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def parse_subparser_arguments(unparsed_arguments, top_level_parser, subparsers):
|
||||||
|
'''
|
||||||
|
Given a sequence of arguments, a top-level parser (containing subparsers), and a subparsers
|
||||||
|
object as returned by argparse.ArgumentParser().add_subparsers(), ask each subparser to parse
|
||||||
|
its own arguments and the top-level parser to parse any remaining arguments.
|
||||||
|
|
||||||
|
Return the result as a dict mapping from subparser name (or "global") to a parsed namespace of
|
||||||
|
arguments.
|
||||||
|
'''
|
||||||
|
arguments = collections.OrderedDict()
|
||||||
|
remaining_arguments = list(unparsed_arguments)
|
||||||
|
alias_to_subparser_name = {
|
||||||
|
alias: subparser_name
|
||||||
|
for subparser_name, aliases in SUBPARSER_ALIASES.items()
|
||||||
|
for alias in aliases
|
||||||
|
}
|
||||||
|
|
||||||
|
# Give each requested action's subparser a shot at parsing all arguments.
|
||||||
|
for subparser_name, subparser in subparsers.choices.items():
|
||||||
|
if subparser_name not in unparsed_arguments:
|
||||||
|
continue
|
||||||
|
|
||||||
|
remaining_arguments.remove(subparser_name)
|
||||||
|
canonical_name = alias_to_subparser_name.get(subparser_name, subparser_name)
|
||||||
|
|
||||||
|
parsed, remaining = subparser.parse_known_args(unparsed_arguments)
|
||||||
|
arguments[canonical_name] = parsed
|
||||||
|
|
||||||
|
# If no actions are explicitly requested, assume defaults: prune, create, and check.
|
||||||
|
if not arguments and '--help' not in unparsed_arguments and '-h' not in unparsed_arguments:
|
||||||
|
for subparser_name in ('prune', 'create', 'check'):
|
||||||
|
subparser = subparsers.choices[subparser_name]
|
||||||
|
parsed, remaining = subparser.parse_known_args(unparsed_arguments)
|
||||||
|
arguments[subparser_name] = parsed
|
||||||
|
|
||||||
|
# Then ask each subparser, one by one, to greedily consume arguments. Any arguments that remain
|
||||||
|
# are global arguments.
|
||||||
|
for subparser_name in arguments.keys():
|
||||||
|
subparser = subparsers.choices[subparser_name]
|
||||||
|
parsed, remaining_arguments = subparser.parse_known_args(remaining_arguments)
|
||||||
|
|
||||||
|
arguments['global'] = top_level_parser.parse_args(remaining_arguments)
|
||||||
|
|
||||||
|
return arguments
|
||||||
|
|
||||||
|
|
||||||
|
def parse_arguments(*unparsed_arguments):
|
||||||
|
'''
|
||||||
|
Given command-line arguments with which this script was invoked, parse the arguments and return
|
||||||
|
them as a dict mapping from subparser name (or "global") to an argparse.Namespace instance.
|
||||||
|
'''
|
||||||
|
config_paths = collect.get_default_config_paths()
|
||||||
|
|
||||||
|
global_parser = ArgumentParser(add_help=False)
|
||||||
|
global_group = global_parser.add_argument_group('global arguments')
|
||||||
|
|
||||||
|
global_group.add_argument(
|
||||||
|
'-c',
|
||||||
|
'--config',
|
||||||
|
nargs='*',
|
||||||
|
dest='config_paths',
|
||||||
|
default=config_paths,
|
||||||
|
help='Configuration filenames or directories, defaults to: {}'.format(
|
||||||
|
' '.join(config_paths)
|
||||||
|
),
|
||||||
|
)
|
||||||
|
global_group.add_argument(
|
||||||
|
'--excludes',
|
||||||
|
dest='excludes_filename',
|
||||||
|
help='Deprecated in favor of exclude_patterns within configuration',
|
||||||
|
)
|
||||||
|
global_group.add_argument(
|
||||||
|
'-n',
|
||||||
|
'--dry-run',
|
||||||
|
dest='dry_run',
|
||||||
|
action='store_true',
|
||||||
|
help='Go through the motions, but do not actually write to any repositories',
|
||||||
|
)
|
||||||
|
global_group.add_argument(
|
||||||
|
'-nc', '--no-color', dest='no_color', action='store_true', help='Disable colored output'
|
||||||
|
)
|
||||||
|
global_group.add_argument(
|
||||||
|
'-v',
|
||||||
|
'--verbosity',
|
||||||
|
type=int,
|
||||||
|
choices=range(0, 3),
|
||||||
|
default=0,
|
||||||
|
help='Display verbose progress to the console (from none to lots: 0, 1, or 2)',
|
||||||
|
)
|
||||||
|
global_group.add_argument(
|
||||||
|
'--syslog-verbosity',
|
||||||
|
type=int,
|
||||||
|
choices=range(0, 3),
|
||||||
|
default=0,
|
||||||
|
help='Display verbose progress to syslog (from none to lots: 0, 1, or 2)',
|
||||||
|
)
|
||||||
|
global_group.add_argument(
|
||||||
|
'--version',
|
||||||
|
dest='version',
|
||||||
|
default=False,
|
||||||
|
action='store_true',
|
||||||
|
help='Display installed version number of borgmatic and exit',
|
||||||
|
)
|
||||||
|
|
||||||
|
top_level_parser = ArgumentParser(
|
||||||
|
description='''
|
||||||
|
A simple wrapper script for the Borg backup software that creates and prunes backups.
|
||||||
|
If none of the action options are given, then borgmatic defaults to: prune, create, and
|
||||||
|
check archives.
|
||||||
|
''',
|
||||||
|
parents=[global_parser],
|
||||||
|
)
|
||||||
|
|
||||||
|
subparsers = top_level_parser.add_subparsers(title='actions', metavar='')
|
||||||
|
init_parser = subparsers.add_parser(
|
||||||
|
'init',
|
||||||
|
aliases=SUBPARSER_ALIASES['init'],
|
||||||
|
help='Initialize an empty Borg repository',
|
||||||
|
description='Initialize an empty Borg repository',
|
||||||
|
add_help=False,
|
||||||
|
)
|
||||||
|
init_group = init_parser.add_argument_group('init arguments')
|
||||||
|
init_group.add_argument(
|
||||||
|
'-e',
|
||||||
|
'--encryption',
|
||||||
|
dest='encryption_mode',
|
||||||
|
help='Borg repository encryption mode',
|
||||||
|
required=True,
|
||||||
|
)
|
||||||
|
init_group.add_argument(
|
||||||
|
'--append-only',
|
||||||
|
dest='append_only',
|
||||||
|
action='store_true',
|
||||||
|
help='Create an append-only repository',
|
||||||
|
)
|
||||||
|
init_group.add_argument(
|
||||||
|
'--storage-quota',
|
||||||
|
dest='storage_quota',
|
||||||
|
help='Create a repository with a fixed storage quota',
|
||||||
|
)
|
||||||
|
init_group.add_argument('-h', '--help', action='help', help='Show this help message and exit')
|
||||||
|
|
||||||
|
prune_parser = subparsers.add_parser(
|
||||||
|
'prune',
|
||||||
|
aliases=SUBPARSER_ALIASES['prune'],
|
||||||
|
help='Prune archives according to the retention policy',
|
||||||
|
description='Prune archives according to the retention policy',
|
||||||
|
add_help=False,
|
||||||
|
)
|
||||||
|
prune_group = prune_parser.add_argument_group('prune arguments')
|
||||||
|
prune_group.add_argument(
|
||||||
|
'--stats',
|
||||||
|
dest='stats',
|
||||||
|
default=False,
|
||||||
|
action='store_true',
|
||||||
|
help='Display statistics of archive',
|
||||||
|
)
|
||||||
|
prune_group.add_argument('-h', '--help', action='help', help='Show this help message and exit')
|
||||||
|
|
||||||
|
create_parser = subparsers.add_parser(
|
||||||
|
'create',
|
||||||
|
aliases=SUBPARSER_ALIASES['create'],
|
||||||
|
help='Create archives (actually perform backups)',
|
||||||
|
description='Create archives (actually perform backups)',
|
||||||
|
add_help=False,
|
||||||
|
)
|
||||||
|
create_group = create_parser.add_argument_group('create arguments')
|
||||||
|
create_group.add_argument(
|
||||||
|
'--progress',
|
||||||
|
dest='progress',
|
||||||
|
default=False,
|
||||||
|
action='store_true',
|
||||||
|
help='Display progress for each file as it is processed',
|
||||||
|
)
|
||||||
|
create_group.add_argument(
|
||||||
|
'--stats',
|
||||||
|
dest='stats',
|
||||||
|
default=False,
|
||||||
|
action='store_true',
|
||||||
|
help='Display statistics of archive',
|
||||||
|
)
|
||||||
|
create_group.add_argument(
|
||||||
|
'--json', dest='json', default=False, action='store_true', help='Output results as JSON'
|
||||||
|
)
|
||||||
|
create_group.add_argument('-h', '--help', action='help', help='Show this help message and exit')
|
||||||
|
|
||||||
|
check_parser = subparsers.add_parser(
|
||||||
|
'check',
|
||||||
|
aliases=SUBPARSER_ALIASES['check'],
|
||||||
|
help='Check archives for consistency',
|
||||||
|
description='Check archives for consistency',
|
||||||
|
add_help=False,
|
||||||
|
)
|
||||||
|
check_group = check_parser.add_argument_group('check arguments')
|
||||||
|
check_group.add_argument('-h', '--help', action='help', help='Show this help message and exit')
|
||||||
|
|
||||||
|
extract_parser = subparsers.add_parser(
|
||||||
|
'extract',
|
||||||
|
aliases=SUBPARSER_ALIASES['extract'],
|
||||||
|
help='Extract a named archive to the current directory',
|
||||||
|
description='Extract a named archive to the current directory',
|
||||||
|
add_help=False,
|
||||||
|
)
|
||||||
|
extract_group = extract_parser.add_argument_group('extract arguments')
|
||||||
|
extract_group.add_argument(
|
||||||
|
'--repository',
|
||||||
|
help='Path of repository to use, defaults to the configured repository if there is only one',
|
||||||
|
)
|
||||||
|
extract_group.add_argument('--archive', help='Name of archive to operate on', required=True)
|
||||||
|
extract_group.add_argument(
|
||||||
|
'--restore-path',
|
||||||
|
nargs='+',
|
||||||
|
dest='restore_paths',
|
||||||
|
help='Paths to restore from archive, defaults to the entire archive',
|
||||||
|
)
|
||||||
|
extract_group.add_argument(
|
||||||
|
'--progress',
|
||||||
|
dest='progress',
|
||||||
|
default=False,
|
||||||
|
action='store_true',
|
||||||
|
help='Display progress for each file as it is processed',
|
||||||
|
)
|
||||||
|
extract_group.add_argument(
|
||||||
|
'-h', '--help', action='help', help='Show this help message and exit'
|
||||||
|
)
|
||||||
|
|
||||||
|
list_parser = subparsers.add_parser(
|
||||||
|
'list',
|
||||||
|
aliases=SUBPARSER_ALIASES['list'],
|
||||||
|
help='List archives',
|
||||||
|
description='List archives',
|
||||||
|
add_help=False,
|
||||||
|
)
|
||||||
|
list_group = list_parser.add_argument_group('list arguments')
|
||||||
|
list_group.add_argument(
|
||||||
|
'--repository',
|
||||||
|
help='Path of repository to use, defaults to the configured repository if there is only one',
|
||||||
|
)
|
||||||
|
list_group.add_argument('--archive', help='Name of archive to operate on')
|
||||||
|
list_group.add_argument(
|
||||||
|
'--json', dest='json', default=False, action='store_true', help='Output results as JSON'
|
||||||
|
)
|
||||||
|
list_group.add_argument('-h', '--help', action='help', help='Show this help message and exit')
|
||||||
|
|
||||||
|
info_parser = subparsers.add_parser(
|
||||||
|
'info',
|
||||||
|
aliases=SUBPARSER_ALIASES['info'],
|
||||||
|
help='Display summary information on archives',
|
||||||
|
description='Display summary information on archives',
|
||||||
|
add_help=False,
|
||||||
|
)
|
||||||
|
info_group = info_parser.add_argument_group('info arguments')
|
||||||
|
info_group.add_argument(
|
||||||
|
'--json', dest='json', default=False, action='store_true', help='Output results as JSON'
|
||||||
|
)
|
||||||
|
info_group.add_argument('-h', '--help', action='help', help='Show this help message and exit')
|
||||||
|
|
||||||
|
arguments = parse_subparser_arguments(unparsed_arguments, top_level_parser, subparsers)
|
||||||
|
|
||||||
|
if arguments['global'].excludes_filename:
|
||||||
|
raise ValueError(
|
||||||
|
'The --excludes option has been replaced with exclude_patterns in configuration'
|
||||||
|
)
|
||||||
|
|
||||||
|
if 'init' in arguments and arguments['global'].dry_run:
|
||||||
|
raise ValueError('The init action cannot be used with the --dry-run option')
|
||||||
|
|
||||||
|
if (
|
||||||
|
'list' in arguments
|
||||||
|
and 'info' in arguments
|
||||||
|
and arguments['list'].json
|
||||||
|
and arguments['info'].json
|
||||||
|
):
|
||||||
|
raise ValueError('With the --json option, list and info actions cannot be used together')
|
||||||
|
|
||||||
|
return arguments
|
|
@ -3,7 +3,6 @@ import json
|
||||||
import logging
|
import logging
|
||||||
import os
|
import os
|
||||||
import sys
|
import sys
|
||||||
from argparse import ArgumentParser
|
|
||||||
from subprocess import CalledProcessError
|
from subprocess import CalledProcessError
|
||||||
|
|
||||||
import colorama
|
import colorama
|
||||||
|
@ -18,6 +17,7 @@ from borgmatic.borg import info as borg_info
|
||||||
from borgmatic.borg import init as borg_init
|
from borgmatic.borg import init as borg_init
|
||||||
from borgmatic.borg import list as borg_list
|
from borgmatic.borg import list as borg_list
|
||||||
from borgmatic.borg import prune as borg_prune
|
from borgmatic.borg import prune as borg_prune
|
||||||
|
from borgmatic.commands.arguments import parse_arguments
|
||||||
from borgmatic.config import checks, collect, convert, validate
|
from borgmatic.config import checks, collect, convert, validate
|
||||||
from borgmatic.logger import configure_logging, should_do_markup
|
from borgmatic.logger import configure_logging, should_do_markup
|
||||||
from borgmatic.signals import configure_signals
|
from borgmatic.signals import configure_signals
|
||||||
|
@ -26,293 +26,6 @@ from borgmatic.verbosity import verbosity_to_log_level
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
LEGACY_CONFIG_PATH = '/etc/borgmatic/config'
|
LEGACY_CONFIG_PATH = '/etc/borgmatic/config'
|
||||||
SUBPARSER_ALIASES = {
|
|
||||||
'init': ['--init', '-I'],
|
|
||||||
'prune': ['--prune', '-p'],
|
|
||||||
'create': ['--create', '-C'],
|
|
||||||
'check': ['--check', '-k'],
|
|
||||||
'extract': ['--extract', '-x'],
|
|
||||||
'list': ['--list', '-l'],
|
|
||||||
'info': ['--info', '-i'],
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
def parse_subparser_arguments(unparsed_arguments, top_level_parser, subparsers):
|
|
||||||
'''
|
|
||||||
Given a sequence of arguments, a top-level parser (containing subparsers), and a subparsers
|
|
||||||
object as returned by argparse.ArgumentParser().add_subparsers(), ask each subparser to parse
|
|
||||||
its own arguments and the top-level parser to parse any remaining arguments.
|
|
||||||
|
|
||||||
Return the result as a dict mapping from subparser name (or "global") to a parsed namespace of
|
|
||||||
arguments.
|
|
||||||
'''
|
|
||||||
arguments = collections.OrderedDict()
|
|
||||||
remaining_arguments = list(unparsed_arguments)
|
|
||||||
alias_to_subparser_name = {
|
|
||||||
alias: subparser_name
|
|
||||||
for subparser_name, aliases in SUBPARSER_ALIASES.items()
|
|
||||||
for alias in aliases
|
|
||||||
}
|
|
||||||
|
|
||||||
# Give each requested action's subparser a shot at parsing all arguments.
|
|
||||||
for subparser_name, subparser in subparsers.choices.items():
|
|
||||||
if subparser_name not in unparsed_arguments:
|
|
||||||
continue
|
|
||||||
|
|
||||||
remaining_arguments.remove(subparser_name)
|
|
||||||
canonical_name = alias_to_subparser_name.get(subparser_name, subparser_name)
|
|
||||||
|
|
||||||
parsed, remaining = subparser.parse_known_args(unparsed_arguments)
|
|
||||||
arguments[canonical_name] = parsed
|
|
||||||
|
|
||||||
# If no actions are explicitly requested, assume defaults: prune, create, and check.
|
|
||||||
if not arguments and '--help' not in unparsed_arguments and '-h' not in unparsed_arguments:
|
|
||||||
for subparser_name in ('prune', 'create', 'check'):
|
|
||||||
subparser = subparsers.choices[subparser_name]
|
|
||||||
parsed, remaining = subparser.parse_known_args(unparsed_arguments)
|
|
||||||
arguments[subparser_name] = parsed
|
|
||||||
|
|
||||||
# Then ask each subparser, one by one, to greedily consume arguments. Any arguments that remain
|
|
||||||
# are global arguments.
|
|
||||||
for subparser_name in arguments.keys():
|
|
||||||
subparser = subparsers.choices[subparser_name]
|
|
||||||
parsed, remaining_arguments = subparser.parse_known_args(remaining_arguments)
|
|
||||||
|
|
||||||
arguments['global'] = top_level_parser.parse_args(remaining_arguments)
|
|
||||||
|
|
||||||
return arguments
|
|
||||||
|
|
||||||
|
|
||||||
def parse_arguments(*unparsed_arguments):
|
|
||||||
'''
|
|
||||||
Given command-line arguments with which this script was invoked, parse the arguments and return
|
|
||||||
them as a dict mapping from subparser name (or "global") to an argparse.Namespace instance.
|
|
||||||
'''
|
|
||||||
config_paths = collect.get_default_config_paths()
|
|
||||||
|
|
||||||
global_parser = ArgumentParser(add_help=False)
|
|
||||||
global_group = global_parser.add_argument_group('global arguments')
|
|
||||||
|
|
||||||
global_group.add_argument(
|
|
||||||
'-c',
|
|
||||||
'--config',
|
|
||||||
nargs='*',
|
|
||||||
dest='config_paths',
|
|
||||||
default=config_paths,
|
|
||||||
help='Configuration filenames or directories, defaults to: {}'.format(
|
|
||||||
' '.join(config_paths)
|
|
||||||
),
|
|
||||||
)
|
|
||||||
global_group.add_argument(
|
|
||||||
'--excludes',
|
|
||||||
dest='excludes_filename',
|
|
||||||
help='Deprecated in favor of exclude_patterns within configuration',
|
|
||||||
)
|
|
||||||
global_group.add_argument(
|
|
||||||
'-n',
|
|
||||||
'--dry-run',
|
|
||||||
dest='dry_run',
|
|
||||||
action='store_true',
|
|
||||||
help='Go through the motions, but do not actually write to any repositories',
|
|
||||||
)
|
|
||||||
global_group.add_argument(
|
|
||||||
'-nc', '--no-color', dest='no_color', action='store_true', help='Disable colored output'
|
|
||||||
)
|
|
||||||
global_group.add_argument(
|
|
||||||
'-v',
|
|
||||||
'--verbosity',
|
|
||||||
type=int,
|
|
||||||
choices=range(0, 3),
|
|
||||||
default=0,
|
|
||||||
help='Display verbose progress to the console (from none to lots: 0, 1, or 2)',
|
|
||||||
)
|
|
||||||
global_group.add_argument(
|
|
||||||
'--syslog-verbosity',
|
|
||||||
type=int,
|
|
||||||
choices=range(0, 3),
|
|
||||||
default=0,
|
|
||||||
help='Display verbose progress to syslog (from none to lots: 0, 1, or 2)',
|
|
||||||
)
|
|
||||||
global_group.add_argument(
|
|
||||||
'--version',
|
|
||||||
dest='version',
|
|
||||||
default=False,
|
|
||||||
action='store_true',
|
|
||||||
help='Display installed version number of borgmatic and exit',
|
|
||||||
)
|
|
||||||
|
|
||||||
top_level_parser = ArgumentParser(
|
|
||||||
description='''
|
|
||||||
A simple wrapper script for the Borg backup software that creates and prunes backups.
|
|
||||||
If none of the action options are given, then borgmatic defaults to: prune, create, and
|
|
||||||
check archives.
|
|
||||||
''',
|
|
||||||
parents=[global_parser],
|
|
||||||
)
|
|
||||||
|
|
||||||
subparsers = top_level_parser.add_subparsers(title='actions', metavar='')
|
|
||||||
init_parser = subparsers.add_parser(
|
|
||||||
'init',
|
|
||||||
aliases=SUBPARSER_ALIASES['init'],
|
|
||||||
help='Initialize an empty Borg repository',
|
|
||||||
description='Initialize an empty Borg repository',
|
|
||||||
add_help=False,
|
|
||||||
)
|
|
||||||
init_group = init_parser.add_argument_group('init arguments')
|
|
||||||
init_group.add_argument(
|
|
||||||
'-e',
|
|
||||||
'--encryption',
|
|
||||||
dest='encryption_mode',
|
|
||||||
help='Borg repository encryption mode',
|
|
||||||
required=True,
|
|
||||||
)
|
|
||||||
init_group.add_argument(
|
|
||||||
'--append-only',
|
|
||||||
dest='append_only',
|
|
||||||
action='store_true',
|
|
||||||
help='Create an append-only repository',
|
|
||||||
)
|
|
||||||
init_group.add_argument(
|
|
||||||
'--storage-quota',
|
|
||||||
dest='storage_quota',
|
|
||||||
help='Create a repository with a fixed storage quota',
|
|
||||||
)
|
|
||||||
init_group.add_argument('-h', '--help', action='help', help='Show this help message and exit')
|
|
||||||
|
|
||||||
prune_parser = subparsers.add_parser(
|
|
||||||
'prune',
|
|
||||||
aliases=SUBPARSER_ALIASES['prune'],
|
|
||||||
help='Prune archives according to the retention policy',
|
|
||||||
description='Prune archives according to the retention policy',
|
|
||||||
add_help=False,
|
|
||||||
)
|
|
||||||
prune_group = prune_parser.add_argument_group('prune arguments')
|
|
||||||
prune_group.add_argument(
|
|
||||||
'--stats',
|
|
||||||
dest='stats',
|
|
||||||
default=False,
|
|
||||||
action='store_true',
|
|
||||||
help='Display statistics of archive',
|
|
||||||
)
|
|
||||||
prune_group.add_argument('-h', '--help', action='help', help='Show this help message and exit')
|
|
||||||
|
|
||||||
create_parser = subparsers.add_parser(
|
|
||||||
'create',
|
|
||||||
aliases=SUBPARSER_ALIASES['create'],
|
|
||||||
help='Create archives (actually perform backups)',
|
|
||||||
description='Create archives (actually perform backups)',
|
|
||||||
add_help=False,
|
|
||||||
)
|
|
||||||
create_group = create_parser.add_argument_group('create arguments')
|
|
||||||
create_group.add_argument(
|
|
||||||
'--progress',
|
|
||||||
dest='progress',
|
|
||||||
default=False,
|
|
||||||
action='store_true',
|
|
||||||
help='Display progress for each file as it is processed',
|
|
||||||
)
|
|
||||||
create_group.add_argument(
|
|
||||||
'--stats',
|
|
||||||
dest='stats',
|
|
||||||
default=False,
|
|
||||||
action='store_true',
|
|
||||||
help='Display statistics of archive',
|
|
||||||
)
|
|
||||||
create_group.add_argument(
|
|
||||||
'--json', dest='json', default=False, action='store_true', help='Output results as JSON'
|
|
||||||
)
|
|
||||||
create_group.add_argument('-h', '--help', action='help', help='Show this help message and exit')
|
|
||||||
|
|
||||||
check_parser = subparsers.add_parser(
|
|
||||||
'check',
|
|
||||||
aliases=SUBPARSER_ALIASES['check'],
|
|
||||||
help='Check archives for consistency',
|
|
||||||
description='Check archives for consistency',
|
|
||||||
add_help=False,
|
|
||||||
)
|
|
||||||
check_group = check_parser.add_argument_group('check arguments')
|
|
||||||
check_group.add_argument('-h', '--help', action='help', help='Show this help message and exit')
|
|
||||||
|
|
||||||
extract_parser = subparsers.add_parser(
|
|
||||||
'extract',
|
|
||||||
aliases=SUBPARSER_ALIASES['extract'],
|
|
||||||
help='Extract a named archive to the current directory',
|
|
||||||
description='Extract a named archive to the current directory',
|
|
||||||
add_help=False,
|
|
||||||
)
|
|
||||||
extract_group = extract_parser.add_argument_group('extract arguments')
|
|
||||||
extract_group.add_argument(
|
|
||||||
'--repository',
|
|
||||||
help='Path of repository to use, defaults to the configured repository if there is only one',
|
|
||||||
)
|
|
||||||
extract_group.add_argument('--archive', help='Name of archive to operate on', required=True)
|
|
||||||
extract_group.add_argument(
|
|
||||||
'--restore-path',
|
|
||||||
nargs='+',
|
|
||||||
dest='restore_paths',
|
|
||||||
help='Paths to restore from archive, defaults to the entire archive',
|
|
||||||
)
|
|
||||||
extract_group.add_argument(
|
|
||||||
'--progress',
|
|
||||||
dest='progress',
|
|
||||||
default=False,
|
|
||||||
action='store_true',
|
|
||||||
help='Display progress for each file as it is processed',
|
|
||||||
)
|
|
||||||
extract_group.add_argument(
|
|
||||||
'-h', '--help', action='help', help='Show this help message and exit'
|
|
||||||
)
|
|
||||||
|
|
||||||
list_parser = subparsers.add_parser(
|
|
||||||
'list',
|
|
||||||
aliases=SUBPARSER_ALIASES['list'],
|
|
||||||
help='List archives',
|
|
||||||
description='List archives',
|
|
||||||
add_help=False,
|
|
||||||
)
|
|
||||||
list_group = list_parser.add_argument_group('list arguments')
|
|
||||||
list_group.add_argument(
|
|
||||||
'--repository',
|
|
||||||
help='Path of repository to use, defaults to the configured repository if there is only one',
|
|
||||||
)
|
|
||||||
list_group.add_argument('--archive', help='Name of archive to operate on')
|
|
||||||
list_group.add_argument(
|
|
||||||
'--json', dest='json', default=False, action='store_true', help='Output results as JSON'
|
|
||||||
)
|
|
||||||
list_group.add_argument('-h', '--help', action='help', help='Show this help message and exit')
|
|
||||||
|
|
||||||
info_parser = subparsers.add_parser(
|
|
||||||
'info',
|
|
||||||
aliases=SUBPARSER_ALIASES['info'],
|
|
||||||
help='Display summary information on archives',
|
|
||||||
description='Display summary information on archives',
|
|
||||||
add_help=False,
|
|
||||||
)
|
|
||||||
info_group = info_parser.add_argument_group('info arguments')
|
|
||||||
info_group.add_argument(
|
|
||||||
'--json', dest='json', default=False, action='store_true', help='Output results as JSON'
|
|
||||||
)
|
|
||||||
info_group.add_argument('-h', '--help', action='help', help='Show this help message and exit')
|
|
||||||
|
|
||||||
arguments = parse_subparser_arguments(unparsed_arguments, top_level_parser, subparsers)
|
|
||||||
|
|
||||||
if arguments['global'].excludes_filename:
|
|
||||||
raise ValueError(
|
|
||||||
'The --excludes option has been replaced with exclude_patterns in configuration'
|
|
||||||
)
|
|
||||||
|
|
||||||
if 'init' in arguments and arguments['global'].dry_run:
|
|
||||||
raise ValueError('The init action cannot be used with the --dry-run option')
|
|
||||||
|
|
||||||
if (
|
|
||||||
'list' in arguments
|
|
||||||
and 'info' in arguments
|
|
||||||
and arguments['list'].json
|
|
||||||
and arguments['info'].json
|
|
||||||
):
|
|
||||||
raise ValueError('With the --json option, list and info actions cannot be used together')
|
|
||||||
|
|
||||||
return arguments
|
|
||||||
|
|
||||||
|
|
||||||
def run_configuration(config_filename, config, arguments): # pragma: no cover
|
def run_configuration(config_filename, config, arguments): # pragma: no cover
|
||||||
|
|
348
tests/integration/commands/test_arguments.py
Normal file
348
tests/integration/commands/test_arguments.py
Normal file
|
@ -0,0 +1,348 @@
|
||||||
|
import pytest
|
||||||
|
from flexmock import flexmock
|
||||||
|
|
||||||
|
from borgmatic.commands import arguments as module
|
||||||
|
|
||||||
|
|
||||||
|
def test_parse_arguments_with_no_arguments_uses_defaults():
|
||||||
|
config_paths = ['default']
|
||||||
|
flexmock(module.collect).should_receive('get_default_config_paths').and_return(config_paths)
|
||||||
|
|
||||||
|
arguments = module.parse_arguments()
|
||||||
|
|
||||||
|
global_arguments = arguments['global']
|
||||||
|
assert global_arguments.config_paths == config_paths
|
||||||
|
assert global_arguments.excludes_filename is None
|
||||||
|
assert global_arguments.verbosity == 0
|
||||||
|
assert global_arguments.syslog_verbosity == 0
|
||||||
|
|
||||||
|
|
||||||
|
def test_parse_arguments_with_multiple_config_paths_parses_as_list():
|
||||||
|
flexmock(module.collect).should_receive('get_default_config_paths').and_return(['default'])
|
||||||
|
|
||||||
|
arguments = module.parse_arguments('--config', 'myconfig', 'otherconfig')
|
||||||
|
|
||||||
|
global_arguments = arguments['global']
|
||||||
|
assert global_arguments.config_paths == ['myconfig', 'otherconfig']
|
||||||
|
assert global_arguments.verbosity == 0
|
||||||
|
assert global_arguments.syslog_verbosity == 0
|
||||||
|
|
||||||
|
|
||||||
|
def test_parse_arguments_with_verbosity_overrides_default():
|
||||||
|
config_paths = ['default']
|
||||||
|
flexmock(module.collect).should_receive('get_default_config_paths').and_return(config_paths)
|
||||||
|
|
||||||
|
arguments = module.parse_arguments('--verbosity', '1')
|
||||||
|
|
||||||
|
global_arguments = arguments['global']
|
||||||
|
assert global_arguments.config_paths == config_paths
|
||||||
|
assert global_arguments.excludes_filename is None
|
||||||
|
assert global_arguments.verbosity == 1
|
||||||
|
assert global_arguments.syslog_verbosity == 0
|
||||||
|
|
||||||
|
|
||||||
|
def test_parse_arguments_with_syslog_verbosity_overrides_default():
|
||||||
|
config_paths = ['default']
|
||||||
|
flexmock(module.collect).should_receive('get_default_config_paths').and_return(config_paths)
|
||||||
|
|
||||||
|
arguments = module.parse_arguments('--syslog-verbosity', '2')
|
||||||
|
|
||||||
|
global_arguments = arguments['global']
|
||||||
|
assert global_arguments.config_paths == config_paths
|
||||||
|
assert global_arguments.excludes_filename is None
|
||||||
|
assert global_arguments.verbosity == 0
|
||||||
|
assert global_arguments.syslog_verbosity == 2
|
||||||
|
|
||||||
|
|
||||||
|
def test_parse_arguments_with_list_json_overrides_default():
|
||||||
|
arguments = module.parse_arguments('list', '--json')
|
||||||
|
|
||||||
|
assert 'list' in arguments
|
||||||
|
assert arguments['list'].json is True
|
||||||
|
|
||||||
|
|
||||||
|
def test_parse_arguments_with_dashed_list_json_overrides_default():
|
||||||
|
arguments = module.parse_arguments('--list', '--json')
|
||||||
|
|
||||||
|
assert 'list' in arguments
|
||||||
|
assert arguments['list'].json is True
|
||||||
|
|
||||||
|
|
||||||
|
def test_parse_arguments_with_no_actions_defaults_to_all_actions_enabled():
|
||||||
|
flexmock(module.collect).should_receive('get_default_config_paths').and_return(['default'])
|
||||||
|
|
||||||
|
arguments = module.parse_arguments()
|
||||||
|
|
||||||
|
assert 'prune' in arguments
|
||||||
|
assert 'create' in arguments
|
||||||
|
assert 'check' in arguments
|
||||||
|
|
||||||
|
|
||||||
|
def test_parse_arguments_with_help_and_no_actions_shows_global_help(capsys):
|
||||||
|
flexmock(module.collect).should_receive('get_default_config_paths').and_return(['default'])
|
||||||
|
|
||||||
|
with pytest.raises(SystemExit) as exit:
|
||||||
|
module.parse_arguments('--help')
|
||||||
|
|
||||||
|
assert exit.value.code == 0
|
||||||
|
captured = capsys.readouterr()
|
||||||
|
assert 'global arguments:' in captured.out
|
||||||
|
assert 'actions:' in captured.out
|
||||||
|
|
||||||
|
|
||||||
|
def test_parse_arguments_with_help_and_action_shows_action_help(capsys):
|
||||||
|
flexmock(module.collect).should_receive('get_default_config_paths').and_return(['default'])
|
||||||
|
|
||||||
|
with pytest.raises(SystemExit) as exit:
|
||||||
|
module.parse_arguments('create', '--help')
|
||||||
|
|
||||||
|
assert exit.value.code == 0
|
||||||
|
captured = capsys.readouterr()
|
||||||
|
assert 'global arguments:' not in captured.out
|
||||||
|
assert 'actions:' not in captured.out
|
||||||
|
assert 'create arguments:' in captured.out
|
||||||
|
|
||||||
|
|
||||||
|
def test_parse_arguments_with_prune_action_leaves_other_actions_disabled():
|
||||||
|
flexmock(module.collect).should_receive('get_default_config_paths').and_return(['default'])
|
||||||
|
|
||||||
|
arguments = module.parse_arguments('prune')
|
||||||
|
|
||||||
|
assert 'prune' in arguments
|
||||||
|
assert 'create' not in arguments
|
||||||
|
assert 'check' not in arguments
|
||||||
|
|
||||||
|
|
||||||
|
def test_parse_arguments_with_dashed_prune_action_leaves_other_actions_disabled():
|
||||||
|
flexmock(module.collect).should_receive('get_default_config_paths').and_return(['default'])
|
||||||
|
|
||||||
|
arguments = module.parse_arguments('--prune')
|
||||||
|
|
||||||
|
assert 'prune' in arguments
|
||||||
|
assert 'create' not in arguments
|
||||||
|
assert 'check' not in arguments
|
||||||
|
|
||||||
|
|
||||||
|
def test_parse_arguments_with_multiple_actions_leaves_other_action_disabled():
|
||||||
|
flexmock(module.collect).should_receive('get_default_config_paths').and_return(['default'])
|
||||||
|
|
||||||
|
arguments = module.parse_arguments('create', 'check')
|
||||||
|
|
||||||
|
assert 'prune' not in arguments
|
||||||
|
assert 'create' in arguments
|
||||||
|
assert 'check' in arguments
|
||||||
|
|
||||||
|
|
||||||
|
def test_parse_arguments_with_multiple_dashed_actions_leaves_other_action_disabled():
|
||||||
|
flexmock(module.collect).should_receive('get_default_config_paths').and_return(['default'])
|
||||||
|
|
||||||
|
arguments = module.parse_arguments('--create', '--check')
|
||||||
|
|
||||||
|
assert 'prune' not in arguments
|
||||||
|
assert 'create' in arguments
|
||||||
|
assert 'check' in arguments
|
||||||
|
|
||||||
|
|
||||||
|
def test_parse_arguments_with_invalid_arguments_exits():
|
||||||
|
flexmock(module.collect).should_receive('get_default_config_paths').and_return(['default'])
|
||||||
|
|
||||||
|
with pytest.raises(SystemExit):
|
||||||
|
module.parse_arguments('--posix-me-harder')
|
||||||
|
|
||||||
|
|
||||||
|
def test_parse_arguments_disallows_deprecated_excludes_option():
|
||||||
|
flexmock(module.collect).should_receive('get_default_config_paths').and_return(['default'])
|
||||||
|
|
||||||
|
with pytest.raises(ValueError):
|
||||||
|
module.parse_arguments('--config', 'myconfig', '--excludes', 'myexcludes')
|
||||||
|
|
||||||
|
|
||||||
|
def test_parse_arguments_disallows_encryption_mode_without_init():
|
||||||
|
flexmock(module.collect).should_receive('get_default_config_paths').and_return(['default'])
|
||||||
|
|
||||||
|
with pytest.raises(SystemExit):
|
||||||
|
module.parse_arguments('--config', 'myconfig', '--encryption', 'repokey')
|
||||||
|
|
||||||
|
|
||||||
|
def test_parse_arguments_allows_encryption_mode_with_init():
|
||||||
|
flexmock(module.collect).should_receive('get_default_config_paths').and_return(['default'])
|
||||||
|
|
||||||
|
module.parse_arguments('--config', 'myconfig', 'init', '--encryption', 'repokey')
|
||||||
|
|
||||||
|
|
||||||
|
def test_parse_arguments_allows_encryption_mode_with_dashed_init():
|
||||||
|
flexmock(module.collect).should_receive('get_default_config_paths').and_return(['default'])
|
||||||
|
|
||||||
|
module.parse_arguments('--config', 'myconfig', '--init', '--encryption', 'repokey')
|
||||||
|
|
||||||
|
|
||||||
|
def test_parse_arguments_requires_encryption_mode_with_init():
|
||||||
|
flexmock(module.collect).should_receive('get_default_config_paths').and_return(['default'])
|
||||||
|
|
||||||
|
with pytest.raises(SystemExit):
|
||||||
|
module.parse_arguments('--config', 'myconfig', 'init')
|
||||||
|
|
||||||
|
|
||||||
|
def test_parse_arguments_disallows_append_only_without_init():
|
||||||
|
flexmock(module.collect).should_receive('get_default_config_paths').and_return(['default'])
|
||||||
|
|
||||||
|
with pytest.raises(SystemExit):
|
||||||
|
module.parse_arguments('--config', 'myconfig', '--append-only')
|
||||||
|
|
||||||
|
|
||||||
|
def test_parse_arguments_disallows_storage_quota_without_init():
|
||||||
|
flexmock(module.collect).should_receive('get_default_config_paths').and_return(['default'])
|
||||||
|
|
||||||
|
with pytest.raises(SystemExit):
|
||||||
|
module.parse_arguments('--config', 'myconfig', '--storage-quota', '5G')
|
||||||
|
|
||||||
|
|
||||||
|
def test_parse_arguments_allows_init_and_prune():
|
||||||
|
flexmock(module.collect).should_receive('get_default_config_paths').and_return(['default'])
|
||||||
|
|
||||||
|
module.parse_arguments('--config', 'myconfig', 'init', '--encryption', 'repokey', 'prune')
|
||||||
|
|
||||||
|
|
||||||
|
def test_parse_arguments_allows_init_and_create():
|
||||||
|
flexmock(module.collect).should_receive('get_default_config_paths').and_return(['default'])
|
||||||
|
|
||||||
|
module.parse_arguments('--config', 'myconfig', 'init', '--encryption', 'repokey', 'create')
|
||||||
|
|
||||||
|
|
||||||
|
def test_parse_arguments_disallows_init_and_dry_run():
|
||||||
|
flexmock(module.collect).should_receive('get_default_config_paths').and_return(['default'])
|
||||||
|
|
||||||
|
with pytest.raises(ValueError):
|
||||||
|
module.parse_arguments(
|
||||||
|
'--config', 'myconfig', 'init', '--encryption', 'repokey', '--dry-run'
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def test_parse_arguments_disallows_repository_without_extract_or_list():
|
||||||
|
flexmock(module.collect).should_receive('get_default_config_paths').and_return(['default'])
|
||||||
|
|
||||||
|
with pytest.raises(SystemExit):
|
||||||
|
module.parse_arguments('--config', 'myconfig', '--repository', 'test.borg')
|
||||||
|
|
||||||
|
|
||||||
|
def test_parse_arguments_allows_repository_with_extract():
|
||||||
|
flexmock(module.collect).should_receive('get_default_config_paths').and_return(['default'])
|
||||||
|
|
||||||
|
module.parse_arguments(
|
||||||
|
'--config', 'myconfig', 'extract', '--repository', 'test.borg', '--archive', 'test'
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def test_parse_arguments_allows_repository_with_list():
|
||||||
|
flexmock(module.collect).should_receive('get_default_config_paths').and_return(['default'])
|
||||||
|
|
||||||
|
module.parse_arguments('--config', 'myconfig', 'list', '--repository', 'test.borg')
|
||||||
|
|
||||||
|
|
||||||
|
def test_parse_arguments_disallows_archive_without_extract_or_list():
|
||||||
|
flexmock(module.collect).should_receive('get_default_config_paths').and_return(['default'])
|
||||||
|
|
||||||
|
with pytest.raises(SystemExit):
|
||||||
|
module.parse_arguments('--config', 'myconfig', '--archive', 'test')
|
||||||
|
|
||||||
|
|
||||||
|
def test_parse_arguments_disallows_restore_paths_without_extract():
|
||||||
|
flexmock(module.collect).should_receive('get_default_config_paths').and_return(['default'])
|
||||||
|
|
||||||
|
with pytest.raises(SystemExit):
|
||||||
|
module.parse_arguments('--config', 'myconfig', '--restore-path', 'test')
|
||||||
|
|
||||||
|
|
||||||
|
def test_parse_arguments_allows_archive_with_extract():
|
||||||
|
flexmock(module.collect).should_receive('get_default_config_paths').and_return(['default'])
|
||||||
|
|
||||||
|
module.parse_arguments('--config', 'myconfig', 'extract', '--archive', 'test')
|
||||||
|
|
||||||
|
|
||||||
|
def test_parse_arguments_allows_archive_with_dashed_extract():
|
||||||
|
flexmock(module.collect).should_receive('get_default_config_paths').and_return(['default'])
|
||||||
|
|
||||||
|
module.parse_arguments('--config', 'myconfig', '--extract', '--archive', 'test')
|
||||||
|
|
||||||
|
|
||||||
|
def test_parse_arguments_allows_archive_with_list():
|
||||||
|
flexmock(module.collect).should_receive('get_default_config_paths').and_return(['default'])
|
||||||
|
|
||||||
|
module.parse_arguments('--config', 'myconfig', 'list', '--archive', 'test')
|
||||||
|
|
||||||
|
|
||||||
|
def test_parse_arguments_requires_archive_with_extract():
|
||||||
|
flexmock(module.collect).should_receive('get_default_config_paths').and_return(['default'])
|
||||||
|
|
||||||
|
with pytest.raises(SystemExit):
|
||||||
|
module.parse_arguments('--config', 'myconfig', 'extract')
|
||||||
|
|
||||||
|
|
||||||
|
def test_parse_arguments_allows_progress_before_create():
|
||||||
|
flexmock(module.collect).should_receive('get_default_config_paths').and_return(['default'])
|
||||||
|
|
||||||
|
module.parse_arguments('--progress', 'create', 'list')
|
||||||
|
|
||||||
|
|
||||||
|
def test_parse_arguments_allows_progress_after_create():
|
||||||
|
flexmock(module.collect).should_receive('get_default_config_paths').and_return(['default'])
|
||||||
|
|
||||||
|
module.parse_arguments('create', '--progress', 'list')
|
||||||
|
|
||||||
|
|
||||||
|
def test_parse_arguments_allows_progress_and_extract():
|
||||||
|
flexmock(module.collect).should_receive('get_default_config_paths').and_return(['default'])
|
||||||
|
|
||||||
|
module.parse_arguments('--progress', 'extract', '--archive', 'test', 'list')
|
||||||
|
|
||||||
|
|
||||||
|
def test_parse_arguments_disallows_progress_without_create():
|
||||||
|
flexmock(module.collect).should_receive('get_default_config_paths').and_return(['default'])
|
||||||
|
|
||||||
|
with pytest.raises(SystemExit):
|
||||||
|
module.parse_arguments('--progress', 'list')
|
||||||
|
|
||||||
|
|
||||||
|
def test_parse_arguments_with_stats_and_create_flags_does_not_raise():
|
||||||
|
flexmock(module.collect).should_receive('get_default_config_paths').and_return(['default'])
|
||||||
|
|
||||||
|
module.parse_arguments('--stats', 'create', 'list')
|
||||||
|
|
||||||
|
|
||||||
|
def test_parse_arguments_with_stats_and_prune_flags_does_not_raise():
|
||||||
|
flexmock(module.collect).should_receive('get_default_config_paths').and_return(['default'])
|
||||||
|
|
||||||
|
module.parse_arguments('--stats', 'prune', 'list')
|
||||||
|
|
||||||
|
|
||||||
|
def test_parse_arguments_with_stats_flag_but_no_create_or_prune_flag_raises_value_error():
|
||||||
|
flexmock(module.collect).should_receive('get_default_config_paths').and_return(['default'])
|
||||||
|
|
||||||
|
with pytest.raises(SystemExit):
|
||||||
|
module.parse_arguments('--stats', 'list')
|
||||||
|
|
||||||
|
|
||||||
|
def test_parse_arguments_with_just_stats_flag_does_not_raise():
|
||||||
|
flexmock(module.collect).should_receive('get_default_config_paths').and_return(['default'])
|
||||||
|
|
||||||
|
module.parse_arguments('--stats')
|
||||||
|
|
||||||
|
|
||||||
|
def test_parse_arguments_allows_json_with_list_or_info():
|
||||||
|
flexmock(module.collect).should_receive('get_default_config_paths').and_return(['default'])
|
||||||
|
|
||||||
|
module.parse_arguments('list', '--json')
|
||||||
|
module.parse_arguments('info', '--json')
|
||||||
|
|
||||||
|
|
||||||
|
def test_parse_arguments_allows_json_with_dashed_info():
|
||||||
|
flexmock(module.collect).should_receive('get_default_config_paths').and_return(['default'])
|
||||||
|
|
||||||
|
module.parse_arguments('--info', '--json')
|
||||||
|
|
||||||
|
|
||||||
|
def test_parse_arguments_disallows_json_with_both_list_and_info():
|
||||||
|
flexmock(module.collect).should_receive('get_default_config_paths').and_return(['default'])
|
||||||
|
|
||||||
|
with pytest.raises(ValueError):
|
||||||
|
module.parse_arguments('list', 'info', '--json')
|
|
@ -1,355 +1,10 @@
|
||||||
import subprocess
|
import subprocess
|
||||||
|
|
||||||
import pytest
|
|
||||||
from flexmock import flexmock
|
from flexmock import flexmock
|
||||||
|
|
||||||
from borgmatic.commands import borgmatic as module
|
from borgmatic.commands import borgmatic as module
|
||||||
|
|
||||||
|
|
||||||
def test_parse_arguments_with_no_arguments_uses_defaults():
|
|
||||||
config_paths = ['default']
|
|
||||||
flexmock(module.collect).should_receive('get_default_config_paths').and_return(config_paths)
|
|
||||||
|
|
||||||
arguments = module.parse_arguments()
|
|
||||||
|
|
||||||
global_arguments = arguments['global']
|
|
||||||
assert global_arguments.config_paths == config_paths
|
|
||||||
assert global_arguments.excludes_filename is None
|
|
||||||
assert global_arguments.verbosity == 0
|
|
||||||
assert global_arguments.syslog_verbosity == 0
|
|
||||||
|
|
||||||
|
|
||||||
def test_parse_arguments_with_multiple_config_paths_parses_as_list():
|
|
||||||
flexmock(module.collect).should_receive('get_default_config_paths').and_return(['default'])
|
|
||||||
|
|
||||||
arguments = module.parse_arguments('--config', 'myconfig', 'otherconfig')
|
|
||||||
|
|
||||||
global_arguments = arguments['global']
|
|
||||||
assert global_arguments.config_paths == ['myconfig', 'otherconfig']
|
|
||||||
assert global_arguments.verbosity == 0
|
|
||||||
assert global_arguments.syslog_verbosity == 0
|
|
||||||
|
|
||||||
|
|
||||||
def test_parse_arguments_with_verbosity_overrides_default():
|
|
||||||
config_paths = ['default']
|
|
||||||
flexmock(module.collect).should_receive('get_default_config_paths').and_return(config_paths)
|
|
||||||
|
|
||||||
arguments = module.parse_arguments('--verbosity', '1')
|
|
||||||
|
|
||||||
global_arguments = arguments['global']
|
|
||||||
assert global_arguments.config_paths == config_paths
|
|
||||||
assert global_arguments.excludes_filename is None
|
|
||||||
assert global_arguments.verbosity == 1
|
|
||||||
assert global_arguments.syslog_verbosity == 0
|
|
||||||
|
|
||||||
|
|
||||||
def test_parse_arguments_with_syslog_verbosity_overrides_default():
|
|
||||||
config_paths = ['default']
|
|
||||||
flexmock(module.collect).should_receive('get_default_config_paths').and_return(config_paths)
|
|
||||||
|
|
||||||
arguments = module.parse_arguments('--syslog-verbosity', '2')
|
|
||||||
|
|
||||||
global_arguments = arguments['global']
|
|
||||||
assert global_arguments.config_paths == config_paths
|
|
||||||
assert global_arguments.excludes_filename is None
|
|
||||||
assert global_arguments.verbosity == 0
|
|
||||||
assert global_arguments.syslog_verbosity == 2
|
|
||||||
|
|
||||||
|
|
||||||
def test_parse_arguments_with_list_json_overrides_default():
|
|
||||||
arguments = module.parse_arguments('list', '--json')
|
|
||||||
|
|
||||||
assert 'list' in arguments
|
|
||||||
assert arguments['list'].json is True
|
|
||||||
|
|
||||||
|
|
||||||
def test_parse_arguments_with_dashed_list_json_overrides_default():
|
|
||||||
arguments = module.parse_arguments('--list', '--json')
|
|
||||||
|
|
||||||
assert 'list' in arguments
|
|
||||||
assert arguments['list'].json is True
|
|
||||||
|
|
||||||
|
|
||||||
def test_parse_arguments_with_no_actions_defaults_to_all_actions_enabled():
|
|
||||||
flexmock(module.collect).should_receive('get_default_config_paths').and_return(['default'])
|
|
||||||
|
|
||||||
arguments = module.parse_arguments()
|
|
||||||
|
|
||||||
assert 'prune' in arguments
|
|
||||||
assert 'create' in arguments
|
|
||||||
assert 'check' in arguments
|
|
||||||
|
|
||||||
|
|
||||||
def test_parse_arguments_with_help_and_no_actions_shows_global_help(capsys):
|
|
||||||
flexmock(module.collect).should_receive('get_default_config_paths').and_return(['default'])
|
|
||||||
|
|
||||||
with pytest.raises(SystemExit) as exit:
|
|
||||||
module.parse_arguments('--help')
|
|
||||||
|
|
||||||
assert exit.value.code == 0
|
|
||||||
captured = capsys.readouterr()
|
|
||||||
assert 'global arguments:' in captured.out
|
|
||||||
assert 'actions:' in captured.out
|
|
||||||
|
|
||||||
|
|
||||||
def test_parse_arguments_with_help_and_action_shows_action_help(capsys):
|
|
||||||
flexmock(module.collect).should_receive('get_default_config_paths').and_return(['default'])
|
|
||||||
|
|
||||||
with pytest.raises(SystemExit) as exit:
|
|
||||||
module.parse_arguments('create', '--help')
|
|
||||||
|
|
||||||
assert exit.value.code == 0
|
|
||||||
captured = capsys.readouterr()
|
|
||||||
assert 'global arguments:' not in captured.out
|
|
||||||
assert 'actions:' not in captured.out
|
|
||||||
assert 'create arguments:' in captured.out
|
|
||||||
|
|
||||||
|
|
||||||
def test_parse_arguments_with_prune_action_leaves_other_actions_disabled():
|
|
||||||
flexmock(module.collect).should_receive('get_default_config_paths').and_return(['default'])
|
|
||||||
|
|
||||||
arguments = module.parse_arguments('prune')
|
|
||||||
|
|
||||||
assert 'prune' in arguments
|
|
||||||
assert 'create' not in arguments
|
|
||||||
assert 'check' not in arguments
|
|
||||||
|
|
||||||
|
|
||||||
def test_parse_arguments_with_dashed_prune_action_leaves_other_actions_disabled():
|
|
||||||
flexmock(module.collect).should_receive('get_default_config_paths').and_return(['default'])
|
|
||||||
|
|
||||||
arguments = module.parse_arguments('--prune')
|
|
||||||
|
|
||||||
assert 'prune' in arguments
|
|
||||||
assert 'create' not in arguments
|
|
||||||
assert 'check' not in arguments
|
|
||||||
|
|
||||||
|
|
||||||
def test_parse_arguments_with_multiple_actions_leaves_other_action_disabled():
|
|
||||||
flexmock(module.collect).should_receive('get_default_config_paths').and_return(['default'])
|
|
||||||
|
|
||||||
arguments = module.parse_arguments('create', 'check')
|
|
||||||
|
|
||||||
assert 'prune' not in arguments
|
|
||||||
assert 'create' in arguments
|
|
||||||
assert 'check' in arguments
|
|
||||||
|
|
||||||
|
|
||||||
def test_parse_arguments_with_multiple_dashed_actions_leaves_other_action_disabled():
|
|
||||||
flexmock(module.collect).should_receive('get_default_config_paths').and_return(['default'])
|
|
||||||
|
|
||||||
arguments = module.parse_arguments('--create', '--check')
|
|
||||||
|
|
||||||
assert 'prune' not in arguments
|
|
||||||
assert 'create' in arguments
|
|
||||||
assert 'check' in arguments
|
|
||||||
|
|
||||||
|
|
||||||
def test_parse_arguments_with_invalid_arguments_exits():
|
|
||||||
flexmock(module.collect).should_receive('get_default_config_paths').and_return(['default'])
|
|
||||||
|
|
||||||
with pytest.raises(SystemExit):
|
|
||||||
module.parse_arguments('--posix-me-harder')
|
|
||||||
|
|
||||||
|
|
||||||
def test_parse_arguments_disallows_deprecated_excludes_option():
|
|
||||||
flexmock(module.collect).should_receive('get_default_config_paths').and_return(['default'])
|
|
||||||
|
|
||||||
with pytest.raises(ValueError):
|
|
||||||
module.parse_arguments('--config', 'myconfig', '--excludes', 'myexcludes')
|
|
||||||
|
|
||||||
|
|
||||||
def test_parse_arguments_disallows_encryption_mode_without_init():
|
|
||||||
flexmock(module.collect).should_receive('get_default_config_paths').and_return(['default'])
|
|
||||||
|
|
||||||
with pytest.raises(SystemExit):
|
|
||||||
module.parse_arguments('--config', 'myconfig', '--encryption', 'repokey')
|
|
||||||
|
|
||||||
|
|
||||||
def test_parse_arguments_allows_encryption_mode_with_init():
|
|
||||||
flexmock(module.collect).should_receive('get_default_config_paths').and_return(['default'])
|
|
||||||
|
|
||||||
module.parse_arguments('--config', 'myconfig', 'init', '--encryption', 'repokey')
|
|
||||||
|
|
||||||
|
|
||||||
def test_parse_arguments_allows_encryption_mode_with_dashed_init():
|
|
||||||
flexmock(module.collect).should_receive('get_default_config_paths').and_return(['default'])
|
|
||||||
|
|
||||||
module.parse_arguments('--config', 'myconfig', '--init', '--encryption', 'repokey')
|
|
||||||
|
|
||||||
|
|
||||||
def test_parse_arguments_requires_encryption_mode_with_init():
|
|
||||||
flexmock(module.collect).should_receive('get_default_config_paths').and_return(['default'])
|
|
||||||
|
|
||||||
with pytest.raises(SystemExit):
|
|
||||||
module.parse_arguments('--config', 'myconfig', 'init')
|
|
||||||
|
|
||||||
|
|
||||||
def test_parse_arguments_disallows_append_only_without_init():
|
|
||||||
flexmock(module.collect).should_receive('get_default_config_paths').and_return(['default'])
|
|
||||||
|
|
||||||
with pytest.raises(SystemExit):
|
|
||||||
module.parse_arguments('--config', 'myconfig', '--append-only')
|
|
||||||
|
|
||||||
|
|
||||||
def test_parse_arguments_disallows_storage_quota_without_init():
|
|
||||||
flexmock(module.collect).should_receive('get_default_config_paths').and_return(['default'])
|
|
||||||
|
|
||||||
with pytest.raises(SystemExit):
|
|
||||||
module.parse_arguments('--config', 'myconfig', '--storage-quota', '5G')
|
|
||||||
|
|
||||||
|
|
||||||
def test_parse_arguments_allows_init_and_prune():
|
|
||||||
flexmock(module.collect).should_receive('get_default_config_paths').and_return(['default'])
|
|
||||||
|
|
||||||
module.parse_arguments('--config', 'myconfig', 'init', '--encryption', 'repokey', 'prune')
|
|
||||||
|
|
||||||
|
|
||||||
def test_parse_arguments_allows_init_and_create():
|
|
||||||
flexmock(module.collect).should_receive('get_default_config_paths').and_return(['default'])
|
|
||||||
|
|
||||||
module.parse_arguments('--config', 'myconfig', 'init', '--encryption', 'repokey', 'create')
|
|
||||||
|
|
||||||
|
|
||||||
def test_parse_arguments_disallows_init_and_dry_run():
|
|
||||||
flexmock(module.collect).should_receive('get_default_config_paths').and_return(['default'])
|
|
||||||
|
|
||||||
with pytest.raises(ValueError):
|
|
||||||
module.parse_arguments(
|
|
||||||
'--config', 'myconfig', 'init', '--encryption', 'repokey', '--dry-run'
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
def test_parse_arguments_disallows_repository_without_extract_or_list():
|
|
||||||
flexmock(module.collect).should_receive('get_default_config_paths').and_return(['default'])
|
|
||||||
|
|
||||||
with pytest.raises(SystemExit):
|
|
||||||
module.parse_arguments('--config', 'myconfig', '--repository', 'test.borg')
|
|
||||||
|
|
||||||
|
|
||||||
def test_parse_arguments_allows_repository_with_extract():
|
|
||||||
flexmock(module.collect).should_receive('get_default_config_paths').and_return(['default'])
|
|
||||||
|
|
||||||
module.parse_arguments(
|
|
||||||
'--config', 'myconfig', 'extract', '--repository', 'test.borg', '--archive', 'test'
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
def test_parse_arguments_allows_repository_with_list():
|
|
||||||
flexmock(module.collect).should_receive('get_default_config_paths').and_return(['default'])
|
|
||||||
|
|
||||||
module.parse_arguments('--config', 'myconfig', 'list', '--repository', 'test.borg')
|
|
||||||
|
|
||||||
|
|
||||||
def test_parse_arguments_disallows_archive_without_extract_or_list():
|
|
||||||
flexmock(module.collect).should_receive('get_default_config_paths').and_return(['default'])
|
|
||||||
|
|
||||||
with pytest.raises(SystemExit):
|
|
||||||
module.parse_arguments('--config', 'myconfig', '--archive', 'test')
|
|
||||||
|
|
||||||
|
|
||||||
def test_parse_arguments_disallows_restore_paths_without_extract():
|
|
||||||
flexmock(module.collect).should_receive('get_default_config_paths').and_return(['default'])
|
|
||||||
|
|
||||||
with pytest.raises(SystemExit):
|
|
||||||
module.parse_arguments('--config', 'myconfig', '--restore-path', 'test')
|
|
||||||
|
|
||||||
|
|
||||||
def test_parse_arguments_allows_archive_with_extract():
|
|
||||||
flexmock(module.collect).should_receive('get_default_config_paths').and_return(['default'])
|
|
||||||
|
|
||||||
module.parse_arguments('--config', 'myconfig', 'extract', '--archive', 'test')
|
|
||||||
|
|
||||||
|
|
||||||
def test_parse_arguments_allows_archive_with_dashed_extract():
|
|
||||||
flexmock(module.collect).should_receive('get_default_config_paths').and_return(['default'])
|
|
||||||
|
|
||||||
module.parse_arguments('--config', 'myconfig', '--extract', '--archive', 'test')
|
|
||||||
|
|
||||||
|
|
||||||
def test_parse_arguments_allows_archive_with_list():
|
|
||||||
flexmock(module.collect).should_receive('get_default_config_paths').and_return(['default'])
|
|
||||||
|
|
||||||
module.parse_arguments('--config', 'myconfig', 'list', '--archive', 'test')
|
|
||||||
|
|
||||||
|
|
||||||
def test_parse_arguments_requires_archive_with_extract():
|
|
||||||
flexmock(module.collect).should_receive('get_default_config_paths').and_return(['default'])
|
|
||||||
|
|
||||||
with pytest.raises(SystemExit):
|
|
||||||
module.parse_arguments('--config', 'myconfig', 'extract')
|
|
||||||
|
|
||||||
|
|
||||||
def test_parse_arguments_allows_progress_before_create():
|
|
||||||
flexmock(module.collect).should_receive('get_default_config_paths').and_return(['default'])
|
|
||||||
|
|
||||||
module.parse_arguments('--progress', 'create', 'list')
|
|
||||||
|
|
||||||
|
|
||||||
def test_parse_arguments_allows_progress_after_create():
|
|
||||||
flexmock(module.collect).should_receive('get_default_config_paths').and_return(['default'])
|
|
||||||
|
|
||||||
module.parse_arguments('create', '--progress', 'list')
|
|
||||||
|
|
||||||
|
|
||||||
def test_parse_arguments_allows_progress_and_extract():
|
|
||||||
flexmock(module.collect).should_receive('get_default_config_paths').and_return(['default'])
|
|
||||||
|
|
||||||
module.parse_arguments('--progress', 'extract', '--archive', 'test', 'list')
|
|
||||||
|
|
||||||
|
|
||||||
def test_parse_arguments_disallows_progress_without_create():
|
|
||||||
flexmock(module.collect).should_receive('get_default_config_paths').and_return(['default'])
|
|
||||||
|
|
||||||
with pytest.raises(SystemExit):
|
|
||||||
module.parse_arguments('--progress', 'list')
|
|
||||||
|
|
||||||
|
|
||||||
def test_parse_arguments_with_stats_and_create_flags_does_not_raise():
|
|
||||||
flexmock(module.collect).should_receive('get_default_config_paths').and_return(['default'])
|
|
||||||
|
|
||||||
module.parse_arguments('--stats', 'create', 'list')
|
|
||||||
|
|
||||||
|
|
||||||
def test_parse_arguments_with_stats_and_prune_flags_does_not_raise():
|
|
||||||
flexmock(module.collect).should_receive('get_default_config_paths').and_return(['default'])
|
|
||||||
|
|
||||||
module.parse_arguments('--stats', 'prune', 'list')
|
|
||||||
|
|
||||||
|
|
||||||
def test_parse_arguments_with_stats_flag_but_no_create_or_prune_flag_raises_value_error():
|
|
||||||
flexmock(module.collect).should_receive('get_default_config_paths').and_return(['default'])
|
|
||||||
|
|
||||||
with pytest.raises(SystemExit):
|
|
||||||
module.parse_arguments('--stats', 'list')
|
|
||||||
|
|
||||||
|
|
||||||
def test_parse_arguments_with_just_stats_flag_does_not_raise():
|
|
||||||
flexmock(module.collect).should_receive('get_default_config_paths').and_return(['default'])
|
|
||||||
|
|
||||||
module.parse_arguments('--stats')
|
|
||||||
|
|
||||||
|
|
||||||
def test_parse_arguments_allows_json_with_list_or_info():
|
|
||||||
flexmock(module.collect).should_receive('get_default_config_paths').and_return(['default'])
|
|
||||||
|
|
||||||
module.parse_arguments('list', '--json')
|
|
||||||
module.parse_arguments('info', '--json')
|
|
||||||
|
|
||||||
|
|
||||||
def test_parse_arguments_allows_json_with_dashed_info():
|
|
||||||
flexmock(module.collect).should_receive('get_default_config_paths').and_return(['default'])
|
|
||||||
|
|
||||||
module.parse_arguments('--info', '--json')
|
|
||||||
|
|
||||||
|
|
||||||
def test_parse_arguments_disallows_json_with_both_list_and_info():
|
|
||||||
flexmock(module.collect).should_receive('get_default_config_paths').and_return(['default'])
|
|
||||||
|
|
||||||
with pytest.raises(ValueError):
|
|
||||||
module.parse_arguments('list', 'info', '--json')
|
|
||||||
|
|
||||||
|
|
||||||
def test_borgmatic_version_matches_news_version():
|
def test_borgmatic_version_matches_news_version():
|
||||||
flexmock(module.collect).should_receive('get_default_config_paths').and_return(['default'])
|
flexmock(module.collect).should_receive('get_default_config_paths').and_return(['default'])
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue