diff --git a/borgmatic/borg/create.py b/borgmatic/borg/create.py index 87a0fdd7..f0169bfc 100644 --- a/borgmatic/borg/create.py +++ b/borgmatic/borg/create.py @@ -291,6 +291,7 @@ def collect_special_file_paths( capture_stderr=True, working_directory=working_directory, extra_environment=borg_environment, + raise_on_exit_code_one=False, ) paths = tuple( diff --git a/borgmatic/execute.py b/borgmatic/execute.py index d4b04bfe..2daac555 100644 --- a/borgmatic/execute.py +++ b/borgmatic/execute.py @@ -213,7 +213,12 @@ def execute_command( def execute_command_and_capture_output( - full_command, capture_stderr=False, shell=False, extra_environment=None, working_directory=None, + full_command, + capture_stderr=False, + shell=False, + extra_environment=None, + working_directory=None, + raise_on_exit_code_one=True, ): ''' Execute the given command (a sequence of command/argument strings), capturing and returning its @@ -221,20 +226,29 @@ def execute_command_and_capture_output( stdout. 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 a working directory is given, use that as the present working directory when running the command. + If raise on exit code one 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, or if the + command exits with a non-zero exit code and raise on exit code one is True. ''' log_command(full_command) environment = {**os.environ, **extra_environment} if extra_environment else None command = ' '.join(full_command) if shell else full_command - output = subprocess.check_output( - command, - stderr=subprocess.STDOUT if capture_stderr else None, - shell=shell, - env=environment, - cwd=working_directory, - ) + try: + output = subprocess.check_output( + command, + stderr=subprocess.STDOUT if capture_stderr else None, + shell=shell, + env=environment, + cwd=working_directory, + ) + logger.warning('Command output: {}'.format(output)) + except subprocess.CalledProcessError as e: + if raise_on_exit_code_one or e.returncode != 1: + raise + output = e.output + logger.warning('Command output: {}'.format(output)) return output.decode() if output is not None else None diff --git a/tests/unit/test_execute.py b/tests/unit/test_execute.py index 0441e9d5..29a80cba 100644 --- a/tests/unit/test_execute.py +++ b/tests/unit/test_execute.py @@ -239,6 +239,32 @@ def test_execute_command_and_capture_output_with_capture_stderr_returns_stderr() assert output == expected_output +def test_execute_command_and_capture_output_returns_output_with_raise_on_exit_code_one_false(): + full_command = ['foo', 'bar'] + expected_output = '[]' + err_output = b'[]' + flexmock(module.os, environ={'a': 'b'}) + flexmock(module.subprocess).should_receive('check_output').with_args( + full_command, stderr=None, shell=False, env=None, cwd=None + ).and_raise(subprocess.CalledProcessError(1, full_command, err_output)).once() + + output = module.execute_command_and_capture_output(full_command, raise_on_exit_code_one=False) + + assert output == expected_output + + +def test_execute_command_and_capture_output_returns_output_with_raise_on_exit_code_one_false_and_exit_code_not_one(): + full_command = ['foo', 'bar'] + expected_output = '[]' + flexmock(module.os, environ={'a': 'b'}) + flexmock(module.subprocess).should_receive('check_output').with_args( + full_command, stderr=None, shell=False, env=None, cwd=None + ).and_raise(subprocess.CalledProcessError(2, full_command, expected_output)).once() + + with pytest.raises(subprocess.CalledProcessError): + module.execute_command_and_capture_output(full_command, raise_on_exit_code_one=False) + + def test_execute_command_and_capture_output_returns_output_with_shell(): full_command = ['foo', 'bar'] expected_output = '[]'