def format(self, width=None, elapsed=None): """ Args: width (int): Width in columns to make progress bar elapsed(float): Time since started. Automatically determined if :py:data:`None` Returns: :py:class:`str`: Formatted progress bar or counter Format progress bar or counter """ width = width or self.manager.width total = self.total iterations = float(abs(self.count - self.start_count)) fields = self.fields.copy() fields.update(self._fields) # Warn on reserved fields reserved_fields = set(fields) & RESERVED_FIELDS | { match.group() for match in (RE_SUBCOUNTER_FIELDS.match(key) for key in fields) if match } if reserved_fields: warn_best_level('Ignoring reserved fields specified as user-defined fields: %s' % ', '.join(reserved_fields), EnlightenWarning) force_float = isinstance(self.count, float) or isinstance(total, float) fields['count'] = Float(self.count) if force_float else self.count fields['desc'] = self.desc or u'' fields['total'] = Float(total) if force_float and total is not None else total fields['unit'] = self.unit or u'' fields['desc_pad'] = u' ' if self.desc else u'' fields['unit_pad'] = u' ' if self.unit else u'' # Get elapsed time if elapsed is None: elapsed = self.elapsed fields['elapsed'] = format_time(elapsed) # Get rate. Elapsed could be 0 if counter was not updated and has a zero total. rate = Float(iterations / elapsed) if elapsed else Float(0.0) fields['rate'] = rate fields['interval'] = rate ** -1 if rate else rate # Only process bar if total was given and n doesn't exceed total if total is not None and self.count <= total: return self._format_bar(fields, iterations, width, elapsed, force_float) # Otherwise return a counter return self._format_counter(fields, width, elapsed, force_float)
def _get_subcounters(self, elapsed, bar_fields=True): """ Args: elapsed(float): Time since started. bar_fields(bool): When False, only set fields for basic counter Returns: :py:class:`tuple`: list of subcounters and dictionary of additional fields Each subcounter in the list will be in a tuple of (subcounter, percentage) Fields in the dictionary are addressed in the Format documentation of this class When `bar_fields` is False, only subcounter count, interval, and rate fields are set. percentage will be set to 0.0 """ fields = {} subcounters = [] for num, subcounter in enumerate(self._subcounters, 1): fields['count_%d' % num] = subcounter.count if self.total and bar_fields: subPercentage = subcounter.count / float(self.total) else: subPercentage = 0.0 if bar_fields: fields['percentage_%d' % num] = subPercentage * 100 # Save in tuple: count, percentage subcounters.append((subcounter, subPercentage)) if subcounter.all_fields: interations = float(abs(subcounter.count - subcounter.start_count)) if elapsed: # Use float to force to float in Python 2 rate = fields['rate_%d' % num] = interations / elapsed else: rate = fields['rate_%d' % num] = 0.0 fields['interval_%d' % num] = rate ** -1 if rate else 0.0 if not bar_fields: continue if self.total == 0: fields['eta_%d' % num] = u'00:00' elif rate: fields['eta_%d' % num] = format_time((self.total - interations) / rate) else: fields['eta_%d' % num] = u'?' return subcounters, fields
def format(self, width=None, elapsed=None): """ Args: width (int): Width in columns to make progress bar elapsed(float): Time since started. Automatically determined if :py:data:`None` Returns: :py:class:`str`: Formatted status bar Format status bar """ width = width or self.manager.width justify = self.justify # If static message was given, just return it if self._static is not None: rtn = self._static # If there is no format, return empty elif self.status_format is None: rtn = '' # Generate from format else: fields = self.fields.copy() fields.update(self._fields) # Warn on reserved fields reserved_fields = (set(fields) & STATUS_FIELDS) if reserved_fields: warn_best_level( 'Ignoring reserved fields specified as user-defined fields: %s' % ', '.join(reserved_fields), EnlightenWarning) elapsed = elapsed if elapsed is not None else self.elapsed fields['elapsed'] = format_time(elapsed) fields['fill'] = u'{0}' # Format try: if FORMAT_MAP_SUPPORT: rtn = self.status_format.format_map(fields) else: # pragma: no cover rtn = self.status_format.format(**fields) except KeyError as e: raise_from_none( ValueError('%r specified in format, but not provided' % e.args[0])) rtn = self._fill_text(rtn, width) return self._colorize(justify(rtn, width=width, fillchar=self.fill))
def format(self, width=None, elapsed=None): """ Args: width (int): Width in columns to make progress bar elapsed(float): Time since started. Automatically determined if :py:data:`None` Returns: :py:class:`str`: Formatted status bar Format status bar """ width = width or self.manager.width justify = self.justify # If static message was given, just return it if self._static is not None: rtn = self._static # If there is no format, return empty elif self.status_format is None: rtn = '' # Generate from format else: fields = self.fields.copy() fields.update(self._fields) elapsed = elapsed if elapsed is not None else self.elapsed fields['elapsed'] = format_time(elapsed) fields['fill'] = u'{0}' # Format try: rtn = self.status_format.format(**fields) except KeyError as e: raise ValueError('%r specified in format, but not provided' % e.args[0]) rtn = self._fill_text(rtn, width) return self._colorize(justify(rtn, width=width, fillchar=self.fill))
def _format_bar(self, fields, iterations, width, elapsed, force_float): """ Args: fields (dict): Initial set of formatting fields iterations (float): Absolute value of count change from start width (int): Width in columns to make progress bar elapsed(float): Time since started Returns: :py:class:`str`: Formatted progress bar Format progress bar """ fields['bar'] = u'{0}' fields['len_total'] = len(str(self.total)) # Get percentage if self.total == 0: # If total is 0, force to 100 percent percentage = 1 fields['eta'] = u'00:00' else: # Use float to force to float in Python 2 percentage = self.count / float(self.total) rate = fields['rate'] # Get eta if rate: # Use iterations so a counter running backwards is accurate fields['eta'] = format_time((self.total - iterations) / rate) else: fields['eta'] = u'?' fields['percentage'] = percentage * 100 # Have to go through subcounters here so the fields are available subcounters = self._get_subcounters(elapsed, fields, force_float=force_float) # Partially format try: if FORMAT_MAP_SUPPORT: rtn = self.bar_format.format_map(fields) else: # pragma: no cover rtn = self.bar_format.format(**fields) except KeyError as e: raise_from_none(ValueError(self._get_format_error(e.args[0]))) # Determine bar width if self.offset is None: barWidth = width - self.manager.term.length(rtn) + 3 # 3 is for the bar placeholder else: # Offset was explicitly given barWidth = width - len(rtn) + self.offset + 3 # 3 is for the bar placeholder complete = barWidth * percentage barLen = int(complete) barText = u'' if subcounters: block_count = [int(barWidth * fields['percentage_0'] / 100)] partial_len = (barLen - 1) if fields['count_0'] else barLen remaining = [] # Get full blocks for subcounters and preserve remainders for idx, entry in enumerate(subcounters, 1): remainder, count = math.modf(barWidth * entry[1]) block_count.append(int(count)) remaining.append((remainder, idx)) # Until blocks are accounted for, add full blocks for highest remainders remaining.sort() while sum(block_count) < partial_len and remaining: block_count[remaining.pop()[1]] += 1 # Format partial bars for idx, subLen in reversed(list(enumerate(block_count))): if idx: subcounter = subcounters[idx - 1][0] # pylint: disable=protected-access barText += subcounter._colorize(self.series[-1] * subLen) else: # Get main partial bar barText += self.series[-1] * subLen partial_len = sum(block_count) else: # Get main partial bar barText += self.series[-1] * barLen partial_len = barLen # If bar isn't complete, add partial block and fill if barLen < barWidth: if fields.get('count_0', self.count): barText += self.series[int(round((complete - barLen) * (len(self.series) - 1)))] partial_len += 1 barText += self.series[0] * (barWidth - partial_len) return rtn.format(self._colorize(barText))
def _get_subcounters(self, elapsed, fields, bar_fields=True, force_float=False): """ Args: elapsed(float): Time since started. bar_fields(bool): When False, only set fields for basic counter Returns: :py:class:`tuple`: list of subcounters and dictionary of additional fields Each subcounter in the list will be in a tuple of (subcounter, percentage) Fields in the dictionary are addressed in the Format documentation of this class When `bar_fields` is False, only subcounter count, interval, and rate fields are set. percentage will be set to 0.0 """ subcounters = [] count_00 = 0 start_count_00 = 0 if not self._subcounters: return subcounters for num, subcounter in enumerate(self._subcounters, 1): count = subcounter.count count_00 += count start_count_00 += subcounter.start_count fields['count_%d' % num] = Float(count) if force_float else count if self.total and bar_fields: subPercentage = count / float(self.total) else: subPercentage = 0.0 if bar_fields: fields['percentage_%d' % num] = subPercentage * 100 # Save in tuple: count, percentage subcounters.append((subcounter, subPercentage)) if not subcounter.all_fields: continue # Explicit conversion to float required for Python 2 interations = float(abs(count - subcounter.start_count)) rate = Float(interations / elapsed) if elapsed else Float(0.0) fields['rate_%d' % num] = rate fields['interval_%d' % num] = rate ** -1 if rate else rate if not bar_fields: continue if self.total == 0: fields['eta_%d' % num] = u'00:00' elif rate: fields['eta_%d' % num] = format_time((self.total - interations) / rate) else: fields['eta_%d' % num] = u'?' # Percentage_0 and percentage_00, bar_format only if bar_fields: fields['percentage_00'] = percentage_00 = sum(sub[1] for sub in subcounters) * 100 fields['percentage_0'] = fields['percentage'] - percentage_00 # count_00 fields (Sum of subcounters) fields['count_00'] = Float(count_00) if force_float else count_00 rate = Float(float(abs(count_00 - start_count_00)) / elapsed) if elapsed else Float(0.0) fields['rate_00'] = rate fields['interval_00'] = rate ** -1 if rate else rate # count_0 fields (Excluding subcounters) count_0 = fields['count_0'] = fields['count'] - count_00 start_count_0 = self.start_count - start_count_00 rate = Float(float(abs(count_0 - start_count_0)) / elapsed) if elapsed else Float(0.0) fields['rate_0'] = rate fields['interval_0'] = rate ** -1 if rate else rate return subcounters
def test_days(self): """Verify days formatting""" self.assertEqual(format_time(86400), '1d 0h 00:00') self.assertEqual(format_time(1447597), '16d 18h 06:37')
def test_hours(self): """Verify hours formatting""" self.assertEqual(format_time(3600), '1h 00:00') self.assertEqual(format_time(43980), '12h 13:00') self.assertEqual(format_time(43998), '12h 13:18')
def test_minutes(self): """Verify minutes formatting""" self.assertEqual(format_time(60), '01:00') self.assertEqual(format_time(128), '02:08') self.assertEqual(format_time(1684), '28:04')
def test_seconds(self): """Verify seconds formatting""" self.assertEqual(format_time(0), '00:00') self.assertEqual(format_time(6), '00:06') self.assertEqual(format_time(42), '00:42')
def format(self, width=None, elapsed=None): """ Args: width (int): Width in columns to make progress bar elapsed(float): Time since started. Automatically determined if :py:data:`None` Returns: :py:class:`str`: Formatted progress bar or counter Format progress bar or counter """ width = width or self.manager.width iterations = float(abs(self.count - self.start_count)) fields = self.fields.copy() fields.update(self._fields) # Warn on reserved fields reserved_fields = (set(fields) & COUNTER_FIELDS) | set( match.group() for match in (RE_SUBCOUNTER_FIELDS.match(key) for key in fields) if match ) if reserved_fields: warn_best_level('Ignoring reserved fields specified as user-defined fields: %s' % ', '.join(reserved_fields), EnlightenWarning) fields.update({'bar': u'{0}', 'count': self.count, 'desc': self.desc or u'', 'total': self.total, 'unit': self.unit or u'', 'desc_pad': u' ' if self.desc else u'', 'unit_pad': u' ' if self.unit else u''}) # Get elapsed time if elapsed is None: elapsed = self.elapsed fields['elapsed'] = format_time(elapsed) # Get rate. Elapsed could be 0 if counter was not updated and has a zero total. if elapsed: # Use iterations so a counter running backwards is accurate rate = fields['rate'] = iterations / elapsed else: rate = fields['rate'] = 0.0 fields['interval'] = rate ** -1 if rate else 0.0 # Only process bar if total was given and n doesn't exceed total if self.total is not None and self.count <= self.total: fields['len_total'] = len(str(self.total)) # Get percentage if self.total == 0: # If total is 0, force to 100 percent percentage = 1 fields['eta'] = u'00:00' else: # Use float to force to float in Python 2 percentage = self.count / float(self.total) # Get eta if rate: # Use iterations so a counter running backwards is accurate fields['eta'] = format_time((self.total - iterations) / rate) else: fields['eta'] = u'?' fields['percentage'] = percentage * 100 # Have to go through subcounters here so the fields are available subcounters, subFields = self._get_subcounters(elapsed) # Calculate count and percentage for remainder if subcounters: fields.update(subFields) fields['count_0'] = self.count - sum(sub[0].count for sub in subcounters) fields['percentage_0'] = (percentage - sum(sub[1] for sub in subcounters)) * 100 # Partially format try: if FORMAT_MAP_SUPPORT: rtn = self.bar_format.format_map(fields) else: # pragma: no cover rtn = self.bar_format.format(**fields) except KeyError as e: raise_from_none(ValueError('%r specified in format, but not provided' % e.args[0])) # Format the bar if self.offset is None: barWidth = width - self.manager.term.length(rtn) + 3 # 3 is for the bar placeholder else: # Offset was explicitly given barWidth = width - len(rtn) + self.offset + 3 # 3 is for the bar placeholder complete = barWidth * percentage barLen = int(complete) barText = u'' subOffset = 0 for subcounter, subPercentage in reversed(subcounters): subLen = int(barWidth * subPercentage) # pylint: disable=protected-access barText += subcounter._colorize(self.series[-1] * subLen) subOffset += subLen barText += self.series[-1] * (barLen - subOffset) if barLen < barWidth: barText += self.series[int(round((complete - barLen) * (len(self.series) - 1)))] barText += self.series[0] * (barWidth - barLen - 1) return rtn.format(self._colorize(barText)) # Otherwise return a counter # Update fields from subcounters fields['fill'] = u'{0}' subcounters, subFields = self._get_subcounters(elapsed, bar_fields=False) if subcounters: fields.update(subFields) fields['count_0'] = self.count - sum(sub[0].count for sub in subcounters) try: if FORMAT_MAP_SUPPORT: rtn = self.counter_format.format_map(fields) else: # pragma: no cover rtn = self.counter_format.format(**fields) except KeyError as e: raise_from_none(ValueError('%r specified in format, but not provided' % e.args[0])) return self._fill_text(rtn, width, offset=self.offset)