Flag for multiple levels of verbosity: some, and lots.

This commit is contained in:
Dan Helfman 2015-07-17 21:58:50 -07:00
parent 1578b44536
commit b501a568aa
8 changed files with 113 additions and 55 deletions

3
NEWS
View file

@ -1,5 +1,6 @@
0.0.7-dev
0.0.7
* Flag for multiple levels of verbosity: some, and lots.
* Improved mocking of Python builtins in unit tests.
0.0.6

View file

@ -78,9 +78,13 @@ and check backups for consistency problems due to things like file damage.
By default, the backup will proceed silently except in the case of errors. But
if you'd like to to get additional information about the progress of the
backup as it proceeds, use the verbose option instead:
backup as it proceeds, use the verbosity option:
atticmattic --verbose
atticmattic --verbosity 1
Or, for even more progress spew:
atticmattic --verbosity 2
If you'd like to see the available command-line arguments, view the help:

View file

@ -3,13 +3,19 @@ import os
import platform
import subprocess
from atticmatic.verbosity import VERBOSITY_SOME, VERBOSITY_LOTS
def create_archive(excludes_filename, verbose, source_directories, repository):
def create_archive(excludes_filename, verbosity, source_directories, repository):
'''
Given an excludes filename, a vebosity flag, a space-separated list of source directories, and
a local or remote repository path, create an attic archive.
'''
sources = tuple(source_directories.split(' '))
verbosity_flags = {
VERBOSITY_SOME: ('--stats',),
VERBOSITY_LOTS: ('--verbose', '--stats'),
}.get(verbosity, ())
command = (
'attic', 'create',
@ -19,9 +25,7 @@ def create_archive(excludes_filename, verbose, source_directories, repository):
hostname=platform.node(),
timestamp=datetime.now().isoformat(),
),
) + sources + (
('--verbose', '--stats') if verbose else ()
)
) + sources + verbosity_flags
subprocess.check_call(command)
@ -48,11 +52,16 @@ def _make_prune_flags(retention_config):
)
def prune_archives(verbose, repository, retention_config):
def prune_archives(verbosity, repository, retention_config):
'''
Given a verbosity flag, a local or remote repository path, and a retention config dict, prune
attic archives according the the retention policy specified in that configuration.
'''
verbosity_flags = {
VERBOSITY_SOME: ('--stats',),
VERBOSITY_LOTS: ('--verbose', '--stats'),
}.get(verbosity, ())
command = (
'attic', 'prune',
repository,
@ -60,7 +69,7 @@ def prune_archives(verbose, repository, retention_config):
element
for pair in _make_prune_flags(retention_config)
for element in pair
) + (('--verbose',) if verbose else ())
) + verbosity_flags
subprocess.check_call(command)
@ -114,7 +123,7 @@ def _make_check_flags(checks):
)
def check_archives(verbose, repository, consistency_config):
def check_archives(verbosity, repository, consistency_config):
'''
Given a verbosity flag, a local or remote repository path, and a consistency config dict, check
the contained attic archives for consistency.
@ -125,12 +134,17 @@ def check_archives(verbose, repository, consistency_config):
if not checks:
return
verbosity_flags = {
VERBOSITY_SOME: ('--verbose',),
VERBOSITY_LOTS: ('--verbose',),
}.get(verbosity, ())
command = (
'attic', 'check',
repository,
) + _make_check_flags(checks) + (('--verbose',) if verbose else ())
) + _make_check_flags(checks) + verbosity_flags
# Attic's check command spews to stdout even without the verbose flag. Suppress it.
stdout = None if verbose else open(os.devnull, 'w')
stdout = None if verbosity_flags else open(os.devnull, 'w')
subprocess.check_call(command, stdout=stdout)

View file

@ -29,9 +29,9 @@ def parse_arguments(*arguments):
help='Excludes filename',
)
parser.add_argument(
'-v', '--verbose',
action='store_true',
help='Display verbose progress information',
'-v', '--verbosity',
type=int,
help='Display verbose progress (1 for some, 2 for lots)',
)
return parser.parse_args(arguments)
@ -43,9 +43,9 @@ def main():
config = parse_configuration(args.config_filename)
repository = config.location['repository']
create_archive(args.excludes_filename, args.verbose, **config.location)
prune_archives(args.verbose, repository, config.retention)
check_archives(args.verbose, repository, config.consistency)
create_archive(args.excludes_filename, args.verbosity, **config.location)
prune_archives(args.verbosity, repository, config.retention)
check_archives(args.verbosity, repository, config.consistency)
except (ValueError, IOError, CalledProcessError) as error:
print(error, file=sys.stderr)
sys.exit(1)

View file

@ -10,7 +10,7 @@ def test_parse_arguments_with_no_arguments_uses_defaults():
assert parser.config_filename == module.DEFAULT_CONFIG_FILENAME
assert parser.excludes_filename == module.DEFAULT_EXCLUDES_FILENAME
assert parser.verbose == False
assert parser.verbosity == None
def test_parse_arguments_with_filename_arguments_overrides_defaults():
@ -18,15 +18,15 @@ def test_parse_arguments_with_filename_arguments_overrides_defaults():
assert parser.config_filename == 'myconfig'
assert parser.excludes_filename == 'myexcludes'
assert parser.verbose == False
assert parser.verbosity == None
def test_parse_arguments_with_verbose_flag_overrides_default():
parser = module.parse_arguments('--verbose')
def test_parse_arguments_with_verbosity_flag_overrides_default():
parser = module.parse_arguments('--verbosity', '1')
assert parser.config_filename == module.DEFAULT_CONFIG_FILENAME
assert parser.excludes_filename == module.DEFAULT_EXCLUDES_FILENAME
assert parser.verbose == True
assert parser.verbosity == 1
def test_parse_arguments_with_invalid_arguments_exits():

View file

@ -4,6 +4,7 @@ from flexmock import flexmock
from atticmatic import attic as module
from atticmatic.tests.builtins import builtins_mock
from atticmatic.verbosity import VERBOSITY_SOME, VERBOSITY_LOTS
def insert_subprocess_mock(check_call_command, **kwargs):
@ -28,34 +29,43 @@ def insert_datetime_mock():
).mock
CREATE_COMMAND = ('attic', 'create', '--exclude-from', 'excludes', 'repo::host-now', 'foo', 'bar')
def test_create_archive_should_call_attic_with_parameters():
insert_subprocess_mock(
('attic', 'create', '--exclude-from', 'excludes', 'repo::host-now', 'foo', 'bar'),
)
insert_subprocess_mock(CREATE_COMMAND)
insert_platform_mock()
insert_datetime_mock()
module.create_archive(
excludes_filename='excludes',
verbose=False,
verbosity=None,
source_directories='foo bar',
repository='repo',
)
def test_create_archive_with_verbose_should_call_attic_with_verbose_parameters():
insert_subprocess_mock(
(
'attic', 'create', '--exclude-from', 'excludes', 'repo::host-now', 'foo', 'bar',
'--verbose', '--stats',
),
)
def test_create_archive_with_verbosity_some_should_call_attic_with_stats_parameter():
insert_subprocess_mock(CREATE_COMMAND + ('--stats',))
insert_platform_mock()
insert_datetime_mock()
module.create_archive(
excludes_filename='excludes',
verbose=True,
verbosity=VERBOSITY_SOME,
source_directories='foo bar',
repository='repo',
)
def test_create_archive_with_verbosity_lots_should_call_attic_with_verbose_parameter():
insert_subprocess_mock(CREATE_COMMAND + ('--verbose', '--stats'))
insert_platform_mock()
insert_datetime_mock()
module.create_archive(
excludes_filename='excludes',
verbosity=VERBOSITY_LOTS,
source_directories='foo bar',
repository='repo',
)
@ -82,40 +92,49 @@ def test_make_prune_flags_should_return_flags_from_config():
assert tuple(result) == BASE_PRUNE_FLAGS
PRUNE_COMMAND = (
'attic', 'prune', 'repo', '--keep-daily', '1', '--keep-weekly', '2', '--keep-monthly', '3',
)
def test_prune_archives_should_call_attic_with_parameters():
retention_config = flexmock()
flexmock(module).should_receive('_make_prune_flags').with_args(retention_config).and_return(
BASE_PRUNE_FLAGS,
)
insert_subprocess_mock(
(
'attic', 'prune', 'repo', '--keep-daily', '1', '--keep-weekly', '2', '--keep-monthly',
'3',
),
)
insert_subprocess_mock(PRUNE_COMMAND)
module.prune_archives(
verbose=False,
verbosity=None,
repository='repo',
retention_config=retention_config,
)
def test_prune_archives_with_verbose_should_call_attic_with_verbose_parameters():
def test_prune_archives_with_verbosity_some_should_call_attic_with_stats_parameter():
retention_config = flexmock()
flexmock(module).should_receive('_make_prune_flags').with_args(retention_config).and_return(
BASE_PRUNE_FLAGS,
)
insert_subprocess_mock(
(
'attic', 'prune', 'repo', '--keep-daily', '1', '--keep-weekly', '2', '--keep-monthly',
'3', '--verbose',
),
)
insert_subprocess_mock(PRUNE_COMMAND + ('--stats',))
module.prune_archives(
repository='repo',
verbose=True,
verbosity=VERBOSITY_SOME,
retention_config=retention_config,
)
def test_prune_archives_with_verbosity_lots_should_call_attic_with_verbose_parameter():
retention_config = flexmock()
flexmock(module).should_receive('_make_prune_flags').with_args(retention_config).and_return(
BASE_PRUNE_FLAGS,
)
insert_subprocess_mock(PRUNE_COMMAND + ('--verbose', '--stats',))
module.prune_archives(
repository='repo',
verbosity=VERBOSITY_LOTS,
retention_config=retention_config,
)
@ -171,13 +190,13 @@ def test_check_archives_should_call_attic_with_parameters():
flexmock(module.os).should_receive('devnull')
module.check_archives(
verbose=False,
verbosity=None,
repository='repo',
consistency_config=consistency_config,
)
def test_check_archives_with_verbose_should_call_attic_with_verbose_parameters():
def test_check_archives_with_verbosity_some_should_call_attic_with_verbose_parameter():
consistency_config = flexmock()
flexmock(module).should_receive('_parse_checks').and_return(flexmock())
flexmock(module).should_receive('_make_check_flags').and_return(())
@ -189,7 +208,25 @@ def test_check_archives_with_verbose_should_call_attic_with_verbose_parameters()
insert_datetime_mock()
module.check_archives(
verbose=True,
verbosity=VERBOSITY_SOME,
repository='repo',
consistency_config=consistency_config,
)
def test_check_archives_with_verbosity_lots_should_call_attic_with_verbose_parameter():
consistency_config = flexmock()
flexmock(module).should_receive('_parse_checks').and_return(flexmock())
flexmock(module).should_receive('_make_check_flags').and_return(())
insert_subprocess_mock(
('attic', 'check', 'repo', '--verbose'),
stdout=None,
)
insert_platform_mock()
insert_datetime_mock()
module.check_archives(
verbosity=VERBOSITY_LOTS,
repository='repo',
consistency_config=consistency_config,
)
@ -201,7 +238,7 @@ def test_check_archives_without_any_checks_should_bail():
insert_subprocess_never()
module.check_archives(
verbose=False,
verbosity=None,
repository='repo',
consistency_config=consistency_config,
)

2
atticmatic/verbosity.py Normal file
View file

@ -0,0 +1,2 @@
VERBOSITY_SOME = 1
VERBOSITY_LOTS = 2

View file

@ -2,7 +2,7 @@ from setuptools import setup, find_packages
setup(
name='atticmatic',
version='0.0.6',
version='0.0.7',
description='A wrapper script for Attic backup software that creates and prunes backups',
author='Dan Helfman',
author_email='witten@torsion.org',