def show(self): """ This is where we go output the content of a block and any valid child block that it contains. """ # Start by finalising the block. # Any active content is added to self.options if self.content: self.options.append(self.content) output = Composite() for index, option in enumerate(self.options): if index in self.valid_blocks: # A block may be valid but has a command that causes this to be # disregarded. if self.is_valid_by_command(index) is False: continue # add the content of the block and any children # to the output for item in option: if isinstance(item, Block): output.append(item.show()) else: output.append(item) break # Build up our output. self.process_composite_chunk_item(output.get_content()) return output
def process_composite(self, response): """ Process a composite response. composites do not have inter item separators as they appear joined. We need to respect the universal options too. """ composite = response['composite'] # if the composite is of not Composite make it one so we can simplify # it. if not isinstance(composite, Composite): composite = Composite(composite) # simplify and get underlying list. composite = composite.simplify().get_content() response['composite'] = composite if not isinstance(composite, list): raise Exception('expecting "composite" key in response') # if list is empty nothing to do if not len(composite): return if 'full_text' in response: err = 'conflicting "full_text" and "composite" in response' raise Exception(err) # set universal options on last component composite[-1].update(self.i3s_module_options) # update all components color = response.get('color') urgent = response.get('urgent') for index, item in enumerate(response['composite']): # validate the response if 'full_text' not in item: raise KeyError('missing "full_text" key in response') # make sure all components have a name if 'name' not in item: instance_index = item.get('index', index) item['instance'] = '{} {}'.format( self.module_inst, instance_index ) item['name'] = self.module_name # hide separator for all inner components unless existing if index != len(response['composite']) - 1: if 'separator' not in item: item['separator'] = False item['separator_block_width'] = 0 # If a color was supplied for the composite and a composite # part does not supply a color, use the composite color. if color and 'color' not in item: item['color'] = color # Remove any none color from our output if hasattr(item.get('color'), 'none_setting'): del item['color'] # remove urgent if not allowed if not self.allow_urgent: if 'urgent' in item: del item['urgent'] # if urgent we want to set this to all parts elif urgent and 'urgent' not in item: item['urgent'] = urgent # set min_width if 'min_width' in self.py3_module_options: min_width = self.py3_module_options['min_width'] # get width, skip if width exceeds min_width width = sum([len(x['full_text']) for x in response['composite']]) if width >= min_width: return # sometimes when we go under min_width to pad both side evenly, # we will add extra space on either side to honor min_width padding = int((min_width / 2.0) - (width / 2.0)) offset = min_width - ((padding * 2) + width) # set align align = self.py3_module_options.get('align', 'center') if align == 'center': left = right = ' ' * padding if self.random_int: left += ' ' * offset else: right += ' ' * offset elif align == 'left': left, right = '', ' ' * (padding * 2 + offset) elif align == 'right': right, left = '', ' ' * (padding * 2 + offset) # padding if left: response['composite'][0]['full_text'] = ( left + response['composite'][0]['full_text'] ) if right: response['composite'][-1]['full_text'] += right
def test_Composite_iadd_4(): c = Composite('moo') c += (Composite('moo')) result = c.get_content() assert result == [{'full_text': 'moo'}, {'full_text': 'moo'}]
def test_Composite_append_3(): c = Composite('moo') c.append([{'full_text': 'moo'}]) result = c.get_content() assert result == [{'full_text': 'moo'}, {'full_text': 'moo'}]
'empty': '', 'None': None, '?bad name': 'evil', u'☂ Very bad name ': u'☂ extremely evil', 'long_str': 'I am a long string though not too long', 'python2_unicode': u'Björk', 'python2_str': 'Björk', 'zero': 0, 'zero_str': '0', 'zero_float': 0.0, 'str_int': '123', 'str_float': '123.456', 'str_nan': "I'm not a number", 'composite_basic': Composite([{'full_text': 'red ', 'color': '#FF0000'}, {'full_text': 'green ', 'color': '#00FF00'}, {'full_text': 'blue', 'color': '#0000FF'}]), 'complex': Composite([{'full_text': 'LA 09:34'}, {'full_text': 'NY 12:34'}]), 'complex2': Composite([{'full_text': 'LA 09:34', 'color': '#FF0000'}, {'full_text': 'NY 12:34'}]), 'simple': Composite({'full_text': 'NY 12:34'}), 'empty_composite': Composite(), 'comp_bad_color': Composite({'full_text': 'BAD', 'color': NoneColor()}), } class Module: module_param = 'something' module_true = True module_false = False
def format( self, format_string, module=None, param_dict=None, force_composite=False, attr_getter=None, ): """ Format a string, substituting place holders which can be found in param_dict, attributes of the supplied module, or provided via calls to the attr_getter function. """ # fix python 2 unicode issues if python2 and isinstance(format_string, str): format_string = format_string.decode("utf-8") if param_dict is None: param_dict = {} # if the processed format string is not in the cache then create it. if format_string not in self.block_cache: self.build_block(format_string) first_block = self.block_cache[format_string] def get_parameter(key): """ function that finds and returns the value for a placeholder. """ if key in param_dict: # was a supplied parameter param = param_dict.get(key) elif module and hasattr(module, key): param = getattr(module, key) if hasattr(param, "__call__"): # we don't allow module methods raise Exception() elif attr_getter: # get value from attr_getter function try: param = attr_getter(key) except: # noqa e722 raise Exception() else: raise Exception() if isinstance(param, Composite): if param.text(): param = param.copy() else: param = u"" elif python2 and isinstance(param, str): param = param.decode("utf-8") return param # render our processed format valid, output = first_block.render(get_parameter, module) # clean things up a little if isinstance(output, list): output = Composite(output) if not output: if force_composite: output = Composite() else: output = "" return output
def process_composite(self, response): """ Process a composite response. composites do not have inter item separators as they appear joined. We need to respect the universal options too. """ composite = response['composite'] # if the composite is of not Composite make it one so we can simplify # it. if not isinstance(composite, Composite): composite = Composite(composite) # simplify and get underlying list. composite = composite.simplify().get_content() response['composite'] = composite if not isinstance(composite, list): raise Exception('expecting "composite" key in response') # if list is empty nothing to do if not len(composite): return if 'full_text' in response: err = 'conflicting "full_text" and "composite" in response' raise Exception(err) # set universal options on last component composite[-1].update(self.module_options) # calculate any min width (we split this across components) min_width = None if 'min_width' in self.module_options: min_width = int(self.module_options['min_width'] / len(composite)) # store alignment align = None if 'align' in self.module_options: align = self.module_options['align'] # update all components color = response.get('color') urgent = response.get('urgent') for index, item in enumerate(response['composite']): # validate the response if 'full_text' not in item: raise KeyError('missing "full_text" key in response') # make sure all components have a name if 'name' not in item: instance_index = item.get('index', index) item['instance'] = '{} {}'.format(self.module_inst, instance_index) item['name'] = self.module_name # hide separator for all inner components unless existing if index != len(response['composite']) - 1: if 'separator' not in item: item['separator'] = False item['separator_block_width'] = 0 # set min width if min_width: item['min_width'] = min_width # set align if align: item['align'] = align # If a color was supplied for the composite and a composite # part does not supply a color, use the composite color. if color and 'color' not in item: item['color'] = color # Remove any none color from our output if hasattr(item.get('color'), 'none_setting'): del item['color'] # remove urgent if not allowed if not self.allow_urgent: if 'urgent' in item: del item['urgent'] # if urgent we want to set this to all parts elif urgent and 'urgent' not in item: item['urgent'] = urgent
def process_composite(self, response): """ Process a composite response. composites do not have inter item separators as they appear joined. We need to respect the universal options too. """ composite = response["composite"] # if the composite is of not Composite make it one so we can simplify # it. if not isinstance(composite, Composite): composite = Composite(composite) # simplify and get underlying list. composite = composite.simplify().get_content() response["composite"] = composite if not isinstance(composite, list): raise Exception('expecting "composite" key in response') # if list is empty nothing to do if not len(composite): return if "full_text" in response: err = 'conflicting "full_text" and "composite" in response' raise Exception(err) # set markup if "markup" in self.py3status_module_options: markup = self.py3status_module_options["markup"] for item in composite: item["markup"] = markup # set universal options on last component composite[-1].update(self.i3bar_module_options) # update all components color = response.get("color") urgent = response.get("urgent") composite_length = len(response["composite"]) - 1 for index, item in enumerate(response["composite"]): # validate the response if "full_text" not in item: raise KeyError('missing "full_text" key in response') # make sure all components have a name if "name" not in item: instance_index = item.get("index", index) item["instance"] = "{} {}".format(self.module_inst, instance_index) item["name"] = self.module_name # hide separator for all inner components unless existing if index != composite_length: if "separator" not in item: item["separator"] = False item["separator_block_width"] = 0 # If a color was supplied for the composite and a composite # part does not supply a color, use the composite color. if color and "color" not in item: item["color"] = color # Remove any none color from our output if hasattr(item.get("color"), "none_setting"): del item["color"] # set background and border colors. set left/right border widths # only on first/last composites and no border width for inner # composites or we will see border lines between composites. for key, value in self.i3bar_gaps_module_options.items(): if (key == "border_left" and index != 0) or ( key == "border_right" and index != composite_length ): item[key] = 0 else: item[key] = value # set urgent based on available user-defined settings if not self.allow_urgent: if "urgent" in item: del item["urgent"] elif urgent: if self.i3bar_gaps_urgent_options: # set background and border colors. set left/right border widths # only on first/last composites and no border width for inner # composites or we will see border lines between composites. for key, value in self.i3bar_gaps_urgent_options.items(): if (key == "border_left" and index != 0) or ( key == "border_right" and index != composite_length ): item[key] = 0 elif key == "foreground": item["color"] = value else: item[key] = value if "urgent" in item: del item["urgent"] else: item["urgent"] = urgent # set min_length if "min_length" in self.py3status_module_options: min_length = self.py3status_module_options["min_length"] # get length, skip if length exceeds min_length length = sum([len(x["full_text"]) for x in response["composite"]]) if length >= min_length: return # sometimes we go under min_length to pad both side evenly, # we will add extra space on either side to honor min_length padding = int((min_length / 2.0) - (length / 2.0)) offset = min_length - ((padding * 2) + length) # set position position = self.py3status_module_options.get("position", "left") if position == "center": left = right = " " * padding if self.random_int: left += " " * offset else: right += " " * offset elif position == "left": left, right = "", " " * (padding * 2 + offset) elif position == "right": right, left = "", " " * (padding * 2 + offset) # padding if left: response["composite"][0]["full_text"] = ( left + response["composite"][0]["full_text"] ) if right: response["composite"][-1]["full_text"] += right
def process_composite(self, response): """ Process a composite response. composites do not have inter item separators as they appear joined. We need to respect the universal options too. """ composite = response['composite'] # if the composite is of not Composite make it one so we can simplify # it. if not isinstance(composite, Composite): composite = Composite(composite) # simplify and get underlying list. composite = composite.simplify().get_content() response['composite'] = composite if not isinstance(composite, list): raise Exception('expecting "composite" key in response') # if list is empty nothing to do if not len(composite): return if 'full_text' in response: err = 'conflicting "full_text" and "composite" in response' raise Exception(err) # set universal options on last component composite[-1].update(self.module_options) # calculate any min width (we split this across components) min_width = None if 'min_width' in self.module_options: min_width = int(self.module_options['min_width'] / len(composite)) # store alignment align = None if 'align' in self.module_options: align = self.module_options['align'] # update all components color = response.get('color') urgent = response.get('urgent') for index, item in enumerate(response['composite']): # validate the response if 'full_text' not in item: raise KeyError('missing "full_text" key in response') # make sure all components have a name if 'name' not in item: instance_index = item.get('index', index) item['instance'] = '{} {}'.format( self.module_inst, instance_index ) item['name'] = self.module_name # hide separator for all inner components unless existing if index != len(response['composite']) - 1: if 'separator' not in item: item['separator'] = False item['separator_block_width'] = 0 # set min width if min_width: item['min_width'] = min_width # set align if align: item['align'] = align # If a color was supplied for the composite and a composite # part does not supply a color, use the composite color. if color and 'color' not in item: item['color'] = color # Remove any none color from our output if hasattr(item.get('color'), 'none_setting'): del item['color'] # remove urgent if not allowed if not self.allow_urgent: if 'urgent' in item: del item['urgent'] # if urgent we want to set this to all parts elif urgent and 'urgent' not in item: item['urgent'] = urgent
def process_composite(self, response): """ Process a composite response. composites do not have inter item separators as they appear joined. We need to respect the universal options too. """ composite = response["composite"] # if the composite is of not Composite make it one so we can simplify # it. if not isinstance(composite, Composite): composite = Composite(composite) # simplify and get underlying list. composite = composite.simplify().get_content() response["composite"] = composite if not isinstance(composite, list): raise Exception('expecting "composite" key in response') # if list is empty nothing to do if not len(composite): return if "full_text" in response: err = 'conflicting "full_text" and "composite" in response' raise Exception(err) # set universal options on last component composite[-1].update(self.i3s_module_options) # update all components color = response.get("color") urgent = response.get("urgent") for index, item in enumerate(response["composite"]): # validate the response if "full_text" not in item: raise KeyError('missing "full_text" key in response') # make sure all components have a name if "name" not in item: instance_index = item.get("index", index) item["instance"] = "{} {}".format(self.module_inst, instance_index) item["name"] = self.module_name # hide separator for all inner components unless existing if index != len(response["composite"]) - 1: if "separator" not in item: item["separator"] = False item["separator_block_width"] = 0 # If a color was supplied for the composite and a composite # part does not supply a color, use the composite color. if color and "color" not in item: item["color"] = color # Remove any none color from our output if hasattr(item.get("color"), "none_setting"): del item["color"] # remove urgent if not allowed if not self.allow_urgent: if "urgent" in item: del item["urgent"] # if urgent we want to set this to all parts elif urgent and "urgent" not in item: item["urgent"] = urgent # set min_length if "min_length" in self.py3_module_options: min_length = self.py3_module_options["min_length"] # get length, skip if length exceeds min_length length = sum([len(x["full_text"]) for x in response["composite"]]) if length >= min_length: return # sometimes we go under min_length to pad both side evenly, # we will add extra space on either side to honor min_length padding = int((min_length / 2.0) - (length / 2.0)) offset = min_length - ((padding * 2) + length) # set position position = self.py3_module_options.get("position", "left") if position == "center": left = right = " " * padding if self.random_int: left += " " * offset else: right += " " * offset elif position == "left": left, right = "", " " * (padding * 2 + offset) elif position == "right": right, left = "", " " * (padding * 2 + offset) # padding if left: response["composite"][0]["full_text"] = ( left + response["composite"][0]["full_text"]) if right: response["composite"][-1]["full_text"] += right
'zero_str': '0', 'zero_float': 0.0, 'str_int': '123', 'str_float': '123.456', 'str_nan': "I'm not a number", 'composite_basic': Composite([{ 'full_text': 'red ', 'color': '#FF0000' }, { 'full_text': 'green ', 'color': '#00FF00' }, { 'full_text': 'blue', 'color': '#0000FF' }]), 'complex': Composite([{ 'full_text': 'LA 09:34' }, { 'full_text': 'NY 12:34' }]), 'complex2': Composite([{ 'full_text': 'LA 09:34', 'color': '#FF0000' }, {
def process_composite(self, response): """ Process a composite response. composites do not have inter item separators as they appear joined. We need to respect the universal options too. """ composite = response['composite'] # if the composite is of not Composite make it one so we can simplify # it. if not isinstance(composite, Composite): composite = Composite(composite) # simplify and get underlying list. composite = composite.simplify().get_content() response['composite'] = composite if not isinstance(composite, list): raise Exception('expecting "composite" key in response') # if list is empty nothing to do if not len(composite): return if 'full_text' in response: err = 'conflicting "full_text" and "composite" in response' raise Exception(err) # set universal options on last component composite[-1].update(self.i3s_module_options) # update all components color = response.get('color') urgent = response.get('urgent') for index, item in enumerate(response['composite']): # validate the response if 'full_text' not in item: raise KeyError('missing "full_text" key in response') # make sure all components have a name if 'name' not in item: instance_index = item.get('index', index) item['instance'] = '{} {}'.format(self.module_inst, instance_index) item['name'] = self.module_name # hide separator for all inner components unless existing if index != len(response['composite']) - 1: if 'separator' not in item: item['separator'] = False item['separator_block_width'] = 0 # If a color was supplied for the composite and a composite # part does not supply a color, use the composite color. if color and 'color' not in item: item['color'] = color # Remove any none color from our output if hasattr(item.get('color'), 'none_setting'): del item['color'] # remove urgent if not allowed if not self.allow_urgent: if 'urgent' in item: del item['urgent'] # if urgent we want to set this to all parts elif urgent and 'urgent' not in item: item['urgent'] = urgent # set min_width if 'min_width' in self.py3_module_options: min_width = self.py3_module_options['min_width'] # get width, skip if width exceeds min_width width = sum([len(x['full_text']) for x in response['composite']]) if width >= min_width: return # sometimes when we go under min_width to pad both side evenly, # we will add extra space on either side to honor min_width padding = int((min_width / 2.0) - (width / 2.0)) offset = min_width - ((padding * 2) + width) # set align align = self.py3_module_options.get('align', 'center') if align == 'center': left = right = ' ' * padding if self.random_int: left += ' ' * offset else: right += ' ' * offset elif align == 'left': left, right = '', ' ' * (padding * 2 + offset) elif align == 'right': right, left = '', ' ' * (padding * 2 + offset) # padding if left: response['composite'][0]['full_text'] = ( left + response['composite'][0]['full_text']) if right: response['composite'][-1]['full_text'] += right
def format(self, format_string, module=None, param_dict=None, force_composite=False, attr_getter=None): """ Format a string, substituting place holders which can be found in param_dict, attributes of the supplied module, or provided via calls to the attr_getter function. """ def set_param(param, value, key, block, format=''): """ Converts a placeholder to a string value. We fix python 2 unicode issues and use string.format() to ensure that formatting is applied correctly """ if self.python2 and isinstance(param, str): param = param.decode('utf-8') # '', None, and False are ignored # numbers like 0 and 0.0 are not. if not (param in ['', None] or param is False): if format.startswith(':'): # if a parameter has been set to be formatted as a numeric # type then we see if we can coerce it to be. This allows # the user to format types that normally would not be # allowed eg '123' it also allows {:d} to be used as a # shorthand for {:.0f}. If the parameter cannot be # successfully converted then the format is removed. try: if 'f' in format: param = float(param) if 'd' in format: param = int(float(param)) except ValueError: value = u'{%s}' % key value = value.format(**{key: param}) block.add(value) # If not_zero block command is used we do not want to mark this # block as valid if the parameter is zero. # we do of course want to et the parameter in case the block is # valid via another route, eg second parameter try: if block.block_config.not_zero and float(param) == 0: return except ValueError: pass block.mark_valid() # fix python 2 unicode issues if self.python2 and isinstance(format_string, str): format_string = format_string.decode('utf-8') if param_dict is None: param_dict = {} block = Block(param_dict, module) # Tokenize the format string and process them for token in self.tokens(format_string): value = token.group(0) if token.group('block_start'): # Create new block new_block = Block(param_dict, module, block) block.add(new_block) block = new_block elif token.group('block_end'): # Close block setting any valid state as needed # and return to parent block to continue block.set_valid_state() if not block.parent: raise Exception('Too many `]`') block = block.parent elif token.group('switch'): # a new option has been created block.set_valid_state() block.switch() elif token.group('placeholder'): # Found a {placeholder} key = token.group('key') if key in param_dict: # was a supplied parameter param = param_dict.get(key) if isinstance(param, Composite): # supplied parameter is a composite if param.get_content(): block.add(param.copy()) block.mark_valid() else: format = token.group('format') set_param(param, value, key, block, format) elif module and hasattr(module, key): # attribute of the module param = getattr(module, key) if not hasattr(param, '__call__'): set_param(param, value, key, block) else: block.add(value) elif attr_getter: # get value from attr_getter function param = attr_getter(key) set_param(param, value, key, block) else: # substitution not found so add as a literal block.add(value) elif token.group('literal'): block.add(value) elif token.group('lost_brace'): # due to how parsing happens we can get a lonesome } # eg in format_string '{{something}' this fixes that issue block.add(value) elif token.group('command'): # a block command has been found block.set_commands(token.group('command')) elif token.group('escaped'): # escaped characters add unescaped values if value[0] in ['\\', '{', '}']: value = value[1:] block.add(value) if block.parent: raise Exception('Block not closed') # This is the main block if none of the sections are valid use the last # one for situations like '{placeholder}|Nothing' if not block.valid_blocks: block.mark_valid() output = block.show() # post format # swap color names to values for item in output: # ignore empty items if not item.get('full_text') and not item.get('separator'): continue # colors color_this = item.get('color') if color_this and color_this[0] != '#': color_name = 'color_%s' % color_this threshold_color_name = 'color_threshold_%s' % color_this # substitute color color_this = ( getattr(module, color_name, None) or getattr(module, threshold_color_name, None) or getattr(module.py3, color_name.upper(), None) ) if color_this: item['color'] = color_this else: del item['color'] output = Composite(output).simplify() # if only text then we can become a string if not force_composite: if len(output) == 0: return '' elif (len(output) == 1 and list(output[0].keys()) == ['full_text']): output = output[0]['full_text'] return output
"str_float": "123.456", "str_nan": "I'm not a number", "trailing_zeroes_1": "50.000", "trailing_zeroes_2": "5.500", "composite_basic": Composite([ { "full_text": "red ", "color": "#FF0000" }, { "full_text": "green ", "color": "#00FF00" }, { "full_text": "blue", "color": "#0000FF" }, ]), "complex": Composite([{ "full_text": "LA 09:34" }, { "full_text": "NY 12:34" }]), "complex2": Composite([{ "full_text": "LA 09:34",
def process_composite(self, response): """ Process a composite response. composites do not have inter item separators as they appear joined. We need to respect the universal options too. """ composite = response["composite"] # if the composite is of not Composite make it one so we can simplify # it. if not isinstance(composite, Composite): composite = Composite(composite) # simplify and get underlying list. composite = composite.simplify().get_content() response["composite"] = composite if not isinstance(composite, list): raise Exception('expecting "composite" key in response') # if list is empty nothing to do if not len(composite): return if "full_text" in response: err = 'conflicting "full_text" and "composite" in response' raise Exception(err) # set markup if "markup" in self.py3status_module_options: markup = self.py3status_module_options["markup"] line = "" for item in composite: # validate the response if "full_text" not in item: raise KeyError('missing "full_text" key in response') color = item.get("color") if color: span = u"<span fgcolor='{}'>{}</span>" line += span.format(color, item["full_text"]) else: line += item["full_text"] composite = [{"full_text": line, "markup": markup}] response["composite"] = composite # set universal options on last component composite[-1].update(self.i3bar_module_options) # update all components color = response.get("color") urgent = response.get("urgent") composite_length = len(response["composite"]) - 1 for index, item in enumerate(response["composite"]): # validate the response if "full_text" not in item: raise KeyError('missing "full_text" key in response') # make sure all components have a name if "name" not in item: instance_index = item.get("index", index) item["instance"] = "{} {}".format(self.module_inst, instance_index) item["name"] = self.module_name # hide separator for all inner components unless existing if index != composite_length: if "separator" not in item: item["separator"] = False item["separator_block_width"] = 0 # If a color was supplied for the composite and a composite # part does not supply a color, use the composite color. if color and "color" not in item: item["color"] = color # Remove any none color from our output if hasattr(item.get("color"), "none_setting"): del item["color"] # set background and border colors. set left/right border widths # only on first/last composites and no border width for inner # composites or we will see border lines between composites. for key, value in self.i3bar_gaps_module_options.items(): if (key == "border_left" and index != 0) or (key == "border_right" and index != composite_length): item[key] = 0 else: item[key] = value # set urgent based on available user-defined settings if not self.allow_urgent: if "urgent" in item: del item["urgent"] elif urgent: if self.i3bar_gaps_urgent_options: # set background and border colors. set left/right border widths # only on first/last composites and no border width for inner # composites or we will see border lines between composites. for key, value in self.i3bar_gaps_urgent_options.items(): if (key == "border_left" and index != 0) or (key == "border_right" and index != composite_length): item[key] = 0 elif key == "foreground": item["color"] = value else: item[key] = value if "urgent" in item: del item["urgent"] else: item["urgent"] = urgent # set min_length if "min_length" in self.py3status_module_options: min_length = self.py3status_module_options["min_length"] # get length, skip if length exceeds min_length length = sum([len(x["full_text"]) for x in response["composite"]]) if length >= min_length: return # sometimes we go under min_length to pad both side evenly, # we will add extra space on either side to honor min_length padding = int((min_length / 2.0) - (length / 2.0)) offset = min_length - ((padding * 2) + length) # set position position = self.py3status_module_options.get("position", "left") if position == "center": left = right = " " * padding if self.random_int: left += " " * offset else: right += " " * offset elif position == "left": left, right = "", " " * (padding * 2 + offset) elif position == "right": right, left = "", " " * (padding * 2 + offset) # padding if left: response["composite"][0]["full_text"] = ( left + response["composite"][0]["full_text"]) if right: response["composite"][-1]["full_text"] += right