Fix for "before_backup" hook not triggering an error when the command contains "borg" and has an exit code of 1 (#256).
continuous-integration/drone/push Build is passing Details
continuous-integration/drone/tag Build is passing Details

This commit is contained in:
Dan Helfman 2019-11-30 16:55:05 -08:00
parent 9b2ca15de6
commit 00f62ca023
17 changed files with 165 additions and 98 deletions

2
NEWS
View File

@ -1,4 +1,6 @@
1.4.16 1.4.16
* #256: Fix for "before_backup" hook not triggering an error when the command contains "borg" and
has an exit code of 1.
* #257: Fix for garbled Borg file listing when using "borgmatic create --progress" with * #257: Fix for garbled Borg file listing when using "borgmatic create --progress" with
verbosity level 1 or 2. verbosity level 1 or 2.
* #260: Fix for missing Healthchecks monitoring payload or HTTP 500 due to incorrect unicode * #260: Fix for missing Healthchecks monitoring payload or HTTP 500 due to incorrect unicode

View File

@ -196,7 +196,7 @@ def create_archive(
# The progress output isn't compatible with captured and logged output, as progress messes with # The progress output isn't compatible with captured and logged output, as progress messes with
# the terminal directly. # the terminal directly.
if progress: if progress:
execute_command_without_capture(full_command) execute_command_without_capture(full_command, error_on_warnings=False)
return return
if json: if json:
@ -206,4 +206,4 @@ def create_archive(
else: else:
output_log_level = logging.INFO output_log_level = logging.INFO
return execute_command(full_command, output_log_level) return execute_command(full_command, output_log_level, error_on_warnings=False)

View File

@ -27,7 +27,7 @@ def extract_last_archive_dry_run(repository, lock_wait=None, local_path='borg',
+ (repository,) + (repository,)
) )
list_output = execute_command(full_list_command, output_log_level=None) list_output = execute_command(full_list_command, output_log_level=None, error_on_warnings=False)
try: try:
last_archive_name = list_output.strip().splitlines()[-1] last_archive_name = list_output.strip().splitlines()[-1]

View File

@ -39,5 +39,7 @@ def display_archives_info(
) )
return execute_command( return execute_command(
full_command, output_log_level=None if info_arguments.json else logging.WARNING full_command,
output_log_level=None if info_arguments.json else logging.WARNING,
error_on_warnings=False,
) )

View File

@ -45,4 +45,4 @@ def initialize_repository(
) )
# Don't use execute_command() here because it doesn't support interactive prompts. # Don't use execute_command() here because it doesn't support interactive prompts.
execute_command_without_capture(init_command) execute_command_without_capture(init_command, error_on_warnings=False)

View File

@ -46,5 +46,7 @@ def list_archives(repository, storage_config, list_arguments, local_path='borg',
) )
return execute_command( return execute_command(
full_command, output_log_level=None if list_arguments.json else logging.WARNING full_command,
output_log_level=None if list_arguments.json else logging.WARNING,
error_on_warnings=False,
) )

View File

@ -40,7 +40,7 @@ def mount_archive(
# Don't capture the output when foreground mode is used so that ctrl-C can work properly. # Don't capture the output when foreground mode is used so that ctrl-C can work properly.
if foreground: if foreground:
execute_command_without_capture(full_command) execute_command_without_capture(full_command, error_on_warnings=False)
return return
execute_command(full_command) execute_command(full_command, error_on_warnings=False)

View File

@ -64,4 +64,8 @@ def prune_archives(
+ (repository,) + (repository,)
) )
execute_command(full_command, output_log_level=logging.WARNING if stats else logging.INFO) execute_command(
full_command,
output_log_level=logging.WARNING if stats else logging.INFO,
error_on_warnings=False,
)

View File

@ -9,15 +9,15 @@ ERROR_OUTPUT_MAX_LINE_COUNT = 25
BORG_ERROR_EXIT_CODE = 2 BORG_ERROR_EXIT_CODE = 2
def exit_code_indicates_error(command, exit_code, error_on_warnings=False): def exit_code_indicates_error(command, exit_code, error_on_warnings=True):
''' '''
Return True if the given exit code from running the command corresponds to an error. Return True if the given exit code from running the command corresponds to an error.
If error on warnings is False, then treat exit code 1 as a warning instead of an error.
''' '''
# If we're running something other than Borg, treat all non-zero exit codes as errors. if error_on_warnings:
if 'borg' in command[0] and not error_on_warnings: return bool(exit_code != 0)
return bool(exit_code >= BORG_ERROR_EXIT_CODE)
return bool(exit_code != 0) return bool(exit_code >= BORG_ERROR_EXIT_CODE)
def log_output(command, process, output_buffer, output_log_level, error_on_warnings): def log_output(command, process, output_buffer, output_log_level, error_on_warnings):
@ -65,7 +65,7 @@ def execute_command(
shell=False, shell=False,
extra_environment=None, extra_environment=None,
working_directory=None, working_directory=None,
error_on_warnings=False, error_on_warnings=True,
): ):
''' '''
Execute the given command (a sequence of command/argument strings) and log its output at the Execute the given command (a sequence of command/argument strings) and log its output at the
@ -75,7 +75,7 @@ def execute_command(
file. If shell is True, execute the command within a shell. If an extra environment dict is file. If shell is True, execute the command within a shell. If an extra environment dict is
given, then use it to augment the current environment, and pass the result into the command. If given, then use it to augment the current environment, and pass the result into the command. If
a working directory is given, use that as the present working directory when running the a working directory is given, use that as the present working directory when running the
command. command. If error on warnings is False, then treat exit code 1 as a warning instead of an error.
Raise subprocesses.CalledProcessError if an error occurs while running the command. Raise subprocesses.CalledProcessError if an error occurs while running the command.
''' '''
@ -110,14 +110,14 @@ def execute_command(
) )
def execute_command_without_capture(full_command, working_directory=None, error_on_warnings=False): def execute_command_without_capture(full_command, working_directory=None, error_on_warnings=True):
''' '''
Execute the given command (a sequence of command/argument strings), but don't capture or log its Execute the given command (a sequence of command/argument strings), but don't capture or log its
output in any way. This is necessary for commands that monkey with the terminal (e.g. progress output in any way. This is necessary for commands that monkey with the terminal (e.g. progress
display) or provide interactive prompts. display) or provide interactive prompts.
If a working directory is given, use that as the present working directory when running the If a working directory is given, use that as the present working directory when running the
command. command. If error on warnings is False, then treat exit code 1 as a warning instead of an error.
''' '''
logger.debug(' '.join(full_command)) logger.debug(' '.join(full_command))

