Compare commits

...
12 Commits
26 changed files with 253 additions and 198 deletions
+31
View File
@@ -0,0 +1,31 @@
#### What I'm trying to do and why
#### Steps to reproduce (if a bug)
Include (sanitized) borgmatic configuration files if applicable.
#### Actual behavior (if a bug)
Include (sanitized) `--verbosity 2` output if applicable.
#### Expected behavior (if a bug)
#### Other notes / implementation ideas
#### Environment
**borgmatic version:** [version here]
Use `sudo borgmatic --version` or `sudo pip show borgmatic | grep ^Version`
**borgmatic installation method:** [e.g., Debian package, Docker container, etc.]
**Borg version:** [version here]
Use `sudo borg --version`
**Python version:** [version here]
Use `python --version`
**operating system and version:** [OS here]
+8
View File
@@ -1,3 +1,11 @@
1.3.8
* #191: Disable console color via "color" option in borgmatic configuration output section.
1.3.7
* #196: Fix for unclear error message for invalid YAML merge include.
* #197: Don't color syslog output.
* Change default syslog verbosity to show errors only.
1.3.6
* #53: Log to syslog in addition to existing console logging. Add --syslog-verbosity flag to
customize the log level. See the documentation for more information:
+1 -2
View File
@@ -2,13 +2,12 @@ import logging
from borgmatic.borg import extract
from borgmatic.execute import execute_command
from borgmatic.logger import get_logger
DEFAULT_CHECKS = ('repository', 'archives')
DEFAULT_PREFIX = '{hostname}-'
logger = get_logger(__name__)
logger = logging.getLogger(__name__)
def _parse_checks(consistency_config):
+1 -2
View File
@@ -5,9 +5,8 @@ import os
import tempfile
from borgmatic.execute import execute_command
from borgmatic.logger import get_logger
logger = get_logger(__name__)
logger = logging.getLogger(__name__)
def _expand_directory(directory):
+1 -2
View File
@@ -1,9 +1,8 @@
import logging
from borgmatic.execute import execute_command
from borgmatic.logger import get_logger
logger = get_logger(__name__)
logger = logging.getLogger(__name__)
def extract_last_archive_dry_run(repository, lock_wait=None, local_path='borg', remote_path=None):
+1 -2
View File
@@ -1,9 +1,8 @@
import logging
from borgmatic.execute import execute_command
from borgmatic.logger import get_logger
logger = get_logger(__name__)
logger = logging.getLogger(__name__)
def display_archives_info(
+1 -2
View File
@@ -2,9 +2,8 @@ import logging
import subprocess
from borgmatic.execute import execute_command
from borgmatic.logger import get_logger
logger = get_logger(__name__)
logger = logging.getLogger(__name__)
INFO_REPOSITORY_NOT_FOUND_EXIT_CODE = 2
+1 -2
View File
@@ -1,9 +1,8 @@
import logging
from borgmatic.execute import execute_command
from borgmatic.logger import get_logger
logger = get_logger(__name__)
logger = logging.getLogger(__name__)
def list_archives(
+1 -2
View File
@@ -1,9 +1,8 @@
import logging
from borgmatic.execute import execute_command
from borgmatic.logger import get_logger
logger = get_logger(__name__)
logger = logging.getLogger(__name__)
def _make_prune_flags(retention_config):
+47 -29
View File
@@ -19,11 +19,11 @@ from borgmatic.borg import init as borg_init
from borgmatic.borg import list as borg_list
from borgmatic.borg import prune as borg_prune
from borgmatic.config import checks, collect, convert, validate
from borgmatic.logger import configure_logging, get_logger, should_do_markup
from borgmatic.logger import configure_logging, should_do_markup
from borgmatic.signals import configure_signals
from borgmatic.verbosity import verbosity_to_log_level
logger = get_logger(__name__)
logger = logging.getLogger(__name__)
LEGACY_CONFIG_PATH = '/etc/borgmatic/config'
@@ -182,7 +182,7 @@ def parse_arguments(*arguments):
'--syslog-verbosity',
type=int,
choices=range(0, 3),
default=1,
default=0,
help='Display verbose progress to syslog (from none to lots: 0, 1, or 2)',
)
common_group.add_argument(
@@ -404,37 +404,50 @@ def run_actions(
yield json.loads(json_output)
def collect_configuration_run_summary_logs(config_filenames, args):
def load_configurations(config_filenames):
'''
Given a sequence of configuration filenames and parsed command-line arguments as an
argparse.ArgumentParser instance, run each configuration file and yield a series of
logging.LogRecord instances containing summary information about each run.
As a side effect of running through these configuration files, output their JSON results, if
any, to stdout.
Given a sequence of configuration filenames, load and validate each configuration file. Return
the results as a tuple of: dict of configuration filename to corresponding parsed configuration,
and sequence of logging.LogRecord instances containing any parse errors.
'''
# Dict mapping from config filename to corresponding parsed config dict.
configs = collections.OrderedDict()
logs = []
# Parse and load each configuration file.
for config_filename in config_filenames:
try:
logger.info('{}: Parsing configuration file'.format(config_filename))
configs[config_filename] = validate.parse_configuration(
config_filename, validate.schema_filename()
)
except (ValueError, OSError, validate.Validation_error) as error:
yield logging.makeLogRecord(
dict(
levelno=logging.CRITICAL,
levelname='CRITICAL',
msg='{}: Error parsing configuration file'.format(config_filename),
)
)
yield logging.makeLogRecord(
dict(levelno=logging.CRITICAL, levelname='CRITICAL', msg=error)
logs.extend(
[
logging.makeLogRecord(
dict(
levelno=logging.CRITICAL,
levelname='CRITICAL',
msg='{}: Error parsing configuration file'.format(config_filename),
)
),
logging.makeLogRecord(
dict(levelno=logging.CRITICAL, levelname='CRITICAL', msg=error)
),
]
)
return (configs, logs)
def collect_configuration_run_summary_logs(configs, args):
'''
Given a dict of configuration filename to corresponding parsed configuration, and parsed
command-line arguments as an argparse.ArgumentParser instance, run each configuration file and
yield a series of logging.LogRecord instances containing summary information about each run.
As a side effect of running through these configuration files, output their JSON results, if
any, to stdout.
'''
# Run cross-file validation checks.
if args.extract or (args.list and args.archive):
try:
@@ -472,7 +485,7 @@ def collect_configuration_run_summary_logs(config_filenames, args):
if json_results:
sys.stdout.write(json.dumps(json_results))
if not config_filenames:
if not configs:
yield logging.makeLogRecord(
dict(
levelno=logging.CRITICAL,
@@ -507,25 +520,30 @@ def main(): # pragma: no cover
logger.critical('Error parsing arguments: {}'.format(' '.join(sys.argv)))
exit_with_help_link()
colorama.init(autoreset=True, strip=not should_do_markup(args.no_color))
configure_logging(
verbosity_to_log_level(args.verbosity), verbosity_to_log_level(args.syslog_verbosity)
)
if args.version:
print(pkg_resources.require('borgmatic')[0].version)
sys.exit(0)
config_filenames = tuple(collect.collect_config_filenames(args.config_paths))
configs, parse_logs = load_configurations(config_filenames)
colorama.init(autoreset=True, strip=not should_do_markup(args.no_color, configs))
configure_logging(
verbosity_to_log_level(args.verbosity), verbosity_to_log_level(args.syslog_verbosity)
)
logger.debug('Ensuring legacy configuration is upgraded')
convert.guard_configuration_upgraded(LEGACY_CONFIG_PATH, config_filenames)
summary_logs = tuple(collect_configuration_run_summary_logs(config_filenames, args))
summary_logs = list(collect_configuration_run_summary_logs(configs, args))
logger.info('')
logger.info('summary:')
[logger.handle(log) for log in summary_logs if log.levelno >= logger.getEffectiveLevel()]
[
logger.handle(log)
for log in parse_logs + summary_logs
if log.levelno >= logger.getEffectiveLevel()
]
if any(log.levelno == logging.CRITICAL for log in summary_logs):
exit_with_help_link()
+1 -2
View File
@@ -3,9 +3,8 @@ import sys
from argparse import ArgumentParser
from borgmatic.config import collect, validate
from borgmatic.logger import get_logger
logger = get_logger(__name__)
logger = logging.getLogger(__name__)
def parse_arguments(*arguments):
+3 -6
View File
@@ -1,10 +1,9 @@
import logging
import os
import ruamel.yaml
from borgmatic.logger import get_logger
logger = get_logger(__name__)
logger = logging.getLogger(__name__)
def load_configuration(filename):
@@ -54,9 +53,7 @@ class Include_constructor(ruamel.yaml.SafeConstructor):
for index, (key_node, value_node) in enumerate(node.value):
if key_node.tag == u'tag:yaml.org,2002:merge' and value_node.tag == '!include':
included_value = representer.represent_mapping(
tag='tag:yaml.org,2002:map', mapping=self.construct_object(value_node)
)
included_value = representer.represent_data(self.construct_object(value_node))
node.value[index] = (key_node, included_value)
super(Include_constructor, self).flatten_mapping(node)
+10
View File
@@ -301,6 +301,16 @@ map:
this prefix. Borg placeholders can be used. See the output of
"borg help placeholders" for details. Defaults to "{hostname}-".
example: sourcehostname
output:
desc: |
Options for customizing borgmatic's own output and logging.
map:
color:
type: bool
desc: |
Apply color to console output. Can be overridden with --no-color command-line
flag. Defaults to true.
example: false
hooks:
desc: |
Shell commands or scripts to execute before and after a backup or if an error has occurred.
-10
View File
@@ -6,9 +6,6 @@ import pykwalify.errors
import ruamel.yaml
from borgmatic.config import load
from borgmatic.logger import get_logger
logger = get_logger(__name__)
def schema_filename():
@@ -66,13 +63,6 @@ def apply_logical_validation(config_filename, parsed_configuration):
),
)
consistency_prefix = parsed_configuration.get('consistency', {}).get('prefix')
if archive_name_format and not consistency_prefix:
logger.warning(
'Since version 1.1.16, if you provide `archive_name_format`, you should also'
' specify `consistency.prefix`.'
)
def parse_configuration(config_filename, schema_filename):
'''
+1 -3
View File
@@ -1,9 +1,7 @@
import logging
import subprocess
from borgmatic.logger import get_logger
logger = get_logger(__name__)
logger = logging.getLogger(__name__)
def execute_and_log_output(full_command, output_log_level, shell):
+1 -2
View File
@@ -2,9 +2,8 @@ import logging
import os
from borgmatic import execute
from borgmatic.logger import get_logger
logger = get_logger(__name__)
logger = logging.getLogger(__name__)
def execute_hook(commands, umask, config_filename, description, dry_run):
+10 -48
View File
@@ -21,13 +21,17 @@ def to_bool(arg):
return False
def should_do_markup(no_color):
def should_do_markup(no_color, configs):
'''
Determine if we should enable colorama marking up.
Given the value of the command-line no-color argument, and a dict of configuration filename to
corresponding parsed configuration, determine if we should enable colorama marking up.
'''
if no_color:
return False
if any(config.get('output', {}).get('color') is False for config in configs.values()):
return False
py_colors = os.environ.get('PY_COLORS', None)
if py_colors is not None:
@@ -45,52 +49,10 @@ LOG_LEVEL_TO_COLOR = {
}
class Borgmatic_logger(logging.Logger):
def critical(self, msg, *args, **kwargs):
color = LOG_LEVEL_TO_COLOR.get(logging.CRITICAL)
return super(Borgmatic_logger, self).critical(color_text(color, msg), *args, **kwargs)
def error(self, msg, *args, **kwargs):
color = LOG_LEVEL_TO_COLOR.get(logging.ERROR)
return super(Borgmatic_logger, self).error(color_text(color, msg), *args, **kwargs)
def warn(self, msg, *args, **kwargs):
color = LOG_LEVEL_TO_COLOR.get(logging.WARN)
return super(Borgmatic_logger, self).warn(color_text(color, msg), *args, **kwargs)
def info(self, msg, *args, **kwargs):
color = LOG_LEVEL_TO_COLOR.get(logging.INFO)
return super(Borgmatic_logger, self).info(color_text(color, msg), *args, **kwargs)
def debug(self, msg, *args, **kwargs):
color = LOG_LEVEL_TO_COLOR.get(logging.DEBUG)
return super(Borgmatic_logger, self).debug(color_text(color, msg), *args, **kwargs)
def handle(self, record):
class Console_color_formatter(logging.Formatter):
def format(self, record):
color = LOG_LEVEL_TO_COLOR.get(record.levelno)
colored_record = logging.makeLogRecord(
dict(
levelno=record.levelno,
levelname=record.levelname,
msg=color_text(color, record.msg),
)
)
return super(Borgmatic_logger, self).handle(colored_record)
def get_logger(name=None):
'''
Build a logger with the given name.
'''
logging.setLoggerClass(Borgmatic_logger)
logger = logging.getLogger(name)
return logger
return color_text(color, record.msg)
def color_text(color, message):
@@ -111,7 +73,7 @@ def configure_logging(console_log_level, syslog_log_level=None):
syslog_log_level = console_log_level
console_handler = logging.StreamHandler()
console_handler.setFormatter(logging.Formatter('%(message)s'))
console_handler.setFormatter(Console_color_formatter())
console_handler.setLevel(console_log_level)
syslog_path = None
+3 -3
View File
@@ -42,11 +42,11 @@ using systemd, try running `journalctl -xe`. Otherwise, try viewing
You can customize the log level used for syslog logging with the
`--syslog-verbosity` flag, and this is independent from the console logging
`--verbosity` flag described above. For instance, to disable syslog logging
except for errors:
`--verbosity` flag described above. For instance, to get additional
information about the progress of the backup as it proceeds:
```bash
borgmatic --syslog-verbosity 0
borgmatic --syslog-verbosity 1
```
Or to increase syslog logging to include debug spew:
@@ -105,6 +105,10 @@ include, the local file's option takes precedent. And note that this is a
shallow merge rather than a deep merge, so the merging does not descend into
nested values.
Note that this `<<` include merging syntax is only for merging in mappings
(keys/values). If you'd like to include other types like scalars or lists
directly, please see the section above about standard includes.
## Related documentation
+6 -5
View File
@@ -199,12 +199,13 @@ sudo systemctl start borgmatic.timer
Feel free to modify the timer file based on how frequently you'd like
borgmatic to run.
## Colored Output
## Colored output
Borgmatic uses [colorama](https://pypi.org/project/colorama/) to produce
colored terminal output by default. It is disabled when a non-interactive
terminal is detected (like a cron job). Otherwise, it can be disabled by
passing `--no-color` or by setting the environment variable `PY_COLORS=False`.
Borgmatic produces colored terminal output by default. It is disabled when a
non-interactive terminal is detected (like a cron job). Otherwise, you can
disable it by passing the `--no-color` flag, setting the environment variable
`PY_COLORS=False`, or setting the `color` option to `false` in the `output`
section of configuration.
## Troubleshooting
+1 -1
View File
@@ -1,6 +1,6 @@
from setuptools import find_packages, setup
VERSION = '1.3.6'
VERSION = '1.3.8'
setup(
+3 -3
View File
@@ -15,7 +15,7 @@ def test_parse_arguments_with_no_arguments_uses_defaults():
assert parser.config_paths == config_paths
assert parser.excludes_filename is None
assert parser.verbosity == 0
assert parser.syslog_verbosity == 1
assert parser.syslog_verbosity == 0
assert parser.json is False
@@ -26,7 +26,7 @@ def test_parse_arguments_with_multiple_config_paths_parses_as_list():
assert parser.config_paths == ['myconfig', 'otherconfig']
assert parser.verbosity == 0
assert parser.syslog_verbosity == 1
assert parser.syslog_verbosity == 0
def test_parse_arguments_with_verbosity_overrides_default():
@@ -38,7 +38,7 @@ def test_parse_arguments_with_verbosity_overrides_default():
assert parser.config_paths == config_paths
assert parser.excludes_filename is None
assert parser.verbosity == 1
assert parser.syslog_verbosity == 1
assert parser.syslog_verbosity == 0
def test_parse_arguments_with_syslog_verbosity_overrides_default():
+22
View File
@@ -1,5 +1,7 @@
import sys
import pytest
import ruamel.yaml
from flexmock import flexmock
from borgmatic.config import load as module
@@ -38,3 +40,23 @@ def test_load_configuration_merges_include():
)
assert module.load_configuration('config.yaml') == {'foo': 'override', 'baz': 'quux'}
def test_load_configuration_does_not_merge_include_list():
builtins = flexmock(sys.modules['builtins'])
builtins.should_receive('open').with_args('include.yaml').and_return(
'''
- one
- two
'''
)
builtins.should_receive('open').with_args('config.yaml').and_return(
'''
foo: bar
repositories:
<<: !include include.yaml
'''
)
with pytest.raises(ruamel.yaml.error.YAMLError):
assert module.load_configuration('config.yaml')
+42 -35
View File
@@ -3,99 +3,106 @@ from flexmock import flexmock
from borgmatic.commands import borgmatic as module
def test_load_configurations_collects_parsed_configurations():
configuration = flexmock()
other_configuration = flexmock()
flexmock(module.validate).should_receive('parse_configuration').and_return(
configuration
).and_return(other_configuration)
configs, logs = tuple(module.load_configurations(('test.yaml', 'other.yaml')))
assert configs == {'test.yaml': configuration, 'other.yaml': other_configuration}
assert logs == []
def test_load_configurations_logs_critical_for_parse_error():
flexmock(module.validate).should_receive('parse_configuration').and_raise(ValueError)
configs, logs = tuple(module.load_configurations(('test.yaml',)))
assert configs == {}
assert any(log for log in logs if log.levelno == module.logging.CRITICAL)
def test_collect_configuration_run_summary_logs_info_for_success():
flexmock(module.validate).should_receive('parse_configuration').and_return({'test.yaml': {}})
flexmock(module).should_receive('run_configuration').and_return([])
args = flexmock(extract=False, list=False)
logs = tuple(module.collect_configuration_run_summary_logs(('test.yaml',), args=args))
logs = tuple(module.collect_configuration_run_summary_logs({'test.yaml': {}}, args=args))
assert all(log for log in logs if log.levelno == module.logging.INFO)
def test_collect_configuration_run_summary_logs_info_for_success_with_extract():
flexmock(module.validate).should_receive('parse_configuration').and_return({'test.yaml': {}})
flexmock(module.validate).should_receive('guard_configuration_contains_repository')
flexmock(module).should_receive('run_configuration').and_return([])
args = flexmock(extract=True, list=False, repository='repo')
logs = tuple(module.collect_configuration_run_summary_logs(('test.yaml',), args=args))
logs = tuple(module.collect_configuration_run_summary_logs({'test.yaml': {}}, args=args))
assert all(log for log in logs if log.levelno == module.logging.INFO)
def test_collect_configuration_run_summary_logs_critical_for_extract_with_repository_error():
flexmock(module.validate).should_receive('parse_configuration').and_return({'test.yaml': {}})
flexmock(module.validate).should_receive('guard_configuration_contains_repository').and_raise(
ValueError
)
args = flexmock(extract=True, list=False, repository='repo')
logs = tuple(module.collect_configuration_run_summary_logs(('test.yaml',), args=args))
logs = tuple(module.collect_configuration_run_summary_logs({'test.yaml': {}}, args=args))
assert any(log for log in logs if log.levelno == module.logging.CRITICAL)
def test_collect_configuration_run_summary_logs_critical_for_list_with_archive_and_repository_error():
flexmock(module.validate).should_receive('parse_configuration').and_return({'test.yaml': {}})
flexmock(module.validate).should_receive('guard_configuration_contains_repository').and_raise(
ValueError
)
args = flexmock(extract=False, list=True, repository='repo', archive='test')
logs = tuple(module.collect_configuration_run_summary_logs(('test.yaml',), args=args))
logs = tuple(module.collect_configuration_run_summary_logs({'test.yaml': {}}, args=args))
assert any(log for log in logs if log.levelno == module.logging.CRITICAL)
def test_collect_configuration_run_summary_logs_info_for_success_with_list():
flexmock(module.validate).should_receive('parse_configuration').and_return({'test.yaml': {}})
flexmock(module).should_receive('run_configuration').and_return([])
args = flexmock(extract=False, list=True, repository='repo', archive=None)
logs = tuple(module.collect_configuration_run_summary_logs(('test.yaml',), args=args))
logs = tuple(module.collect_configuration_run_summary_logs({'test.yaml': {}}, args=args))
assert all(log for log in logs if log.levelno == module.logging.INFO)
def test_collect_configuration_run_summary_logs_critical_for_parse_error():
flexmock(module.validate).should_receive('parse_configuration').and_raise(ValueError)
args = flexmock(extract=False, list=False)
logs = tuple(module.collect_configuration_run_summary_logs(('test.yaml',), args=args))
assert any(log for log in logs if log.levelno == module.logging.CRITICAL)
def test_collect_configuration_run_summary_logs_critical_for_run_error():
flexmock(module.validate).should_receive('parse_configuration').and_return({'test.yaml': {}})
flexmock(module.validate).should_receive('guard_configuration_contains_repository')
flexmock(module).should_receive('run_configuration').and_raise(ValueError)
args = flexmock(extract=False, list=False)
logs = tuple(module.collect_configuration_run_summary_logs(('test.yaml',), args=args))
assert any(log for log in logs if log.levelno == module.logging.CRITICAL)
def test_collect_configuration_run_summary_logs_critical_for_missing_configs():
flexmock(module.validate).should_receive('parse_configuration').and_return({'test.yaml': {}})
flexmock(module).should_receive('run_configuration').and_return([])
args = flexmock(config_paths=(), extract=False, list=False)
logs = tuple(module.collect_configuration_run_summary_logs(config_filenames=(), args=args))
logs = tuple(module.collect_configuration_run_summary_logs({'test.yaml': {}}, args=args))
assert any(log for log in logs if log.levelno == module.logging.CRITICAL)
def test_collect_configuration_run_summary_logs_outputs_merged_json_results():
flexmock(module.validate).should_receive('parse_configuration').and_return(
{'test.yaml': {}, 'test2.yaml': {}}
)
flexmock(module).should_receive('run_configuration').and_return(['foo', 'bar']).and_return(
['baz']
)
flexmock(module.sys.stdout).should_receive('write').with_args('["foo", "bar", "baz"]').once()
args = flexmock(extract=False, list=False)
tuple(module.collect_configuration_run_summary_logs(('test.yaml', 'test2.yaml'), args=args))
tuple(
module.collect_configuration_run_summary_logs(
{'test.yaml': {}, 'test2.yaml': {}}, args=args
)
)
def test_collect_configuration_run_summary_logs_critical_for_missing_configs():
flexmock(module).should_receive('run_configuration').and_return([])
args = flexmock(extract=False, list=False, config_paths=[])
logs = tuple(module.collect_configuration_run_summary_logs({}, args=args))
assert any(log for log in logs if log.levelno == module.logging.CRITICAL)
+1 -19
View File
@@ -1,5 +1,4 @@
import pytest
from flexmock import flexmock
from borgmatic.config import validate as module
@@ -37,20 +36,6 @@ def test_apply_logical_validation_raises_if_archive_name_format_present_without_
)
def test_apply_logical_validation_warns_if_archive_name_format_present_without_consistency_prefix():
logger = flexmock(module.logger)
logger.should_receive('warning').once()
module.apply_logical_validation(
'config.yaml',
{
'storage': {'archive_name_format': '{hostname}-{now}'},
'retention': {'prefix': '{hostname}-'},
'consistency': {},
},
)
def test_apply_locical_validation_raises_if_unknown_repository_in_check_repositories():
with pytest.raises(module.Validation_error):
module.apply_logical_validation(
@@ -74,10 +59,7 @@ def test_apply_locical_validation_does_not_raise_if_known_repository_in_check_re
)
def test_apply_logical_validation_does_not_raise_or_warn_if_archive_name_format_and_prefix_present():
logger = flexmock(module.logger)
logger.should_receive('warning').never()
def test_apply_logical_validation_does_not_raise_if_archive_name_format_and_prefix_present():
module.apply_logical_validation(
'config.yaml',
{
+52 -18
View File
@@ -21,51 +21,81 @@ def test_to_bool_passes_none_through():
def test_should_do_markup_respects_no_color_value():
assert module.should_do_markup(no_color=True) is False
assert module.should_do_markup(no_color=True, configs={}) is False
def test_should_do_markup_respects_config_value():
assert (
module.should_do_markup(no_color=False, configs={'foo.yaml': {'output': {'color': False}}})
is False
)
def test_should_do_markup_prefers_any_false_config_value():
assert (
module.should_do_markup(
no_color=False,
configs={
'foo.yaml': {'output': {'color': True}},
'bar.yaml': {'output': {'color': False}},
},
)
is False
)
def test_should_do_markup_respects_PY_COLORS_environment_variable():
flexmock(module.os.environ).should_receive('get').and_return('True')
flexmock(module).should_receive('to_bool').and_return(True)
assert module.should_do_markup(no_color=False) is True
assert module.should_do_markup(no_color=False, configs={}) is True
def test_should_do_markup_prefers_no_color_value_to_config_value():
assert (
module.should_do_markup(no_color=True, configs={'foo.yaml': {'output': {'color': True}}})
is False
)
def test_should_do_markup_prefers_config_value_to_PY_COLORS():
flexmock(module.os.environ).should_receive('get').and_return('True')
flexmock(module).should_receive('to_bool').and_return(True)
assert (
module.should_do_markup(no_color=False, configs={'foo.yaml': {'output': {'color': False}}})
is False
)
def test_should_do_markup_prefers_no_color_value_to_PY_COLORS():
flexmock(module.os.environ).should_receive('get').and_return('True')
flexmock(module).should_receive('to_bool').and_return(True)
assert module.should_do_markup(no_color=True) is False
assert module.should_do_markup(no_color=True, configs={}) is False
def test_should_do_markup_respects_stdout_tty_value():
flexmock(module.os.environ).should_receive('get').and_return(None)
assert module.should_do_markup(no_color=False) is False
assert module.should_do_markup(no_color=False, configs={}) is False
def test_should_do_markup_prefers_PY_COLORS_to_stdout_tty_value():
flexmock(module.os.environ).should_receive('get').and_return('True')
flexmock(module).should_receive('to_bool').and_return(True)
assert module.should_do_markup(no_color=False) is True
assert module.should_do_markup(no_color=False, configs={}) is True
@pytest.mark.parametrize('method_name', ('critical', 'error', 'warn', 'info', 'debug'))
def test_borgmatic_logger_log_method_does_not_raise(method_name):
flexmock(module).should_receive('color_text')
flexmock(module.logging.Logger).should_receive(method_name)
def test_console_color_formatter_format_includes_log_message():
plain_message = 'uh oh'
record = flexmock(levelno=logging.CRITICAL, msg=plain_message)
getattr(module.Borgmatic_logger('test'), method_name)(msg='hi')
colored_message = module.Console_color_formatter().format(record)
def test_borgmatic_logger_handle_does_not_raise():
flexmock(module).should_receive('color_text')
flexmock(module.logging.Logger).should_receive('handle')
module.Borgmatic_logger('test').handle(
module.logging.makeLogRecord(dict(levelno=module.logging.CRITICAL, msg='hi'))
)
assert colored_message != plain_message
assert plain_message in colored_message
def test_color_text_does_not_raise():
@@ -77,6 +107,7 @@ def test_color_text_without_color_does_not_raise():
def test_configure_logging_probes_for_log_socket_on_linux():
flexmock(module).should_receive('Console_color_formatter')
flexmock(module.logging).should_receive('basicConfig').with_args(
level=logging.INFO, handlers=tuple
)
@@ -91,6 +122,7 @@ def test_configure_logging_probes_for_log_socket_on_linux():
def test_configure_logging_probes_for_log_socket_on_macos():
flexmock(module).should_receive('Console_color_formatter')
flexmock(module.logging).should_receive('basicConfig').with_args(
level=logging.INFO, handlers=tuple
)
@@ -105,6 +137,7 @@ def test_configure_logging_probes_for_log_socket_on_macos():
def test_configure_logging_sets_global_logger_to_most_verbose_log_level():
flexmock(module).should_receive('Console_color_formatter')
flexmock(module.logging).should_receive('basicConfig').with_args(
level=logging.DEBUG, handlers=tuple
).once()
@@ -114,6 +147,7 @@ def test_configure_logging_sets_global_logger_to_most_verbose_log_level():
def test_configure_logging_skips_syslog_if_not_found():
flexmock(module).should_receive('Console_color_formatter')
flexmock(module.logging).should_receive('basicConfig').with_args(
level=logging.INFO, handlers=tuple
)