瀏覽代碼

Remove "borgmatic restore --progress" flag, as it now conflicts with streaming database restores.

Dan Helfman 5 年之前
父節點
當前提交
1ea047dd94

+ 1 - 0
NEWS

@@ -3,6 +3,7 @@
    filesystem space. This feature is automatic, and works even on restores from archives made with
    filesystem space. This feature is automatic, and works even on restores from archives made with
    previous versions of borgmatic.
    previous versions of borgmatic.
  * #293: Documentation on macOS launchd permissions issues with work-around for Full Disk Access.
  * #293: Documentation on macOS launchd permissions issues with work-around for Full Disk Access.
+ * Remove "borgmatic restore --progress" flag, as it now conflicts with streaming database restores.
 
 
 1.5.2
 1.5.2
  * #301: Fix MySQL restore error on "all" database dump by excluding system tables.
  * #301: Fix MySQL restore error on "all" database dump by excluding system tables.

+ 2 - 2
borgmatic/borg/check.py

@@ -1,7 +1,7 @@
 import logging
 import logging
 
 
 from borgmatic.borg import extract
 from borgmatic.borg import extract
-from borgmatic.execute import execute_command, execute_command_without_capture
+from borgmatic.execute import DO_NOT_CAPTURE, execute_command
 
 
 DEFAULT_CHECKS = ('repository', 'archives')
 DEFAULT_CHECKS = ('repository', 'archives')
 DEFAULT_PREFIX = '{hostname}-'
 DEFAULT_PREFIX = '{hostname}-'
@@ -134,7 +134,7 @@ def check_archives(
         # The Borg repair option trigger an interactive prompt, which won't work when output is
         # The Borg repair option trigger an interactive prompt, which won't work when output is
         # captured. And progress messes with the terminal directly.
         # captured. And progress messes with the terminal directly.
         if repair or progress:
         if repair or progress:
-            execute_command_without_capture(full_command, error_on_warnings=True)
+            execute_command(full_command, output_file=DO_NOT_CAPTURE, error_on_warnings=True)
         else:
         else:
             execute_command(full_command, error_on_warnings=True)
             execute_command(full_command, error_on_warnings=True)
 
 

+ 7 - 13
borgmatic/borg/create.py

@@ -4,11 +4,7 @@ import logging
 import os
 import os
 import tempfile
 import tempfile
 
 
-from borgmatic.execute import (
-    execute_command,
-    execute_command_with_processes,
-    execute_command_without_capture,
-)
+from borgmatic.execute import DO_NOT_CAPTURE, execute_command, execute_command_with_processes
 
 
 logger = logging.getLogger(__name__)
 logger = logging.getLogger(__name__)
 
 
@@ -206,12 +202,6 @@ def create_archive(
         + sources
         + sources
     )
     )
 
 
-    # The progress output isn't compatible with captured and logged output, as progress messes with
-    # the terminal directly.
-    if progress:
-        execute_command_without_capture(full_command, error_on_warnings=False)
-        return
-
     if json:
     if json:
         output_log_level = None
         output_log_level = None
     elif (stats or files) and logger.getEffectiveLevel() == logging.WARNING:
     elif (stats or files) and logger.getEffectiveLevel() == logging.WARNING:
