2121
2222from awscli .compat import ensure_text_type , queue
2323from awscli .customizations .s3 .subscribers import OnDoneFilteredSubscriber
24- from awscli .customizations .s3 .utils import WarningResult , human_readable_size
24+ from awscli .customizations .s3 .utils import (
25+ WarningResult ,
26+ human_readable_size ,
27+ )
2528from awscli .customizations .utils import uni_print
2629
2730LOGGER = logging .getLogger (__name__ )
@@ -54,6 +57,7 @@ def _create_new_result_cls(name, extra_fields=None, base_cls=BaseResult):
5457FailureResult = _create_new_result_cls ('FailureResult' , ['exception' ])
5558
5659DryRunResult = _create_new_result_cls ('DryRunResult' )
60+ SkipFileResult = _create_new_result_cls ('SkipFileResult' )
5761
5862ErrorResult = namedtuple ('ErrorResult' , ['exception' ])
5963
@@ -123,6 +127,17 @@ def _on_failure(self, future, e):
123127 if isinstance (e , FatalError ):
124128 error_result_cls = ErrorResult
125129 self ._result_queue .put (error_result_cls (exception = e ))
130+ elif self ._is_precondition_failed (e ):
131+ LOGGER .debug (
132+ f"warning: Skipping file { self ._src } as it already exists on { self ._dest } "
133+ )
134+ self ._result_queue .put (
135+ SkipFileResult (
136+ transfer_type = self ._transfer_type ,
137+ src = self ._src ,
138+ dest = self ._dest ,
139+ )
140+ )
126141 else :
127142 self ._result_queue .put (
128143 FailureResult (
@@ -133,6 +148,13 @@ def _on_failure(self, future, e):
133148 )
134149 )
135150
151+ def _is_precondition_failed (self , exception ):
152+ return (
153+ hasattr (exception , 'response' )
154+ and exception .response .get ('Error' , {}).get ('Code' )
155+ == 'PreconditionFailed'
156+ )
157+
136158
137159class BaseResultHandler :
138160 """Base handler class to be called in the ResultProcessor"""
@@ -150,6 +172,7 @@ def __init__(self):
150172 self .files_transferred = 0
151173 self .files_failed = 0
152174 self .files_warned = 0
175+ self .files_skipped = 0
153176 self .errors = 0
154177 self .expected_bytes_transferred = 0
155178 self .expected_files_transferred = 0
@@ -167,6 +190,7 @@ def __init__(self):
167190 SuccessResult : self ._record_success_result ,
168191 FailureResult : self ._record_failure_result ,
169192 WarningResult : self ._record_warning_result ,
193+ SkipFileResult : self ._record_skipped_file_result ,
170194 ErrorResult : self ._record_error_result ,
171195 CtrlCResult : self ._record_error_result ,
172196 FinalTotalSubmissionsResult : self ._record_final_expected_files ,
@@ -282,6 +306,9 @@ def _record_failure_result(self, result, **kwargs):
282306 self .files_failed += 1
283307 self .files_transferred += 1
284308
309+ def _record_skipped_file_result (self , result , ** kwargs ):
310+ self .files_skipped += 1
311+
285312 def _record_warning_result (self , ** kwargs ):
286313 self .files_warned += 1
287314
@@ -362,6 +389,7 @@ def __init__(
362389 SuccessResult : self ._print_success ,
363390 FailureResult : self ._print_failure ,
364391 WarningResult : self ._print_warning ,
392+ SkipFileResult : self ._print_skip ,
365393 ErrorResult : self ._print_error ,
366394 CtrlCResult : self ._print_ctrl_c ,
367395 DryRunResult : self ._print_dry_run ,
@@ -380,6 +408,10 @@ def _print_noop(self, **kwargs):
380408 # If the result does not have a handler, then do nothing with it.
381409 pass
382410
411+ def _print_skip (self , ** kwargs ):
412+ # Don't reset progress length since this result printer doesn't print a newline
413+ self ._redisplay_progress (reset_progress_length = False )
414+
383415 def _print_dry_run (self , result , ** kwargs ):
384416 statement = self .DRY_RUN_FORMAT .format (
385417 transfer_type = result .transfer_type ,
@@ -432,16 +464,19 @@ def _get_transfer_location(self, result):
432464 src = result .src , dest = result .dest
433465 )
434466
435- def _redisplay_progress (self ):
467+ def _redisplay_progress (self , reset_progress_length = True ):
436468 # Reset to zero because done statements are printed with new lines
437469 # meaning there are no carriage returns to take into account when
438470 # printing the next line.
439- self ._progress_length = 0
471+ if reset_progress_length :
472+ self ._progress_length = 0
440473 self ._add_progress_if_needed ()
441474
442475 def _add_progress_if_needed (self ):
443476 if self ._has_remaining_progress ():
444477 self ._print_progress ()
478+ else :
479+ self ._clear_progress_if_no_more_expected_transfers (ending_char = '\r ' )
445480
446481 def _should_print_progress_now (self ):
447482 """Check to see if should print progres based on frequency.
@@ -467,7 +502,7 @@ def _print_progress(self, **kwargs):
467502 remaining_files = self ._get_expected_total (
468503 str (
469504 self ._result_recorder .expected_files_transferred
470- - self ._result_recorder .files_transferred
505+ - ( self ._result_recorder .files_transferred + self . _result_recorder . files_skipped )
471506 )
472507 )
473508
@@ -535,7 +570,7 @@ def _adjust_statement_padding(self, print_statement, ending_char='\n'):
535570 def _has_remaining_progress (self ):
536571 if not self ._result_recorder .expected_totals_are_final ():
537572 return True
538- actual = self ._result_recorder .files_transferred
573+ actual = self ._result_recorder .files_transferred + self . _result_recorder . files_skipped
539574 expected = self ._result_recorder .expected_files_transferred
540575 return actual != expected
541576
@@ -545,9 +580,9 @@ def _print_to_out_file(self, statement):
545580 def _print_to_error_file (self , statement ):
546581 uni_print (statement , self ._error_file )
547582
548- def _clear_progress_if_no_more_expected_transfers (self , ** kwargs ):
583+ def _clear_progress_if_no_more_expected_transfers (self , ending_char = ' \n ' , ** kwargs ):
549584 if self ._progress_length and not self ._has_remaining_progress ():
550- uni_print (self ._adjust_statement_padding ('' ), self ._out_file )
585+ uni_print (self ._adjust_statement_padding ('' , ending_char = ending_char ), self ._out_file )
551586
552587
553588class NoProgressResultPrinter (ResultPrinter ):
0 commit comments