From 020854f69768d503a229a848cac1cbe55a5beb40 Mon Sep 17 00:00:00 2001 From: Griatch Date: Sat, 5 Nov 2022 23:30:11 +0100 Subject: [PATCH] Fix EvTable issues. --- evennia/commands/default/system.py | 5 +++-- evennia/commands/default/tests.py | 11 +++++----- evennia/utils/evtable.py | 11 +++++----- evennia/utils/tests/test_evtable.py | 2 +- evennia/utils/tests/test_utils.py | 33 ++++++++++++++++++++++++----- evennia/utils/utils.py | 12 ++++++++--- 6 files changed, 52 insertions(+), 22 deletions(-) diff --git a/evennia/commands/default/system.py b/evennia/commands/default/system.py index cc0f4f3819..a39b0066cd 100644 --- a/evennia/commands/default/system.py +++ b/evennia/commands/default/system.py @@ -487,7 +487,8 @@ class CmdAccounts(COMMAND_DEFAULT_CLASS): string += " Reason given:\n '%s'" % reason account.msg(string) logger.log_sec( - f"Account Deleted: {account} (Reason: {reason}, Caller: {caller}, IP: {self.session.address})." + f"Account Deleted: {account} (Reason: {reason}, Caller: {caller}, IP:" + f" {self.session.address})." ) account.delete() self.msg("Account %s was successfully deleted." % username) @@ -1169,7 +1170,7 @@ class CmdTasks(COMMAND_DEFAULT_CLASS): tasks_list[i].append(task_data[i]) # create and display the table tasks_table = EvTable( - *tasks_header, table=tasks_list, maxwidth=width, border="cells", align="center" + *tasks_header, table=tasks_list, maxwidth=width, border="cells", align="c" ) actions = (f"/{switch}" for switch in self.switch_options) helptxt = f"\nActions: {iter_to_str(actions)}" diff --git a/evennia/commands/default/tests.py b/evennia/commands/default/tests.py index cb990d507e..5b92eda25e 100644 --- a/evennia/commands/default/tests.py +++ b/evennia/commands/default/tests.py @@ -1584,9 +1584,8 @@ class TestBuilding(BaseEvenniaCommandTest): self.call( building.CmdTeleport(), "Obj = Room2", - "Obj(#{}) is leaving Room(#{}), heading for Room2(#{}).|Teleported Obj -> Room2.".format( - oid, rid, rid2 - ), + "Obj(#{}) is leaving Room(#{}), heading for Room2(#{}).|Teleported Obj -> Room2." + .format(oid, rid, rid2), ) self.call(building.CmdTeleport(), "NotFound = Room", "Could not find 'NotFound'.") self.call( @@ -1702,7 +1701,8 @@ class TestBuilding(BaseEvenniaCommandTest): self.call( building.CmdSpawn(), "{'prototype_key':'GOBLIN', 'typeclass':'evennia.objects.objects.DefaultCharacter', " - "'key':'goblin', 'location':'%s'}" % spawnLoc.dbref, + "'key':'goblin', 'location':'%s'}" + % spawnLoc.dbref, "Spawned goblin", ) goblin = get_object(self, "goblin") @@ -1750,7 +1750,8 @@ class TestBuilding(BaseEvenniaCommandTest): self.call( building.CmdSpawn(), "/noloc {'prototype_parent':'TESTBALL', 'key': 'Ball', 'prototype_key': 'foo'," - " 'location':'%s'}" % spawnLoc.dbref, + " 'location':'%s'}" + % spawnLoc.dbref, "Spawned Ball", ) ball = get_object(self, "Ball") diff --git a/evennia/utils/evtable.py b/evennia/utils/evtable.py index 1b914c234d..39c4908d34 100644 --- a/evennia/utils/evtable.py +++ b/evennia/utils/evtable.py @@ -354,8 +354,9 @@ class EvCell: desired size). This can be overruled by individual settings below. hfill_char (str): Character used for horizontal fill (default `" "`). vfill_char (str): Character used for vertical fill (default `" "`). - align (str): Should be one of "l", "r" or "c" for left-, right- or center - horizontal alignment respectively. Default is left-aligned. + align (str): Should be one of "l", "r", "c", "f" or "a" for left-, right-, center-, + full-justified (with space between words) or absolute (keep as much original + whitespace as possible). Default is left-aligned. valign (str): Should be one of "t", "b" or "c" for top-, bottom and center vertical alignment respectively. Default is centered. border_width (int): General border width. This is overruled @@ -388,7 +389,6 @@ class EvCell: small. """ - self.formatted = None padwidth = kwargs.get("pad_width", None) padwidth = int(padwidth) if padwidth is not None else None @@ -601,7 +601,6 @@ class EvCell: align = self.align hfill_char = self.hfill_char width = self.width - return [justify(line, width, align=align, fillchar=hfill_char) for line in data] def _valign(self, data): @@ -900,7 +899,7 @@ class EvCell: # EvColumn class -class EvColumn(object): +class EvColumn: """ This class holds a list of Cells to represent a column of a table. It holds operations and settings that affect *all* cells in the @@ -1030,7 +1029,7 @@ class EvColumn(object): # Main Evtable class -class EvTable(object): +class EvTable: """ The table class holds a list of EvColumns, each consisting of EvCells so that the result is a 2D matrix. diff --git a/evennia/utils/tests/test_evtable.py b/evennia/utils/tests/test_evtable.py index d5cffcefa1..e562452412 100644 --- a/evennia/utils/tests/test_evtable.py +++ b/evennia/utils/tests/test_evtable.py @@ -1,5 +1,5 @@ """ -Tests for EvTable +Tests for EvTable component. """ diff --git a/evennia/utils/tests/test_utils.py b/evennia/utils/tests/test_utils.py index 50339104b3..b7ebaa2223 100644 --- a/evennia/utils/tests/test_utils.py +++ b/evennia/utils/tests/test_utils.py @@ -7,16 +7,15 @@ TODO: Not nearly all utilities are covered yet. import os.path import random -from parameterized import parameterized -import mock from datetime import datetime, timedelta +import mock from django.test import TestCase -from twisted.internet import task - -from evennia.utils.ansi import ANSIString from evennia.utils import utils +from evennia.utils.ansi import ANSIString from evennia.utils.test_resources import BaseEvenniaTest +from parameterized import parameterized +from twisted.internet import task class TestIsIter(TestCase): @@ -735,3 +734,27 @@ class TestIntConversions(TestCase): with self.assertRaises(ValueError): utils.str2int("not a number") + + +class TestJustify(TestCase): + def test_justify_whitespace(self): + result = utils.justify(" ", 1, align="l") + self.assertEqual(" ", result) + + result = utils.justify("", 1, align="l") + self.assertEqual(" ", result) + + @parameterized.expand( + [ + (5, "Task \n ID "), + (6, " Task \n ID "), + (7, "Task ID"), + (8, "Task ID "), + (9, " Task ID "), + (10, " Task ID "), + (11, " Task ID "), + ] + ) + def test_center_justify_small(self, width, expected): + result = utils.justify("Task ID", width, align="c", indent=0, fillchar=" ") + self.assertEqual(expected, result) diff --git a/evennia/utils/utils.py b/evennia/utils/utils.py index 1cd5471de8..958a00c918 100644 --- a/evennia/utils/utils.py +++ b/evennia/utils/utils.py @@ -214,7 +214,7 @@ def dedent(text, baseline_index=None, indent=None): ) -def justify(text, width=None, align="f", indent=0, fillchar=" "): +def justify(text, width=None, align="l", indent=0, fillchar=" "): """ Fully justify a text so that it fits inside `width`. When using full justification (default) this will be done by padding between @@ -240,7 +240,7 @@ def justify(text, width=None, align="f", indent=0, fillchar=" "): """ helper function that distributes extra spaces between words. The number of gaps is nwords - 1 but must be at least 1 for single-word lines. We - distribute odd spaces randomly to one of the gaps. + distribute odd spaces to one of the gaps. """ line_rest = width - (wlen + ngaps) gap = " " # minimum gap between words @@ -270,7 +270,7 @@ def justify(text, width=None, align="f", indent=0, fillchar=" "): return [sp * width] return gap.join(line) - width = width if width else settings.CLIENT_DEFAULT_WIDTH + width = width if width is not None else settings.CLIENT_DEFAULT_WIDTH sp = fillchar if align == "a": @@ -296,7 +296,12 @@ def justify(text, width=None, align="f", indent=0, fillchar=" "): words.extend((word, len(word)) for word in paragraph.split()) ngaps, wlen, line = 0, 0, [] + if not words: + # Just whitespace! + return sp * width + lines = [] + while words: if not line: # start a new line @@ -322,6 +327,7 @@ def justify(text, width=None, align="f", indent=0, fillchar=" "): if line: # catch any line left behind lines.append(_process_line(line)) indentstring = sp * indent + out = "\n".join([indentstring + line for line in lines]) return "\n".join([indentstring + line for line in lines])