@@ -219,9 +209,13 @@ def create_archive(
     else:
     else:
         output_log_level = logging.INFO
         output_log_level = logging.INFO
 
 
+    # The progress output isn't compatible with captured and logged output, as progress messes with
+    # the terminal directly.
+    output_file = DO_NOT_CAPTURE if progress else None
+
     if stream_processes:
     if stream_processes:
         return execute_command_with_processes(
         return execute_command_with_processes(
-            full_command, stream_processes, output_log_level, error_on_warnings=False
+            full_command, stream_processes, output_log_level, output_file, error_on_warnings=False
         )
         )
 
 
-    return execute_command(full_command, output_log_level, error_on_warnings=False)
+    return execute_command(full_command, output_log_level, output_file, error_on_warnings=False)

+ 9 - 3
borgmatic/borg/extract.py

@@ -2,7 +2,7 @@ import logging
 import os
 import os
 import subprocess
 import subprocess
 
 
-from borgmatic.execute import execute_command, execute_command_without_capture
+from borgmatic.execute import DO_NOT_CAPTURE, execute_command
 
 
 logger = logging.getLogger(__name__)
 logger = logging.getLogger(__name__)
 
 
@@ -78,6 +78,9 @@ def extract_archive(
     umask = storage_config.get('umask', None)
     umask = storage_config.get('umask', None)
     lock_wait = storage_config.get('lock_wait', None)
     lock_wait = storage_config.get('lock_wait', None)
 
 
+    if progress and extract_to_stdout:
+        raise ValueError('progress and extract_to_stdout cannot both be set')
+
     full_command = (
     full_command = (
         (local_path, 'extract')
         (local_path, 'extract')
         + (('--remote-path', remote_path) if remote_path else ())
         + (('--remote-path', remote_path) if remote_path else ())
@@ -96,8 +99,11 @@ def extract_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, working_directory=destination_path, error_on_warnings=error_on_warnings
+        return execute_command(
+            full_command,
+            output_file=DO_NOT_CAPTURE,
+            working_directory=destination_path,
+            error_on_warnings=error_on_warnings,
         )
         )
         return None
         return None
 
 

+ 3 - 3
borgmatic/borg/init.py

@@ -1,7 +1,7 @@
 import logging
 import logging
 import subprocess
 import subprocess
 
 
-from borgmatic.execute import execute_command, execute_command_without_capture
+from borgmatic.execute import DO_NOT_CAPTURE, execute_command
 
 
 logger = logging.getLogger(__name__)
 logger = logging.getLogger(__name__)
 
 
@@ -54,5 +54,5 @@ def initialize_repository(
         + (repository,)
         + (repository,)
     )
     )
 
 
-    # Don't use execute_command() here because it doesn't support interactive prompts.
-    execute_command_without_capture(init_command, error_on_warnings=False)
+    # Do not capture output here, so as to support interactive prompts.
+    execute_command(init_command, output_file=DO_NOT_CAPTURE, error_on_warnings=False)

+ 2 - 2
borgmatic/borg/mount.py

@@ -1,6 +1,6 @@
 import logging
 import logging
 
 
-from borgmatic.execute import execute_command, execute_command_without_capture
+from borgmatic.execute import DO_NOT_CAPTURE, execute_command
 
 
 logger = logging.getLogger(__name__)
 logger = logging.getLogger(__name__)
 
 
@@ -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, error_on_warnings=False)
+        execute_command(full_command, output_file=DO_NOT_CAPTURE, error_on_warnings=False)
         return
         return
 
 
     execute_command(full_command, error_on_warnings=False)
     execute_command(full_command, error_on_warnings=False)

+ 0 - 7
borgmatic/commands/arguments.py

@@ -427,13 +427,6 @@ def parse_arguments(*unparsed_arguments):
         dest='databases',
         dest='databases',
         help='Names of databases to restore from archive, defaults to all databases. Note that any databases to restore must be defined in borgmatic\'s configuration',
         help='Names of databases to restore from archive, defaults to all databases. Note that any databases to restore must be defined in borgmatic\'s configuration',
     )
     )
-    restore_group.add_argument(
-        '--progress',
-        dest='progress',
-        default=False,
-        action='store_true',
-        help='Display progress for each database dump file as it is extracted from archive',
-    )
     restore_group.add_argument(
     restore_group.add_argument(
         '-h', '--help', action='help', help='Show this help message and exit'
         '-h', '--help', action='help', help='Show this help message and exit'
     )
     )

+ 0 - 1
borgmatic/commands/borgmatic.py

@@ -386,7 +386,6 @@ def run_actions(
                         local_path=local_path,
                         local_path=local_path,
                         remote_path=remote_path,
                         remote_path=remote_path,
                         destination_path='/',
                         destination_path='/',
-                        progress=arguments['restore'].progress,
                         extract_to_stdout=True,
                         extract_to_stdout=True,
                     )
                     )
 
 

+ 46 - 40
borgmatic/execute.py

