def tabular_table(word_list=None, field_width=26, line_length=78, output_separator=" ", truncate_elements=True): """ This function returns a tabulated string composed of a basic list of words. """ if not word_list: word_list = list() elements = [ANSIString(entry) for entry in word_list] if truncate_elements: elements = [entry[:field_width] for entry in elements] elements = [entry.ljust(field_width) for entry in elements] separator_length = len(output_separator) per_line = line_length / (field_width + separator_length) result_string = ANSIString("") count = 0 total = len(elements) for num, element in enumerate(elements): count += 1 if count == 1: result_string += element elif count == per_line: result_string += output_separator result_string += element if not num+1 == total: result_string += '\n' count = 0 elif count > 1: result_string += output_separator result_string += element return result_string
def rename(self, key): """ Renames a channel category and updates all relevant fields. Args: key (str): The category's new name. Can include ANSI codes. Returns: key (ANSIString): The successful key set. """ if '|' in key and not key.endswith('|n'): key += '|n' key = ANSIString(key) clean_key = str(key.clean()) if '|' in clean_key: raise ValueError("Malformed ANSI in Channel CategoryName.") if not self.re_name.match(clean_key): raise ValueError( "Channel Category name does not meet standards. Avoid double spaces and special characters." ) bridge = self.bridge if bridge.db_system.channel_categories.filter( db_iname=clean_key.lower()).exclude(db_script=self).count(): raise ValueError("Name conflicts with another Channel Category.") self.key = clean_key bridge.db_name = clean_key bridge.db_iname = clean_key.lower() bridge.db_cname = key.raw() bridge.save(update_fields=['db_name', 'db_iname', 'db_cname']) return key
def test_slice_insert(self): """ Inserting a slice should not remove ansi markup (issue #2205) """ string = ANSIString("|rTest|n") split_string = string[:0] + "Test" + string[4:] self.assertEqual(string.raw(), split_string.raw())
def wod_header(title=None, align="l", linecolor="W", textcolor="w", accentcolor="w"): ''' Shows a 78 character wide header. Optional header imbedded in it. :param title: String that will be shown in white over the header, optional. :param align: "l", "c", or "r" alignment. Only used if title is provided. :param linecolor: Single character color code, default is 'W' (Light grey) :param textcolor: Single character color code, default is 'w' (White) :param accentcolor: Single character color code, default is 'w' (White) ''' if title: fulltitle = "|%s===|%s|||%s %s |%s|||%s===" % ( linecolor, accentcolor, textcolor, title, accentcolor, linecolor) w = len(strip_ansi(fulltitle)) if align == "r": line = '|%s%s|n' % (linecolor, ANSIString(fulltitle).rjust( width=78, fillchar="=")) elif align == "c": line = '|%s%s|n' % (linecolor, ANSIString(fulltitle).center( width=78, fillchar="=")) else: line = '|%s%s|n' % (linecolor, ANSIString(fulltitle).ljust( width=78, fillchar="=")) else: line = '|%s%s|n' % (linecolor, pad('', width=78, fillchar='=')) return line
def test_capitalize(self): """ Make sure that capitalization works. This is the simplest of the _transform functions. """ target = ANSIString('|gtest|n') result = u'\x1b[1m\x1b[32mTest\x1b[0m' self.checker(target.capitalize(), result, u'Test')
def evtable_options_formatter(optionlist, caller=None): """ Formats the option list display. """ if not optionlist: return "" # column separation distance colsep = 4 nlist = len(optionlist) # get the widest option line in the table. table_width_max = -1 table = [] for key, desc in optionlist: if not (key or desc): continue table_width_max = max(table_width_max, max(m_len(p) for p in key.split("\n")) + max(m_len(p) for p in desc.split("\n")) + colsep) raw_key = strip_ansi(key) if raw_key != key: # already decorations in key definition table.append(ANSIString(" |lc%s|lt%s|le: %s" % (raw_key, key, desc))) else: # add a default white color to key table.append(ANSIString(" |lc%s|lt|w%s|n|le: %s" % (raw_key, raw_key, desc))) ncols = (_MAX_TEXT_WIDTH // table_width_max) + 1 # number of ncols nlastcol = nlist % ncols # number of elements left in last row # get the amount of rows needed (start with 4 rows) nrows = 4 while nrows * ncols < nlist: nrows += 1 ncols = nlist // nrows # number of full columns nlastcol = nlist % nrows # number of elements in last column # get the final column count ncols = ncols + 1 if nlastcol > 0 else ncols if ncols > 1: # only extend if longer than one column table.extend([" " for i in range(nrows - nlastcol)]) # build the actual table grid table = [table[icol * nrows : (icol * nrows) + nrows] for icol in range(0, ncols)] # adjust the width of each column for icol in range(len(table)): col_width = max(max(m_len(p) for p in part.split("\n")) for part in table[icol]) + colsep table[icol] = [pad(part, width=col_width + colsep, align="l") for part in table[icol]] # format the table into columns return unicode(EvTable(table=table, border="none"))
def clean_and_ansi(input_text, thing_name="Name"): if not input_text: raise ValueError(f"{thing_name} must not be empty!") input_text = input_text.strip() if '|' in input_text and not input_text.endswith('|n'): input_text += "|n" colored_text = ANSIString(input_text) clean_text = str(colored_text.clean()) if '|' in clean_text: raise ValueError(f"Malformed ANSI in {thing_name}.") return clean_text, colored_text
def create_event(cls, key, **kwargs): key = ANSIString(key) clean_key = str(key.clean()) if '|' in clean_key: raise ValueError("Malformed ANSI in Plot Name.") if PlotBridge.objects.filter(db_iname=clean_key.lower()).count(): raise ValueError("Name conflicts with another Plot.") script, errors = cls.create(clean_key, **kwargs) if script: script.create_bridge(key, clean_key) script.setup_locks() return script
def unread_star(self, account, admin=False): link = self.links.filter(account_stub__account=account).first() if not link: return ANSIString('|r*|n') if admin: if max(self.admin_update, self.public_update) > link.check_date: return ANSIString('|r*|n') else: return " " if self.public_update > link.check_date: return ANSIString('|r*|n') else: return " "
def render_stafflist(self, session): color = self['category_color'] output = list() output.append(session.ath['render'].header("Staff List")) for cat in StaffCategory.objects.all().order_by('order'): rank_string = ANSIString('|%s%s|n' % (color, cat.key)) output.append(rank_string.center(78)) staffers = ", ".join( str(s) for s in cat.staffers.all().order_by('order')).center(78) output.append(staffers) output.append(session.ath['render'].footer()) return "\n".join(str(line) for line in output)
def create_post(self, account, character, subject, text, date=None): if not date: date = utcnow() name = ANSIString(subject) if '|' in name: raise ValueError("Malformed ANSI in post subject!") cname = name.raw() next = self.next_post_id new_post = self.posts.create(account=account, character=character, name=name.clean(), cname=cname, order=next, body=text, date_created=date, date_modified=date) new_post.update_read(account) next += 1 return new_post
def rename(self, key): key = ANSIString(key) clean_key = str(key.clean()) if '|' in clean_key: raise ValueError("Malformed ANSI in Theme Name.") bridge = self.theme_bridge if ThemeBridge.objects.filter(db_iname=clean_key.lower()).exclude( id=bridge).count(): raise ValueError("Name conflicts with another Theme.") self.key = clean_key bridge.db_name = clean_key bridge.db_iname = clean_key.lower() bridge.db_cname = key
def test_instance(self): """ Make sure the ANSIString is always constructed correctly. """ clean = u'This isA|r testTest' encoded = u'\x1b[1m\x1b[32mThis is\x1b[1m\x1b[31mA|r test\x1b[0mTest\x1b[0m' target = ANSIString(r'|gThis is|rA||r test|nTest|n') char_table = [ 9, 10, 11, 12, 13, 14, 15, 25, 26, 27, 28, 29, 30, 31, 32, 37, 38, 39, 40 ] code_table = [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 16, 17, 18, 19, 20, 21, 22, 23, 24, 33, 34, 35, 36, 41, 42, 43, 44 ] self.checker(target, encoded, clean) self.table_check(target, char_table, code_table) self.checker(ANSIString(target), encoded, clean) self.table_check(ANSIString(target), char_table, code_table) self.checker(ANSIString(encoded, decoded=True), encoded, clean) self.table_check(ANSIString(encoded, decoded=True), char_table, code_table) self.checker(ANSIString('Test'), u'Test', u'Test') self.table_check(ANSIString('Test'), [0, 1, 2, 3], []) self.checker(ANSIString(''), u'', u'')
def create_theme(cls, name, description, **kwargs): key = ANSIString(name) clean_key = str(key.clean()) if '|' in clean_key: raise ValueError("Malformed ANSI in Theme Name.") if ThemeBridge.objects.filter(db_iname=clean_key.lower()).count(): raise ValueError("Name conflicts with another Theme.") obj, errors = cls.create(clean_key, **kwargs) if obj: obj.create_bridge(key, clean_key) obj.db.desc = description else: raise ValueError(errors) return obj
def test_add(self): """ Verify concatenation works correctly. """ a = ANSIString("|gTest") b = ANSIString("|cString|n") c = a + b result = u'\x1b[1m\x1b[32mTest\x1b[1m\x1b[36mString\x1b[0m' self.checker(c, result, u'TestString') char_table = [9, 10, 11, 12, 22, 23, 24, 25, 26, 27] code_table = [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 13, 14, 15, 16, 17, 18, 19, 20, 21, 28, 29, 30, 31 ] self.table_check(c, char_table, code_table)
def test_split(self): """ Verifies that re.split and .split behave similarly and that color codes end up where they should. """ target = ANSIString("|gThis is |nA split string|g") first = (u'\x1b[1m\x1b[32mThis is \x1b[0m', u'This is ') second = (u'\x1b[1m\x1b[32m\x1b[0m split string\x1b[1m\x1b[32m', u' split string') re_split = re.split('A', target) normal_split = target.split('A') self.assertEqual(re_split, normal_split) self.assertEqual(len(normal_split), 2) self.checker(normal_split[0], *first) self.checker(normal_split[1], *second)
def test_regex_replace(self): """ Inserting text into an ansistring at an index position should ignore the ansi markers but not remove them! """ string = ANSIString("A |rTest|n string") match = re.search(r"Test", string) ix1, ix2 = match.span() self.assertEqual((ix1, ix2), (2, 6)) result = string[:ix1] + "Replacement" + string[ix2:] expected = ANSIString("A |rReplacement|n string") self.assertEqual(expected, result)
def rename(self, key): key = ANSIString(key) clean_key = str(key.clean()) if '|' in clean_key: raise ValueError("Malformed ANSI in Faction Name.") bridge = self.faction_bridge parent = bridge.db_parent if FactionBridge.objects.filter( db_iname=clean_key.lower(), db_parent=parent).exclude(id=bridge).count(): raise ValueError( "Name conflicts with another Faction with the same Parent.") self.key = clean_key bridge.db_name = clean_key bridge.db_iname = clean_key.lower() bridge.db_cname = key
def create_channel(cls, category, key, unique_key=None): key = ANSIString(key) clean_key = str(key.clean()) if '|' in clean_key: raise ValueError("Malformed ANSI in Channel Name.") if not cls.re_name.match(clean_key): raise ValueError("Channel Names must be EXPLANATION HERE.") if category.channel_category_bridge.channels.filter( db_iname=clean_key.lower()).count(): raise ValueError("Name conflicts with another Channel.") channel, errors = cls.create(clean_key) if channel: channel.create_bridge(category, key.raw(), clean_key, unique_key) else: raise ValueError(errors) return channel
def test_join(self): """ Verify that joining a set of ANSIStrings works. """ # This isn't the desired behavior, but the expected one. Python # concatenates the in-memory representation with the built-in string's # join. l = [ANSIString("|gTest|r") for _ in range(0, 3)] # Force the generator to be evaluated. result = "".join(l) self.assertEqual(unicode(result), u'TestTestTest') result = ANSIString("").join(l) self.checker( result, u'\x1b[1m\x1b[32mTest\x1b[1m\x1b[31m\x1b[1m\x1b' u'[32mTest\x1b[1m\x1b[31m\x1b[1m\x1b[32mTest' u'\x1b[1m\x1b[31m', u'TestTestTest')
def create_faction(cls, key, parent=None, abbr=None, tier=0, **kwargs): key = ANSIString(key) clean_key = str(key.clean()) if '|' in clean_key: raise ValueError("Malformed ANSI in Faction Name.") if FactionBridge.objects.filter(db_iname=clean_key.lower(), db_parent=parent).count(): raise ValueError( "Name conflicts with another Faction with the same Parent.") obj, errors = cls.create(clean_key, **kwargs) if obj: obj.create_bridge(parent, key, clean_key, abbr, tier) obj.setup_faction() else: raise ValueError(errors) return obj
def test_slice_insert_longer(self): """ The ANSIString replays the color code before the split in order to produce a *visually* identical result. The result is a longer string in raw characters, but one which correctly represents the color output. """ string = ANSIString("A bigger |rTest|n of things |bwith more color|n") # from evennia import set_trace;set_trace() split_string = string[:9] + "Test" + string[13:] self.assertEqual( repr(( ANSIString("A bigger ") + ANSIString( "|rTest" ) # note that the |r|n is replayed together on next line + ANSIString("|r|n of things |bwith more color|n")).raw()), repr(split_string.raw()))
def sanitize_string(text=None, length=None, strip_ansi=False, strip_mxp=True, strip_newlines=True, strip_indents=True): if not text: return '' text = text.strip() if strip_mxp: text = ANSI_PARSER.strip_mxp(text) if strip_ansi: text = ANSIString(text).clean() if strip_newlines: for bad_char in ['\n', '%r', '%R', '|/']: text = text.replace(bad_char, '') if strip_indents: for bad_char in ['\t', '%t', '%T', '|-']: text = text.replace(bad_char, '') if length: text = text[:length] return text
def test_regex_search(self): """ Test regex-search in ANSIString - the found position should ignore any ansi-markers """ string = ANSIString(" |r|[b Test ") match = re.search(r"Test", string) self.assertTrue(match) self.assertEqual(match.span(), (3, 7))
def _to_ansi(obj, regexable=False): "convert to ANSIString" if isinstance(obj, dict): return dict((key, _to_ansi(value, regexable=regexable)) for key, value in obj.items()) elif hasattr(obj, "__iter__"): return [_to_ansi(o) for o in obj] else: return ANSIString(to_unicode(obj), regexable=regexable)
def create_channel_category(cls, chan_sys, key): if '|' in key and not key.endswith('|n'): key += '|n' key = ANSIString(key) clean_key = str(key.clean()) if '|' in clean_key: raise ValueError("Malformed ANSI in Channel Category Name.") if not cls.re_name.match(clean_key): raise ValueError("Channel Category names must be EXPLANATION.") if chan_sys.channel_system_bridge.channel_categories.filter( db_iname=clean_key.lower()).count(): raise ValueError("Name conflicts with another Channel Category.") script, errors = cls.create(clean_key, persistent=True) if script: script.create_bridge(chan_sys, key.raw(), clean_key) else: raise ValueError(errors) return script
def sanitize_string(input=None, length=None, strip_ansi=False, strip_mxp=True, strip_newlines=True, strip_indents=True): if not input: return '' input = input.strip() if strip_mxp: input = ANSI_PARSER.strip_mxp(input) if strip_ansi: input = ANSIString(input).clean() input = unicode(input) if strip_newlines: for bad_char in ['\n', '%r', '%R', '{/']: input = input.replace(bad_char, '') if strip_indents: for bad_char in ['\t', '%t', '%T', '{-']: input = input.replace(bad_char, '') if length: input = input[:length] return unicode(input)
def change_prefix(self, new_prefix): if '|' in new_prefix and not new_prefix.endswith('|n'): new_prefix += '|n' abbr = ANSIString(new_prefix) clean_abbr = str(abbr.clean()) iclean_abbr = clean_abbr.lower() if '|' in clean_abbr: raise ValueError("Malformed ANSI in BBSCategory Prefix.") if not self.re_abbr.match(clean_abbr): raise ValueError("Prefixes must be between 0-3 alphabetical characters.") if BBSCategoryBridge.objects.filter(db_iabbr=iclean_abbr.lower()).count(): raise ValueError("Name or Prefix conflicts with another BBSCategory.") bridge = self.bridge bridge.db_abbr = clean_abbr self.key = clean_abbr bridge.db_iabbr = iclean_abbr bridge.db_cabbr = abbr bridge.save_abbr() return abbr
def create_channel_system(cls, name, category_typeclass, channel_typeclass, command_class): if '|' in name and not name.endswith('|n'): name += '|n' key = ANSIString(name) clean_key = str(key.clean()) if '|' in clean_key: raise ValueError("Malformed ANSI in Channel System Name.") if ChannelSystemBridge.objects.filter( db_system_key=clean_key.lower()).count(): raise ValueError("Name conflicts with another Channel System.") script, errors = cls.create(clean_key, persistent=True) if script: script.create_bridge(clean_key, category_typeclass, channel_typeclass, command_class) script.at_start() else: raise ValueError(errors) return script
def rename(self, key): if '|' in key and not key.endswith('|n'): key += '|n' key = ANSIString(key) clean_key = str(key.clean()) iclean_key = clean_key.lower() if '|' in clean_key: raise ValueError("Malformed ANSI in BBSCategory Name.") if not self.re_name.match(clean_key): raise ValueError("BBS Categories must have simpler names than that!") if BBSCategoryBridge.objects.filter(db_iname=iclean_key).count(): raise ValueError("Name conflicts with another BBSCategory.") bridge = self.bridge bridge.db_name = clean_key self.key = clean_key bridge.db_iname = iclean_key bridge.db_cname = key bridge.save_name() return key
def create_bbs_board(cls, category, key, order, **kwargs): if '|' in key and not key.endswith('|n'): key += '|n' key = ANSIString(key) clean_key = str(key.clean()) if '|' in clean_key: raise ValueError("Malformed ANSI in BBSCategory Name.") if not cls.re_name.match(clean_key): raise ValueError("BBS Board Names must <qualifier>") if BBSBoardBridge.objects.filter(db_category=category.bbs_category_bridge).filter( Q(db_iname=clean_key.lower()) | Q(db_order=order)).count(): raise ValueError("Name or Order conflicts with another BBS Board in this category.") script, errors = cls.create(clean_key, persistent=True, **kwargs) if script: script.create_bridge(category, key.raw(), clean_key, order) script.setup_locks() else: raise ValueError(errors) return script
def test_strip(self): """ Test the ansi-aware .strip() methods """ a = ANSIString(" |r Test of stuff |b with spaces |n ") b = ANSIString("|r|b") self.assertEqual(a.strip(), ANSIString("|rTest of stuff |b with spaces|n")) self.assertEqual(a.lstrip(), ANSIString("|rTest of stuff |b with spaces |n ")) self.assertEqual(a.rstrip(), ANSIString(" |r Test of stuff |b with spaces|n")) self.assertEqual(b.strip(), b)