99import sys
1010import pprint
1111import datetime
12+ import functools
1213
1314from python_utils import converters
1415
@@ -32,7 +33,49 @@ def render_input(progress, data, width):
3233 return input_
3334
3435
35- def create_marker (marker ):
36+ def create_wrapper (wrapper ):
37+ '''Convert a wrapper tuple or format string to a format string
38+
39+ >>> create_wrapper('')
40+
41+ >>> create_wrapper('a{}b')
42+ 'a{}b'
43+
44+ >>> create_wrapper(('a', 'b'))
45+ 'a{}b'
46+ '''
47+ if isinstance (wrapper , tuple ) and len (wrapper ) == 2 :
48+ a , b = wrapper
49+ wrapper = (a or '' ) + '{}' + (b or '' )
50+ elif not wrapper :
51+ return
52+
53+ if isinstance (wrapper , six .string_types ):
54+ assert '{}' in wrapper , 'Expected string with {} for formatting'
55+ else :
56+ raise RuntimeError ('Pass either a begin/end string as a tuple or a'
57+ ' template string with {}' )
58+
59+ return wrapper
60+
61+
62+ def wrapper (function , wrapper ):
63+ '''Wrap the output of a function in a template string or a tuple with
64+ begin/end strings
65+
66+ '''
67+ wrapper = create_wrapper (wrapper )
68+ if not wrapper :
69+ return function
70+
71+ @functools .wraps (function )
72+ def wrap (* args , ** kwargs ):
73+ return wrapper .format (function (* args , ** kwargs ))
74+
75+ return wrap
76+
77+
78+ def create_marker (marker , wrap = None ):
3679 def _marker (progress , data , width ):
3780 if progress .max_value is not base .UnknownLength \
3881 and progress .max_value > 0 :
@@ -45,9 +88,9 @@ def _marker(progress, data, width):
4588 marker = converters .to_unicode (marker )
4689 assert utils .len_color (marker ) == 1 , \
4790 'Markers are required to be 1 char'
48- return _marker
91+ return wrapper ( _marker , wrap )
4992 else :
50- return marker
93+ return wrapper ( marker , wrap )
5194
5295
5396class FormatWidgetMixin (object ):
@@ -525,10 +568,13 @@ class AnimatedMarker(TimeSensitiveWidgetBase):
525568 it were rotating.
526569 '''
527570
528- def __init__ (self , markers = '|/-\\ ' , default = None , fill = '' , ** kwargs ):
571+ def __init__ (self , markers = '|/-\\ ' , default = None , fill = '' ,
572+ marker_wrap = None , fill_wrap = None , ** kwargs ):
529573 self .markers = markers
574+ self .marker_wrap = create_wrapper (marker_wrap )
530575 self .default = default or markers [0 ]
531- self .fill = create_marker (fill ) if fill else None
576+ self .fill_wrap = create_wrapper (fill_wrap )
577+ self .fill = create_marker (fill , self .fill_wrap ) if fill else None
532578 WidgetBase .__init__ (self , ** kwargs )
533579
534580 def __call__ (self , progress , data , width = None ):
@@ -538,14 +584,17 @@ def __call__(self, progress, data, width=None):
538584 if progress .end_time :
539585 return self .default
540586
587+ marker = self .markers [data ['updates' ] % len (self .markers )]
588+ if self .marker_wrap :
589+ marker = self .marker_wrap .format (marker )
590+
541591 if self .fill :
542592 # Cut the last character so we can replace it with our marker
543- fill = self .fill (progress , data , width )[:- 1 ]
593+ fill = self .fill (progress , data , width - progress .custom_len (
594+ marker ))
544595 else :
545596 fill = ''
546-
547- marker = self .markers [data ['updates' ] % len (self .markers )]
548-
597+
549598 # Python 3 returns an int when indexing bytes
550599 if isinstance (marker , int ): # pragma: no cover
551600 marker = bytes (marker )
@@ -642,7 +691,7 @@ class Bar(AutoWidthWidgetBase):
642691 '''A progress bar which stretches to fill the line.'''
643692
644693 def __init__ (self , marker = '#' , left = '|' , right = '|' , fill = ' ' ,
645- fill_left = True , ** kwargs ):
694+ fill_left = True , marker_wrap = None , ** kwargs ):
646695 '''Creates a customizable progress bar.
647696
648697 The callable takes the same parameters as the `__call__` method
@@ -654,7 +703,7 @@ def __init__(self, marker='#', left='|', right='|', fill=' ',
654703 fill_left - whether to fill from the left or the right
655704 '''
656705
657- self .marker = create_marker (marker )
706+ self .marker = create_marker (marker , marker_wrap )
658707 self .left = string_or_lambda (left )
659708 self .right = string_or_lambda (right )
660709 self .fill = string_or_lambda (fill )
@@ -670,6 +719,10 @@ def __call__(self, progress, data, width):
670719 width -= progress .custom_len (left ) + progress .custom_len (right )
671720 marker = converters .to_unicode (self .marker (progress , data , width ))
672721 fill = converters .to_unicode (self .fill (progress , data , width ))
722+
723+ # Make sure we ignore invisible characters when filling
724+ width += len (marker ) - progress .custom_len (marker )
725+
673726 if self .fill_left :
674727 marker = marker .ljust (width , fill )
675728 else :
@@ -796,7 +849,7 @@ def __call__(self, progress, data, width):
796849 width_accumulated = 0
797850 for marker , value in zip (self .markers , values ):
798851 marker = converters .to_unicode (marker (progress , data , width ))
799- assert utils . len_color (marker ) == 1
852+ assert progress . custom_len (marker ) == 1
800853
801854 values_accumulated += value
802855 item_width = int (values_accumulated / values_sum * width )
@@ -805,7 +858,7 @@ def __call__(self, progress, data, width):
805858 middle += item_width * marker
806859 else :
807860 fill = converters .to_unicode (self .fill (progress , data , width ))
808- assert utils . len_color (fill ) == 1
861+ assert progress . custom_len (fill ) == 1
809862 middle = fill * width
810863
811864 return left + middle + right
0 commit comments