@@ -89,7 +89,11 @@ def log_many_outputs(processes, exclude_stdouts, output_log_level, error_on_warn
     '''
     '''
     # Map from output buffer to sequence of last lines.
     # Map from output buffer to sequence of last lines.
     buffer_last_lines = collections.defaultdict(list)
     buffer_last_lines = collections.defaultdict(list)
-    output_buffers = [output_buffer_for_process(process, exclude_stdouts) for process in processes]
+    output_buffers = [
+        output_buffer_for_process(process, exclude_stdouts)
+        for process in processes
+        if process.stdout or process.stderr
+    ]
 
 
     while True:
     while True:
         (ready_buffers, _, _) = select.select(output_buffers, [], [])
         (ready_buffers, _, _) = select.select(output_buffers, [], [])
@@ -112,9 +116,13 @@ def log_many_outputs(processes, exclude_stdouts, output_log_level, error_on_warn
             break
             break
 
 
     for process in processes:
     for process in processes:
-        remaining_output = (
-            output_buffer_for_process(process, exclude_stdouts).read().rstrip().decode()
-        )
+        output_buffer = output_buffer_for_process(process, exclude_stdouts)
+
+        if not output_buffer:
+            continue
+
+        remaining_output = output_buffer.read().rstrip().decode()
+
         if remaining_output:  # pragma: no cover
         if remaining_output:  # pragma: no cover
             logger.log(output_log_level, remaining_output)
             logger.log(output_log_level, remaining_output)
 
 
@@ -125,7 +133,8 @@ def log_many_outputs(processes, exclude_stdouts, output_log_level, error_on_warn
             # If an error occurs, include its output in the raised exception so that we don't
             # If an error occurs, include its output in the raised exception so that we don't
             # inadvertently hide error output.
             # inadvertently hide error output.
             output_buffer = output_buffer_for_process(process, exclude_stdouts)
             output_buffer = output_buffer_for_process(process, exclude_stdouts)
-            last_lines = buffer_last_lines[output_buffer]
+
+            last_lines = buffer_last_lines[output_buffer] if output_buffer else []
             if len(last_lines) == ERROR_OUTPUT_MAX_LINE_COUNT:
             if len(last_lines) == ERROR_OUTPUT_MAX_LINE_COUNT:
                 last_lines.insert(0, '...')
                 last_lines.insert(0, '...')
 
 
@@ -146,6 +155,9 @@ def log_command(full_command, input_file, output_file):
     )
     )
 
 
 
 
+DO_NOT_CAPTURE = object()
+
+
 def execute_command(
 def execute_command(
     full_command,
     full_command,
     output_log_level=logging.INFO,
     output_log_level=logging.INFO,
@@ -173,49 +185,40 @@ def execute_command(
     '''
     '''
     log_command(full_command, input_file, output_file)
     log_command(full_command, input_file, output_file)
     environment = {**os.environ, **extra_environment} if extra_environment else None
     environment = {**os.environ, **extra_environment} if extra_environment else None
+    do_not_capture = bool(output_file is DO_NOT_CAPTURE)
 
 
     if output_log_level is None:
     if output_log_level is None:
         output = subprocess.check_output(
         output = subprocess.check_output(
             full_command, shell=shell, env=environment, cwd=working_directory
             full_command, shell=shell, env=environment, cwd=working_directory
         )
         )
         return output.decode() if output is not None else None
         return output.decode() if output is not None else None
-    else:
-        process = subprocess.Popen(
-            ' '.join(full_command) if shell else full_command,
-            stdin=input_file,
-            stdout=output_file or subprocess.PIPE,
-            stderr=subprocess.PIPE if output_file else subprocess.STDOUT,
-            shell=shell,
-            env=environment,
-            cwd=working_directory,
-        )
-        if not run_to_completion:
-            return process
-
-        log_output(
-            process,
-            process.stderr if output_file else process.stdout,
-            output_log_level,
-            error_on_warnings,
-        )
 
 
+    process = subprocess.Popen(
+        ' '.join(full_command) if shell else full_command,
+        stdin=input_file,
+        stdout=None if do_not_capture else (output_file or subprocess.PIPE),
+        stderr=None if do_not_capture else (subprocess.PIPE if output_file else subprocess.STDOUT),
+        shell=shell,
+        env=environment,
+        cwd=working_directory,
+    )
+    if not run_to_completion:
+        return process
 
 
-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
-    output in any way. This is necessary for commands that monkey with the terminal (e.g. progress
-    display) or provide interactive prompts.
+    if do_not_capture:
+        exit_code = process.poll()
 
 
-    If a working directory is given, use that as the present working directory when running the
-    command. If error on warnings is False, then treat exit code 1 as a warning instead of an error.
-    '''
-    logger.debug(' '.join(full_command))
+        if exit_code_indicates_error(exit_code, error_on_warnings):
+            raise subprocess.CalledProcessError(exit_code, process_command(process))
 
 
-    try:
-        subprocess.check_call(full_command, cwd=working_directory)
-    except subprocess.CalledProcessError as error:
-        if exit_code_indicates_error(error.returncode, error_on_warnings):
-            raise
+        return
+
+    log_output(
+        process,
+        process.stderr if output_file else process.stdout,
+        output_log_level,
+        error_on_warnings,
+    )
 
 
 
 
 def execute_command_with_processes(
 def execute_command_with_processes(
@@ -248,13 +251,16 @@ def execute_command_with_processes(
     '''
     '''
     log_command(full_command, input_file, output_file)
     log_command(full_command, input_file, output_file)
     environment = {**os.environ, **extra_environment} if extra_environment else None
     environment = {**os.environ, **extra_environment} if extra_environment else None
+    do_not_capture = bool(output_file is DO_NOT_CAPTURE)
 
 
     try:
     try:
         command_process = subprocess.Popen(
         command_process = subprocess.Popen(
             full_command,
             full_command,
             stdin=input_file,
             stdin=input_file,
-            stdout=output_file or subprocess.PIPE,
-            stderr=subprocess.PIPE if output_file else subprocess.STDOUT,
+            stdout=None if do_not_capture else (output_file or subprocess.PIPE),
+            stderr=None
+            if do_not_capture
+            else (subprocess.PIPE if output_file else subprocess.STDOUT),
             shell=shell,
             shell=shell,
             env=environment,
             env=environment,
             cwd=working_directory,
             cwd=working_directory,

+ 0 - 6
tests/integration/commands/test_arguments.py

@@ -393,12 +393,6 @@ def test_parse_arguments_allows_progress_and_extract():
     module.parse_arguments('--progress', 'extract', '--archive', 'test', 'list')
     module.parse_arguments('--progress', 'extract', '--archive', 'test', 'list')
 
 
 
 
-def test_parse_arguments_allows_progress_and_restore():
-    flexmock(module.collect).should_receive('get_default_config_paths').and_return(['default'])
-
-    module.parse_arguments('--progress', 'restore', '--archive', 'test', 'list')
-
-
 def test_parse_arguments_disallows_progress_without_create():
 def test_parse_arguments_disallows_progress_without_create():
     flexmock(module.collect).should_receive('get_default_config_paths').and_return(['default'])
     flexmock(module.collect).should_receive('get_default_config_paths').and_return(['default'])
 
 

+ 8 - 4
tests/unit/borg/test_check.py

@@ -164,8 +164,10 @@ def test_check_archives_with_progress_calls_borg_with_progress_parameter():
     flexmock(module).should_receive('_parse_checks').and_return(checks)
     flexmock(module).should_receive('_parse_checks').and_return(checks)
     flexmock(module).should_receive('_make_check_flags').and_return(())
     flexmock(module).should_receive('_make_check_flags').and_return(())
     flexmock(module).should_receive('execute_command').never()
     flexmock(module).should_receive('execute_command').never()
-    flexmock(module).should_receive('execute_command_without_capture').with_args(
-        ('borg', 'check', '--progress', 'repo'), error_on_warnings=True
+    flexmock(module).should_receive('execute_command').with_args(
+        ('borg', 'check', '--progress', 'repo'),
+        output_file=module.DO_NOT_CAPTURE,
+        error_on_warnings=True,
     ).once()
     ).once()
 
 
     module.check_archives(
     module.check_archives(
@@ -179,8 +181,10 @@ def test_check_archives_with_repair_calls_borg_with_repair_parameter():
     flexmock(module).should_receive('_parse_checks').and_return(checks)
     flexmock(module).should_receive('_parse_checks').and_return(checks)
     flexmock(module).should_receive('_make_check_flags').and_return(())
     flexmock(module).should_receive('_make_check_flags').and_return(())
     flexmock(module).should_receive('execute_command').never()
     flexmock(module).should_receive('execute_command').never()
-    flexmock(module).should_receive('execute_command_without_capture').with_args(
-        ('borg', 'check', '--repair', 'repo'), error_on_warnings=True
+    flexmock(module).should_receive('execute_command').with_args(
+        ('borg', 'check', '--repair', 'repo'),
+        output_file=module.DO_NOT_CAPTURE,
+        error_on_warnings=True,
     ).once()
     ).once()
 
 
     module.check_archives(
     module.check_archives(

+ 46 - 4
tests/unit/borg/test_create.py

@@ -222,6 +222,7 @@ def test_create_archive_calls_borg_with_parameters():
     flexmock(module).should_receive('execute_command').with_args(
     flexmock(module).should_receive('execute_command').with_args(
         ('borg', 'create') + ARCHIVE_WITH_PATHS,
         ('borg', 'create') + ARCHIVE_WITH_PATHS,
         output_log_level=logging.INFO,
         output_log_level=logging.INFO,
+        output_file=None,
         error_on_warnings=False,
         error_on_warnings=False,
     )
     )
 
 
@@ -250,6 +251,7 @@ def test_create_archive_with_patterns_calls_borg_with_patterns():
     flexmock(module).should_receive('execute_command').with_args(
     flexmock(module).should_receive('execute_command').with_args(
         ('borg', 'create') + pattern_flags + ARCHIVE_WITH_PATHS,
         ('borg', 'create') + pattern_flags + ARCHIVE_WITH_PATHS,
         output_log_level=logging.INFO,
         output_log_level=logging.INFO,
+        output_file=None,
         error_on_warnings=False,
         error_on_warnings=False,
     )
     )
 
 
@@ -278,6 +280,7 @@ def test_create_archive_with_exclude_patterns_calls_borg_with_excludes():
     flexmock(module).should_receive('execute_command').with_args(
     flexmock(module).should_receive('execute_command').with_args(
         ('borg', 'create') + exclude_flags + ARCHIVE_WITH_PATHS,
         ('borg', 'create') + exclude_flags + ARCHIVE_WITH_PATHS,
         output_log_level=logging.INFO,
         output_log_level=logging.INFO,
+        output_file=None,
         error_on_warnings=False,
         error_on_warnings=False,
     )
     )
 
 
@@ -304,6 +307,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', '--info') + ARCHIVE_WITH_PATHS,
         ('borg', 'create', '--info') + ARCHIVE_WITH_PATHS,
         output_log_level=logging.INFO,
         output_log_level=logging.INFO,
+        output_file=None,
         error_on_warnings=False,
         error_on_warnings=False,
     )
     )
     insert_logging_mock(logging.INFO)
     insert_logging_mock(logging.INFO)
@@ -331,6 +335,7 @@ def test_create_archive_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', 'create', '--json') + ARCHIVE_WITH_PATHS,
         ('borg', 'create', '--json') + ARCHIVE_WITH_PATHS,
         output_log_level=None,
         output_log_level=None,
+        output_file=None,
         error_on_warnings=False,
         error_on_warnings=False,
     )
     )
     insert_logging_mock(logging.INFO)
     insert_logging_mock(logging.INFO)
@@ -358,6 +363,7 @@ def test_create_archive_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', 'create', '--debug', '--show-rc') + ARCHIVE_WITH_PATHS,
         ('borg', 'create', '--debug', '--show-rc') + ARCHIVE_WITH_PATHS,
         output_log_level=logging.INFO,
         output_log_level=logging.INFO,
+        output_file=None,
         error_on_warnings=False,
         error_on_warnings=False,
     )
     )
     insert_logging_mock(logging.DEBUG)
     insert_logging_mock(logging.DEBUG)
@@ -384,6 +390,7 @@ def test_create_archive_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', 'create', '--json') + ARCHIVE_WITH_PATHS,
         ('borg', 'create', '--json') + ARCHIVE_WITH_PATHS,
         output_log_level=None,
         output_log_level=None,
+        output_file=None,
         error_on_warnings=False,
         error_on_warnings=False,
     )
     )
     insert_logging_mock(logging.DEBUG)
     insert_logging_mock(logging.DEBUG)
@@ -412,6 +419,7 @@ def test_create_archive_with_dry_run_calls_borg_with_dry_run_parameter():
     flexmock(module).should_receive('execute_command').with_args(
     flexmock(module).should_receive('execute_command').with_args(
         ('borg', 'create', '--dry-run') + ARCHIVE_WITH_PATHS,
         ('borg', 'create', '--dry-run') + ARCHIVE_WITH_PATHS,
         output_log_level=logging.INFO,
         output_log_level=logging.INFO,
+        output_file=None,
         error_on_warnings=False,
         error_on_warnings=False,
     )
     )
 
 
@@ -440,6 +448,7 @@ def test_create_archive_with_stats_and_dry_run_calls_borg_without_stats_paramete
     flexmock(module).should_receive('execute_command').with_args(
     flexmock(module).should_receive('execute_command').with_args(
         ('borg', 'create', '--info', '--dry-run') + ARCHIVE_WITH_PATHS,
         ('borg', 'create', '--info', '--dry-run') + ARCHIVE_WITH_PATHS,
         output_log_level=logging.INFO,
         output_log_level=logging.INFO,
+        output_file=None,
         error_on_warnings=False,
         error_on_warnings=False,
     )
     )
     insert_logging_mock(logging.INFO)
     insert_logging_mock(logging.INFO)
@@ -467,6 +476,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,
+        output_file=None,
         error_on_warnings=False,
         error_on_warnings=False,
     )
     )
 
 
@@ -492,6 +502,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,
+        output_file=None,
         error_on_warnings=False,
         error_on_warnings=False,
     )
     )
 
 
@@ -517,6 +528,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,
+        output_file=None,
         error_on_warnings=False,
         error_on_warnings=False,
     )
     )
 
 
@@ -542,6 +554,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,
+        output_file=None,
         error_on_warnings=False,
         error_on_warnings=False,
     )
     )
 
 
@@ -567,6 +580,7 @@ def test_create_archive_with_one_file_system_calls_borg_with_one_file_system_par
     flexmock(module).should_receive('execute_command').with_args(
     flexmock(module).should_receive('execute_command').with_args(
         ('borg', 'create', '--one-file-system') + ARCHIVE_WITH_PATHS,
         ('borg', 'create', '--one-file-system') + ARCHIVE_WITH_PATHS,
         output_log_level=logging.INFO,
         output_log_level=logging.INFO,
+        output_file=None,
         error_on_warnings=False,
         error_on_warnings=False,
     )
     )
 
 
@@ -593,6 +607,7 @@ def test_create_archive_with_numeric_owner_calls_borg_with_numeric_owner_paramet
     flexmock(module).should_receive('execute_command').with_args(
     flexmock(module).should_receive('execute_command').with_args(
         ('borg', 'create', '--numeric-owner') + ARCHIVE_WITH_PATHS,
         ('borg', 'create', '--numeric-owner') + ARCHIVE_WITH_PATHS,
         output_log_level=logging.INFO,
         output_log_level=logging.INFO,
+        output_file=None,
         error_on_warnings=False,
         error_on_warnings=False,
     )
     )
 
 
@@ -619,6 +634,7 @@ def test_create_archive_with_read_special_calls_borg_with_read_special_parameter
     flexmock(module).should_receive('execute_command').with_args(
     flexmock(module).should_receive('execute_command').with_args(
         ('borg', 'create', '--read-special') + ARCHIVE_WITH_PATHS,
         ('borg', 'create', '--read-special') + ARCHIVE_WITH_PATHS,
         output_log_level=logging.INFO,
         output_log_level=logging.INFO,
+        output_file=None,
         error_on_warnings=False,
         error_on_warnings=False,
     )
     )
 
 
@@ -646,6 +662,7 @@ def test_create_archive_with_option_true_calls_borg_without_corresponding_parame
     flexmock(module).should_receive('execute_command').with_args(
     flexmock(module).should_receive('execute_command').with_args(
         ('borg', 'create') + ARCHIVE_WITH_PATHS,
         ('borg', 'create') + ARCHIVE_WITH_PATHS,
         output_log_level=logging.INFO,
         output_log_level=logging.INFO,
+        output_file=None,
         error_on_warnings=False,
         error_on_warnings=False,
     )
     )
 
 
@@ -673,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,
+        output_file=None,
         error_on_warnings=False,
         error_on_warnings=False,
     )
     )
 
 
@@ -699,6 +717,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,
+        output_file=None,
         error_on_warnings=False,
         error_on_warnings=False,
     )
     )
 
 
@@ -725,6 +744,7 @@ def test_create_archive_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', 'create') + ARCHIVE_WITH_PATHS,
         ('borg1', 'create') + ARCHIVE_WITH_PATHS,
         output_log_level=logging.INFO,
         output_log_level=logging.INFO,
+        output_file=None,
         error_on_warnings=False,
         error_on_warnings=False,
     )
     )
 
 
@@ -751,6 +771,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,
+        output_file=None,
         error_on_warnings=False,
         error_on_warnings=False,
     )
     )
 
 
@@ -777,6 +798,7 @@ def test_create_archive_with_umask_calls_borg_with_umask_parameters():
     flexmock(module).should_receive('execute_command').with_args(
     flexmock(module).should_receive('execute_command').with_args(
         ('borg', 'create', '--umask', '740') + ARCHIVE_WITH_PATHS,
         ('borg', 'create', '--umask', '740') + ARCHIVE_WITH_PATHS,
         output_log_level=logging.INFO,
         output_log_level=logging.INFO,
+        output_file=None,
         error_on_warnings=False,
         error_on_warnings=False,
     )
     )
 
 
@@ -802,6 +824,7 @@ def test_create_archive_with_lock_wait_calls_borg_with_lock_wait_parameters():
     flexmock(module).should_receive('execute_command').with_args(
     flexmock(module).should_receive('execute_command').with_args(
         ('borg', 'create', '--lock-wait', '5') + ARCHIVE_WITH_PATHS,
         ('borg', 'create', '--lock-wait', '5') + ARCHIVE_WITH_PATHS,
         output_log_level=logging.INFO,
         output_log_level=logging.INFO,
+        output_file=None,
         error_on_warnings=False,
         error_on_warnings=False,
     )
     )
 
 
@@ -827,6 +850,7 @@ def test_create_archive_with_stats_calls_borg_with_stats_parameter_and_warning_o
     flexmock(module).should_receive('execute_command').with_args(
     flexmock(module).should_receive('execute_command').with_args(
         ('borg', 'create', '--stats') + ARCHIVE_WITH_PATHS,
         ('borg', 'create', '--stats') + ARCHIVE_WITH_PATHS,
         output_log_level=logging.WARNING,
         output_log_level=logging.WARNING,
+        output_file=None,
         error_on_warnings=False,
         error_on_warnings=False,
     )
     )
 
 
@@ -853,6 +877,7 @@ def test_create_archive_with_stats_and_log_info_calls_borg_with_stats_parameter_
     flexmock(module).should_receive('execute_command').with_args(
     flexmock(module).should_receive('execute_command').with_args(
         ('borg', 'create', '--info', '--stats') + ARCHIVE_WITH_PATHS,
         ('borg', 'create', '--info', '--stats') + ARCHIVE_WITH_PATHS,
         output_log_level=logging.INFO,
         output_log_level=logging.INFO,
+        output_file=None,
         error_on_warnings=False,
         error_on_warnings=False,
     )
     )
     insert_logging_mock(logging.INFO)
     insert_logging_mock(logging.INFO)
@@ -880,6 +905,7 @@ def test_create_archive_with_files_calls_borg_with_list_parameter_and_warning_ou
     flexmock(module).should_receive('execute_command').with_args(
     flexmock(module).should_receive('execute_command').with_args(
         ('borg', 'create', '--list', '--filter', 'AME-') + ARCHIVE_WITH_PATHS,
         ('borg', 'create', '--list', '--filter', 'AME-') + ARCHIVE_WITH_PATHS,
         output_log_level=logging.WARNING,
         output_log_level=logging.WARNING,
+        output_file=None,
         error_on_warnings=False,
         error_on_warnings=False,
     )
     )
 
 
@@ -906,6 +932,7 @@ def test_create_archive_with_files_and_log_info_calls_borg_with_list_parameter_a
     flexmock(module).should_receive('execute_command').with_args(
     flexmock(module).should_receive('execute_command').with_args(
         ('borg', 'create', '--list', '--filter', 'AME-', '--info') + ARCHIVE_WITH_PATHS,
         ('borg', 'create', '--list', '--filter', 'AME-', '--info') + ARCHIVE_WITH_PATHS,
         output_log_level=logging.INFO,
         output_log_level=logging.INFO,
+        output_file=None,
         error_on_warnings=False,
         error_on_warnings=False,
     )
     )
     insert_logging_mock(logging.INFO)
     insert_logging_mock(logging.INFO)
@@ -930,8 +957,11 @@ def test_create_archive_with_progress_and_log_info_calls_borg_with_progress_para
     flexmock(module).should_receive('_write_pattern_file').and_return(None)
     flexmock(module).should_receive('_write_pattern_file').and_return(None)
     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(
-        ('borg', 'create', '--info', '--progress') + ARCHIVE_WITH_PATHS, error_on_warnings=False
+    flexmock(module).should_receive('execute_command').with_args(
+        ('borg', 'create', '--info', '--progress') + ARCHIVE_WITH_PATHS,
+        output_log_level=logging.INFO,
+        output_file=module.DO_NOT_CAPTURE,
+        error_on_warnings=False,
     )
     )
     insert_logging_mock(logging.INFO)
     insert_logging_mock(logging.INFO)
 
 
@@ -955,8 +985,11 @@ def test_create_archive_with_progress_calls_borg_with_progress_parameter():
     flexmock(module).should_receive('_write_pattern_file').and_return(None)
     flexmock(module).should_receive('_write_pattern_file').and_return(None)
     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(
-        ('borg', 'create', '--progress') + ARCHIVE_WITH_PATHS, error_on_warnings=False
+    flexmock(module).should_receive('execute_command').with_args(
+        ('borg', 'create', '--progress') + ARCHIVE_WITH_PATHS,
+        output_log_level=logging.INFO,
+        output_file=module.DO_NOT_CAPTURE,
+        error_on_warnings=False,
     )
     )
 
 
     module.create_archive(
     module.create_archive(
@@ -982,6 +1015,7 @@ def test_create_archive_with_json_calls_borg_with_json_parameter():
     flexmock(module).should_receive('execute_command').with_args(
     flexmock(module).should_receive('execute_command').with_args(
         ('borg', 'create', '--json') + ARCHIVE_WITH_PATHS,
         ('borg', 'create', '--json') + ARCHIVE_WITH_PATHS,
         output_log_level=None,
         output_log_level=None,
+        output_file=None,
         error_on_warnings=False,
         error_on_warnings=False,
     ).and_return('[]')
     ).and_return('[]')
 
 
@@ -1010,6 +1044,7 @@ def test_create_archive_with_stats_and_json_calls_borg_without_stats_parameter()
     flexmock(module).should_receive('execute_command').with_args(
     flexmock(module).should_receive('execute_command').with_args(
         ('borg', 'create', '--json') + ARCHIVE_WITH_PATHS,
         ('borg', 'create', '--json') + ARCHIVE_WITH_PATHS,
         output_log_level=None,
         output_log_level=None,
+        output_file=None,
         error_on_warnings=False,
         error_on_warnings=False,
     ).and_return('[]')
     ).and_return('[]')
 
 
@@ -1039,6 +1074,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,
+        output_file=None,
         error_on_warnings=False,
         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'])
@@ -1065,6 +1101,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,
+        output_file=None,
         error_on_warnings=False,
         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([])
@@ -1091,6 +1128,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,
+        output_file=None,
         error_on_warnings=False,
         error_on_warnings=False,
     )
     )
 
 
@@ -1116,6 +1154,7 @@ def test_create_archive_with_archive_name_format_calls_borg_with_archive_name():
     flexmock(module).should_receive('execute_command').with_args(
     flexmock(module).should_receive('execute_command').with_args(
         ('borg', 'create', 'repo::ARCHIVE_NAME', 'foo', 'bar'),
         ('borg', 'create', 'repo::ARCHIVE_NAME', 'foo', 'bar'),
         output_log_level=logging.INFO,
         output_log_level=logging.INFO,
+        output_file=None,
         error_on_warnings=False,
         error_on_warnings=False,
     )
     )
 
 
@@ -1141,6 +1180,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,
+        output_file=None,
         error_on_warnings=False,
         error_on_warnings=False,
     )
     )
 
 
@@ -1166,6 +1206,7 @@ def test_create_archive_with_extra_borg_options_calls_borg_with_extra_options():
     flexmock(module).should_receive('execute_command').with_args(
     flexmock(module).should_receive('execute_command').with_args(
         ('borg', 'create', '--extra', '--options') + ARCHIVE_WITH_PATHS,
         ('borg', 'create', '--extra', '--options') + ARCHIVE_WITH_PATHS,
         output_log_level=logging.INFO,
         output_log_level=logging.INFO,
+        output_file=None,
         error_on_warnings=False,
         error_on_warnings=False,
     )
     )
 
 
@@ -1193,6 +1234,7 @@ def test_create_archive_with_stream_processes_calls_borg_with_processes():
         ('borg', 'create', '--read-special') + ARCHIVE_WITH_PATHS,
         ('borg', 'create', '--read-special') + ARCHIVE_WITH_PATHS,
         processes=processes,
         processes=processes,
         output_log_level=logging.INFO,
         output_log_level=logging.INFO,
+        output_file=None,
         error_on_warnings=False,
         error_on_warnings=False,
     )
     )
 
 

+ 2 - 1
tests/unit/borg/test_extract.py

@@ -221,8 +221,9 @@ def test_extract_archive_calls_borg_with_destination_path():
 
 
 def test_extract_archive_calls_borg_with_progress_parameter():
 def test_extract_archive_calls_borg_with_progress_parameter():
     flexmock(module.os.path).should_receive('abspath').and_return('repo')
     flexmock(module.os.path).should_receive('abspath').and_return('repo')
-    flexmock(module).should_receive('execute_command_without_capture').with_args(
+    flexmock(module).should_receive('execute_command').with_args(
         ('borg', 'extract', '--progress', 'repo::archive'),
         ('borg', 'extract', '--progress', 'repo::archive'),
+        output_file=module.DO_NOT_CAPTURE,
         working_directory=None,
         working_directory=None,
         error_on_warnings=True,
         error_on_warnings=True,
     ).once()
     ).once()

+ 4 - 5
tests/unit/borg/test_init.py

@@ -23,8 +23,8 @@ 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(
-        init_command, error_on_warnings=False
+    flexmock(module).should_receive('execute_command').with_args(
+        init_command, output_file=module.DO_NOT_CAPTURE, error_on_warnings=False
     ).once()
     ).once()
 
 
 
 
@@ -37,7 +37,7 @@ def test_initialize_repository_calls_borg_with_parameters():
 
 
 def test_initialize_repository_raises_for_borg_init_error():
 def test_initialize_repository_raises_for_borg_init_error():
     insert_info_command_not_found_mock()
     insert_info_command_not_found_mock()
-    flexmock(module).should_receive('execute_command_without_capture').and_raise(
+    flexmock(module).should_receive('execute_command').and_raise(
         module.subprocess.CalledProcessError(2, 'borg init')
         module.subprocess.CalledProcessError(2, 'borg init')
     )
     )
 
 
@@ -48,8 +48,7 @@ def test_initialize_repository_raises_for_borg_init_error():
 
 
 
 
 def test_initialize_repository_skips_initialization_when_repository_already_exists():
 def test_initialize_repository_skips_initialization_when_repository_already_exists():
-    insert_info_command_found_mock()
-    flexmock(module).should_receive('execute_command_without_capture').never()
+    flexmock(module).should_receive('execute_command').once()
 
 
     module.initialize_repository(repository='repo', storage_config={}, encryption_mode='repokey')
     module.initialize_repository(repository='repo', storage_config={}, encryption_mode='repokey')
 
 

+ 4 - 2
tests/unit/borg/test_mount.py

@@ -117,8 +117,10 @@ 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(
-        ('borg', 'mount', '--foreground', 'repo::archive', '/mnt'), error_on_warnings=False
+    flexmock(module).should_receive('execute_command').with_args(
+        ('borg', 'mount', '--foreground', 'repo::archive', '/mnt'),
+        output_file=module.DO_NOT_CAPTURE,
+        error_on_warnings=False,
     ).once()
     ).once()
 
 
     module.mount_archive(
     module.mount_archive(

+ 0 - 27
tests/unit/test_execute.py

@@ -225,30 +225,3 @@ def test_execute_command_captures_output_with_working_directory():
     )
     )
 
 
     assert output == expected_output
     assert output == expected_output
-
-
-def test_execute_command_without_capture_does_not_raise_on_success():
-    flexmock(module.subprocess).should_receive('check_call').and_raise(
-        module.subprocess.CalledProcessError(0, 'borg init')
-    )
-
-    module.execute_command_without_capture(('borg', 'init'))
-
-
-def test_execute_command_without_capture_does_not_raise_on_warning():
-    flexmock(module).should_receive('exit_code_indicates_error').and_return(False)
-    flexmock(module.subprocess).should_receive('check_call').and_raise(
-        module.subprocess.CalledProcessError(1, 'borg init')
-    )
-
-    module.execute_command_without_capture(('borg', 'init'))
-
-
-def test_execute_command_without_capture_raises_on_error():
-    flexmock(module).should_receive('exit_code_indicates_error').and_return(True)
-    flexmock(module.subprocess).should_receive('check_call').and_raise(
-        module.subprocess.CalledProcessError(2, 'borg init')
-    )
-
-    with pytest.raises(module.subprocess.CalledProcessError):
-        module.execute_command_without_capture(('borg', 'init'))