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.eveditor import EvEditor
|
||||
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
|
||||
|
||||
COMMAND_DEFAULT_CLASS = class_from_module(settings.COMMAND_DEFAULT_CLASS)
|
||||
|
|
@ -27,12 +28,8 @@ __all__ = ("ObjManipCommand", "CmdSetObjAlias", "CmdCopy",
|
|||
"CmdLock", "CmdExamine", "CmdFind", "CmdTeleport",
|
||||
"CmdScript", "CmdTag", "CmdSpawn")
|
||||
|
||||
try:
|
||||
# used by @set
|
||||
from ast import literal_eval as _LITERAL_EVAL
|
||||
except ImportError:
|
||||
# literal_eval is not available before Python 2.6
|
||||
_LITERAL_EVAL = None
|
||||
# used by @set
|
||||
from ast import literal_eval as _LITERAL_EVAL
|
||||
|
||||
# used by @find
|
||||
CHAR_TYPECLASS = settings.BASE_CHARACTER_TYPECLASS
|
||||
|
|
@ -1450,17 +1447,16 @@ def _convert_from_string(cmd, strobj):
|
|||
# if nothing matches, return as-is
|
||||
return obj
|
||||
|
||||
if _LITERAL_EVAL:
|
||||
# Use literal_eval to parse python structure exactly.
|
||||
try:
|
||||
return _LITERAL_EVAL(strobj)
|
||||
except (SyntaxError, ValueError):
|
||||
# treat as string
|
||||
strobj = utils.to_str(strobj)
|
||||
string = "|RNote: name \"|r%s|R\" was converted to a string. " \
|
||||
"Make sure this is acceptable." % strobj
|
||||
cmd.caller.msg(string)
|
||||
return strobj
|
||||
# Use literal_eval to parse python structure exactly.
|
||||
try:
|
||||
return _LITERAL_EVAL(strobj)
|
||||
except (SyntaxError, ValueError):
|
||||
# treat as string
|
||||
strobj = utils.to_str(strobj)
|
||||
string = "|RNote: name \"|r%s|R\" was converted to a string. " \
|
||||
"Make sure this is acceptable." % strobj
|
||||
cmd.caller.msg(string)
|
||||
return strobj
|
||||
else:
|
||||
# fall back to old recursive solution (does not support
|
||||
# nested lists/dicts)
|
||||
|
|
@ -2786,46 +2782,44 @@ class CmdSpawn(COMMAND_DEFAULT_CLASS):
|
|||
def func(self):
|
||||
"""Implements the spawner"""
|
||||
|
||||
def _parse_prototype(inp, allow_key=False):
|
||||
def _parse_prototype(inp, expect=dict):
|
||||
err = None
|
||||
try:
|
||||
# make use of _convert_from_string from the SetAttribute command
|
||||
prototype = _convert_from_string(self, inp)
|
||||
except SyntaxError:
|
||||
# this means literal_eval tried to parse a faulty string
|
||||
string = ("|RCritical Python syntax error in argument. Only primitive "
|
||||
"Python structures are allowed. \nYou also need to use correct "
|
||||
"Python syntax. Remember especially to put quotes around all "
|
||||
"strings inside lists and dicts.|n")
|
||||
self.caller.msg(string)
|
||||
return None
|
||||
if isinstance(prototype, dict):
|
||||
prototype = _LITERAL_EVAL(inp)
|
||||
except (SyntaxError, ValueError) as err:
|
||||
# treat as string
|
||||
prototype = utils.to_str(inp)
|
||||
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 syntax. Remember especially to put quotes around all "
|
||||
"strings inside lists and dicts.|n".format(err))
|
||||
else:
|
||||
string = "Expected {}, got {}.".format(expect, type(prototype))
|
||||
self.caller.msg(string)
|
||||
return None
|
||||
if expect == dict:
|
||||
# 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"):
|
||||
self.caller.msg("Spawn aborted: You don't have access to "
|
||||
"use the 'exec' prototype key.")
|
||||
return None
|
||||
elif isinstance(prototype, basestring):
|
||||
# a prototype key
|
||||
if allow_key:
|
||||
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
|
||||
return prototype
|
||||
|
||||
|
||||
def _search_show_prototype(query):
|
||||
def _search_show_prototype(query, metaprots=None):
|
||||
# prototype detail
|
||||
strings = []
|
||||
metaprots = search_prototype(key=query, return_meta=True)
|
||||
if not metaprots:
|
||||
metaprots = search_prototype(key=query, return_meta=True)
|
||||
if metaprots:
|
||||
for metaprot in metaprots:
|
||||
header = (
|
||||
"|cprototype key:|n {}, |ctags:|n {}, |clocks:|n {} \n"
|
||||
"|cdesc:|n {} \n|cprototype:|n ".format(
|
||||
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)
|
||||
for key, value in
|
||||
sorted(metaprot.prototype.items())).rstrip(",")))
|
||||
|
|
@ -2869,11 +2863,12 @@ class CmdSpawn(COMMAND_DEFAULT_CLASS):
|
|||
|
||||
if 'save' in self.switches:
|
||||
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
|
||||
|
||||
# handle lhs
|
||||
parts = self.rhs.split(";", 3)
|
||||
parts = self.lhs.split(";", 3)
|
||||
key, desc, tags, lockstring = "", "", [], ""
|
||||
nparts = len(parts)
|
||||
if nparts == 1:
|
||||
|
|
@ -2889,17 +2884,26 @@ class CmdSpawn(COMMAND_DEFAULT_CLASS):
|
|||
tags = [tag.strip().lower() for tag in tags.split(",")]
|
||||
|
||||
# handle rhs:
|
||||
prototype = _parse_prototype(caller, self.rhs)
|
||||
prototype = _parse_prototype(self.rhs)
|
||||
if not prototype:
|
||||
return
|
||||
|
||||
# check for existing prototype
|
||||
matchstring = _search_show_prototype(key)
|
||||
if matchstring:
|
||||
caller.msg("|yExisting saved prototype found:|n\n{}".format(matchstring))
|
||||
answer = ("Do you want to replace the existing prototype? Y/[N]")
|
||||
if not answer.lower() not in ["y", "yes"]:
|
||||
caller.msg("Save cancelled.")
|
||||
# present prototype to save
|
||||
new_matchstring = _search_show_prototype(
|
||||
"", metaprots=[build_metaproto(key, desc, [lockstring], tags, prototype)])
|
||||
string = "|yCreating new prototype:|n\n{}".format(new_matchstring)
|
||||
question = "\nDo you want to continue saving? [Y]/N"
|
||||
|
||||
# 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.
|
||||
try:
|
||||
|
|
@ -2907,8 +2911,7 @@ class CmdSpawn(COMMAND_DEFAULT_CLASS):
|
|||
except PermissionError as err:
|
||||
caller.msg("|rError saving:|R {}|n".format(err))
|
||||
return
|
||||
caller.msg("Saved prototype:")
|
||||
caller.execute_cmd("spawn/show {}".format(key))
|
||||
caller.msg("|gSaved prototype:|n {}".format(key))
|
||||
return
|
||||
|
||||
if not self.args:
|
||||
|
|
@ -2919,12 +2922,16 @@ class CmdSpawn(COMMAND_DEFAULT_CLASS):
|
|||
|
||||
# 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:
|
||||
# this will only let through dicts or strings
|
||||
return
|
||||
|
||||
key = '<unnamed>'
|
||||
if isinstance(prototype, basestring):
|
||||
# A prototype key we are looking to apply
|
||||
key = prototype
|
||||
metaprotos = search_prototype(prototype)
|
||||
nprots = len(metaprotos)
|
||||
if not metaprotos:
|
||||
|
|
@ -2945,5 +2952,8 @@ class CmdSpawn(COMMAND_DEFAULT_CLASS):
|
|||
prototype["location"] = self.caller.location
|
||||
|
||||
# proceed to spawning
|
||||
for obj in spawn(prototype):
|
||||
self.caller.msg("Spawned %s." % obj.get_display_name(self.caller))
|
||||
try:
|
||||
for obj in spawn(prototype):
|
||||
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"
|
||||
|
||||
|
||||
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):
|
||||
"""
|
||||
Store a prototype persistently.
|
||||
|
|
@ -386,7 +394,7 @@ 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 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)]
|
||||
|
||||
if key in _READONLY_PROTOTYPES:
|
||||
|
|
@ -506,34 +514,19 @@ def search_prototype(key=None, tags=None, return_meta=True):
|
|||
be found.
|
||||
|
||||
"""
|
||||
matches = []
|
||||
if key and key in _READONLY_PROTOTYPES:
|
||||
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
|
||||
readonly_prototypes = search_readonly_prototype(key, tags)
|
||||
persistent_prototypes = search_persistent_prototype(key, tags)
|
||||
|
||||
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):
|
||||
"""
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue