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