Merge branch 'main' of https://github.com/evennia/evennia into editor_echo

This commit is contained in:
Chiizujin 2024-03-31 15:26:18 +11:00
commit 4a1ff3deeb
18 changed files with 711 additions and 273 deletions

View file

@ -780,13 +780,14 @@ class CmdSetHelp(CmdHelp):
Edit the help database.
Usage:
sethelp[/switches] <topic>[[;alias;alias][,category[,locks]] [= <text>]
sethelp[/switches] <topic>[[;alias;alias][,category[,locks]]
[= <text or new category>]
Switches:
edit - open a line editor to edit the topic's help text.
replace - overwrite existing help topic.
append - add text to the end of existing topic with a newline between.
extend - as append, but don't add a newline.
category - change category of existing help topic.
delete - remove help topic.
Examples:
@ -794,6 +795,7 @@ class CmdSetHelp(CmdHelp):
sethelp/append pickpocketing,Thievery = This steals ...
sethelp/replace pickpocketing, ,attr(is_thief) = This steals ...
sethelp/edit thievery
sethelp/category thievery = classes
If not assigning a category, the `settings.DEFAULT_HELP_CATEGORY` category
will be used. If no lockstring is specified, everyone will be able to read
@ -840,7 +842,7 @@ class CmdSetHelp(CmdHelp):
key = "sethelp"
aliases = []
switch_options = ("edit", "replace", "append", "extend", "delete")
switch_options = ("edit", "replace", "append", "extend", "category", "delete")
locks = "cmd:perm(Helper)"
help_category = "Building"
arg_regex = None
@ -857,7 +859,7 @@ class CmdSetHelp(CmdHelp):
if not self.args:
self.msg(
"Usage: sethelp[/switches] <topic>[;alias;alias][,category[,locks,..] = <text>"
"Usage: sethelp[/switches] <topic>[[;alias;alias][,category[,locks]] [= <text or new category>]"
)
return
@ -953,7 +955,7 @@ class CmdSetHelp(CmdHelp):
else:
helpentry = create.create_help_entry(
topicstr,
self.rhs,
self.rhs if self.rhs is not None else "",
category=category,
locks=lockstring,
aliases=aliases,
@ -986,6 +988,19 @@ class CmdSetHelp(CmdHelp):
self.msg(f"Entry updated:\n{old_entry.entrytext}{aliastxt}")
return
if "category" in switches:
# set the category
if not old_entry:
self.msg(f"Could not find topic '{topicstr}'{aliastxt}.")
return
if not self.rhs:
self.msg("You must supply a category.")
return
category = self.rhs.lower()
old_entry.help_category = category
self.msg(f"Category for entry '{topicstr}'{aliastxt} changed to '{category}'.")
return
if "delete" in switches or "del" in switches:
# delete the help entry
if not old_entry:

View file

@ -197,6 +197,12 @@ class TestHelp(BaseEvenniaCommandTest):
cmdset=CharacterCmdSet(),
)
self.call(help_module.CmdHelp(), "testhelp", "Help for testhelp", cmdset=CharacterCmdSet())
self.call(
help_module.CmdSetHelp(),
"/category testhelp = misc",
"Category for entry 'testhelp' changed to 'misc'.",
cmdset=CharacterCmdSet(),
)
@parameterized.expand(
[

View file

@ -237,7 +237,7 @@ class CraftingRecipeBase:
**kwargs: Any optional properties relevant to this send.
"""
self.crafter.msg(message, {"type": "crafting"})
self.crafter.msg(text=(message, {"type": "crafting"}))
def pre_craft(self, **kwargs):
"""

View file

@ -78,7 +78,7 @@ class TestCraftingRecipeBase(BaseEvenniaTestCase):
"""Test messaging to crafter"""
self.recipe.msg("message")
self.crafter.msg.assert_called_with("message", {"type": "crafting"})
self.crafter.msg.assert_called_with(text=("message", {"type": "crafting"}))
def test_pre_craft(self):
"""Test validating hook"""
@ -206,7 +206,7 @@ class TestCraftingRecipe(BaseEvenniaTestCase):
self.assertEqual(result[0].key, "Result1")
self.assertEqual(result[0].tags.all(), ["result1", "resultprot"])
self.crafter.msg.assert_called_with(
recipe.success_message.format(outputs="Result1"), {"type": "crafting"}
text=(recipe.success_message.format(outputs="Result1"), {"type": "crafting"})
)
# make sure consumables are gone
@ -235,7 +235,7 @@ class TestCraftingRecipe(BaseEvenniaTestCase):
self.assertEqual(result[0].key, "Result1")
self.assertEqual(result[0].tags.all(), ["result1", "resultprot"])
self.crafter.msg.assert_called_with(
recipe.success_message.format(outputs="Result1"), {"type": "crafting"}
text=(recipe.success_message.format(outputs="Result1"), {"type": "crafting"})
)
# make sure consumables are gone
@ -251,8 +251,10 @@ class TestCraftingRecipe(BaseEvenniaTestCase):
result = recipe.craft()
self.assertFalse(result)
self.crafter.msg.assert_called_with(
recipe.error_tool_missing_message.format(outputs="Result1", missing="tool2"),
{"type": "crafting"},
text=(
recipe.error_tool_missing_message.format(outputs="Result1", missing="tool2"),
{"type": "crafting"},
)
)
# make sure consumables are still there
@ -269,8 +271,10 @@ class TestCraftingRecipe(BaseEvenniaTestCase):
result = recipe.craft()
self.assertFalse(result)
self.crafter.msg.assert_called_with(
recipe.error_consumable_missing_message.format(outputs="Result1", missing="cons3"),
{"type": "crafting"},
text=(
recipe.error_consumable_missing_message.format(outputs="Result1", missing="cons3"),
{"type": "crafting"},
)
)
# make sure consumables are still there
@ -293,8 +297,10 @@ class TestCraftingRecipe(BaseEvenniaTestCase):
self.assertFalse(result)
self.crafter.msg.assert_called_with(
recipe.error_consumable_missing_message.format(outputs="Result1", missing="cons3"),
{"type": "crafting"},
text=(
recipe.error_consumable_missing_message.format(outputs="Result1", missing="cons3"),
{"type": "crafting"},
)
)
# make sure consumables are deleted even though we failed
@ -317,10 +323,12 @@ class TestCraftingRecipe(BaseEvenniaTestCase):
result = recipe.craft()
self.assertFalse(result)
self.crafter.msg.assert_called_with(
recipe.error_tool_excess_message.format(
outputs="Result1", excess=wrong.get_display_name(looker=self.crafter)
),
{"type": "crafting"},
text=(
recipe.error_tool_excess_message.format(
outputs="Result1", excess=wrong.get_display_name(looker=self.crafter)
),
{"type": "crafting"},
)
)
# make sure consumables are still there
self.assertIsNotNone(self.cons1.pk)
@ -342,10 +350,12 @@ class TestCraftingRecipe(BaseEvenniaTestCase):
result = recipe.craft()
self.assertFalse(result)
self.crafter.msg.assert_called_with(
recipe.error_tool_excess_message.format(
outputs="Result1", excess=tool3.get_display_name(looker=self.crafter)
),
{"type": "crafting"},
text=(
recipe.error_tool_excess_message.format(
outputs="Result1", excess=tool3.get_display_name(looker=self.crafter)
),
{"type": "crafting"},
)
)
# make sure consumables are still there
@ -369,10 +379,12 @@ class TestCraftingRecipe(BaseEvenniaTestCase):
result = recipe.craft()
self.assertFalse(result)
self.crafter.msg.assert_called_with(
recipe.error_consumable_excess_message.format(
outputs="Result1", excess=cons4.get_display_name(looker=self.crafter)
),
{"type": "crafting"},
text=(
recipe.error_consumable_excess_message.format(
outputs="Result1", excess=cons4.get_display_name(looker=self.crafter)
),
{"type": "crafting"},
)
)
# make sure consumables are still there
@ -396,7 +408,7 @@ class TestCraftingRecipe(BaseEvenniaTestCase):
result = recipe.craft()
self.assertTrue(result)
self.crafter.msg.assert_called_with(
recipe.success_message.format(outputs="Result1"), {"type": "crafting"}
text=(recipe.success_message.format(outputs="Result1"), {"type": "crafting"})
)
# make sure consumables are gone
@ -419,7 +431,7 @@ class TestCraftingRecipe(BaseEvenniaTestCase):
result = recipe.craft()
self.assertTrue(result)
self.crafter.msg.assert_called_with(
recipe.success_message.format(outputs="Result1"), {"type": "crafting"}
text=(recipe.success_message.format(outputs="Result1"), {"type": "crafting"})
)
# make sure consumables are gone
@ -439,10 +451,12 @@ class TestCraftingRecipe(BaseEvenniaTestCase):
result = recipe.craft()
self.assertFalse(result)
self.crafter.msg.assert_called_with(
recipe.error_tool_order_message.format(
outputs="Result1", missing=self.tool2.get_display_name(looker=self.crafter)
),
{"type": "crafting"},
text=(
recipe.error_tool_order_message.format(
outputs="Result1", missing=self.tool2.get_display_name(looker=self.crafter)
),
{"type": "crafting"},
)
)
# make sure consumables are still there
@ -462,10 +476,12 @@ class TestCraftingRecipe(BaseEvenniaTestCase):
result = recipe.craft()
self.assertFalse(result)
self.crafter.msg.assert_called_with(
recipe.error_consumable_order_message.format(
outputs="Result1", missing=self.cons3.get_display_name(looker=self.crafter)
),
{"type": "crafting"},
text=(
recipe.error_consumable_order_message.format(
outputs="Result1", missing=self.cons3.get_display_name(looker=self.crafter)
),
{"type": "crafting"},
)
)
# make sure consumables are still there

View file

@ -47,17 +47,8 @@ from collections import deque
from django.conf import settings
from django.db.models import Q
from evennia import (
CmdSet,
DefaultRoom,
EvEditor,
FuncParser,
InterruptCommand,
default_cmds,
gametime,
utils,
)
from evennia import (CmdSet, DefaultRoom, EvEditor, FuncParser,
InterruptCommand, default_cmds, gametime, utils)
from evennia.typeclasses.attributes import AttributeProperty
from evennia.utils.utils import list_to_string, repeat

View file

@ -6,6 +6,7 @@ import time
from anything import Anything
from evennia import DefaultObject, create_object, default_cmds
from evennia.commands.default import building
from evennia.commands.default.tests import BaseEvenniaCommandTest
from evennia.utils.test_resources import BaseEvenniaTest
@ -413,10 +414,9 @@ class TestRPSystemCommands(BaseEvenniaCommandTest):
expected_first_call = [
"More than one match for 'Mushroom' (please narrow target):",
f" Mushroom-1 []",
f" Mushroom-2 []",
f" Mushroom-1",
f" Mushroom-2",
]
self.call(default_cmds.CmdLook(), "Mushroom", "\n".join(expected_first_call)) # PASSES
expected_second_call = f"Mushroom(#{mushroom1.id})\nThe first mushroom is brown."
@ -424,3 +424,13 @@ class TestRPSystemCommands(BaseEvenniaCommandTest):
expected_third_call = f"Mushroom(#{mushroom2.id})\nThe second mushroom is red."
self.call(default_cmds.CmdLook(), "Mushroom-2", expected_third_call) # FAILS
expected_fourth_call = "Alias(es) for 'Mushroom' set to 'fungus'."
self.call(building.CmdSetObjAlias(), "Mushroom-1 = fungus", expected_fourth_call) #PASSES
expected_fifth_call = [
"More than one match for 'Mushroom' (please narrow target):",
f" Mushroom-1 [fungus]",
f" Mushroom-2",
]
self.call(default_cmds.CmdLook(), "Mushroom", "\n".join(expected_fifth_call)) # PASSES

View file

@ -84,15 +84,3 @@ class ObjType(Enum):
MAGIC = "magic"
QUEST = "quest"
TREASURE = "treasure"
class QuestStatus(Enum):
"""
Quest status
"""
STARTED = "started"
COMPLETED = "completed"
ABANDONED = "abandoned"
FAILED = "failed"

View file

@ -2,19 +2,17 @@
A simple quest system for EvAdventure.
A quest is represented by a quest-handler sitting as
.quest on a Character. Individual Quests are objects
that track the state and can have multiple steps, each
of which are checked off during the quest's progress.
The player can use the quest handler to track the
progress of their quests.
`.quests` on a Character. Individual Quests are child classes of `EvAdventureQuest` with
methods for each step of the quest. The quest handler can add, remove, and track the progress
by calling the `progress` method on the quest. Persistent changes are stored on the quester
using the `add_data` and `get_data` methods with an Attribute as storage backend.
A quest ending can mean a reward or the start of
another quest.
"""
from .enums import QuestStatus
from evennia import Command
class EvAdventureQuest:
@ -47,6 +45,9 @@ class EvAdventureQuest:
self.current_step = "end"
self.progress()
def step_B(self, *args, **kwargs):
def step_end(self, *args, **kwargs):
if len(self.quester.contents) > 4:
self.quester.msg("Quest complete!")
@ -54,62 +55,23 @@ class EvAdventureQuest:
```
"""
key = "basequest"
key = "base quest"
desc = "This is the base quest class"
start_step = "start"
completed_text = "This quest is completed!"
abandoned_text = "This quest is abandoned."
# help entries for quests (could also be methods)
help_start = "You need to start first"
help_end = "You need to end the quest"
def __init__(self, quester, data=None):
if " " in self.key:
raise TypeError("The Quest name must not have spaces in it.")
def __init__(self, quester):
self.quester = quester
self.data = data or dict()
self.data = self.questhandler.load_quest_data(self.key)
self._current_step = self.get_data("current_step")
if not self.current_step:
self.current_step = self.start_step
@property
def questhandler(self):
return self.quester.quests
@property
def current_step(self):
return self._current_step
@current_step.setter
def current_step(self, step_name):
self._current_step = step_name
self.add_data("current_step", step_name)
self.questhandler.do_save = True
@property
def status(self):
return self.get_data("status", QuestStatus.STARTED)
@status.setter
def status(self, value):
self.add_data("status", value)
@property
def is_completed(self):
return self.status == QuestStatus.COMPLETED
@property
def is_abandoned(self):
return self.status == QuestStatus.ABANDONED
@property
def is_failed(self):
return self.status == QuestStatus.FAILED
def add_data(self, key, value):
"""
Add data to the quest. This saves it permanently.
@ -120,18 +82,7 @@ class EvAdventureQuest:
"""
self.data[key] = value
self.questhandler.save_quest_data(self.key, self.data)
def remove_data(self, key):
"""
Remove data from the quest permanently.
Args:
key (str): The key to remove.
"""
self.data.pop(key, None)
self.questhandler.save_quest_data(self.key, self.data)
self.questhandler.save_quest_data(self.key)
def get_data(self, key, default=None):
"""
@ -147,23 +98,70 @@ class EvAdventureQuest:
"""
return self.data.get(key, default)
def abandon(self):
def remove_data(self, key):
"""
Call when quest is abandoned.
Remove data from the quest permanently.
Args:
key (str): The key to remove.
"""
self.add_data("status", QuestStatus.ABANDONED)
self.questhandler.clean_quest_data(self.key)
self.cleanup()
self.data.pop(key, None)
self.questhandler.save_quest_data(self.key)
@property
def questhandler(self):
return self.quester.quests
@property
def current_step(self):
return self._current_step
@current_step.setter
def current_step(self, step_name):
self._current_step = step_name
self.add_data("current_step", step_name)
@property
def status(self):
return self.get_data("status", "started")
@status.setter
def status(self, value):
self.add_data("status", value)
@property
def is_completed(self):
return self.status == "completed"
@property
def is_abandoned(self):
return self.status == "abandoned"
@property
def is_failed(self):
return self.status == "failed"
def complete(self):
"""
Call this to end the quest.
Complete the quest.
"""
self.add_data("status", QuestStatus.COMPLETED)
self.questhandler.clean_quest_data(self.key)
self.cleanup()
self.status = "completed"
def abandon(self):
"""
Abandon the quest.
"""
self.status = "abandoned"
def fail(self):
"""
Fail the quest.
"""
self.status = "failed"
def progress(self, *args, **kwargs):
"""
@ -174,34 +172,38 @@ class EvAdventureQuest:
Args:
*args, **kwargs: Will be passed into the step method.
"""
return getattr(self, f"step_{self.current_step}")(*args, **kwargs)
Notes:
`self.quester` is available as the character following the quest.
def help(self):
"""
getattr(self, f"step_{self.current_step}")(*args, **kwargs)
def help(self, *args, **kwargs):
"""
This is used to get help (or a reminder) of what needs to be done to complete the current
quest-step.
quest-step. It will look for a `help_<stepname>` method or string attribute on the quest.
Args:
*args, **kwargs: Will be passed into any help_* method.
Returns:
str: The help text for the current step.
"""
if self.is_completed:
return self.completed_text
if self.is_abandoned:
return self.abandoned_text
if self.status in ("abandoned", "completed", "failed"):
help_resource = getattr(self, f"help_{self.status}",
f"You have {self.status} this quest.")
else:
help_resource = getattr(self, f"help_{self.current_step}", "No help available.")
help_resource = (
getattr(self, f"help_{self.current_step}", None)
or "You need to {self.current_step} ..."
)
if callable(help_resource):
# the help_<current_step> can be a method to call
return help_resource()
# the help_* methods can be used to dynamically generate help
return help_resource(*args, **kwargs)
else:
# normally it's just a string
return str(help_resource)
# step methods and hooks
def step_start(self, *args, **kwargs):
@ -243,7 +245,6 @@ class EvAdventureQuestHandler:
def __init__(self, obj):
self.obj = obj
self.do_save = False
self.quests = {}
self.quest_classes = {}
self._load()
@ -256,7 +257,7 @@ class EvAdventureQuestHandler:
)
# instantiate all quests
for quest_key, quest_class in self.quest_classes.items():
self.quests[quest_key] = quest_class(self.obj, self.load_quest_data(quest_key))
self.quests[quest_key] = quest_class(self.obj)
def _save(self):
self.obj.attributes.add(
@ -264,57 +265,6 @@ class EvAdventureQuestHandler:
self.quest_classes,
category=self.quest_storage_attribute_category,
)
self._load() # important
self.do_save = False
def save_quest_data(self, quest_key, data):
"""
Save data for a quest. We store this on the quester as well as updating the quest itself.
Args:
data (dict): The data to store. This is commonly flags or other data needed to track the
quest.
"""
quest = self.get(quest_key)
if quest:
quest.data = data
self.obj.attributes.add(
self.quest_data_attribute_template.format(quest_key=quest_key),
data,
category=self.quest_data_attribute_category,
)
def load_quest_data(self, quest_key):
"""
Load data for a quest.
Args:
quest_key (str): The quest to load data for.
Returns:
dict: The data stored for the quest.
"""
return self.obj.attributes.get(
self.quest_data_attribute_template.format(quest_key=quest_key),
category=self.quest_data_attribute_category,
default={},
)
def clean_quest_data(self, quest_key):
"""
Remove data for a quest.
Args:
quest_key (str): The quest to remove data for.
"""
self.obj.attributes.remove(
self.quest_data_attribute_template.format(quest_key=quest_key),
category=self.quest_data_attribute_category,
)
def has(self, quest_key):
"""
@ -344,6 +294,16 @@ class EvAdventureQuestHandler:
"""
return self.quests.get(quest_key)
def all(self):
"""
Get all quests stored on character.
Returns:
list: All quests stored on character.
"""
return list(self.quests.values())
def add(self, quest_class):
"""
Add a new quest
@ -353,6 +313,7 @@ class EvAdventureQuestHandler:
"""
self.quest_classes[quest_class.key] = quest_class
self.quests[quest_class.key] = quest_class(self.obj)
self._save()
def remove(self, quest_key):
@ -368,50 +329,74 @@ class EvAdventureQuestHandler:
# make sure to cleanup
quest.abandon()
self.quest_classes.pop(quest_key, None)
self.quests.pop(quest_key, None)
self._save()
def get_help(self, quest_key=None):
def save_quest_data(self, quest_key):
"""
Get help text for a quest or for all quests. The help text is
a combination of the description of the quest and the help-text
of the current step.
Save data for a quest. We store this on the quester as well as updating the quest itself.
Args:
quest_key (str, optional): The quest-key. If not given, get help for all
quests in handler.
quest_key (str): The quest to save data for. The data is assumed to be stored on the
quest as `.data` (a dict).
"""
quest = self.get(quest_key)
if quest:
self.obj.attributes.add(
self.quest_data_attribute_template.format(quest_key=quest_key),
quest.data,
category=self.quest_data_attribute_category,
)
def load_quest_data(self, quest_key):
"""
Load data for a quest.
Args:
quest_key (str): The quest to load data for.
Returns:
list: Help texts, one for each quest, or only one if `quest_key` is given.
dict: The data stored for the quest.
"""
help_texts = []
if quest_key in self.quests:
quests = [self.quests[quest_key]]
else:
quests = self.quests.values()
return self.obj.attributes.get(
self.quest_data_attribute_template.format(quest_key=quest_key),
category=self.quest_data_attribute_category,
default={},
)
class CmdQuests(Command):
"""
List all quests and their statuses as well as get info about the status of
a specific quest.
Usage:
quests
quest <questname>
"""
key = "quests"
aliases = ["quest"]
def parse(self):
self.quest_name = self.args.strip()
def func(self):
if self.quest_name:
quest = self.caller.quests.get(self.quest_name)
if not quest:
self.msg(f"Quest {self.quest_name} not found.")
return
self.msg(f"Quest {quest.key}: {quest.status}\n{quest.help()}")
return
quests = self.caller.quests.all()
if not quests:
self.msg("No quests.")
return
for quest in quests:
help_texts.append(f"|c{quest.key}|n\n {quest.desc}\n\n - {quest.help()}")
return help_texts
self.msg(f"Quest {quest.key}: {quest.status}")
def progress(self, quest_key=None, *args, **kwargs):
"""
Check progress of a given quest or all quests.
Args:
quest_key (str, optional): If given, check the progress of this quest (if we have it),
otherwise check progress on all quests.
*args, **kwargs: Will be passed into each quest's `progress` call.
"""
if quest_key in self.quests:
quests = [self.quests[quest_key]]
else:
quests = self.quests.values()
for quest in quests:
quest.progress(*args, **kwargs)
if self.do_save:
# do_save is set by the quest
self._save()

View file

@ -99,52 +99,50 @@ class EvAdventureQuestTest(EvAdventureMixin, BaseEvenniaTest):
def test_help(self):
"""Get help"""
# get help for all quests
help_txt = self.character.quests.get_help()
self.assertEqual(help_txt, ["|ctestquest|n\n A test quest!\n\n - You need to do A first."])
# get help for one specific quest
help_txt = self.character.quests.get_help(_TestQuest.key)
self.assertEqual(help_txt, ["|ctestquest|n\n A test quest!\n\n - You need to do A first."])
quest = self._get_quest()
# get help for a specific quest
help_txt = quest.help()
self.assertEqual(help_txt, "You need to do A first.")
# help for finished quest
self._get_quest().complete()
help_txt = self.character.quests.get_help()
self.assertEqual(help_txt, ["|ctestquest|n\n A test quest!\n\n - This quest is completed!"])
quest.complete()
help_txt = quest.help()
self.assertEqual(help_txt, "You have completed this quest.")
def test_progress__fail(self):
"""
Check progress without having any.
"""
# progress all quests
self.character.quests.progress()
# progress one quest
self.character.quests.progress(_TestQuest.key)
quest = self._get_quest()
# progress quest
quest.progress()
# still on step A
self.assertEqual(self._get_quest().current_step, "A")
self.assertEqual(quest.current_step, "A")
def test_progress(self):
"""
Fulfill the quest steps in sequess
Fulfill the quest steps in sequence.
"""
quest = self._get_quest()
# A requires a certain object in inventory
self._fulfillA()
self.character.quests.progress()
self.assertEqual(self._get_quest().current_step, "B")
quest.progress()
self.assertEqual(quest.current_step, "B")
# B requires progress be called with specific kwarg
# should not step (no kwarg)
self.character.quests.progress()
self.assertEqual(self._get_quest().current_step, "B")
quest.progress()
self.assertEqual(quest.current_step, "B")
# should step (kwarg sent)
self.character.quests.progress(complete_quest_B=True)
self.assertEqual(self._get_quest().current_step, "C")
quest.progress(complete_quest_B=True)
self.assertEqual(quest.current_step, "C")
# C requires a counter Attribute on char be high enough
self._fulfillC()
self.character.quests.progress()
self.assertEqual(self._get_quest().current_step, "C") # still on last step
self.assertEqual(self._get_quest().is_completed, True)
quest.progress()
self.assertEqual(quest.current_step, "C") # still on last step
self.assertEqual(quest.is_completed, True)

View file

@ -227,7 +227,7 @@ class FileHelpStorageHandler:
for dct in loaded_help_dicts:
key = dct.get("key").lower().strip()
category = dct.get("category", _DEFAULT_HELP_CATEGORY).strip()
category = dct.get("category", _DEFAULT_HELP_CATEGORY).lower().strip()
aliases = list(dct.get("aliases", []))
entrytext = dct.get("text", "")
locks = dct.get("locks", "")

View file

@ -138,5 +138,5 @@ class TestFileHelp(TestCase):
for inum, helpentry in enumerate(result):
self.assertEqual(HELP_ENTRY_DICTS[inum]["key"], helpentry.key)
self.assertEqual(HELP_ENTRY_DICTS[inum].get("aliases", []), helpentry.aliases)
self.assertEqual(HELP_ENTRY_DICTS[inum]["category"], helpentry.help_category)
self.assertEqual(HELP_ENTRY_DICTS[inum]["category"].lower(), helpentry.help_category)
self.assertEqual(HELP_ENTRY_DICTS[inum]["text"], helpentry.entrytext)

View file

@ -2070,7 +2070,7 @@ def format_grid(elements, width=78, sep=" ", verbatim_elements=None, line_prefi
else:
row += " " * max(0, width - lrow)
rows.append(row)
row = ""
row = element
ic = 0
else:
# add a new slot
@ -2403,9 +2403,9 @@ def at_search_result(matches, caller, query="", quiet=False, **kwargs):
aliases = result.aliases.all(return_objs=True)
# remove pluralization aliases
aliases = [
alias
alias.db_key
for alias in aliases
if hasattr(alias, "category") and alias.category not in ("plural_key",)
if alias.db_category != "plural_key"
]
else:
# result is likely a Command, where `.aliases` is a list of strings.
@ -2416,7 +2416,7 @@ def at_search_result(matches, caller, query="", quiet=False, **kwargs):
name=result.get_display_name(caller)
if hasattr(result, "get_display_name")
else query,
aliases=" [{alias}]".format(alias=";".join(aliases) if aliases else ""),
aliases=" [{alias}]".format(alias=";".join(aliases)) if aliases else "",
info=result.get_extra_info(caller),
)
matches = None