Fix unit tests

This commit is contained in:
Griatch 2018-03-18 12:53:38 +01:00
parent ed38c17cd2
commit a16e8894b3
3 changed files with 62 additions and 50 deletions

View file

@ -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.eveditor import EvEditor
from evennia.utils.evmore import EvMore from evennia.utils.evmore import EvMore
from evennia.utils.spawner import (spawn, search_prototype, list_prototypes, from evennia.utils.spawner import (spawn, search_prototype, list_prototypes,
store_prototype, build_metaproto, validate_prototype, save_db_prototype, build_metaproto, validate_prototype,
delete_prototype, PermissionError) delete_db_prototype, PermissionError)
from evennia.utils.ansi import raw from evennia.utils.ansi import raw
COMMAND_DEFAULT_CLASS = class_from_module(settings.COMMAND_DEFAULT_CLASS) COMMAND_DEFAULT_CLASS = class_from_module(settings.COMMAND_DEFAULT_CLASS)
@ -1739,6 +1739,7 @@ class CmdTypeclass(COMMAND_DEFAULT_CLASS):
key = "@typeclass" key = "@typeclass"
aliases = ["@type", "@parent", "@swap", "@update"] aliases = ["@type", "@parent", "@swap", "@update"]
switch_options = ("show", "examine", "update", "reset", "force", "list")
locks = "cmd:perm(typeclass) or perm(Builder)" locks = "cmd:perm(typeclass) or perm(Builder)"
help_category = "Building" help_category = "Building"
@ -1749,7 +1750,6 @@ class CmdTypeclass(COMMAND_DEFAULT_CLASS):
if 'list' in self.switches: if 'list' in self.switches:
tclasses = get_all_typeclasses() tclasses = get_all_typeclasses()
print(list(tclasses.keys()))
contribs = [key for key in sorted(tclasses) contribs = [key for key in sorted(tclasses)
if key.startswith("evennia.contrib")] or ["<None loaded>"] if key.startswith("evennia.contrib")] or ["<None loaded>"]
core = [key for key in sorted(tclasses) core = [key for key in sorted(tclasses)
@ -1764,7 +1764,7 @@ class CmdTypeclass(COMMAND_DEFAULT_CLASS):
" {game}").format(core="\n ".join(core), " {game}").format(core="\n ".join(core),
contrib="\n ".join(contribs), contrib="\n ".join(contribs),
game="\n ".join(game)) game="\n ".join(game))
caller.msg(string) EvMore(caller, string, exit_on_lastpage=True)
return return
if not self.args: if not self.args:
@ -2841,7 +2841,7 @@ class CmdSpawn(COMMAND_DEFAULT_CLASS):
""" """
key = "@spawn" key = "@spawn"
switch_options = ("noloc", ) switch_options = ("noloc", "search", "list", "show", "save", "delete", "menu")
locks = "cmd:perm(spawn) or perm(Builder)" locks = "cmd:perm(spawn) or perm(Builder)"
help_category = "Building" help_category = "Building"
@ -2912,7 +2912,7 @@ class CmdSpawn(COMMAND_DEFAULT_CLASS):
tags = [tag.strip() for tag in tags.split(",")] if tags else None tags = [tag.strip() for tag in tags.split(",")] if tags else None
EvMore(caller, unicode(list_prototypes(caller, key=key, tags=tags)), EvMore(caller, unicode(list_prototypes(caller, key=key, tags=tags)),
exit_on_lastpage=True) exit_on_lastpage=True)
return return
if 'show' in self.switches or 'examine' in self.switches: if 'show' in self.switches or 'examine' in self.switches:
# the argument is a key in this case (may be a partial key) # 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") caller.msg("|rDeletion cancelled.|n")
return return
try: try:
success = delete_prototype(caller, self.args) success = delete_db_prototype(caller, self.args)
except PermissionError as err: except PermissionError as err:
caller.msg("|rError deleting:|R {}|n".format(err)) caller.msg("|rError deleting:|R {}|n".format(err))
caller.msg("Deletion {}.".format( caller.msg("Deletion {}.".format(
@ -2961,10 +2961,12 @@ class CmdSpawn(COMMAND_DEFAULT_CLASS):
# handle lhs # handle lhs
parts = self.lhs.split(";", 3) 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) nparts = len(parts)
if nparts == 1: if nparts == 1:
key = parts.strip() key = parts[0].strip()
elif nparts == 2: elif nparts == 2:
key, desc = (part.strip() for part in parts) key, desc = (part.strip() for part in parts)
elif nparts == 3: elif nparts == 3:
@ -3000,7 +3002,7 @@ class CmdSpawn(COMMAND_DEFAULT_CLASS):
# all seems ok. Try to save. # all seems ok. Try to save.
try: 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: except PermissionError as err:
caller.msg("|rError saving:|R {}|n".format(err)) caller.msg("|rError saving:|R {}|n".format(err))
return return
@ -3038,6 +3040,7 @@ class CmdSpawn(COMMAND_DEFAULT_CLASS):
metaproto = metaprotos[0] metaproto = metaprotos[0]
if not caller.locks.check_lockstring(caller, metaproto.locks, access_type='use'): if not caller.locks.check_lockstring(caller, metaproto.locks, access_type='use'):
caller.msg("You don't have access to use this prototype.") caller.msg("You don't have access to use this prototype.")
print("spawning2 {}:{} - {}".format(self.cmdstring, self.args, prototype))
return return
prototype = metaproto.prototype prototype = metaproto.prototype

View file

@ -27,6 +27,7 @@ from evennia.utils import ansi, utils
from evennia.server.sessionhandler import SESSIONS from evennia.server.sessionhandler import SESSIONS
from evennia import search_object from evennia import search_object
from evennia import DefaultObject, DefaultCharacter from evennia import DefaultObject, DefaultCharacter
from evennia.utils import spawner
# set up signal here since we are not starting the server # set up signal here since we are not starting the server
@ -390,8 +391,10 @@ class TestBuilding(CommandTest):
self.assertEqual(goblin.location, spawnLoc) self.assertEqual(goblin.location, spawnLoc)
goblin.delete() goblin.delete()
spawner.save_db_prototype(self.char1, "ball", {'key': 'Ball', 'prototype': 'GOBLIN'})
# Tests "@spawn <prototype_name>" # Tests "@spawn <prototype_name>"
self.call(building.CmdSpawn(), "'BALL'", "Spawned Ball") self.call(building.CmdSpawn(), "ball", "Spawned Ball")
ball = getObject(self, "Ball") ball = getObject(self, "Ball")
self.assertEqual(ball.location, self.char1.location) self.assertEqual(ball.location, self.char1.location)
self.assertIsInstance(ball, DefaultObject) self.assertIsInstance(ball, DefaultObject)
@ -416,6 +419,9 @@ class TestBuilding(CommandTest):
# test calling spawn with an invalid prototype. # test calling spawn with an invalid prototype.
self.call(building.CmdSpawn(), "'NO_EXIST'", "No prototype named 'NO_EXIST'") self.call(building.CmdSpawn(), "'NO_EXIST'", "No prototype named 'NO_EXIST'")
# Test listing commands
self.call(building.CmdSpawn(), "/list", "| Key ")
class TestComms(CommandTest): class TestComms(CommandTest):

View file

@ -109,17 +109,17 @@ from django.conf import settings
from random import randint from random import randint
import evennia import evennia
from evennia.objects.models import ObjectDB 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.scripts.scripts import DefaultScript
from evennia.utils.create import create_script from evennia.utils.create import create_script
from evennia.utils.evtable import EvTable from evennia.utils.evtable import EvTable
_CREATE_OBJECT_KWARGS = ("key", "location", "home", "destination") _CREATE_OBJECT_KWARGS = ("key", "location", "home", "destination")
_READONLY_PROTOTYPES = {} _MODULE_PROTOTYPES = {}
_READONLY_PROTOTYPE_MODULES = {} _MODULE_PROTOTYPE_MODULES = {}
class PermissionError(RuntimeError): class PermissionError(RuntimeError):
@ -133,7 +133,7 @@ for mod in settings.PROTOTYPE_MODULES:
# internally we store as (key, desc, locks, tags, prototype_dict) # internally we store as (key, desc, locks, tags, prototype_dict)
prots = [(key, prot) for key, prot in all_from_module(mod).items() prots = [(key, prot) for key, prot in all_from_module(mod).items()
if prot and isinstance(prot, dict)] if prot and isinstance(prot, dict)]
_READONLY_PROTOTYPES.update( _MODULE_PROTOTYPES.update(
{key.lower(): MetaProto( {key.lower(): MetaProto(
key.lower(), key.lower(),
prot['prototype_desc'] if 'prototype_desc' in prot else mod, 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['prototype_tags']) if 'prototype_tags' in prot else ["base-prototype"]),
prot) prot)
for key, prot in prots}) 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 # Prototype storage mechanisms
class PersistentPrototype(DefaultScript): class DbPrototype(DefaultScript):
""" """
This stores a single prototype This stores a single prototype
""" """
@ -161,10 +161,10 @@ def build_metaproto(key, desc, locks, tags, prototype):
Create a metaproto from combinant parts. 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. Store a prototype persistently.
@ -176,7 +176,7 @@ def store_prototype(caller, key, prototype, desc="", tags=None, locks="", delete
prototype (dict): Prototype dict. prototype (dict): Prototype dict.
desc (str, optional): Description of prototype, to use in listing. desc (str, optional): Description of prototype, to use in listing.
tags (list, optional): Tag-strings to apply to prototype. These are always 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 locks (str, optional): Locks to apply to this prototype. Used locks
are 'use' and 'edit' are 'use' and 'edit'
delete (bool, optional): Delete an existing prototype identified by 'key'. 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_orig = key
key = key.lower() key = key.lower()
locks = locks if locks else "use:all();edit:id({}) or perm(Admin)".format(caller.id) 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: if key in _MODULE_PROTOTYPES:
mod = _READONLY_PROTOTYPE_MODULES.get(key, "N/A") mod = _MODULE_PROTOTYPE_MODULES.get(key, "N/A")
raise PermissionError("{} is a read-only prototype " raise PermissionError("{} is a read-only prototype "
"(defined as code in {}).".format(key_orig, mod)) "(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: if stored_prototype:
# edit existing prototype # edit existing prototype
@ -227,12 +227,12 @@ def store_prototype(caller, key, prototype, desc="", tags=None, locks="", delete
else: else:
# create a new prototype # create a new prototype
stored_prototype = create_script( 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)]) locks=locks, tags=tags, attributes=[("prototype", prototype)])
return stored_prototype return stored_prototype
def delete_prototype(caller, key): def delete_db_prototype(caller, key):
""" """
Delete a stored prototype Delete a stored prototype
@ -245,21 +245,21 @@ def delete_prototype(caller, key):
PermissionError: If 'edit' lock was not passed. 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. Find persistent (database-stored) prototypes based on key and/or tags.
Kwargs: Kwargs:
key (str): An exact or partial key to query for. key (str): An exact or partial key to query for.
tags (str or list): Tag key or keys to query for. These 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. tag category.
return_metaproto (bool): Return results as metaprotos. return_metaproto (bool): Return results as metaprotos.
Return: 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. is set, return a list of MetaProtos.
Note: Note:
@ -269,22 +269,22 @@ def search_persistent_prototype(key=None, tags=None, return_metaprotos=False):
if tags: if tags:
# exact match on tag(s) # exact match on tag(s)
tags = make_iter(tags) tags = make_iter(tags)
tag_categories = ["persistent_prototype" for _ in tags] tag_categories = ["db_prototype" for _ in tags]
matches = PersistentPrototype.objects.get_by_tag(tags, tag_categories) matches = DbPrototype.objects.get_by_tag(tags, tag_categories)
else: else:
matches = PersistentPrototype.objects.all() matches = DbPrototype.objects.all()
if key: if key:
# exact or partial match on key # exact or partial match on key
matches = matches.filter(db_key=key) or matches.filter(db_key__icontains=key) matches = matches.filter(db_key=key) or matches.filter(db_key__icontains=key)
if return_metaprotos: if return_metaprotos:
return [build_metaproto(match.key, match.desc, match.locks.all(), 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")) match.attributes.get("prototype"))
for match in matches] for match in matches]
return 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. Find read-only prototypes, defined in modules.
@ -301,10 +301,10 @@ def search_readonly_prototype(key=None, tags=None):
if tags: if tags:
# use tags to limit selection # use tags to limit selection
tagset = set(tags) 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)} if tagset.intersection(metaproto.tags)}
else: else:
matches = _READONLY_PROTOTYPES matches = _MODULE_PROTOTYPES
if key: if key:
if key in matches: if key in matches:
@ -324,7 +324,7 @@ def search_prototype(key=None, tags=None, return_meta=True):
Kwargs: Kwargs:
key (str): An exact or partial key to query for. key (str): An exact or partial key to query for.
tags (str or list): Tag key or keys to query for. These 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. tag category.
return_meta (bool): If False, only return prototype dicts, if True return_meta (bool): If False, only return prototype dicts, if True
return MetaProto namedtuples including prototype meta info return MetaProto namedtuples including prototype meta info
@ -340,15 +340,15 @@ def search_prototype(key=None, tags=None, return_meta=True):
be found. be found.
""" """
readonly_prototypes = search_readonly_prototype(key, tags) module_prototypes = search_module_prototype(key, tags)
persistent_prototypes = search_persistent_prototype(key, tags, return_metaprotos=True) 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: if len(matches) > 1 and key:
key = key.lower() key = key.lower()
# avoid duplicates if an exact match exist between the two types # avoid duplicates if an exact match exist between the two types
filter_matches = [mta for mta in matches if mta.key == key] 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 matches = filter_matches
if not return_meta: if not return_meta:
@ -369,8 +369,7 @@ def get_protparents():
return {metaproto.key: metaproto.prototype for metaproto in metaprotos} return {metaproto.key: metaproto.prototype for metaproto in metaprotos}
def list_prototypes(caller, key=None, tags=None, show_non_use=False, def list_prototypes(caller, key=None, tags=None, show_non_use=False, show_non_edit=True):
show_non_edit=True, sort_tree=True):
""" """
Collate a list of found prototypes based on search criteria and access. 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. 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_use (bool, optional): Show also prototypes the caller may not use.
show_non_edit (bool, optional): Show also prototypes the caller may not edit. show_non_edit (bool, optional): Show also prototypes the caller may not edit.
sort_tree (bool, optional): Order prototypes by inheritance tree.
Returns: Returns:
table (EvTable or None): An EvTable representation of the prototypes. None table (EvTable or None): An EvTable representation of the prototypes. None
if no prototypes were found. 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 # get metaprotos for readonly and db-based prototypes
metaprotos = search_readonly_prototype(key, tags) metaprotos = search_module_prototype(key, tags)
metaprotos += search_persistent_prototype(key, tags, return_metaprotos=True) metaprotos += search_db_prototype(key, tags, return_metaprotos=True)
# get use-permissions of readonly attributes (edit is always False) # get use-permissions of readonly attributes (edit is always False)
prototypes = [ prototypes = [
(metaproto.key, (metaproto.key,
metaproto.desc, metaproto.desc,
("{}/N".format('Y' ("{}/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)) ",".join(metaproto.tags))
for metaproto in sorted(metaprotos, key=lambda o: o.key)] for metaproto in sorted(metaprotos, key=lambda o: o.key)]
@ -642,7 +646,6 @@ def spawn(*prototypes, **kwargs):
return _batch_create_object(*objsparams) return _batch_create_object(*objsparams)
# Testing # Testing
if __name__ == "__main__": if __name__ == "__main__":