Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(498)

Side by Side Diff: gslib/progress_callback.py

Issue 698893003: Update checked in version of gsutil to version 4.6 (Closed) Base URL: http://dart.googlecode.com/svn/third_party/gsutil/
Patch Set: Created 6 years, 1 month ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « gslib/plurality_checkable_iterator.py ('k') | gslib/project_id.py » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Property Changes:
Added: svn:eol-style
+ LF
OLDNEW
(Empty)
1 # -*- coding: utf-8 -*-
2 # Copyright 2014 Google Inc. All Rights Reserved.
3 #
4 # Licensed under the Apache License, Version 2.0 (the "License");
5 # you may not use this file except in compliance with the License.
6 # You may obtain a copy of the License at
7 #
8 # http://www.apache.org/licenses/LICENSE-2.0
9 #
10 # Unless required by applicable law or agreed to in writing, software
11 # distributed under the License is distributed on an "AS IS" BASIS,
12 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 # See the License for the specific language governing permissions and
14 # limitations under the License.
15 """Helper functions for progress callbacks."""
16
17 import logging
18 import sys
19
20 from gslib.util import MakeHumanReadable
21 from gslib.util import UTF8
22
23 # Default upper and lower bounds for progress callback frequency.
24 _START_BYTES_PER_CALLBACK = 1024*64
25 _MAX_BYTES_PER_CALLBACK = 1024*1024*100
26
27 # Max width of URL to display in progress indicator. Wide enough to allow
28 # 15 chars for x/y display on an 80 char wide terminal.
29 MAX_PROGRESS_INDICATOR_COLUMNS = 65
30
31
32 class ProgressCallbackWithBackoff(object):
33 """Makes progress callbacks with exponential backoff to a maximum value.
34
35 This prevents excessive log message output.
36 """
37
38 def __init__(self, total_size, callback_func,
39 start_bytes_per_callback=_START_BYTES_PER_CALLBACK,
40 max_bytes_per_callback=_MAX_BYTES_PER_CALLBACK,
41 calls_per_exponent=10):
42 """Initializes the callback with backoff.
43
44 Args:
45 total_size: Total bytes to process.
46 callback_func: Func of (int: processed_so_far, int: total_bytes)
47 used to make callbacks.
48 start_bytes_per_callback: Lower bound of bytes per callback.
49 max_bytes_per_callback: Upper bound of bytes per callback.
50 calls_per_exponent: Number of calls to make before reducing rate.
51 """
52 self._bytes_per_callback = start_bytes_per_callback
53 self._callback_func = callback_func
54 self._calls_per_exponent = calls_per_exponent
55 self._max_bytes_per_callback = max_bytes_per_callback
56 self._total_size = total_size
57
58 self._bytes_processed_since_callback = 0
59 self._callbacks_made = 0
60 self._total_bytes_processed = 0
61
62 def Progress(self, bytes_processed):
63 """Tracks byte processing progress, making a callback if necessary."""
64 self._bytes_processed_since_callback += bytes_processed
65 # TODO: We check if >= total_size and truncate because JSON uploads count
66 # metadata during their send progress.
67 if (self._bytes_processed_since_callback > self._bytes_per_callback or
68 (self._total_bytes_processed + self._bytes_processed_since_callback >=
69 self._total_size)):
70 self._total_bytes_processed += self._bytes_processed_since_callback
71 self._callback_func(min(self._total_bytes_processed, self._total_size),
72 self._total_size)
73 self._bytes_processed_since_callback = 0
74 self._callbacks_made += 1
75
76 if self._callbacks_made > self._calls_per_exponent:
77 self._bytes_per_callback = min(self._bytes_per_callback * 2,
78 self._max_bytes_per_callback)
79 self._callbacks_made = 0
80
81
82 def ConstructAnnounceText(operation_name, url_string):
83 """Constructs announce text for ongoing operations on url_to_display.
84
85 This truncates the text to a maximum of MAX_PROGRESS_INDICATOR_COLUMNS.
86 Thus, concurrent output (gsutil -m) leaves progress counters in a readable
87 (fixed) position.
88
89 Args:
90 operation_name: String describing the operation, i.e.
91 'Uploading' or 'Hashing'.
92 url_string: String describing the file/object being processed.
93
94 Returns:
95 Formatted announce text for outputting operation progress.
96 """
97 # Operation name occupies 11 characters (enough for 'Downloading'), plus a
98 # space. The rest is used for url_to_display. If a longer operation name is
99 # used, it will be truncated. We can revisit this size if we need to support
100 # a longer operation, but want to make sure the terminal output is meaningful.
101 justified_op_string = operation_name[:11].ljust(12)
102 start_len = len(justified_op_string)
103 end_len = len(': ')
104 if (start_len + len(url_string) + end_len >
105 MAX_PROGRESS_INDICATOR_COLUMNS):
106 ellipsis_len = len('...')
107 url_string = '...%s' % url_string[
108 -(MAX_PROGRESS_INDICATOR_COLUMNS - start_len - end_len - ellipsis_len):]
109 base_announce_text = '%s%s:' % (justified_op_string, url_string)
110 format_str = '{0:%ds}' % MAX_PROGRESS_INDICATOR_COLUMNS
111 return format_str.format(base_announce_text.encode(UTF8))
112
113
114 class FileProgressCallbackHandler(object):
115 """Outputs progress info for large operations like file copy or hash."""
116
117 def __init__(self, announce_text, logger):
118 """Initializes the callback handler.
119
120 Args:
121 announce_text: String describing the operation.
122 logger: For outputting log messages.
123 """
124 self._announce_text = announce_text
125 self._logger = logger
126
127 # Function signature is in boto callback format, which cannot be changed.
128 def call(self, # pylint: disable=invalid-name
129 total_bytes_processed,
130 total_size):
131 """Prints an overwriting line to stderr describing the operation progress.
132
133 Args:
134 total_bytes_processed: Number of bytes processed so far.
135 total_size: Total size of the ongoing operation.
136 """
137 if not self._logger.isEnabledFor(logging.INFO):
138 return
139
140 # Handle streaming case specially where we don't know the total size:
141 if total_size:
142 total_size_string = '/%s' % MakeHumanReadable(total_size)
143 else:
144 total_size_string = ''
145 # Use sys.stderr.write instead of self.logger.info so progress messages
146 # output on a single continuously overwriting line.
147 # TODO: Make this work with logging.Logger.
148 sys.stderr.write('%s%s%s \r' % (
149 self._announce_text,
150 MakeHumanReadable(total_bytes_processed),
151 total_size_string))
152 if total_size and total_bytes_processed == total_size:
153 sys.stderr.write('\n')
OLDNEW
« no previous file with comments | « gslib/plurality_checkable_iterator.py ('k') | gslib/project_id.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698