Improve parse of spawn arguments
This commit is contained in:
parent
9e7dc14cbb
commit
ce602716f1
2 changed files with 89 additions and 86 deletions
|
|
@ -13,7 +13,8 @@ from evennia.utils import create, utils, search
|
||||||
from evennia.utils.utils import inherits_from, class_from_module
|
from evennia.utils.utils import inherits_from, class_from_module
|
||||||
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, store_prototype
|
from evennia.utils.spawner import (spawn, search_prototype, list_prototypes,
|
||||||
|
store_prototype, build_metaproto)
|
||||||
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)
|
||||||
|
|
@ -27,12 +28,8 @@ __all__ = ("ObjManipCommand", "CmdSetObjAlias", "CmdCopy",
|
||||||
"CmdLock", "CmdExamine", "CmdFind", "CmdTeleport",
|
"CmdLock", "CmdExamine", "CmdFind", "CmdTeleport",
|
||||||
"CmdScript", "CmdTag", "CmdSpawn")
|
"CmdScript", "CmdTag", "CmdSpawn")
|
||||||
|
|
||||||
try:
|
|
||||||
# used by @set
|
# used by @set
|
||||||
from ast import literal_eval as _LITERAL_EVAL
|
from ast import literal_eval as _LITERAL_EVAL
|
||||||
except ImportError:
|
|
||||||
# literal_eval is not available before Python 2.6
|
|
||||||
_LITERAL_EVAL = None
|
|
||||||
|
|
||||||
# used by @find
|
# used by @find
|
||||||
CHAR_TYPECLASS = settings.BASE_CHARACTER_TYPECLASS
|
CHAR_TYPECLASS = settings.BASE_CHARACTER_TYPECLASS
|
||||||
|
|
@ -1450,7 +1447,6 @@ def _convert_from_string(cmd, strobj):
|
||||||
# if nothing matches, return as-is
|
# if nothing matches, return as-is
|
||||||
return obj
|
return obj
|
||||||
|
|
||||||
if _LITERAL_EVAL:
|
|
||||||
# Use literal_eval to parse python structure exactly.
|
# Use literal_eval to parse python structure exactly.
|
||||||
try:
|
try:
|
||||||
return _LITERAL_EVAL(strobj)
|
return _LITERAL_EVAL(strobj)
|
||||||
|
|
@ -2786,38 +2782,36 @@ class CmdSpawn(COMMAND_DEFAULT_CLASS):
|
||||||
def func(self):
|
def func(self):
|
||||||
"""Implements the spawner"""
|
"""Implements the spawner"""
|
||||||
|
|
||||||
def _parse_prototype(inp, allow_key=False):
|
def _parse_prototype(inp, expect=dict):
|
||||||
|
err = None
|
||||||
try:
|
try:
|
||||||
# make use of _convert_from_string from the SetAttribute command
|
prototype = _LITERAL_EVAL(inp)
|
||||||
prototype = _convert_from_string(self, inp)
|
except (SyntaxError, ValueError) as err:
|
||||||
except SyntaxError:
|
# treat as string
|
||||||
# this means literal_eval tried to parse a faulty string
|
prototype = utils.to_str(inp)
|
||||||
string = ("|RCritical Python syntax error in argument. Only primitive "
|
finally:
|
||||||
|
if not isinstance(prototype, expect):
|
||||||
|
if err:
|
||||||
|
string = ("{}\n|RCritical Python syntax error in argument. Only primitive "
|
||||||
"Python structures are allowed. \nYou also need to use correct "
|
"Python structures are allowed. \nYou also need to use correct "
|
||||||
"Python syntax. Remember especially to put quotes around all "
|
"Python syntax. Remember especially to put quotes around all "
|
||||||
"strings inside lists and dicts.|n")
|
"strings inside lists and dicts.|n".format(err))
|
||||||
|
else:
|
||||||
|
string = "Expected {}, got {}.".format(expect, type(prototype))
|
||||||
self.caller.msg(string)
|
self.caller.msg(string)
|
||||||
return None
|
return None
|
||||||
if isinstance(prototype, dict):
|
if expect == dict:
|
||||||
# an actual prototype. We need to make sure it's safe. Don't allow exec
|
# an actual prototype. We need to make sure it's safe. Don't allow exec
|
||||||
if "exec" in prototype and not self.caller.check_permstring("Developer"):
|
if "exec" in prototype and not self.caller.check_permstring("Developer"):
|
||||||
self.caller.msg("Spawn aborted: You don't have access to "
|
self.caller.msg("Spawn aborted: You don't have access to "
|
||||||
"use the 'exec' prototype key.")
|
"use the 'exec' prototype key.")
|
||||||
return None
|
return None
|
||||||
elif isinstance(prototype, basestring):
|
|
||||||
# a prototype key
|
|
||||||
if allow_key:
|
|
||||||
return prototype
|
return prototype
|
||||||
else:
|
|
||||||
self.caller.msg("The prototype must be defined as a Python dictionary.")
|
|
||||||
else:
|
|
||||||
caller.msg("The prototype must be given either as a Python dictionary or a key")
|
|
||||||
return None
|
|
||||||
|
|
||||||
|
def _search_show_prototype(query, metaprots=None):
|
||||||
def _search_show_prototype(query):
|
|
||||||
# prototype detail
|
# prototype detail
|
||||||
strings = []
|
strings = []
|
||||||
|
if not metaprots:
|
||||||
metaprots = search_prototype(key=query, return_meta=True)
|
metaprots = search_prototype(key=query, return_meta=True)
|
||||||
if metaprots:
|
if metaprots:
|
||||||
for metaprot in metaprots:
|
for metaprot in metaprots:
|
||||||
|
|
@ -2825,7 +2819,7 @@ class CmdSpawn(COMMAND_DEFAULT_CLASS):
|
||||||
"|cprototype key:|n {}, |ctags:|n {}, |clocks:|n {} \n"
|
"|cprototype key:|n {}, |ctags:|n {}, |clocks:|n {} \n"
|
||||||
"|cdesc:|n {} \n|cprototype:|n ".format(
|
"|cdesc:|n {} \n|cprototype:|n ".format(
|
||||||
metaprot.key, ", ".join(metaprot.tags),
|
metaprot.key, ", ".join(metaprot.tags),
|
||||||
metaprot.locks, metaprot.desc))
|
"; ".join(metaprot.locks), metaprot.desc))
|
||||||
prototype = ("{{\n {} \n}}".format("\n ".join("{!r}: {!r},".format(key, value)
|
prototype = ("{{\n {} \n}}".format("\n ".join("{!r}: {!r},".format(key, value)
|
||||||
for key, value in
|
for key, value in
|
||||||
sorted(metaprot.prototype.items())).rstrip(",")))
|
sorted(metaprot.prototype.items())).rstrip(",")))
|
||||||
|
|
@ -2869,11 +2863,12 @@ class CmdSpawn(COMMAND_DEFAULT_CLASS):
|
||||||
|
|
||||||
if 'save' in self.switches:
|
if 'save' in self.switches:
|
||||||
if not self.args or not self.rhs:
|
if not self.args or not self.rhs:
|
||||||
caller.msg("Usage: @spawn/save <key>[;desc[;tag,tag[,...][;lockstring]]] = <prototype_dict>")
|
caller.msg(
|
||||||
|
"Usage: @spawn/save <key>[;desc[;tag,tag[,...][;lockstring]]] = <prototype_dict>")
|
||||||
return
|
return
|
||||||
|
|
||||||
# handle lhs
|
# handle lhs
|
||||||
parts = self.rhs.split(";", 3)
|
parts = self.lhs.split(";", 3)
|
||||||
key, desc, tags, lockstring = "", "", [], ""
|
key, desc, tags, lockstring = "", "", [], ""
|
||||||
nparts = len(parts)
|
nparts = len(parts)
|
||||||
if nparts == 1:
|
if nparts == 1:
|
||||||
|
|
@ -2889,17 +2884,26 @@ class CmdSpawn(COMMAND_DEFAULT_CLASS):
|
||||||
tags = [tag.strip().lower() for tag in tags.split(",")]
|
tags = [tag.strip().lower() for tag in tags.split(",")]
|
||||||
|
|
||||||
# handle rhs:
|
# handle rhs:
|
||||||
prototype = _parse_prototype(caller, self.rhs)
|
prototype = _parse_prototype(self.rhs)
|
||||||
if not prototype:
|
if not prototype:
|
||||||
return
|
return
|
||||||
|
|
||||||
# check for existing prototype
|
# present prototype to save
|
||||||
matchstring = _search_show_prototype(key)
|
new_matchstring = _search_show_prototype(
|
||||||
if matchstring:
|
"", metaprots=[build_metaproto(key, desc, [lockstring], tags, prototype)])
|
||||||
caller.msg("|yExisting saved prototype found:|n\n{}".format(matchstring))
|
string = "|yCreating new prototype:|n\n{}".format(new_matchstring)
|
||||||
answer = ("Do you want to replace the existing prototype? Y/[N]")
|
question = "\nDo you want to continue saving? [Y]/N"
|
||||||
if not answer.lower() not in ["y", "yes"]:
|
|
||||||
caller.msg("Save cancelled.")
|
# check for existing prototype,
|
||||||
|
old_matchstring = _search_show_prototype(key)
|
||||||
|
if old_matchstring:
|
||||||
|
string += "\n|yExisting saved prototype found:|n\n{}".format(old_matchstring)
|
||||||
|
question = "\n|yDo you want to replace the existing prototype?|n [Y]/N"
|
||||||
|
|
||||||
|
answer = yield(string + question)
|
||||||
|
if answer.lower() in ["n", "no"]:
|
||||||
|
caller.msg("|rSave cancelled.|n")
|
||||||
|
return
|
||||||
|
|
||||||
# all seems ok. Try to save.
|
# all seems ok. Try to save.
|
||||||
try:
|
try:
|
||||||
|
|
@ -2907,8 +2911,7 @@ class CmdSpawn(COMMAND_DEFAULT_CLASS):
|
||||||
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
|
||||||
caller.msg("Saved prototype:")
|
caller.msg("|gSaved prototype:|n {}".format(key))
|
||||||
caller.execute_cmd("spawn/show {}".format(key))
|
|
||||||
return
|
return
|
||||||
|
|
||||||
if not self.args:
|
if not self.args:
|
||||||
|
|
@ -2919,12 +2922,16 @@ class CmdSpawn(COMMAND_DEFAULT_CLASS):
|
||||||
|
|
||||||
# A direct creation of an object from a given prototype
|
# A direct creation of an object from a given prototype
|
||||||
|
|
||||||
prototype = _parse_prototype(self.args, allow_key=True)
|
prototype = _parse_prototype(
|
||||||
|
self.args, expect=dict if self.args.strip().startswith("{") else basestring)
|
||||||
if not prototype:
|
if not prototype:
|
||||||
|
# this will only let through dicts or strings
|
||||||
return
|
return
|
||||||
|
|
||||||
|
key = '<unnamed>'
|
||||||
if isinstance(prototype, basestring):
|
if isinstance(prototype, basestring):
|
||||||
# A prototype key we are looking to apply
|
# A prototype key we are looking to apply
|
||||||
|
key = prototype
|
||||||
metaprotos = search_prototype(prototype)
|
metaprotos = search_prototype(prototype)
|
||||||
nprots = len(metaprotos)
|
nprots = len(metaprotos)
|
||||||
if not metaprotos:
|
if not metaprotos:
|
||||||
|
|
@ -2945,5 +2952,8 @@ class CmdSpawn(COMMAND_DEFAULT_CLASS):
|
||||||
prototype["location"] = self.caller.location
|
prototype["location"] = self.caller.location
|
||||||
|
|
||||||
# proceed to spawning
|
# proceed to spawning
|
||||||
|
try:
|
||||||
for obj in spawn(prototype):
|
for obj in spawn(prototype):
|
||||||
self.caller.msg("Spawned %s." % obj.get_display_name(self.caller))
|
self.caller.msg("Spawned %s." % obj.get_display_name(self.caller))
|
||||||
|
except RuntimeError as err:
|
||||||
|
caller.msg(err)
|
||||||
|
|
|
||||||
|
|
@ -359,6 +359,14 @@ class PersistentPrototype(DefaultScript):
|
||||||
self.desc = "A prototype"
|
self.desc = "A prototype"
|
||||||
|
|
||||||
|
|
||||||
|
def build_metaproto(key, desc, locks, tags, prototype):
|
||||||
|
"""
|
||||||
|
Create a metaproto from combinant parts.
|
||||||
|
|
||||||
|
"""
|
||||||
|
return MetaProto(key, desc, locks, tags, dict(prototype))
|
||||||
|
|
||||||
|
|
||||||
def store_prototype(caller, key, prototype, desc="", tags=None, locks="", delete=False):
|
def store_prototype(caller, key, prototype, desc="", tags=None, locks="", delete=False):
|
||||||
"""
|
"""
|
||||||
Store a prototype persistently.
|
Store a prototype persistently.
|
||||||
|
|
@ -386,7 +394,7 @@ 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 edit: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, "persistent_prototype") for tag in make_iter(tags)]
|
||||||
|
|
||||||
if key in _READONLY_PROTOTYPES:
|
if key in _READONLY_PROTOTYPES:
|
||||||
|
|
@ -506,34 +514,19 @@ def search_prototype(key=None, tags=None, return_meta=True):
|
||||||
be found.
|
be found.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
matches = []
|
readonly_prototypes = search_readonly_prototype(key, tags)
|
||||||
if key and key in _READONLY_PROTOTYPES:
|
persistent_prototypes = search_persistent_prototype(key, tags)
|
||||||
if return_meta:
|
|
||||||
matches.append(_READONLY_PROTOTYPES[key])
|
|
||||||
else:
|
|
||||||
matches.append(_READONLY_PROTOTYPES[key][3])
|
|
||||||
elif tags:
|
|
||||||
if return_meta:
|
|
||||||
matches.extend(
|
|
||||||
[MetaProto(prot.key, prot.desc, prot.locks.all(),
|
|
||||||
prot.tags.all(), prot.attributes.get("prototype"))
|
|
||||||
for prot in search_persistent_prototype(key, tags)])
|
|
||||||
else:
|
|
||||||
matches.extend([prot.attributes.get("prototype")
|
|
||||||
for prot in search_persistent_prototype(key, tags)])
|
|
||||||
else:
|
|
||||||
# neither key nor tags given. Return all.
|
|
||||||
if return_meta:
|
|
||||||
matches = [MetaProto(prot.key, prot.desc, prot.locks.all(),
|
|
||||||
prot.tags.all(), prot.attributes.get("prototype"))
|
|
||||||
for prot in search_persistent_prototype(key, tags)] + \
|
|
||||||
list(_READONLY_PROTOTYPES.values())
|
|
||||||
else:
|
|
||||||
matches = [prot.attributes.get("prototype")
|
|
||||||
for prot in search_persistent_prototype()] + \
|
|
||||||
[metaprot[3] for metaprot in _READONLY_PROTOTYPES.values()]
|
|
||||||
return matches
|
|
||||||
|
|
||||||
|
if return_meta:
|
||||||
|
persistent_prototypes = [
|
||||||
|
build_metaproto(prot.key, prot.desc, prot.locks.all(),
|
||||||
|
prot.tags.all(), prot.attributes.get("prototype"))
|
||||||
|
for prot in persistent_prototypes]
|
||||||
|
else:
|
||||||
|
readonly_prototypes = [metaprot.prototyp for metaprot in readonly_prototypes]
|
||||||
|
persistent_prototypes = [prot.attributes.get("prototype") for prot in persistent_prototypes]
|
||||||
|
|
||||||
|
return persistent_prototypes + readonly_prototypes
|
||||||
|
|
||||||
def list_prototypes(caller, key=None, tags=None, show_non_use=False, show_non_edit=True):
|
def list_prototypes(caller, key=None, tags=None, show_non_use=False, show_non_edit=True):
|
||||||
"""
|
"""
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue