From b75cd81eda87f33e582a26cd537c88dc46957f43 Mon Sep 17 00:00:00 2001 From: Griatch Date: Sun, 18 Mar 2018 12:53:38 +0100 Subject: [PATCH] Fix unit tests --- evennia/commands/default/building.py | 23 ++++---- evennia/commands/default/tests.py | 8 ++- evennia/utils/spawner.py | 81 ++++++++++++++-------------- 3 files changed, 62 insertions(+), 50 deletions(-) diff --git a/evennia/commands/default/building.py b/evennia/commands/default/building.py index a948da14a9..c2200470c3 100644 --- a/evennia/commands/default/building.py +++ b/evennia/commands/default/building.py @@ -14,8 +14,8 @@ from evennia.utils.utils import inherits_from, class_from_module, get_all_typecl from evennia.utils.eveditor import EvEditor from evennia.utils.evmore import EvMore from evennia.utils.spawner import (spawn, search_prototype, list_prototypes, - store_prototype, build_metaproto, validate_prototype, - delete_prototype, PermissionError) + save_db_prototype, build_metaproto, validate_prototype, + delete_db_prototype, PermissionError) from evennia.utils.ansi import raw COMMAND_DEFAULT_CLASS = class_from_module(settings.COMMAND_DEFAULT_CLASS) @@ -1739,6 +1739,7 @@ class CmdTypeclass(COMMAND_DEFAULT_CLASS): key = "@typeclass" aliases = ["@type", "@parent", "@swap", "@update"] + switch_options = ("show", "examine", "update", "reset", "force", "list") locks = "cmd:perm(typeclass) or perm(Builder)" help_category = "Building" @@ -1749,7 +1750,6 @@ class CmdTypeclass(COMMAND_DEFAULT_CLASS): if 'list' in self.switches: tclasses = get_all_typeclasses() - print(list(tclasses.keys())) contribs = [key for key in sorted(tclasses) if key.startswith("evennia.contrib")] or [""] core = [key for key in sorted(tclasses) @@ -1764,7 +1764,7 @@ class CmdTypeclass(COMMAND_DEFAULT_CLASS): " {game}").format(core="\n ".join(core), contrib="\n ".join(contribs), game="\n ".join(game)) - caller.msg(string) + EvMore(caller, string, exit_on_lastpage=True) return if not self.args: @@ -2841,7 +2841,7 @@ class CmdSpawn(COMMAND_DEFAULT_CLASS): """ key = "@spawn" - switch_options = ("noloc", ) + switch_options = ("noloc", "search", "list", "show", "save", "delete", "menu") locks = "cmd:perm(spawn) or perm(Builder)" help_category = "Building" @@ -2912,7 +2912,7 @@ class CmdSpawn(COMMAND_DEFAULT_CLASS): tags = [tag.strip() for tag in tags.split(",")] if tags else None EvMore(caller, unicode(list_prototypes(caller, key=key, tags=tags)), exit_on_lastpage=True) - return + return if 'show' in self.switches or 'examine' in self.switches: # the argument is a key in this case (may be a partial key) @@ -2943,7 +2943,7 @@ class CmdSpawn(COMMAND_DEFAULT_CLASS): caller.msg("|rDeletion cancelled.|n") return try: - success = delete_prototype(caller, self.args) + success = delete_db_prototype(caller, self.args) except PermissionError as err: caller.msg("|rError deleting:|R {}|n".format(err)) caller.msg("Deletion {}.".format( @@ -2961,10 +2961,12 @@ class CmdSpawn(COMMAND_DEFAULT_CLASS): # handle lhs parts = self.lhs.split(";", 3) - key, desc, tags, lockstring = "", "", [], "" + key, desc, tags, lockstring = ( + "", "User-created prototype", ["user-created"], + "edit:id({}) or perm(Admin); use:all()".format(caller.id)) nparts = len(parts) if nparts == 1: - key = parts.strip() + key = parts[0].strip() elif nparts == 2: key, desc = (part.strip() for part in parts) elif nparts == 3: @@ -3000,7 +3002,7 @@ class CmdSpawn(COMMAND_DEFAULT_CLASS): # all seems ok. Try to save. try: - store_prototype(caller, key, prototype, desc=desc, tags=tags, locks=lockstring) + save_db_prototype(caller, key, prototype, desc=desc, tags=tags, locks=lockstring) except PermissionError as err: caller.msg("|rError saving:|R {}|n".format(err)) return @@ -3038,6 +3040,7 @@ class CmdSpawn(COMMAND_DEFAULT_CLASS): metaproto = metaprotos[0] if not caller.locks.check_lockstring(caller, metaproto.locks, access_type='use'): caller.msg("You don't have access to use this prototype.") + print("spawning2 {}:{} - {}".format(self.cmdstring, self.args, prototype)) return prototype = metaproto.prototype diff --git a/evennia/commands/default/tests.py b/evennia/commands/default/tests.py index bf01ac3039..fe738a3e07 100644 --- a/evennia/commands/default/tests.py +++ b/evennia/commands/default/tests.py @@ -27,6 +27,7 @@ from evennia.utils import ansi, utils from evennia.server.sessionhandler import SESSIONS from evennia import search_object from evennia import DefaultObject, DefaultCharacter +from evennia.utils import spawner # set up signal here since we are not starting the server @@ -390,8 +391,10 @@ class TestBuilding(CommandTest): self.assertEqual(goblin.location, spawnLoc) goblin.delete() + spawner.save_db_prototype(self.char1, "ball", {'key': 'Ball', 'prototype': 'GOBLIN'}) + # Tests "@spawn " - self.call(building.CmdSpawn(), "'BALL'", "Spawned Ball") + self.call(building.CmdSpawn(), "ball", "Spawned Ball") ball = getObject(self, "Ball") self.assertEqual(ball.location, self.char1.location) self.assertIsInstance(ball, DefaultObject) @@ -416,6 +419,9 @@ class TestBuilding(CommandTest): # test calling spawn with an invalid prototype. self.call(building.CmdSpawn(), "'NO_EXIST'", "No prototype named 'NO_EXIST'") + # Test listing commands + self.call(building.CmdSpawn(), "/list", "| Key ") + class TestComms(CommandTest): diff --git a/evennia/utils/spawner.py b/evennia/utils/spawner.py index 2a003069a4..6426aa0acc 100644 --- a/evennia/utils/spawner.py +++ b/evennia/utils/spawner.py @@ -109,17 +109,17 @@ from django.conf import settings from random import randint import evennia from evennia.objects.models import ObjectDB -from evennia.utils.utils import make_iter, all_from_module, dbid_to_obj +from evennia.utils.utils import make_iter, all_from_module, dbid_to_obj, is_iter -from collections import namedtuple, defaultdict +from collections import namedtuple from evennia.scripts.scripts import DefaultScript from evennia.utils.create import create_script from evennia.utils.evtable import EvTable _CREATE_OBJECT_KWARGS = ("key", "location", "home", "destination") -_READONLY_PROTOTYPES = {} -_READONLY_PROTOTYPE_MODULES = {} +_MODULE_PROTOTYPES = {} +_MODULE_PROTOTYPE_MODULES = {} class PermissionError(RuntimeError): @@ -133,7 +133,7 @@ for mod in settings.PROTOTYPE_MODULES: # internally we store as (key, desc, locks, tags, prototype_dict) prots = [(key, prot) for key, prot in all_from_module(mod).items() if prot and isinstance(prot, dict)] - _READONLY_PROTOTYPES.update( + _MODULE_PROTOTYPES.update( {key.lower(): MetaProto( key.lower(), prot['prototype_desc'] if 'prototype_desc' in prot else mod, @@ -142,12 +142,12 @@ for mod in settings.PROTOTYPE_MODULES: prot['prototype_tags']) if 'prototype_tags' in prot else ["base-prototype"]), prot) for key, prot in prots}) - _READONLY_PROTOTYPE_MODULES.update({tup[0]: mod for tup in prots}) + _MODULE_PROTOTYPE_MODULES.update({tup[0]: mod for tup in prots}) # Prototype storage mechanisms -class PersistentPrototype(DefaultScript): +class DbPrototype(DefaultScript): """ This stores a single prototype """ @@ -161,10 +161,10 @@ def build_metaproto(key, desc, locks, tags, prototype): Create a metaproto from combinant parts. """ - return MetaProto(key, desc, make_iter(locks), tags, dict(prototype)) + return MetaProto(key, desc, ";".join(locks) if is_iter(locks) else locks, tags, dict(prototype)) -def store_prototype(caller, key, prototype, desc="", tags=None, locks="", delete=False): +def save_db_prototype(caller, key, prototype, desc="", tags=None, locks="", delete=False): """ Store a prototype persistently. @@ -176,7 +176,7 @@ def store_prototype(caller, key, prototype, desc="", tags=None, locks="", delete prototype (dict): Prototype dict. desc (str, optional): Description of prototype, to use in listing. tags (list, optional): Tag-strings to apply to prototype. These are always - applied with the 'persistent_prototype' category. + applied with the 'db_prototype' category. locks (str, optional): Locks to apply to this prototype. Used locks are 'use' and 'edit' delete (bool, optional): Delete an existing prototype identified by 'key'. @@ -192,14 +192,14 @@ def store_prototype(caller, key, prototype, desc="", tags=None, locks="", delete key_orig = key key = key.lower() locks = locks if locks else "use:all();edit:id({}) or perm(Admin)".format(caller.id) - tags = [(tag, "persistent_prototype") for tag in make_iter(tags)] + tags = [(tag, "db_prototype") for tag in make_iter(tags)] - if key in _READONLY_PROTOTYPES: - mod = _READONLY_PROTOTYPE_MODULES.get(key, "N/A") + if key in _MODULE_PROTOTYPES: + mod = _MODULE_PROTOTYPE_MODULES.get(key, "N/A") raise PermissionError("{} is a read-only prototype " "(defined as code in {}).".format(key_orig, mod)) - stored_prototype = PersistentPrototype.objects.filter(db_key=key) + stored_prototype = DbPrototype.objects.filter(db_key=key) if stored_prototype: # edit existing prototype @@ -227,12 +227,12 @@ def store_prototype(caller, key, prototype, desc="", tags=None, locks="", delete else: # create a new prototype stored_prototype = create_script( - PersistentPrototype, key=key, desc=desc, persistent=True, + DbPrototype, key=key, desc=desc, persistent=True, locks=locks, tags=tags, attributes=[("prototype", prototype)]) return stored_prototype -def delete_prototype(caller, key): +def delete_db_prototype(caller, key): """ Delete a stored prototype @@ -245,21 +245,21 @@ def delete_prototype(caller, key): PermissionError: If 'edit' lock was not passed. """ - return store_prototype(caller, key, None, delete=True) + return save_db_prototype(caller, key, None, delete=True) -def search_persistent_prototype(key=None, tags=None, return_metaprotos=False): +def search_db_prototype(key=None, tags=None, return_metaprotos=False): """ Find persistent (database-stored) prototypes based on key and/or tags. Kwargs: key (str): An exact or partial key to query for. tags (str or list): Tag key or keys to query for. These - will always be applied with the 'persistent_protototype' + will always be applied with the 'db_protototype' tag category. return_metaproto (bool): Return results as metaprotos. Return: - matches (queryset or list): All found PersistentPrototypes. If `return_metaprotos` + matches (queryset or list): All found DbPrototypes. If `return_metaprotos` is set, return a list of MetaProtos. Note: @@ -269,22 +269,22 @@ def search_persistent_prototype(key=None, tags=None, return_metaprotos=False): if tags: # exact match on tag(s) tags = make_iter(tags) - tag_categories = ["persistent_prototype" for _ in tags] - matches = PersistentPrototype.objects.get_by_tag(tags, tag_categories) + tag_categories = ["db_prototype" for _ in tags] + matches = DbPrototype.objects.get_by_tag(tags, tag_categories) else: - matches = PersistentPrototype.objects.all() + matches = DbPrototype.objects.all() if key: # exact or partial match on key matches = matches.filter(db_key=key) or matches.filter(db_key__icontains=key) if return_metaprotos: return [build_metaproto(match.key, match.desc, match.locks.all(), - match.tags.get(category="persistent_prototype", return_list=True), + match.tags.get(category="db_prototype", return_list=True), match.attributes.get("prototype")) for match in matches] return matches -def search_readonly_prototype(key=None, tags=None): +def search_module_prototype(key=None, tags=None): """ Find read-only prototypes, defined in modules. @@ -301,10 +301,10 @@ def search_readonly_prototype(key=None, tags=None): if tags: # use tags to limit selection tagset = set(tags) - matches = {key: metaproto for key, metaproto in _READONLY_PROTOTYPES.items() + matches = {key: metaproto for key, metaproto in _MODULE_PROTOTYPES.items() if tagset.intersection(metaproto.tags)} else: - matches = _READONLY_PROTOTYPES + matches = _MODULE_PROTOTYPES if key: if key in matches: @@ -324,7 +324,7 @@ def search_prototype(key=None, tags=None, return_meta=True): Kwargs: key (str): An exact or partial key to query for. tags (str or list): Tag key or keys to query for. These - will always be applied with the 'persistent_protototype' + will always be applied with the 'db_protototype' tag category. return_meta (bool): If False, only return prototype dicts, if True return MetaProto namedtuples including prototype meta info @@ -340,15 +340,15 @@ def search_prototype(key=None, tags=None, return_meta=True): be found. """ - readonly_prototypes = search_readonly_prototype(key, tags) - persistent_prototypes = search_persistent_prototype(key, tags, return_metaprotos=True) + module_prototypes = search_module_prototype(key, tags) + db_prototypes = search_db_prototype(key, tags, return_metaprotos=True) - matches = persistent_prototypes + readonly_prototypes + matches = db_prototypes + module_prototypes if len(matches) > 1 and key: key = key.lower() # avoid duplicates if an exact match exist between the two types filter_matches = [mta for mta in matches if mta.key == key] - if len(filter_matches) < len(matches): + if filter_matches and len(filter_matches) < len(matches): matches = filter_matches if not return_meta: @@ -369,8 +369,7 @@ def get_protparents(): return {metaproto.key: metaproto.prototype for metaproto in metaprotos} -def list_prototypes(caller, key=None, tags=None, show_non_use=False, - show_non_edit=True, sort_tree=True): +def list_prototypes(caller, key=None, tags=None, show_non_use=False, show_non_edit=True): """ Collate a list of found prototypes based on search criteria and access. @@ -380,22 +379,27 @@ def list_prototypes(caller, key=None, tags=None, show_non_use=False, tags (str or list, optional): Tag key or keys to query for. show_non_use (bool, optional): Show also prototypes the caller may not use. show_non_edit (bool, optional): Show also prototypes the caller may not edit. - sort_tree (bool, optional): Order prototypes by inheritance tree. Returns: table (EvTable or None): An EvTable representation of the prototypes. None if no prototypes were found. """ + # this allows us to pass lists of empty strings + tags = [tag for tag in make_iter(tags) if tag] + # get metaprotos for readonly and db-based prototypes - metaprotos = search_readonly_prototype(key, tags) - metaprotos += search_persistent_prototype(key, tags, return_metaprotos=True) + metaprotos = search_module_prototype(key, tags) + metaprotos += search_db_prototype(key, tags, return_metaprotos=True) # get use-permissions of readonly attributes (edit is always False) prototypes = [ (metaproto.key, metaproto.desc, ("{}/N".format('Y' - if caller.locks.check_lockstring(caller, metaproto.locks, access_type='use') else 'N')), + if caller.locks.check_lockstring( + caller, + metaproto.locks, + access_type='use') else 'N')), ",".join(metaproto.tags)) for metaproto in sorted(metaprotos, key=lambda o: o.key)] @@ -642,7 +646,6 @@ def spawn(*prototypes, **kwargs): return _batch_create_object(*objsparams) - # Testing if __name__ == "__main__":