From 83ee48106819d37dac5968906d53ebd52d228145 Mon Sep 17 00:00:00 2001 From: Fredrick Brennan Date: Sat, 18 Mar 2023 18:46:37 -0400 Subject: [PATCH 1/2] Make progress bar use STDERR. --- S3/Progress.py | 38 +++++++++++++++++++------------------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/S3/Progress.py b/S3/Progress.py index a348a569c..a78883b3c 100644 --- a/S3/Progress.py +++ b/S3/Progress.py @@ -14,11 +14,11 @@ import S3.Utils class Progress(object): - _stdout = sys.stdout + _stderr = sys.stderr _last_display = 0 def __init__(self, labels, total_size): - self._stdout = sys.stdout + self._stderr = sys.stderr self.new_file(labels, total_size) def new_file(self, labels, total_size): @@ -52,8 +52,8 @@ def done(self, message): self.display(done_message = message) def output_labels(self): - self._stdout.write(u"%(action)s: '%(source)s' -> '%(destination)s' %(extra)s\n" % self.labels) - self._stdout.flush() + self._stderr.write(u"%(action)s: '%(source)s' -> '%(destination)s' %(extra)s\n" % self.labels) + self._stderr.flush() def _display_needed(self): # We only need to update the display every so often. @@ -79,9 +79,9 @@ def display(self, new_file = False, done_message = None): timedelta = self.time_current - self.time_start sec_elapsed = timedelta.days * 86400 + timedelta.seconds + float(timedelta.microseconds) / 1000000.0 print_speed = S3.Utils.formatSize((self.current_position - self.initial_position) / sec_elapsed, True, True) - self._stdout.write("100%% %s%s in %.2fs (%.2f %sB/s)\n" % + self._stderr.write("100%% %s%s in %.2fs (%.2f %sB/s)\n" % (print_size[0], print_size[1], sec_elapsed, print_speed[0], print_speed[1])) - self._stdout.flush() + self._stderr.flush() return rel_position = (self.current_position * 100) // self.total_size @@ -89,8 +89,8 @@ def display(self, new_file = False, done_message = None): # Move by increments of 5. # NOTE: to check: Looks like to not do what is looks like to be designed to do self.last_milestone = (rel_position // 5) * 5 - self._stdout.write("%d%% ", self.last_milestone) - self._stdout.flush() + self._stderr.write("%d%% ", self.last_milestone) + self._stderr.flush() return class ProgressANSI(Progress): @@ -110,8 +110,8 @@ def display(self, new_file = False, done_message = None): """ if new_file: self.output_labels() - self._stdout.write(self.ANSI_save_cursor_pos) - self._stdout.flush() + self._stderr.write(self.ANSI_save_cursor_pos) + self._stderr.flush() return # Only display progress every so often @@ -124,9 +124,9 @@ def display(self, new_file = False, done_message = None): print_speed = S3.Utils.formatSize((self.current_position - self.initial_position) / sec_elapsed, True, True) else: print_speed = (0, "") - self._stdout.write(self.ANSI_restore_cursor_pos) - self._stdout.write(self.ANSI_erase_to_eol) - self._stdout.write("%(current)s of %(total)s %(percent)3d%% in %(elapsed)ds %(speed).2f %(speed_coeff)sB/s" % { + self._stderr.write(self.ANSI_restore_cursor_pos) + self._stderr.write(self.ANSI_erase_to_eol) + self._stderr.write("%(current)s of %(total)s %(percent)3d%% in %(elapsed)ds %(speed).2f %(speed_coeff)sB/s" % { "current" : str(self.current_position).rjust(len(str(self.total_size))), "total" : self.total_size, "percent" : self.total_size and ((self.current_position * 100) // self.total_size) or 0, @@ -136,9 +136,9 @@ def display(self, new_file = False, done_message = None): }) if done_message: - self._stdout.write(" %s\n" % done_message) + self._stderr.write(" %s\n" % done_message) - self._stdout.flush() + self._stderr.flush() class ProgressCR(Progress): ## Uses CR char (Carriage Return) just like other progress bars do. @@ -162,7 +162,7 @@ def display(self, new_file = False, done_message = None): print_speed = S3.Utils.formatSize((self.current_position - self.initial_position) / sec_elapsed, True, True) else: print_speed = (0, "") - self._stdout.write(self.CR_char) + self._stderr.write(self.CR_char) output = " %(current)s of %(total)s %(percent)3d%% in %(elapsed)4ds %(speed)7.2f %(speed_coeff)sB/s" % { "current" : str(self.current_position).rjust(len(str(self.total_size))), "total" : self.total_size, @@ -171,11 +171,11 @@ def display(self, new_file = False, done_message = None): "speed" : print_speed[0], "speed_coeff" : print_speed[1] } - self._stdout.write(output) + self._stderr.write(output) if done_message: - self._stdout.write(" %s\n" % done_message) + self._stderr.write(" %s\n" % done_message) - self._stdout.flush() + self._stderr.flush() class StatsInfo(object): """Holding info for stats totals""" From 464a63cb752f5073daf55192c39d33d7705776a4 Mon Sep 17 00:00:00 2001 From: Fredrick Brennan Date: Sat, 18 Mar 2023 18:47:23 -0400 Subject: [PATCH 2/2] Add option: --stderr-output (-2) --- s3cmd | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/s3cmd b/s3cmd index 9d56b1f76..671251b89 100755 --- a/s3cmd +++ b/s3cmd @@ -23,6 +23,8 @@ from __future__ import absolute_import, print_function, division import sys +output = lambda s: (sys.stdout.write(s + "\n"), sys.stdout.flush()) # to be reset in main() + if sys.version_info < (2, 6): sys.stderr.write(u"ERROR: Python 2.6 or higher required, sorry.\n") # 72 == EX_OSFILE @@ -84,10 +86,6 @@ if not PY3: pass -def output(message): - sys.stdout.write(message + "\n") - sys.stdout.flush() - def check_args_type(args, type, verbose_type): """NOTE: This function looks like to not be used.""" for arg in args: @@ -3109,6 +3107,7 @@ def main(): optparser.add_option( "--max-retries", dest="max_retries", action="store", help="Maximum number of times to retry a failed request before giving up. Default is 5", metavar="NUM") optparser.add_option( "--content-disposition", dest="content_disposition", action="store", help="Provide a Content-Disposition for signed URLs, e.g., \"inline; filename=myvideo.mp4\"") optparser.add_option( "--content-type", dest="content_type", action="store", help="Provide a Content-Type for signed URLs, e.g., \"video/mp4\"") + optparser.add_option("-2", "--stderr-output", dest="stderr_output", action="store", help="Write all output to STDERR rather than STDOUT") optparser.set_usage(optparser.usage + " COMMAND [parameters]") optparser.set_description('S3cmd is a tool for managing objects in '+ @@ -3126,6 +3125,12 @@ def main(): format='%(levelname)s: %(message)s', stream = sys.stderr) + def output(message): + pipe = sys.stderr if args.stderr_output else sys.stdout + pipe.write(message + "\n") + pipe.flush() + + if options.show_version: output(u"s3cmd version %s" % PkgInfo.version) sys.exit(EX_OK) @@ -3135,6 +3140,7 @@ def main(): try: f = open("/dev/null", "w") sys.stdout = f + sys.stderr = f except IOError: warning(u"Unable to open /dev/null: --quiet disabled.")