View File

@ -206,7 +206,9 @@ def test_create_archive_calls_borg_with_parameters():
flexmock(module).should_receive('_make_pattern_flags').and_return(()) flexmock(module).should_receive('_make_pattern_flags').and_return(())
flexmock(module).should_receive('_make_exclude_flags').and_return(()) flexmock(module).should_receive('_make_exclude_flags').and_return(())
flexmock(module).should_receive('execute_command').with_args( flexmock(module).should_receive('execute_command').with_args(
('borg', 'create') + ARCHIVE_WITH_PATHS, output_log_level=logging.INFO ('borg', 'create') + ARCHIVE_WITH_PATHS,
output_log_level=logging.INFO,
error_on_warnings=False,
) )
module.create_archive( module.create_archive(
@ -232,7 +234,9 @@ def test_create_archive_with_patterns_calls_borg_with_patterns():
flexmock(module).should_receive('_make_pattern_flags').and_return(pattern_flags) flexmock(module).should_receive('_make_pattern_flags').and_return(pattern_flags)
flexmock(module).should_receive('_make_exclude_flags').and_return(()) flexmock(module).should_receive('_make_exclude_flags').and_return(())
flexmock(module).should_receive('execute_command').with_args( flexmock(module).should_receive('execute_command').with_args(
('borg', 'create') + pattern_flags + ARCHIVE_WITH_PATHS, output_log_level=logging.INFO ('borg', 'create') + pattern_flags + ARCHIVE_WITH_PATHS,
output_log_level=logging.INFO,
error_on_warnings=False,
) )
module.create_archive( module.create_archive(
@ -258,7 +262,9 @@ def test_create_archive_with_exclude_patterns_calls_borg_with_excludes():
flexmock(module).should_receive('_make_pattern_flags').and_return(()) flexmock(module).should_receive('_make_pattern_flags').and_return(())
flexmock(module).should_receive('_make_exclude_flags').and_return(exclude_flags) flexmock(module).should_receive('_make_exclude_flags').and_return(exclude_flags)
flexmock(module).should_receive('execute_command').with_args( flexmock(module).should_receive('execute_command').with_args(
('borg', 'create') + exclude_flags + ARCHIVE_WITH_PATHS, output_log_level=logging.INFO ('borg', 'create') + exclude_flags + ARCHIVE_WITH_PATHS,
output_log_level=logging.INFO,
error_on_warnings=False,
) )
module.create_archive( module.create_archive(
@ -284,6 +290,7 @@ def test_create_archive_with_log_info_calls_borg_with_info_parameter():
flexmock(module).should_receive('execute_command').with_args( flexmock(module).should_receive('execute_command').with_args(
('borg', 'create', '--list', '--filter', 'AME-', '--info', '--stats') + ARCHIVE_WITH_PATHS, ('borg', 'create', '--list', '--filter', 'AME-', '--info', '--stats') + ARCHIVE_WITH_PATHS,
output_log_level=logging.INFO, output_log_level=logging.INFO,
error_on_warnings=False,
) )
insert_logging_mock(logging.INFO) insert_logging_mock(logging.INFO)
@ -308,7 +315,9 @@ def test_create_archive_with_log_info_and_json_suppresses_most_borg_output():
flexmock(module).should_receive('_make_pattern_flags').and_return(()) flexmock(module).should_receive('_make_pattern_flags').and_return(())
flexmock(module).should_receive('_make_exclude_flags').and_return(()) flexmock(module).should_receive('_make_exclude_flags').and_return(())
flexmock(module).should_receive('execute_command').with_args( flexmock(module).should_receive('execute_command').with_args(
('borg', 'create', '--json') + ARCHIVE_WITH_PATHS, output_log_level=None ('borg', 'create', '--json') + ARCHIVE_WITH_PATHS,
output_log_level=None,
error_on_warnings=False,
) )
insert_logging_mock(logging.INFO) insert_logging_mock(logging.INFO)
@ -336,6 +345,7 @@ def test_create_archive_with_log_debug_calls_borg_with_debug_parameter():
('borg', 'create', '--list', '--filter', 'AME-', '--stats', '--debug', '--show-rc') ('borg', 'create', '--list', '--filter', 'AME-', '--stats', '--debug', '--show-rc')
+ ARCHIVE_WITH_PATHS, + ARCHIVE_WITH_PATHS,
output_log_level=logging.INFO, output_log_level=logging.INFO,
error_on_warnings=False,
) )
insert_logging_mock(logging.DEBUG) insert_logging_mock(logging.DEBUG)
@ -359,7 +369,9 @@ def test_create_archive_with_log_debug_and_json_suppresses_most_borg_output():
flexmock(module).should_receive('_make_pattern_flags').and_return(()) flexmock(module).should_receive('_make_pattern_flags').and_return(())
flexmock(module).should_receive('_make_exclude_flags').and_return(()) flexmock(module).should_receive('_make_exclude_flags').and_return(())
flexmock(module).should_receive('execute_command').with_args( flexmock(module).should_receive('execute_command').with_args(
('borg', 'create', '--json') + ARCHIVE_WITH_PATHS, output_log_level=None ('borg', 'create', '--json') + ARCHIVE_WITH_PATHS,
output_log_level=None,
error_on_warnings=False,
) )
insert_logging_mock(logging.DEBUG) insert_logging_mock(logging.DEBUG)
@ -385,7 +397,9 @@ def test_create_archive_with_dry_run_calls_borg_with_dry_run_parameter():
flexmock(module).should_receive('_make_pattern_flags').and_return(()) flexmock(module).should_receive('_make_pattern_flags').and_return(())
flexmock(module).should_receive('_make_exclude_flags').and_return(()) flexmock(module).should_receive('_make_exclude_flags').and_return(())
flexmock(module).should_receive('execute_command').with_args( flexmock(module).should_receive('execute_command').with_args(
('borg', 'create', '--dry-run') + ARCHIVE_WITH_PATHS, output_log_level=logging.INFO ('borg', 'create', '--dry-run') + ARCHIVE_WITH_PATHS,
output_log_level=logging.INFO,
error_on_warnings=False,
) )
module.create_archive( module.create_archive(
@ -414,6 +428,7 @@ def test_create_archive_with_dry_run_and_log_info_calls_borg_without_stats_param
('borg', 'create', '--list', '--filter', 'AME-', '--info', '--dry-run') ('borg', 'create', '--list', '--filter', 'AME-', '--info', '--dry-run')
+ ARCHIVE_WITH_PATHS, + ARCHIVE_WITH_PATHS,
output_log_level=logging.INFO, output_log_level=logging.INFO,
error_on_warnings=False,
) )
insert_logging_mock(logging.INFO) insert_logging_mock(logging.INFO)
@ -443,6 +458,7 @@ def test_create_archive_with_dry_run_and_log_debug_calls_borg_without_stats_para
('borg', 'create', '--list', '--filter', 'AME-', '--debug', '--show-rc', '--dry-run') ('borg', 'create', '--list', '--filter', 'AME-', '--debug', '--show-rc', '--dry-run')
+ ARCHIVE_WITH_PATHS, + ARCHIVE_WITH_PATHS,
output_log_level=logging.INFO, output_log_level=logging.INFO,
error_on_warnings=False,
) )
insert_logging_mock(logging.DEBUG) insert_logging_mock(logging.DEBUG)
@ -468,6 +484,7 @@ def test_create_archive_with_checkpoint_interval_calls_borg_with_checkpoint_inte
flexmock(module).should_receive('execute_command').with_args( flexmock(module).should_receive('execute_command').with_args(
('borg', 'create', '--checkpoint-interval', '600') + ARCHIVE_WITH_PATHS, ('borg', 'create', '--checkpoint-interval', '600') + ARCHIVE_WITH_PATHS,
output_log_level=logging.INFO, output_log_level=logging.INFO,
error_on_warnings=False,
) )
module.create_archive( module.create_archive(
@ -492,6 +509,7 @@ def test_create_archive_with_chunker_params_calls_borg_with_chunker_params_param
flexmock(module).should_receive('execute_command').with_args( flexmock(module).should_receive('execute_command').with_args(
('borg', 'create', '--chunker-params', '1,2,3,4') + ARCHIVE_WITH_PATHS, ('borg', 'create', '--chunker-params', '1,2,3,4') + ARCHIVE_WITH_PATHS,
output_log_level=logging.INFO, output_log_level=logging.INFO,
error_on_warnings=False,
) )
module.create_archive( module.create_archive(
@ -516,6 +534,7 @@ def test_create_archive_with_compression_calls_borg_with_compression_parameters(
flexmock(module).should_receive('execute_command').with_args( flexmock(module).should_receive('execute_command').with_args(
('borg', 'create', '--compression', 'rle') + ARCHIVE_WITH_PATHS, ('borg', 'create', '--compression', 'rle') + ARCHIVE_WITH_PATHS,
output_log_level=logging.INFO, output_log_level=logging.INFO,
error_on_warnings=False,
) )
module.create_archive( module.create_archive(
@ -540,6 +559,7 @@ def test_create_archive_with_remote_rate_limit_calls_borg_with_remote_ratelimit_
flexmock(module).should_receive('execute_command').with_args( flexmock(module).should_receive('execute_command').with_args(
('borg', 'create', '--remote-ratelimit', '100') + ARCHIVE_WITH_PATHS, ('borg', 'create', '--remote-ratelimit', '100') + ARCHIVE_WITH_PATHS,
output_log_level=logging.INFO, output_log_level=logging.INFO,
error_on_warnings=False,
) )
module.create_archive( module.create_archive(
@ -562,7 +582,9 @@ def test_create_archive_with_one_file_system_calls_borg_with_one_file_system_par
flexmock(module).should_receive('_make_pattern_flags').and_return(()) flexmock(module).should_receive('_make_pattern_flags').and_return(())
flexmock(module).should_receive('_make_exclude_flags').and_return(()) flexmock(module).should_receive('_make_exclude_flags').and_return(())
flexmock(module).should_receive('execute_command').with_args( flexmock(module).should_receive('execute_command').with_args(
('borg', 'create', '--one-file-system') + ARCHIVE_WITH_PATHS, output_log_level=logging.INFO ('borg', 'create', '--one-file-system') + ARCHIVE_WITH_PATHS,
output_log_level=logging.INFO,
error_on_warnings=False,
) )
module.create_archive( module.create_archive(
@ -586,7 +608,9 @@ def test_create_archive_with_numeric_owner_calls_borg_with_numeric_owner_paramet
flexmock(module).should_receive('_make_pattern_flags').and_return(()) flexmock(module).should_receive('_make_pattern_flags').and_return(())
flexmock(module).should_receive('_make_exclude_flags').and_return(()) flexmock(module).should_receive('_make_exclude_flags').and_return(())
flexmock(module).should_receive('execute_command').with_args( flexmock(module).should_receive('execute_command').with_args(
('borg', 'create', '--numeric-owner') + ARCHIVE_WITH_PATHS, output_log_level=logging.INFO ('borg', 'create', '--numeric-owner') + ARCHIVE_WITH_PATHS,
output_log_level=logging.INFO,
error_on_warnings=False,
) )
module.create_archive( module.create_archive(
@ -610,7 +634,9 @@ def test_create_archive_with_read_special_calls_borg_with_read_special_parameter
flexmock(module).should_receive('_make_pattern_flags').and_return(()) flexmock(module).should_receive('_make_pattern_flags').and_return(())
flexmock(module).should_receive('_make_exclude_flags').and_return(()) flexmock(module).should_receive('_make_exclude_flags').and_return(())
flexmock(module).should_receive('execute_command').with_args( flexmock(module).should_receive('execute_command').with_args(
('borg', 'create', '--read-special') + ARCHIVE_WITH_PATHS, output_log_level=logging.INFO ('borg', 'create', '--read-special') + ARCHIVE_WITH_PATHS,
output_log_level=logging.INFO,
error_on_warnings=False,
) )
module.create_archive( module.create_archive(
@ -635,7 +661,9 @@ def test_create_archive_with_option_true_calls_borg_without_corresponding_parame
flexmock(module).should_receive('_make_pattern_flags').and_return(()) flexmock(module).should_receive('_make_pattern_flags').and_return(())
flexmock(module).should_receive('_make_exclude_flags').and_return(()) flexmock(module).should_receive('_make_exclude_flags').and_return(())
flexmock(module).should_receive('execute_command').with_args( flexmock(module).should_receive('execute_command').with_args(
('borg', 'create') + ARCHIVE_WITH_PATHS, output_log_level=logging.INFO ('borg', 'create') + ARCHIVE_WITH_PATHS,
output_log_level=logging.INFO,
error_on_warnings=False,
) )
module.create_archive( module.create_archive(
@ -662,6 +690,7 @@ def test_create_archive_with_option_false_calls_borg_with_corresponding_paramete
flexmock(module).should_receive('execute_command').with_args( flexmock(module).should_receive('execute_command').with_args(
('borg', 'create', '--no' + option_name.replace('_', '')) + ARCHIVE_WITH_PATHS, ('borg', 'create', '--no' + option_name.replace('_', '')) + ARCHIVE_WITH_PATHS,
output_log_level=logging.INFO, output_log_level=logging.INFO,
error_on_warnings=False,
) )
module.create_archive( module.create_archive(
@ -687,6 +716,7 @@ def test_create_archive_with_files_cache_calls_borg_with_files_cache_parameters(
flexmock(module).should_receive('execute_command').with_args( flexmock(module).should_receive('execute_command').with_args(
('borg', 'create', '--files-cache', 'ctime,size') + ARCHIVE_WITH_PATHS, ('borg', 'create', '--files-cache', 'ctime,size') + ARCHIVE_WITH_PATHS,
output_log_level=logging.INFO, output_log_level=logging.INFO,
error_on_warnings=False,
) )
module.create_archive( module.create_archive(
@ -710,7 +740,9 @@ def test_create_archive_with_local_path_calls_borg_via_local_path():
flexmock(module).should_receive('_make_pattern_flags').and_return(()) flexmock(module).should_receive('_make_pattern_flags').and_return(())
flexmock(module).should_receive('_make_exclude_flags').and_return(()) flexmock(module).should_receive('_make_exclude_flags').and_return(())
flexmock(module).should_receive('execute_command').with_args( flexmock(module).should_receive('execute_command').with_args(
('borg1', 'create') + ARCHIVE_WITH_PATHS, output_log_level=logging.INFO ('borg1', 'create') + ARCHIVE_WITH_PATHS,
output_log_level=logging.INFO,
error_on_warnings=False,
) )
module.create_archive( module.create_archive(
@ -736,6 +768,7 @@ def test_create_archive_with_remote_path_calls_borg_with_remote_path_parameters(
flexmock(module).should_receive('execute_command').with_args( flexmock(module).should_receive('execute_command').with_args(
('borg', 'create', '--remote-path', 'borg1') + ARCHIVE_WITH_PATHS, ('borg', 'create', '--remote-path', 'borg1') + ARCHIVE_WITH_PATHS,
output_log_level=logging.INFO, output_log_level=logging.INFO,
error_on_warnings=False,
) )
module.create_archive( module.create_archive(
@ -759,7 +792,9 @@ def test_create_archive_with_umask_calls_borg_with_umask_parameters():
flexmock(module).should_receive('_make_pattern_flags').and_return(()) flexmock(module).should_receive('_make_pattern_flags').and_return(())
flexmock(module).should_receive('_make_exclude_flags').and_return(()) flexmock(module).should_receive('_make_exclude_flags').and_return(())
flexmock(module).should_receive('execute_command').with_args( flexmock(module).should_receive('execute_command').with_args(
('borg', 'create', '--umask', '740') + ARCHIVE_WITH_PATHS, output_log_level=logging.INFO ('borg', 'create', '--umask', '740') + ARCHIVE_WITH_PATHS,
output_log_level=logging.INFO,
error_on_warnings=False,
) )
module.create_archive( module.create_archive(
@ -782,7 +817,9 @@ def test_create_archive_with_lock_wait_calls_borg_with_lock_wait_parameters():
flexmock(module).should_receive('_make_pattern_flags').and_return(()) flexmock(module).should_receive('_make_pattern_flags').and_return(())
flexmock(module).should_receive('_make_exclude_flags').and_return(()) flexmock(module).should_receive('_make_exclude_flags').and_return(())
flexmock(module).should_receive('execute_command').with_args( flexmock(module).should_receive('execute_command').with_args(
('borg', 'create', '--lock-wait', '5') + ARCHIVE_WITH_PATHS, output_log_level=logging.INFO ('borg', 'create', '--lock-wait', '5') + ARCHIVE_WITH_PATHS,
output_log_level=logging.INFO,
error_on_warnings=False,
) )
module.create_archive( module.create_archive(
@ -805,7 +842,9 @@ def test_create_archive_with_stats_calls_borg_with_stats_parameter():
flexmock(module).should_receive('_make_pattern_flags').and_return(()) flexmock(module).should_receive('_make_pattern_flags').and_return(())
flexmock(module).should_receive('_make_exclude_flags').and_return(()) flexmock(module).should_receive('_make_exclude_flags').and_return(())
flexmock(module).should_receive('execute_command').with_args( flexmock(module).should_receive('execute_command').with_args(
('borg', 'create', '--stats') + ARCHIVE_WITH_PATHS, output_log_level=logging.WARNING ('borg', 'create', '--stats') + ARCHIVE_WITH_PATHS,
output_log_level=logging.WARNING,
error_on_warnings=False,
) )
module.create_archive( module.create_archive(
@ -829,7 +868,8 @@ def test_create_archive_with_progress_and_log_info_calls_borg_with_progress_para
flexmock(module).should_receive('_make_pattern_flags').and_return(()) flexmock(module).should_receive('_make_pattern_flags').and_return(())
flexmock(module).should_receive('_make_exclude_flags').and_return(()) flexmock(module).should_receive('_make_exclude_flags').and_return(())
flexmock(module).should_receive('execute_command_without_capture').with_args( flexmock(module).should_receive('execute_command_without_capture').with_args(
('borg', 'create', '--info', '--stats', '--progress') + ARCHIVE_WITH_PATHS ('borg', 'create', '--info', '--stats', '--progress') + ARCHIVE_WITH_PATHS,
error_on_warnings=False,
) )
insert_logging_mock(logging.INFO) insert_logging_mock(logging.INFO)
@ -854,7 +894,7 @@ def test_create_archive_with_progress_calls_borg_with_progress_parameter():
flexmock(module).should_receive('_make_pattern_flags').and_return(()) flexmock(module).should_receive('_make_pattern_flags').and_return(())
flexmock(module).should_receive('_make_exclude_flags').and_return(()) flexmock(module).should_receive('_make_exclude_flags').and_return(())
flexmock(module).should_receive('execute_command_without_capture').with_args( flexmock(module).should_receive('execute_command_without_capture').with_args(
('borg', 'create', '--progress') + ARCHIVE_WITH_PATHS ('borg', 'create', '--progress') + ARCHIVE_WITH_PATHS, error_on_warnings=False
) )
module.create_archive( module.create_archive(
@ -878,7 +918,9 @@ def test_create_archive_with_json_calls_borg_with_json_parameter():
flexmock(module).should_receive('_make_pattern_flags').and_return(()) flexmock(module).should_receive('_make_pattern_flags').and_return(())
flexmock(module).should_receive('_make_exclude_flags').and_return(()) flexmock(module).should_receive('_make_exclude_flags').and_return(())
flexmock(module).should_receive('execute_command').with_args( flexmock(module).should_receive('execute_command').with_args(
('borg', 'create', '--json') + ARCHIVE_WITH_PATHS, output_log_level=None ('borg', 'create', '--json') + ARCHIVE_WITH_PATHS,
output_log_level=None,
error_on_warnings=False,
).and_return('[]') ).and_return('[]')
json_output = module.create_archive( json_output = module.create_archive(
@ -904,7 +946,9 @@ def test_create_archive_with_stats_and_json_calls_borg_without_stats_parameter()
flexmock(module).should_receive('_make_pattern_flags').and_return(()) flexmock(module).should_receive('_make_pattern_flags').and_return(())
flexmock(module).should_receive('_make_exclude_flags').and_return(()) flexmock(module).should_receive('_make_exclude_flags').and_return(())
flexmock(module).should_receive('execute_command').with_args( flexmock(module).should_receive('execute_command').with_args(
('borg', 'create', '--json') + ARCHIVE_WITH_PATHS, output_log_level=None ('borg', 'create', '--json') + ARCHIVE_WITH_PATHS,
output_log_level=None,
error_on_warnings=False,
).and_return('[]') ).and_return('[]')
json_output = module.create_archive( json_output = module.create_archive(
@ -933,6 +977,7 @@ def test_create_archive_with_source_directories_glob_expands():
flexmock(module).should_receive('execute_command').with_args( flexmock(module).should_receive('execute_command').with_args(
('borg', 'create', 'repo::{}'.format(DEFAULT_ARCHIVE_NAME), 'foo', 'food'), ('borg', 'create', 'repo::{}'.format(DEFAULT_ARCHIVE_NAME), 'foo', 'food'),
output_log_level=logging.INFO, output_log_level=logging.INFO,
error_on_warnings=False,
) )
flexmock(module.glob).should_receive('glob').with_args('foo*').and_return(['foo', 'food']) flexmock(module.glob).should_receive('glob').with_args('foo*').and_return(['foo', 'food'])
@ -958,6 +1003,7 @@ def test_create_archive_with_non_matching_source_directories_glob_passes_through
flexmock(module).should_receive('execute_command').with_args( flexmock(module).should_receive('execute_command').with_args(
('borg', 'create', 'repo::{}'.format(DEFAULT_ARCHIVE_NAME), 'foo*'), ('borg', 'create', 'repo::{}'.format(DEFAULT_ARCHIVE_NAME), 'foo*'),
output_log_level=logging.INFO, output_log_level=logging.INFO,
error_on_warnings=False,
) )
flexmock(module.glob).should_receive('glob').with_args('foo*').and_return([]) flexmock(module.glob).should_receive('glob').with_args('foo*').and_return([])
@ -983,6 +1029,7 @@ def test_create_archive_with_glob_calls_borg_with_expanded_directories():
flexmock(module).should_receive('execute_command').with_args( flexmock(module).should_receive('execute_command').with_args(
('borg', 'create', 'repo::{}'.format(DEFAULT_ARCHIVE_NAME), 'foo', 'food'), ('borg', 'create', 'repo::{}'.format(DEFAULT_ARCHIVE_NAME), 'foo', 'food'),
output_log_level=logging.INFO, output_log_level=logging.INFO,
error_on_warnings=False,
) )
module.create_archive( module.create_archive(
@ -1005,7 +1052,9 @@ def test_create_archive_with_archive_name_format_calls_borg_with_archive_name():
flexmock(module).should_receive('_make_pattern_flags').and_return(()) flexmock(module).should_receive('_make_pattern_flags').and_return(())
flexmock(module).should_receive('_make_exclude_flags').and_return(()) flexmock(module).should_receive('_make_exclude_flags').and_return(())
flexmock(module).should_receive('execute_command').with_args( flexmock(module).should_receive('execute_command').with_args(
('borg', 'create', 'repo::ARCHIVE_NAME', 'foo', 'bar'), output_log_level=logging.INFO ('borg', 'create', 'repo::ARCHIVE_NAME', 'foo', 'bar'),
output_log_level=logging.INFO,
error_on_warnings=False,
) )
module.create_archive( module.create_archive(
@ -1030,6 +1079,7 @@ def test_create_archive_with_archive_name_format_accepts_borg_placeholders():
flexmock(module).should_receive('execute_command').with_args( flexmock(module).should_receive('execute_command').with_args(
('borg', 'create', 'repo::Documents_{hostname}-{now}', 'foo', 'bar'), ('borg', 'create', 'repo::Documents_{hostname}-{now}', 'foo', 'bar'),
output_log_level=logging.INFO, output_log_level=logging.INFO,
error_on_warnings=False,
) )
module.create_archive( module.create_archive(

View File

@ -15,7 +15,7 @@ def insert_execute_command_mock(command, working_directory=None, error_on_warnin
def insert_execute_command_output_mock(command, result): def insert_execute_command_output_mock(command, result):
flexmock(module).should_receive('execute_command').with_args( flexmock(module).should_receive('execute_command').with_args(
command, output_log_level=None command, output_log_level=None, error_on_warnings=False
).and_return(result).once() ).and_return(result).once()

View File

@ -10,7 +10,7 @@ from ..test_verbosity import insert_logging_mock
def test_display_archives_info_calls_borg_with_parameters(): def test_display_archives_info_calls_borg_with_parameters():
flexmock(module).should_receive('execute_command').with_args( flexmock(module).should_receive('execute_command').with_args(
('borg', 'info', 'repo'), output_log_level=logging.WARNING ('borg', 'info', 'repo'), output_log_level=logging.WARNING, error_on_warnings=False
) )
module.display_archives_info( module.display_archives_info(
@ -20,7 +20,9 @@ def test_display_archives_info_calls_borg_with_parameters():
def test_display_archives_info_with_log_info_calls_borg_with_info_parameter(): def test_display_archives_info_with_log_info_calls_borg_with_info_parameter():
flexmock(module).should_receive('execute_command').with_args( flexmock(module).should_receive('execute_command').with_args(
('borg', 'info', '--info', 'repo'), output_log_level=logging.WARNING ('borg', 'info', '--info', 'repo'),
output_log_level=logging.WARNING,
error_on_warnings=False,
) )
insert_logging_mock(logging.INFO) insert_logging_mock(logging.INFO)
module.display_archives_info( module.display_archives_info(
@ -30,7 +32,7 @@ def test_display_archives_info_with_log_info_calls_borg_with_info_parameter():
def test_display_archives_info_with_log_info_and_json_suppresses_most_borg_output(): def test_display_archives_info_with_log_info_and_json_suppresses_most_borg_output():
flexmock(module).should_receive('execute_command').with_args( flexmock(module).should_receive('execute_command').with_args(
('borg', 'info', '--json', 'repo'), output_log_level=None ('borg', 'info', '--json', 'repo'), output_log_level=None, error_on_warnings=False
).and_return('[]') ).and_return('[]')
insert_logging_mock(logging.INFO) insert_logging_mock(logging.INFO)
@ -43,7 +45,9 @@ def test_display_archives_info_with_log_info_and_json_suppresses_most_borg_outpu
def test_display_archives_info_with_log_debug_calls_borg_with_debug_parameter(): def test_display_archives_info_with_log_debug_calls_borg_with_debug_parameter():
flexmock(module).should_receive('execute_command').with_args( flexmock(module).should_receive('execute_command').with_args(
('borg', 'info', '--debug', '--show-rc', 'repo'), output_log_level=logging.WARNING ('borg', 'info', '--debug', '--show-rc', 'repo'),
output_log_level=logging.WARNING,
error_on_warnings=False,
) )
insert_logging_mock(logging.DEBUG) insert_logging_mock(logging.DEBUG)
@ -54,7 +58,7 @@ def test_display_archives_info_with_log_debug_calls_borg_with_debug_parameter():
def test_display_archives_info_with_log_debug_and_json_suppresses_most_borg_output(): def test_display_archives_info_with_log_debug_and_json_suppresses_most_borg_output():
flexmock(module).should_receive('execute_command').with_args( flexmock(module).should_receive('execute_command').with_args(
('borg', 'info', '--json', 'repo'), output_log_level=None ('borg', 'info', '--json', 'repo'), output_log_level=None, error_on_warnings=False
).and_return('[]') ).and_return('[]')
insert_logging_mock(logging.DEBUG) insert_logging_mock(logging.DEBUG)
@ -67,7 +71,7 @@ def test_display_archives_info_with_log_debug_and_json_suppresses_most_borg_outp
def test_display_archives_info_with_json_calls_borg_with_json_parameter(): def test_display_archives_info_with_json_calls_borg_with_json_parameter():
flexmock(module).should_receive('execute_command').with_args( flexmock(module).should_receive('execute_command').with_args(
('borg', 'info', '--json', 'repo'), output_log_level=None ('borg', 'info', '--json', 'repo'), output_log_level=None, error_on_warnings=False
).and_return('[]') ).and_return('[]')
json_output = module.display_archives_info( json_output = module.display_archives_info(
@ -79,7 +83,7 @@ def test_display_archives_info_with_json_calls_borg_with_json_parameter():
def test_display_archives_info_with_archive_calls_borg_with_archive_parameter(): def test_display_archives_info_with_archive_calls_borg_with_archive_parameter():
flexmock(module).should_receive('execute_command').with_args( flexmock(module).should_receive('execute_command').with_args(
('borg', 'info', 'repo::archive'), output_log_level=logging.WARNING ('borg', 'info', 'repo::archive'), output_log_level=logging.WARNING, error_on_warnings=False
) )
module.display_archives_info( module.display_archives_info(
@ -89,7 +93,7 @@ def test_display_archives_info_with_archive_calls_borg_with_archive_parameter():
def test_display_archives_info_with_local_path_calls_borg_via_local_path(): def test_display_archives_info_with_local_path_calls_borg_via_local_path():
flexmock(module).should_receive('execute_command').with_args( flexmock(module).should_receive('execute_command').with_args(
('borg1', 'info', 'repo'), output_log_level=logging.WARNING ('borg1', 'info', 'repo'), output_log_level=logging.WARNING, error_on_warnings=False
) )
module.display_archives_info( module.display_archives_info(
@ -102,7 +106,9 @@ def test_display_archives_info_with_local_path_calls_borg_via_local_path():
def test_display_archives_info_with_remote_path_calls_borg_with_remote_path_parameters(): def test_display_archives_info_with_remote_path_calls_borg_with_remote_path_parameters():
flexmock(module).should_receive('execute_command').with_args( flexmock(module).should_receive('execute_command').with_args(
('borg', 'info', '--remote-path', 'borg1', 'repo'), output_log_level=logging.WARNING ('borg', 'info', '--remote-path', 'borg1', 'repo'),
output_log_level=logging.WARNING,
error_on_warnings=False,
) )
module.display_archives_info( module.display_archives_info(
@ -116,7 +122,9 @@ def test_display_archives_info_with_remote_path_calls_borg_with_remote_path_para
def test_display_archives_info_with_lock_wait_calls_borg_with_lock_wait_parameters(): def test_display_archives_info_with_lock_wait_calls_borg_with_lock_wait_parameters():
storage_config = {'lock_wait': 5} storage_config = {'lock_wait': 5}
flexmock(module).should_receive('execute_command').with_args( flexmock(module).should_receive('execute_command').with_args(
('borg', 'info', '--lock-wait', '5', 'repo'), output_log_level=logging.WARNING ('borg', 'info', '--lock-wait', '5', 'repo'),
output_log_level=logging.WARNING,
error_on_warnings=False,
) )
module.display_archives_info( module.display_archives_info(
@ -131,6 +139,7 @@ def test_display_archives_info_passes_through_arguments_to_borg(argument_name):
flexmock(module).should_receive('execute_command').with_args( flexmock(module).should_receive('execute_command').with_args(
('borg', 'info', '--' + argument_name.replace('_', '-'), 'value', 'repo'), ('borg', 'info', '--' + argument_name.replace('_', '-'), 'value', 'repo'),
output_log_level=logging.WARNING, output_log_level=logging.WARNING,
error_on_warnings=False,
) )
module.display_archives_info( module.display_archives_info(

View File

@ -24,7 +24,7 @@ def insert_info_command_not_found_mock():
def insert_init_command_mock(init_command, **kwargs): def insert_init_command_mock(init_command, **kwargs):
flexmock(module).should_receive('execute_command_without_capture').with_args( flexmock(module).should_receive('execute_command_without_capture').with_args(
init_command init_command, error_on_warnings=False
).once() ).once()

View File

@ -10,7 +10,7 @@ from ..test_verbosity import insert_logging_mock
def test_list_archives_calls_borg_with_parameters(): def test_list_archives_calls_borg_with_parameters():
flexmock(module).should_receive('execute_command').with_args( flexmock(module).should_receive('execute_command').with_args(
('borg', 'list', 'repo'), output_log_level=logging.WARNING ('borg', 'list', 'repo'), output_log_level=logging.WARNING, error_on_warnings=False
) )
module.list_archives( module.list_archives(
@ -22,7 +22,9 @@ def test_list_archives_calls_borg_with_parameters():
def test_list_archives_with_log_info_calls_borg_with_info_parameter(): def test_list_archives_with_log_info_calls_borg_with_info_parameter():
flexmock(module).should_receive('execute_command').with_args( flexmock(module).should_receive('execute_command').with_args(
('borg', 'list', '--info', 'repo'), output_log_level=logging.WARNING ('borg', 'list', '--info', 'repo'),
output_log_level=logging.WARNING,
error_on_warnings=False,
) )
insert_logging_mock(logging.INFO) insert_logging_mock(logging.INFO)
@ -35,7 +37,7 @@ def test_list_archives_with_log_info_calls_borg_with_info_parameter():
def test_list_archives_with_log_info_and_json_suppresses_most_borg_output(): def test_list_archives_with_log_info_and_json_suppresses_most_borg_output():
flexmock(module).should_receive('execute_command').with_args( flexmock(module).should_receive('execute_command').with_args(
('borg', 'list', '--json', 'repo'), output_log_level=None ('borg', 'list', '--json', 'repo'), output_log_level=None, error_on_warnings=False
) )
insert_logging_mock(logging.INFO) insert_logging_mock(logging.INFO)
@ -48,7 +50,9 @@ def test_list_archives_with_log_info_and_json_suppresses_most_borg_output():
def test_list_archives_with_log_debug_calls_borg_with_debug_parameter(): def test_list_archives_with_log_debug_calls_borg_with_debug_parameter():
flexmock(module).should_receive('execute_command').with_args( flexmock(module).should_receive('execute_command').with_args(
('borg', 'list', '--debug', '--show-rc', 'repo'), output_log_level=logging.WARNING ('borg', 'list', '--debug', '--show-rc', 'repo'),
output_log_level=logging.WARNING,
error_on_warnings=False,
) )
insert_logging_mock(logging.DEBUG) insert_logging_mock(logging.DEBUG)
@ -61,7 +65,7 @@ def test_list_archives_with_log_debug_calls_borg_with_debug_parameter():
def test_list_archives_with_log_debug_and_json_suppresses_most_borg_output(): def test_list_archives_with_log_debug_and_json_suppresses_most_borg_output():
flexmock(module).should_receive('execute_command').with_args( flexmock(module).should_receive('execute_command').with_args(
('borg', 'list', '--json', 'repo'), output_log_level=None ('borg', 'list', '--json', 'repo'), output_log_level=None, error_on_warnings=False
) )
insert_logging_mock(logging.DEBUG) insert_logging_mock(logging.DEBUG)
@ -75,7 +79,9 @@ def test_list_archives_with_log_debug_and_json_suppresses_most_borg_output():
def test_list_archives_with_lock_wait_calls_borg_with_lock_wait_parameters(): def test_list_archives_with_lock_wait_calls_borg_with_lock_wait_parameters():
storage_config = {'lock_wait': 5} storage_config = {'lock_wait': 5}
flexmock(module).should_receive('execute_command').with_args( flexmock(module).should_receive('execute_command').with_args(
('borg', 'list', '--lock-wait', '5', 'repo'), output_log_level=logging.WARNING ('borg', 'list', '--lock-wait', '5', 'repo'),
output_log_level=logging.WARNING,
error_on_warnings=False,
) )
module.list_archives( module.list_archives(
@ -88,7 +94,7 @@ def test_list_archives_with_lock_wait_calls_borg_with_lock_wait_parameters():
def test_list_archives_with_archive_calls_borg_with_archive_parameter(): def test_list_archives_with_archive_calls_borg_with_archive_parameter():
storage_config = {} storage_config = {}
flexmock(module).should_receive('execute_command').with_args( flexmock(module).should_receive('execute_command').with_args(
('borg', 'list', 'repo::archive'), output_log_level=logging.WARNING ('borg', 'list', 'repo::archive'), output_log_level=logging.WARNING, error_on_warnings=False
) )
module.list_archives( module.list_archives(
@ -100,7 +106,7 @@ def test_list_archives_with_archive_calls_borg_with_archive_parameter():
def test_list_archives_with_local_path_calls_borg_via_local_path(): def test_list_archives_with_local_path_calls_borg_via_local_path():
flexmock(module).should_receive('execute_command').with_args( flexmock(module).should_receive('execute_command').with_args(
('borg1', 'list', 'repo'), output_log_level=logging.WARNING ('borg1', 'list', 'repo'), output_log_level=logging.WARNING, error_on_warnings=False
) )
module.list_archives( module.list_archives(
@ -113,7 +119,9 @@ def test_list_archives_with_local_path_calls_borg_via_local_path():
def test_list_archives_with_remote_path_calls_borg_with_remote_path_parameters(): def test_list_archives_with_remote_path_calls_borg_with_remote_path_parameters():
flexmock(module).should_receive('execute_command').with_args( flexmock(module).should_receive('execute_command').with_args(
('borg', 'list', '--remote-path', 'borg1', 'repo'), output_log_level=logging.WARNING ('borg', 'list', '--remote-path', 'borg1', 'repo'),
output_log_level=logging.WARNING,
error_on_warnings=False,
) )
module.list_archives( module.list_archives(
@ -126,7 +134,9 @@ def test_list_archives_with_remote_path_calls_borg_with_remote_path_parameters()
def test_list_archives_with_short_calls_borg_with_short_parameter(): def test_list_archives_with_short_calls_borg_with_short_parameter():
flexmock(module).should_receive('execute_command').with_args( flexmock(module).should_receive('execute_command').with_args(
('borg', 'list', '--short', 'repo'), output_log_level=logging.WARNING ('borg', 'list', '--short', 'repo'),
output_log_level=logging.WARNING,
error_on_warnings=False,
).and_return('[]') ).and_return('[]')
module.list_archives( module.list_archives(
@ -154,6 +164,7 @@ def test_list_archives_passes_through_arguments_to_borg(argument_name):
flexmock(module).should_receive('execute_command').with_args( flexmock(module).should_receive('execute_command').with_args(
('borg', 'list', '--' + argument_name.replace('_', '-'), 'value', 'repo'), ('borg', 'list', '--' + argument_name.replace('_', '-'), 'value', 'repo'),
output_log_level=logging.WARNING, output_log_level=logging.WARNING,
error_on_warnings=False,
).and_return('[]') ).and_return('[]')
module.list_archives( module.list_archives(
@ -169,6 +180,7 @@ def test_list_archives_with_successful_calls_borg_to_exclude_checkpoints():
flexmock(module).should_receive('execute_command').with_args( flexmock(module).should_receive('execute_command').with_args(
('borg', 'list', '--glob-archives', module.BORG_EXCLUDE_CHECKPOINTS_GLOB, 'repo'), ('borg', 'list', '--glob-archives', module.BORG_EXCLUDE_CHECKPOINTS_GLOB, 'repo'),
output_log_level=logging.WARNING, output_log_level=logging.WARNING,
error_on_warnings=False,
).and_return('[]') ).and_return('[]')
module.list_archives( module.list_archives(
@ -180,7 +192,7 @@ def test_list_archives_with_successful_calls_borg_to_exclude_checkpoints():
def test_list_archives_with_json_calls_borg_with_json_parameter(): def test_list_archives_with_json_calls_borg_with_json_parameter():
flexmock(module).should_receive('execute_command').with_args( flexmock(module).should_receive('execute_command').with_args(
('borg', 'list', '--json', 'repo'), output_log_level=None ('borg', 'list', '--json', 'repo'), output_log_level=None, error_on_warnings=False
).and_return('[]') ).and_return('[]')
json_output = module.list_archives( json_output = module.list_archives(

View File

@ -8,7 +8,9 @@ from ..test_verbosity import insert_logging_mock
def insert_execute_command_mock(command): def insert_execute_command_mock(command):
flexmock(module).should_receive('execute_command').with_args(command).once() flexmock(module).should_receive('execute_command').with_args(
command, error_on_warnings=False
).once()
def test_mount_archive_calls_borg_with_required_parameters(): def test_mount_archive_calls_borg_with_required_parameters():
@ -116,7 +118,7 @@ def test_mount_archive_with_log_debug_calls_borg_with_debug_parameters():
def test_mount_archive_calls_borg_with_foreground_parameter(): def test_mount_archive_calls_borg_with_foreground_parameter():
flexmock(module).should_receive('execute_command_without_capture').with_args( flexmock(module).should_receive('execute_command_without_capture').with_args(
('borg', 'mount', '--foreground', 'repo::archive', '/mnt') ('borg', 'mount', '--foreground', 'repo::archive', '/mnt'), error_on_warnings=False
).once() ).once()
module.mount_archive( module.mount_archive(

View File

@ -10,7 +10,7 @@ from ..test_verbosity import insert_logging_mock
def insert_execute_command_mock(prune_command, output_log_level): def insert_execute_command_mock(prune_command, output_log_level):
flexmock(module).should_receive('execute_command').with_args( flexmock(module).should_receive('execute_command').with_args(
prune_command, output_log_level=output_log_level prune_command, output_log_level=output_log_level, error_on_warnings=False
).once() ).once()

View File

@ -4,44 +4,28 @@ from flexmock import flexmock
from borgmatic import execute as module from borgmatic import execute as module
def test_exit_code_indicates_error_with_borg_error_is_true(): @pytest.mark.parametrize(
assert module.exit_code_indicates_error(('/usr/bin/borg1', 'init'), 2) 'exit_code,error_on_warnings,expected_result',
(
(2, True, True),
def test_exit_code_indicates_error_with_borg_warning_is_false(): (2, False, True),
assert not module.exit_code_indicates_error(('/usr/bin/borg1', 'init'), 1) (1, True, True),
(1, False, False),
(0, True, False),
def test_exit_code_indicates_error_with_borg_success_is_false(): (0, False, False),
assert not module.exit_code_indicates_error(('/usr/bin/borg1', 'init'), 0) ),
)
def test_exit_code_indicates_error_respects_exit_code_and_error_on_warnings(
def test_exit_code_indicates_error_with_borg_error_and_error_on_warnings_is_true(): exit_code, error_on_warnings, expected_result
assert module.exit_code_indicates_error(('/usr/bin/borg1', 'init'), 2, error_on_warnings=True) ):
assert (
module.exit_code_indicates_error(
def test_exit_code_indicates_error_with_borg_warning_and_error_on_warnings_is_true(): ('command',), exit_code, error_on_warnings=error_on_warnings
assert module.exit_code_indicates_error(('/usr/bin/borg1', 'init'), 1, error_on_warnings=True) )
is expected_result
def test_exit_code_indicates_error_with_borg_success_and_error_on_warnings_is_false():
assert not module.exit_code_indicates_error(
('/usr/bin/borg1', 'init'), 0, error_on_warnings=True
) )
def test_exit_code_indicates_error_with_non_borg_error_is_true():
assert module.exit_code_indicates_error(('/usr/bin/command',), 2)
def test_exit_code_indicates_error_with_non_borg_warning_is_true():
assert module.exit_code_indicates_error(('/usr/bin/command',), 1)
def test_exit_code_indicates_error_with_non_borg_success_is_false():
assert not module.exit_code_indicates_error(('/usr/bin/command',), 0)
def test_execute_command_calls_full_command(): def test_execute_command_calls_full_command():
full_command = ['foo', 'bar'] full_command = ['foo', 'bar']
flexmock(module.os, environ={'a': 'b'}) flexmock(module.os, environ={'a': 'b'})