You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 

189 lines
6.3 KiB

  1. # -*- coding: utf-8 -*-
  2. # MinIO Python Library for Amazon S3 Compatible Cloud Storage,
  3. # (C) 2018 MinIO, Inc.
  4. #
  5. # Licensed under the Apache License, Version 2.0 (the "License");
  6. # you may not use this file except in compliance with the License.
  7. # You may obtain a copy of the License at
  8. #
  9. # http://www.apache.org/licenses/LICENSE-2.0
  10. #
  11. # Unless required by applicable law or agreed to in writing, software
  12. # distributed under the License is distributed on an "AS IS" BASIS,
  13. # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14. # See the License for the specific language governing permissions and
  15. # limitations under the License.
  16. """
  17. This module implements a progress printer while communicating with MinIO server
  18. :copyright: (c) 2018 by MinIO, Inc.
  19. :license: Apache 2.0, see LICENSE for more details.
  20. """
  21. import sys
  22. import time
  23. from queue import Empty, Queue
  24. from threading import Thread
  25. _BAR_SIZE = 20
  26. _KILOBYTE = 1024
  27. _FINISHED_BAR = '#'
  28. _REMAINING_BAR = '-'
  29. _UNKNOWN_SIZE = '?'
  30. _STR_MEGABYTE = ' MB'
  31. _HOURS_OF_ELAPSED = '%d:%02d:%02d'
  32. _MINUTES_OF_ELAPSED = '%02d:%02d'
  33. _RATE_FORMAT = '%5.2f'
  34. _PERCENTAGE_FORMAT = '%3d%%'
  35. _HUMANINZED_FORMAT = '%0.2f'
  36. _DISPLAY_FORMAT = '|%s| %s/%s %s [elapsed: %s left: %s, %s MB/sec]'
  37. _REFRESH_CHAR = '\r'
  38. class Progress(Thread):
  39. """
  40. Constructs a :class:`Progress` object.
  41. :param interval: Sets the time interval to be displayed on the screen.
  42. :param stdout: Sets the standard output
  43. :return: :class:`Progress` object
  44. """
  45. def __init__(self, interval=1, stdout=sys.stdout):
  46. Thread.__init__(self)
  47. self.daemon = True
  48. self.total_length = 0
  49. self.interval = interval
  50. self.object_name = None
  51. self.last_printed_len = 0
  52. self.current_size = 0
  53. self.display_queue = Queue()
  54. self.initial_time = time.time()
  55. self.stdout = stdout
  56. self.start()
  57. def set_meta(self, total_length, object_name):
  58. """
  59. Metadata settings for the object. This method called before uploading
  60. object
  61. :param total_length: Total length of object.
  62. :param object_name: Object name to be showed.
  63. """
  64. self.total_length = total_length
  65. self.object_name = object_name
  66. self.prefix = self.object_name + ': ' if self.object_name else ''
  67. def run(self):
  68. displayed_time = 0
  69. while True:
  70. try:
  71. # display every interval secs
  72. task = self.display_queue.get(timeout=self.interval)
  73. except Empty:
  74. elapsed_time = time.time() - self.initial_time
  75. if elapsed_time > displayed_time:
  76. displayed_time = elapsed_time
  77. self.print_status(current_size=self.current_size,
  78. total_length=self.total_length,
  79. displayed_time=displayed_time,
  80. prefix=self.prefix)
  81. if self.current_size == self.total_length:
  82. # once we have done uploading everything return
  83. self.done_progress()
  84. return
  85. continue
  86. current_size, total_length = task
  87. displayed_time = time.time() - self.initial_time
  88. self.print_status(current_size=current_size,
  89. total_length=total_length,
  90. displayed_time=displayed_time,
  91. prefix=self.prefix)
  92. self.display_queue.task_done()
  93. if current_size == total_length:
  94. # once we have done uploading everything return
  95. self.done_progress()
  96. return
  97. def update(self, size):
  98. """
  99. Update object size to be showed. This method called while uploading
  100. :param size: Object size to be showed. The object size should be in
  101. bytes.
  102. """
  103. if not isinstance(size, int):
  104. raise ValueError('{} type can not be displayed. '
  105. 'Please change it to Int.'.format(type(size)))
  106. self.current_size += size
  107. self.display_queue.put((self.current_size, self.total_length))
  108. def done_progress(self):
  109. self.total_length = 0
  110. self.object_name = None
  111. self.last_printed_len = 0
  112. self.current_size = 0
  113. def print_status(self, current_size, total_length, displayed_time, prefix):
  114. if total_length == 0:
  115. return
  116. formatted_str = prefix + format_string(
  117. current_size, total_length, displayed_time)
  118. self.stdout.write(_REFRESH_CHAR + formatted_str + ' ' *
  119. max(self.last_printed_len - len(formatted_str), 0))
  120. self.stdout.flush()
  121. self.last_printed_len = len(formatted_str)
  122. def seconds_to_time(seconds):
  123. """
  124. Consistent time format to be displayed on the elapsed time in screen.
  125. :param seconds: seconds
  126. """
  127. minutes, seconds = divmod(int(seconds), 60)
  128. hours, m = divmod(minutes, 60)
  129. if hours:
  130. return _HOURS_OF_ELAPSED % (hours, m, seconds)
  131. else:
  132. return _MINUTES_OF_ELAPSED % (m, seconds)
  133. def format_string(current_size, total_length, elapsed_time):
  134. """
  135. Consistent format to be displayed on the screen.
  136. :param current_size: Number of finished object size
  137. :param total_length: Total object size
  138. :param elapsed_time: number of seconds passed since start
  139. """
  140. n_to_mb = current_size / _KILOBYTE / _KILOBYTE
  141. elapsed_str = seconds_to_time(elapsed_time)
  142. rate = _RATE_FORMAT % (
  143. n_to_mb / elapsed_time) if elapsed_time else _UNKNOWN_SIZE
  144. frac = float(current_size) / total_length
  145. bar_length = int(frac * _BAR_SIZE)
  146. bar = (_FINISHED_BAR * bar_length +
  147. _REMAINING_BAR * (_BAR_SIZE - bar_length))
  148. percentage = _PERCENTAGE_FORMAT % (frac * 100)
  149. left_str = (
  150. seconds_to_time(
  151. elapsed_time / current_size * (total_length - current_size))
  152. if current_size else _UNKNOWN_SIZE)
  153. humanized_total = _HUMANINZED_FORMAT % (
  154. total_length / _KILOBYTE / _KILOBYTE) + _STR_MEGABYTE
  155. humanized_n = _HUMANINZED_FORMAT % n_to_mb + _STR_MEGABYTE
  156. return _DISPLAY_FORMAT % (bar, humanized_n, humanized_total, percentage,
  157. elapsed_str, left_str, rate)