1eb8dc403SDave Cobbley# -*- coding: utf-8 -*- 2eb8dc403SDave Cobbley# 3eb8dc403SDave Cobbley# progressbar - Text progress bar library for Python. 4eb8dc403SDave Cobbley# Copyright (c) 2005 Nilton Volpato 5eb8dc403SDave Cobbley# 6*c342db35SBrad Bishop# SPDX-License-Identifier: LGPL-2.1-or-later OR BSD-3-Clause-Clear 7*c342db35SBrad Bishop# 8eb8dc403SDave Cobbley# This library is free software; you can redistribute it and/or 9eb8dc403SDave Cobbley# modify it under the terms of the GNU Lesser General Public 10eb8dc403SDave Cobbley# License as published by the Free Software Foundation; either 11eb8dc403SDave Cobbley# version 2.1 of the License, or (at your option) any later version. 12eb8dc403SDave Cobbley# 13eb8dc403SDave Cobbley# This library is distributed in the hope that it will be useful, 14eb8dc403SDave Cobbley# but WITHOUT ANY WARRANTY; without even the implied warranty of 15eb8dc403SDave Cobbley# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 16eb8dc403SDave Cobbley# Lesser General Public License for more details. 17eb8dc403SDave Cobbley# 18eb8dc403SDave Cobbley# You should have received a copy of the GNU Lesser General Public 19eb8dc403SDave Cobbley# License along with this library; if not, write to the Free Software 20eb8dc403SDave Cobbley# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 21eb8dc403SDave Cobbley 22eb8dc403SDave Cobbley"""Default ProgressBar widgets.""" 23eb8dc403SDave Cobbley 24eb8dc403SDave Cobbleyfrom __future__ import division 25eb8dc403SDave Cobbley 26eb8dc403SDave Cobbleyimport datetime 27eb8dc403SDave Cobbleyimport math 28eb8dc403SDave Cobbley 29eb8dc403SDave Cobbleytry: 30eb8dc403SDave Cobbley from abc import ABCMeta, abstractmethod 31eb8dc403SDave Cobbleyexcept ImportError: 32eb8dc403SDave Cobbley AbstractWidget = object 33eb8dc403SDave Cobbley abstractmethod = lambda fn: fn 34eb8dc403SDave Cobbleyelse: 35eb8dc403SDave Cobbley AbstractWidget = ABCMeta('AbstractWidget', (object,), {}) 36eb8dc403SDave Cobbley 37eb8dc403SDave Cobbley 38eb8dc403SDave Cobbleydef format_updatable(updatable, pbar): 39eb8dc403SDave Cobbley if hasattr(updatable, 'update'): return updatable.update(pbar) 40eb8dc403SDave Cobbley else: return updatable 41eb8dc403SDave Cobbley 42eb8dc403SDave Cobbley 43eb8dc403SDave Cobbleyclass Widget(AbstractWidget): 44eb8dc403SDave Cobbley """The base class for all widgets. 45eb8dc403SDave Cobbley 46eb8dc403SDave Cobbley The ProgressBar will call the widget's update value when the widget should 47eb8dc403SDave Cobbley be updated. The widget's size may change between calls, but the widget may 48eb8dc403SDave Cobbley display incorrectly if the size changes drastically and repeatedly. 49eb8dc403SDave Cobbley 50eb8dc403SDave Cobbley The boolean TIME_SENSITIVE informs the ProgressBar that it should be 51eb8dc403SDave Cobbley updated more often because it is time sensitive. 52eb8dc403SDave Cobbley """ 53eb8dc403SDave Cobbley 54eb8dc403SDave Cobbley TIME_SENSITIVE = False 55eb8dc403SDave Cobbley __slots__ = () 56eb8dc403SDave Cobbley 57eb8dc403SDave Cobbley @abstractmethod 58eb8dc403SDave Cobbley def update(self, pbar): 59eb8dc403SDave Cobbley """Updates the widget. 60eb8dc403SDave Cobbley 61eb8dc403SDave Cobbley pbar - a reference to the calling ProgressBar 62eb8dc403SDave Cobbley """ 63eb8dc403SDave Cobbley 64eb8dc403SDave Cobbley 65eb8dc403SDave Cobbleyclass WidgetHFill(Widget): 66eb8dc403SDave Cobbley """The base class for all variable width widgets. 67eb8dc403SDave Cobbley 68eb8dc403SDave Cobbley This widget is much like the \\hfill command in TeX, it will expand to 69eb8dc403SDave Cobbley fill the line. You can use more than one in the same line, and they will 70eb8dc403SDave Cobbley all have the same width, and together will fill the line. 71eb8dc403SDave Cobbley """ 72eb8dc403SDave Cobbley 73eb8dc403SDave Cobbley @abstractmethod 74eb8dc403SDave Cobbley def update(self, pbar, width): 75eb8dc403SDave Cobbley """Updates the widget providing the total width the widget must fill. 76eb8dc403SDave Cobbley 77eb8dc403SDave Cobbley pbar - a reference to the calling ProgressBar 78eb8dc403SDave Cobbley width - The total width the widget must fill 79eb8dc403SDave Cobbley """ 80eb8dc403SDave Cobbley 81eb8dc403SDave Cobbley 82eb8dc403SDave Cobbleyclass Timer(Widget): 83eb8dc403SDave Cobbley """Widget which displays the elapsed seconds.""" 84eb8dc403SDave Cobbley 85eb8dc403SDave Cobbley __slots__ = ('format_string',) 86eb8dc403SDave Cobbley TIME_SENSITIVE = True 87eb8dc403SDave Cobbley 88eb8dc403SDave Cobbley def __init__(self, format='Elapsed Time: %s'): 89eb8dc403SDave Cobbley self.format_string = format 90eb8dc403SDave Cobbley 91eb8dc403SDave Cobbley @staticmethod 92eb8dc403SDave Cobbley def format_time(seconds): 93eb8dc403SDave Cobbley """Formats time as the string "HH:MM:SS".""" 94eb8dc403SDave Cobbley 95eb8dc403SDave Cobbley return str(datetime.timedelta(seconds=int(seconds))) 96eb8dc403SDave Cobbley 97eb8dc403SDave Cobbley 98eb8dc403SDave Cobbley def update(self, pbar): 99eb8dc403SDave Cobbley """Updates the widget to show the elapsed time.""" 100eb8dc403SDave Cobbley 101eb8dc403SDave Cobbley return self.format_string % self.format_time(pbar.seconds_elapsed) 102eb8dc403SDave Cobbley 103eb8dc403SDave Cobbley 104eb8dc403SDave Cobbleyclass ETA(Timer): 105eb8dc403SDave Cobbley """Widget which attempts to estimate the time of arrival.""" 106eb8dc403SDave Cobbley 107eb8dc403SDave Cobbley TIME_SENSITIVE = True 108eb8dc403SDave Cobbley 109eb8dc403SDave Cobbley def update(self, pbar): 110eb8dc403SDave Cobbley """Updates the widget to show the ETA or total time when finished.""" 111eb8dc403SDave Cobbley 112eb8dc403SDave Cobbley if pbar.currval == 0: 113eb8dc403SDave Cobbley return 'ETA: --:--:--' 114eb8dc403SDave Cobbley elif pbar.finished: 115eb8dc403SDave Cobbley return 'Time: %s' % self.format_time(pbar.seconds_elapsed) 116eb8dc403SDave Cobbley else: 117eb8dc403SDave Cobbley elapsed = pbar.seconds_elapsed 118eb8dc403SDave Cobbley eta = elapsed * pbar.maxval / pbar.currval - elapsed 119eb8dc403SDave Cobbley return 'ETA: %s' % self.format_time(eta) 120eb8dc403SDave Cobbley 121eb8dc403SDave Cobbley 122eb8dc403SDave Cobbleyclass AdaptiveETA(Timer): 123eb8dc403SDave Cobbley """Widget which attempts to estimate the time of arrival. 124eb8dc403SDave Cobbley 125eb8dc403SDave Cobbley Uses a weighted average of two estimates: 126eb8dc403SDave Cobbley 1) ETA based on the total progress and time elapsed so far 127eb8dc403SDave Cobbley 2) ETA based on the progress as per the last 10 update reports 128eb8dc403SDave Cobbley 129eb8dc403SDave Cobbley The weight depends on the current progress so that to begin with the 130eb8dc403SDave Cobbley total progress is used and at the end only the most recent progress is 131eb8dc403SDave Cobbley used. 132eb8dc403SDave Cobbley """ 133eb8dc403SDave Cobbley 134eb8dc403SDave Cobbley TIME_SENSITIVE = True 135eb8dc403SDave Cobbley NUM_SAMPLES = 10 136eb8dc403SDave Cobbley 137eb8dc403SDave Cobbley def _update_samples(self, currval, elapsed): 138eb8dc403SDave Cobbley sample = (currval, elapsed) 139eb8dc403SDave Cobbley if not hasattr(self, 'samples'): 140eb8dc403SDave Cobbley self.samples = [sample] * (self.NUM_SAMPLES + 1) 141eb8dc403SDave Cobbley else: 142eb8dc403SDave Cobbley self.samples.append(sample) 143eb8dc403SDave Cobbley return self.samples.pop(0) 144eb8dc403SDave Cobbley 145eb8dc403SDave Cobbley def _eta(self, maxval, currval, elapsed): 146eb8dc403SDave Cobbley return elapsed * maxval / float(currval) - elapsed 147eb8dc403SDave Cobbley 148eb8dc403SDave Cobbley def update(self, pbar): 149eb8dc403SDave Cobbley """Updates the widget to show the ETA or total time when finished.""" 150eb8dc403SDave Cobbley if pbar.currval == 0: 151eb8dc403SDave Cobbley return 'ETA: --:--:--' 152eb8dc403SDave Cobbley elif pbar.finished: 153eb8dc403SDave Cobbley return 'Time: %s' % self.format_time(pbar.seconds_elapsed) 154eb8dc403SDave Cobbley else: 155eb8dc403SDave Cobbley elapsed = pbar.seconds_elapsed 156eb8dc403SDave Cobbley currval1, elapsed1 = self._update_samples(pbar.currval, elapsed) 157eb8dc403SDave Cobbley eta = self._eta(pbar.maxval, pbar.currval, elapsed) 158eb8dc403SDave Cobbley if pbar.currval > currval1: 159eb8dc403SDave Cobbley etasamp = self._eta(pbar.maxval - currval1, 160eb8dc403SDave Cobbley pbar.currval - currval1, 161eb8dc403SDave Cobbley elapsed - elapsed1) 162eb8dc403SDave Cobbley weight = (pbar.currval / float(pbar.maxval)) ** 0.5 163eb8dc403SDave Cobbley eta = (1 - weight) * eta + weight * etasamp 164eb8dc403SDave Cobbley return 'ETA: %s' % self.format_time(eta) 165eb8dc403SDave Cobbley 166eb8dc403SDave Cobbley 167eb8dc403SDave Cobbleyclass FileTransferSpeed(Widget): 168eb8dc403SDave Cobbley """Widget for showing the transfer speed (useful for file transfers).""" 169eb8dc403SDave Cobbley 170eb8dc403SDave Cobbley FORMAT = '%6.2f %s%s/s' 171eb8dc403SDave Cobbley PREFIXES = ' kMGTPEZY' 172eb8dc403SDave Cobbley __slots__ = ('unit',) 173eb8dc403SDave Cobbley 174eb8dc403SDave Cobbley def __init__(self, unit='B'): 175eb8dc403SDave Cobbley self.unit = unit 176eb8dc403SDave Cobbley 177eb8dc403SDave Cobbley def update(self, pbar): 178eb8dc403SDave Cobbley """Updates the widget with the current SI prefixed speed.""" 179eb8dc403SDave Cobbley 180eb8dc403SDave Cobbley if pbar.seconds_elapsed < 2e-6 or pbar.currval < 2e-6: # =~ 0 181eb8dc403SDave Cobbley scaled = power = 0 182eb8dc403SDave Cobbley else: 183eb8dc403SDave Cobbley speed = pbar.currval / pbar.seconds_elapsed 184eb8dc403SDave Cobbley power = int(math.log(speed, 1000)) 185eb8dc403SDave Cobbley scaled = speed / 1000.**power 186eb8dc403SDave Cobbley 187eb8dc403SDave Cobbley return self.FORMAT % (scaled, self.PREFIXES[power], self.unit) 188eb8dc403SDave Cobbley 189eb8dc403SDave Cobbley 190eb8dc403SDave Cobbleyclass AnimatedMarker(Widget): 191eb8dc403SDave Cobbley """An animated marker for the progress bar which defaults to appear as if 192eb8dc403SDave Cobbley it were rotating. 193eb8dc403SDave Cobbley """ 194eb8dc403SDave Cobbley 195eb8dc403SDave Cobbley __slots__ = ('markers', 'curmark') 196eb8dc403SDave Cobbley 197eb8dc403SDave Cobbley def __init__(self, markers='|/-\\'): 198eb8dc403SDave Cobbley self.markers = markers 199eb8dc403SDave Cobbley self.curmark = -1 200eb8dc403SDave Cobbley 201eb8dc403SDave Cobbley def update(self, pbar): 202eb8dc403SDave Cobbley """Updates the widget to show the next marker or the first marker when 203eb8dc403SDave Cobbley finished""" 204eb8dc403SDave Cobbley 205eb8dc403SDave Cobbley if pbar.finished: return self.markers[0] 206eb8dc403SDave Cobbley 207eb8dc403SDave Cobbley self.curmark = (self.curmark + 1) % len(self.markers) 208eb8dc403SDave Cobbley return self.markers[self.curmark] 209eb8dc403SDave Cobbley 210eb8dc403SDave Cobbley# Alias for backwards compatibility 211eb8dc403SDave CobbleyRotatingMarker = AnimatedMarker 212eb8dc403SDave Cobbley 213eb8dc403SDave Cobbley 214eb8dc403SDave Cobbleyclass Counter(Widget): 215eb8dc403SDave Cobbley """Displays the current count.""" 216eb8dc403SDave Cobbley 217eb8dc403SDave Cobbley __slots__ = ('format_string',) 218eb8dc403SDave Cobbley 219eb8dc403SDave Cobbley def __init__(self, format='%d'): 220eb8dc403SDave Cobbley self.format_string = format 221eb8dc403SDave Cobbley 222eb8dc403SDave Cobbley def update(self, pbar): 223eb8dc403SDave Cobbley return self.format_string % pbar.currval 224eb8dc403SDave Cobbley 225eb8dc403SDave Cobbley 226eb8dc403SDave Cobbleyclass Percentage(Widget): 227eb8dc403SDave Cobbley """Displays the current percentage as a number with a percent sign.""" 228eb8dc403SDave Cobbley 229eb8dc403SDave Cobbley def update(self, pbar): 230eb8dc403SDave Cobbley return '%3d%%' % pbar.percentage() 231eb8dc403SDave Cobbley 232eb8dc403SDave Cobbley 233eb8dc403SDave Cobbleyclass FormatLabel(Timer): 234eb8dc403SDave Cobbley """Displays a formatted label.""" 235eb8dc403SDave Cobbley 236eb8dc403SDave Cobbley mapping = { 237eb8dc403SDave Cobbley 'elapsed': ('seconds_elapsed', Timer.format_time), 238eb8dc403SDave Cobbley 'finished': ('finished', None), 239eb8dc403SDave Cobbley 'last_update': ('last_update_time', None), 240eb8dc403SDave Cobbley 'max': ('maxval', None), 241eb8dc403SDave Cobbley 'seconds': ('seconds_elapsed', None), 242eb8dc403SDave Cobbley 'start': ('start_time', None), 243eb8dc403SDave Cobbley 'value': ('currval', None) 244eb8dc403SDave Cobbley } 245eb8dc403SDave Cobbley 246eb8dc403SDave Cobbley __slots__ = ('format_string',) 247eb8dc403SDave Cobbley def __init__(self, format): 248eb8dc403SDave Cobbley self.format_string = format 249eb8dc403SDave Cobbley 250eb8dc403SDave Cobbley def update(self, pbar): 251eb8dc403SDave Cobbley context = {} 252eb8dc403SDave Cobbley for name, (key, transform) in self.mapping.items(): 253eb8dc403SDave Cobbley try: 254eb8dc403SDave Cobbley value = getattr(pbar, key) 255eb8dc403SDave Cobbley 256eb8dc403SDave Cobbley if transform is None: 257eb8dc403SDave Cobbley context[name] = value 258eb8dc403SDave Cobbley else: 259eb8dc403SDave Cobbley context[name] = transform(value) 260eb8dc403SDave Cobbley except: pass 261eb8dc403SDave Cobbley 262eb8dc403SDave Cobbley return self.format_string % context 263eb8dc403SDave Cobbley 264eb8dc403SDave Cobbley 265eb8dc403SDave Cobbleyclass SimpleProgress(Widget): 266eb8dc403SDave Cobbley """Returns progress as a count of the total (e.g.: "5 of 47").""" 267eb8dc403SDave Cobbley 268eb8dc403SDave Cobbley __slots__ = ('sep',) 269eb8dc403SDave Cobbley 270eb8dc403SDave Cobbley def __init__(self, sep=' of '): 271eb8dc403SDave Cobbley self.sep = sep 272eb8dc403SDave Cobbley 273eb8dc403SDave Cobbley def update(self, pbar): 274eb8dc403SDave Cobbley return '%d%s%d' % (pbar.currval, self.sep, pbar.maxval) 275eb8dc403SDave Cobbley 276eb8dc403SDave Cobbley 277eb8dc403SDave Cobbleyclass Bar(WidgetHFill): 278eb8dc403SDave Cobbley """A progress bar which stretches to fill the line.""" 279eb8dc403SDave Cobbley 280eb8dc403SDave Cobbley __slots__ = ('marker', 'left', 'right', 'fill', 'fill_left') 281eb8dc403SDave Cobbley 282eb8dc403SDave Cobbley def __init__(self, marker='#', left='|', right='|', fill=' ', 283eb8dc403SDave Cobbley fill_left=True): 284eb8dc403SDave Cobbley """Creates a customizable progress bar. 285eb8dc403SDave Cobbley 286eb8dc403SDave Cobbley marker - string or updatable object to use as a marker 287eb8dc403SDave Cobbley left - string or updatable object to use as a left border 288eb8dc403SDave Cobbley right - string or updatable object to use as a right border 289eb8dc403SDave Cobbley fill - character to use for the empty part of the progress bar 290eb8dc403SDave Cobbley fill_left - whether to fill from the left or the right 291eb8dc403SDave Cobbley """ 292eb8dc403SDave Cobbley self.marker = marker 293eb8dc403SDave Cobbley self.left = left 294eb8dc403SDave Cobbley self.right = right 295eb8dc403SDave Cobbley self.fill = fill 296eb8dc403SDave Cobbley self.fill_left = fill_left 297eb8dc403SDave Cobbley 298eb8dc403SDave Cobbley 299eb8dc403SDave Cobbley def update(self, pbar, width): 300eb8dc403SDave Cobbley """Updates the progress bar and its subcomponents.""" 301eb8dc403SDave Cobbley 302eb8dc403SDave Cobbley left, marked, right = (format_updatable(i, pbar) for i in 303eb8dc403SDave Cobbley (self.left, self.marker, self.right)) 304eb8dc403SDave Cobbley 305eb8dc403SDave Cobbley width -= len(left) + len(right) 306eb8dc403SDave Cobbley # Marked must *always* have length of 1 307eb8dc403SDave Cobbley if pbar.maxval: 308eb8dc403SDave Cobbley marked *= int(pbar.currval / pbar.maxval * width) 309eb8dc403SDave Cobbley else: 310eb8dc403SDave Cobbley marked = '' 311eb8dc403SDave Cobbley 312eb8dc403SDave Cobbley if self.fill_left: 313eb8dc403SDave Cobbley return '%s%s%s' % (left, marked.ljust(width, self.fill), right) 314eb8dc403SDave Cobbley else: 315eb8dc403SDave Cobbley return '%s%s%s' % (left, marked.rjust(width, self.fill), right) 316eb8dc403SDave Cobbley 317eb8dc403SDave Cobbley 318eb8dc403SDave Cobbleyclass ReverseBar(Bar): 319eb8dc403SDave Cobbley """A bar which has a marker which bounces from side to side.""" 320eb8dc403SDave Cobbley 321eb8dc403SDave Cobbley def __init__(self, marker='#', left='|', right='|', fill=' ', 322eb8dc403SDave Cobbley fill_left=False): 323eb8dc403SDave Cobbley """Creates a customizable progress bar. 324eb8dc403SDave Cobbley 325eb8dc403SDave Cobbley marker - string or updatable object to use as a marker 326eb8dc403SDave Cobbley left - string or updatable object to use as a left border 327eb8dc403SDave Cobbley right - string or updatable object to use as a right border 328eb8dc403SDave Cobbley fill - character to use for the empty part of the progress bar 329eb8dc403SDave Cobbley fill_left - whether to fill from the left or the right 330eb8dc403SDave Cobbley """ 331eb8dc403SDave Cobbley self.marker = marker 332eb8dc403SDave Cobbley self.left = left 333eb8dc403SDave Cobbley self.right = right 334eb8dc403SDave Cobbley self.fill = fill 335eb8dc403SDave Cobbley self.fill_left = fill_left 336eb8dc403SDave Cobbley 337eb8dc403SDave Cobbley 338eb8dc403SDave Cobbleyclass BouncingBar(Bar): 339eb8dc403SDave Cobbley def update(self, pbar, width): 340eb8dc403SDave Cobbley """Updates the progress bar and its subcomponents.""" 341eb8dc403SDave Cobbley 342eb8dc403SDave Cobbley left, marker, right = (format_updatable(i, pbar) for i in 343eb8dc403SDave Cobbley (self.left, self.marker, self.right)) 344eb8dc403SDave Cobbley 345eb8dc403SDave Cobbley width -= len(left) + len(right) 346eb8dc403SDave Cobbley 347eb8dc403SDave Cobbley if pbar.finished: return '%s%s%s' % (left, width * marker, right) 348eb8dc403SDave Cobbley 349eb8dc403SDave Cobbley position = int(pbar.currval % (width * 2 - 1)) 350eb8dc403SDave Cobbley if position > width: position = width * 2 - position 351eb8dc403SDave Cobbley lpad = self.fill * (position - 1) 352eb8dc403SDave Cobbley rpad = self.fill * (width - len(marker) - len(lpad)) 353eb8dc403SDave Cobbley 354eb8dc403SDave Cobbley # Swap if we want to bounce the other way 355eb8dc403SDave Cobbley if not self.fill_left: rpad, lpad = lpad, rpad 356eb8dc403SDave Cobbley 357eb8dc403SDave Cobbley return '%s%s%s%s%s' % (left, lpad, marker, rpad, right) 358eb8dc403SDave Cobbley 359eb8dc403SDave Cobbley 360eb8dc403SDave Cobbleyclass BouncingSlider(Bar): 361eb8dc403SDave Cobbley """ 362eb8dc403SDave Cobbley A slider that bounces back and forth in response to update() calls 363eb8dc403SDave Cobbley without reference to the actual value. Based on a combination of 364eb8dc403SDave Cobbley BouncingBar from a newer version of this module and RotatingMarker. 365eb8dc403SDave Cobbley """ 366eb8dc403SDave Cobbley def __init__(self, marker='<=>'): 367eb8dc403SDave Cobbley self.curmark = -1 368eb8dc403SDave Cobbley self.forward = True 369eb8dc403SDave Cobbley Bar.__init__(self, marker=marker) 370eb8dc403SDave Cobbley def update(self, pbar, width): 371eb8dc403SDave Cobbley left, marker, right = (format_updatable(i, pbar) for i in 372eb8dc403SDave Cobbley (self.left, self.marker, self.right)) 373eb8dc403SDave Cobbley 374eb8dc403SDave Cobbley width -= len(left) + len(right) 375eb8dc403SDave Cobbley if width < 0: 376eb8dc403SDave Cobbley return '' 377eb8dc403SDave Cobbley 378eb8dc403SDave Cobbley if pbar.finished: return '%s%s%s' % (left, width * '=', right) 379eb8dc403SDave Cobbley 380eb8dc403SDave Cobbley self.curmark = self.curmark + 1 381eb8dc403SDave Cobbley position = int(self.curmark % (width * 2 - 1)) 382eb8dc403SDave Cobbley if position + len(marker) > width: 383eb8dc403SDave Cobbley self.forward = not self.forward 384eb8dc403SDave Cobbley self.curmark = 1 385eb8dc403SDave Cobbley position = 1 386eb8dc403SDave Cobbley lpad = ' ' * (position - 1) 387eb8dc403SDave Cobbley rpad = ' ' * (width - len(marker) - len(lpad)) 388eb8dc403SDave Cobbley 389eb8dc403SDave Cobbley if not self.forward: 390eb8dc403SDave Cobbley temp = lpad 391eb8dc403SDave Cobbley lpad = rpad 392eb8dc403SDave Cobbley rpad = temp 393eb8dc403SDave Cobbley return '%s%s%s%s%s' % (left, lpad, marker, rpad, right) 394