diff --git a/CHANGELOG.md b/CHANGELOG.md index f92cdab5ee..25b114905a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,8 +5,14 @@ - [Feature][pull3470]: New `exit_order` kwarg to `DefaultObject.get_display_exits` to easier customize the order in which standard exits are displayed in a room (chiizujin) +- [Fix][pull3495]: Fix rate in Trait contribs not updating after reload (jaborsh) +- [Fix][pull3491]: Fix traceback in EvEditor when searching with malformed regex (chiizujin) +- [Docs]: Doc fixes (Griatch, chiizujin) [pull3470]: https://github.com/evennia/evennia/pull/3470 +[pull3495]: https://github.com/evennia/evennia/pull/3495 +[pull3491]: https://github.com/evennia/evennia/pull/3491 + ## Evennia 4.1.1 diff --git a/docs/source/Howtos/Beginner-Tutorial/Part3/Beginner-Tutorial-Utilities.md b/docs/source/Howtos/Beginner-Tutorial/Part3/Beginner-Tutorial-Utilities.md index effd4bce3d..b715cfb559 100644 --- a/docs/source/Howtos/Beginner-Tutorial/Part3/Beginner-Tutorial-Utilities.md +++ b/docs/source/Howtos/Beginner-Tutorial/Part3/Beginner-Tutorial-Utilities.md @@ -135,7 +135,7 @@ ABILITY_REVERSE_MAP = { Above, the `Ability` class holds some basic properties of a character sheet. -The `ABILITY_REVERSE_MAP` is a convenient map to go the other way &mdas; if in some command we were to enter the string 'cha', we could use this mapping to directly convert your input to the correct `Ability`. For example: +The `ABILITY_REVERSE_MAP` is a convenient map to go the other way — if in some command we were to enter the string 'cha', we could use this mapping to directly convert your input to the correct `Ability`. For example: ability = ABILITY_REVERSE_MAP.get(your_input) diff --git a/evennia/contrib/base_systems/components/dbfield.py b/evennia/contrib/base_systems/components/dbfield.py index 67f812b484..4b9c6d4fa8 100644 --- a/evennia/contrib/base_systems/components/dbfield.py +++ b/evennia/contrib/base_systems/components/dbfield.py @@ -6,8 +6,7 @@ This file contains the Descriptors used to set Fields in Components import typing -from evennia.typeclasses.attributes import (AttributeProperty, - NAttributeProperty) +from evennia.typeclasses.attributes import AttributeProperty, NAttributeProperty if typing.TYPE_CHECKING: from .components import Component diff --git a/evennia/contrib/base_systems/components/tests.py b/evennia/contrib/base_systems/components/tests.py index 75e169b825..57d072b8f7 100644 --- a/evennia/contrib/base_systems/components/tests.py +++ b/evennia/contrib/base_systems/components/tests.py @@ -268,7 +268,7 @@ class TestComponents(EvenniaTest): def test_mutables_are_not_shared_when_autocreate(self): self.char1.test_a.my_list.append(1) - self.assertNotEqual(id(self.char1.test_a.my_list), id(self.char2.test_a.my_list)) + self.assertIsNot(self.char1.test_a.my_list, self.char2.test_a.my_list) def test_replacing_class_component_slot_with_runtime_component(self): self.char1.components.add_default("replacement_inherited_test_a") diff --git a/evennia/contrib/rpg/traits/traits.py b/evennia/contrib/rpg/traits/traits.py index fbcbeda34a..59daf7d70f 100644 --- a/evennia/contrib/rpg/traits/traits.py +++ b/evennia/contrib/rpg/traits/traits.py @@ -661,7 +661,12 @@ class TraitHandler: return trait def add( - self, trait_key, name=None, trait_type=DEFAULT_TRAIT_TYPE, force=True, **trait_properties + self, + trait_key, + name=None, + trait_type=DEFAULT_TRAIT_TYPE, + force=True, + **trait_properties, ): """ Create a new Trait and add it to the handler. @@ -945,7 +950,12 @@ class Trait: def __getattr__(self, key): """Access extra parameters as attributes.""" - if key in ("default_keys", "data_default", "trait_type", "allow_extra_properties"): + if key in ( + "default_keys", + "data_default", + "trait_type", + "allow_extra_properties", + ): return _GA(self, key) try: return self._data[key] @@ -1276,7 +1286,7 @@ class CounterTrait(Trait): ) # set up rate if trait_data["rate"] != 0: - trait_data["last_update"] = time() + trait_data["last_update"] = trait_data.get("last_update", time()) else: trait_data["last_update"] = None return trait_data @@ -1552,6 +1562,7 @@ class GaugeTrait(CounterTrait): rate = self.rate if rate != 0 and self._data["last_update"] is not None: now = time() + tdiff = now - self._data["last_update"] current += rate * tdiff value = current diff --git a/evennia/objects/objects.py b/evennia/objects/objects.py index 150c95f5d1..dcf9b49f90 100644 --- a/evennia/objects/objects.py +++ b/evennia/objects/objects.py @@ -1588,6 +1588,7 @@ class DefaultObject(ObjectDB, metaclass=TypeclassBase): obj.get_display_name(looker, exit_order=('north', 'south')) -> "Exits: north, south, out, and portal." (markup not shown here) """ + def _sort_exit_names(names): exit_order = kwargs.get("exit_order") if not exit_order: @@ -1595,7 +1596,7 @@ class DefaultObject(ObjectDB, metaclass=TypeclassBase): sort_index = {name: key for key, name in enumerate(exit_order)} names = sorted(names) end_pos = len(names) + 1 - names.sort(key=lambda name:sort_index.get(name, end_pos)) + names.sort(key=lambda name: sort_index.get(name, end_pos)) return names exits = self.filter_visible(self.contents_get(content_type="exit"), looker, **kwargs) diff --git a/evennia/objects/tests.py b/evennia/objects/tests.py index 5c300865dc..df00189d50 100644 --- a/evennia/objects/tests.py +++ b/evennia/objects/tests.py @@ -3,8 +3,12 @@ from unittest import skip from evennia import DefaultCharacter, DefaultExit, DefaultObject, DefaultRoom from evennia.objects.models import ObjectDB from evennia.typeclasses.attributes import AttributeProperty -from evennia.typeclasses.tags import (AliasProperty, PermissionProperty, - TagCategoryProperty, TagProperty) +from evennia.typeclasses.tags import ( + AliasProperty, + PermissionProperty, + TagCategoryProperty, + TagProperty, +) from evennia.utils import create, search from evennia.utils.ansi import strip_ansi from evennia.utils.test_resources import BaseEvenniaTest, EvenniaTestCase @@ -102,7 +106,7 @@ class DefaultObjectTest(BaseEvenniaTest): self.assertEqual(exits, "Exits: out, south, portal, north, and aperture") # in specified order with unspecified exits alpbabetically on the end - exit_order = ('north', 'south', 'out') + exit_order = ("north", "south", "out") exits = strip_ansi(self.room1.get_display_exits(self.char1, exit_order=exit_order)) self.assertEqual(exits, "Exits: north, south, out, aperture, and portal") @@ -589,7 +593,6 @@ class TestProperties(EvenniaTestCase): # check cross-instance sharing self.assertEqual(obj2.attr5, [], "cross-instance sharing detected") - def test_mutable_defaults__autocreate_false(self): """ Test https://github.com/evennia/evennia/issues/3488, where a mutable default value (like a @@ -631,7 +634,6 @@ class TestProperties(EvenniaTestCase): # check cross-instance sharing self.assertEqual(obj2.attr7, []) - def test_mutable_defaults__autocreate_true(self): """ Test mutable defaults with autocreate=True. @@ -652,4 +654,3 @@ class TestProperties(EvenniaTestCase): obj1.delete() obj2.delete() - diff --git a/evennia/typeclasses/attributes.py b/evennia/typeclasses/attributes.py index bdef3bdc33..03929dac43 100644 --- a/evennia/typeclasses/attributes.py +++ b/evennia/typeclasses/attributes.py @@ -17,6 +17,7 @@ from copy import copy from django.conf import settings from django.db import models from django.utils.encoding import smart_str + from evennia.locks.lockhandler import LockHandler from evennia.utils.dbserialize import from_pickle, to_pickle from evennia.utils.idmapper.models import SharedMemoryModel diff --git a/evennia/utils/eveditor.py b/evennia/utils/eveditor.py index 9101fdcac6..c7a7a04c92 100644 --- a/evennia/utils/eveditor.py +++ b/evennia/utils/eveditor.py @@ -654,29 +654,34 @@ class CmdEditorGroup(CmdEditorBase): if not self.linerange: lstart = 0 lend = self.cline + 1 - caller.msg( - _("Search-replaced {arg1} -> {arg2} for lines {l1}-{l2}.").format( - arg1=self.arg1, arg2=self.arg2, l1=lstart + 1, l2=lend - ) - ) - else: - caller.msg( - _("Search-replaced {arg1} -> {arg2} for {line}.").format( - arg1=self.arg1, arg2=self.arg2, line=self.lstr - ) - ) sarea = "\n".join(linebuffer[lstart:lend]) regex = r"%s|^%s(?=\s)|(?<=\s)%s(?=\s)|^%s$|(?<=\s)%s$" regarg = self.arg1.strip("'").strip('"') if " " in regarg: regarg = regarg.replace(" ", " +") - sarea = re.sub( - regex % (regarg, regarg, regarg, regarg, regarg), - self.arg2.strip("'").strip('"'), - sarea, - re.MULTILINE, - ) + try: + sarea = re.sub( + regex % (regarg, regarg, regarg, regarg, regarg), + self.arg2.strip("'").strip('"'), + sarea, + re.MULTILINE, + ) + except re.error as e: + caller.msg(_("Invalid regular expression.")) + else: + if not self.linerange: + caller.msg( + _("Search-replaced {arg1} -> {arg2} for lines {l1}-{l2}.").format( + arg1=self.arg1, arg2=self.arg2, l1=lstart + 1, l2=lend + ) + ) + else: + caller.msg( + _("Search-replaced {arg1} -> {arg2} for {line}.").format( + arg1=self.arg1, arg2=self.arg2, line=self.lstr + ) + ) buf = linebuffer[:lstart] + sarea.split("\n") + linebuffer[lend:] editor.update_buffer(buf) elif cmd == ":f":