Many more tests, debugging of protfuncs/inlinefuncs
This commit is contained in:
parent
646b73e872
commit
721cdb5ae0
5 changed files with 267 additions and 92 deletions
|
|
@ -23,8 +23,8 @@ are specified as functions
|
|||
where *args are the arguments given in the prototype, and **kwargs are inserted by Evennia:
|
||||
|
||||
- session (Session): The Session of the entity spawning using this prototype.
|
||||
- prototype_key (str): The currently spawning prototype-key.
|
||||
- prototype (dict): The dict this protfunc is a part of.
|
||||
- current_key (str): The active key this value belongs to in the prototype.
|
||||
- testing (bool): This is set if this function is called as part of the prototype validation; if
|
||||
set, the protfunc should take care not to perform any persistent actions, such as operate on
|
||||
objects or add things to the database.
|
||||
|
|
@ -38,68 +38,10 @@ prototype key (this value must be possible to serialize in an Attribute).
|
|||
from ast import literal_eval
|
||||
from random import randint as base_randint, random as base_random
|
||||
|
||||
from django.conf import settings
|
||||
from evennia.utils import inlinefuncs
|
||||
from evennia.utils.utils import callables_from_module
|
||||
from evennia.utils.utils import justify as base_justify, is_iter
|
||||
from evennia.utils import search
|
||||
from evennia.utils.utils import justify as base_justify, is_iter, to_str
|
||||
|
||||
_PROTLIB = None
|
||||
_PROT_FUNCS = {}
|
||||
|
||||
for mod in settings.PROT_FUNC_MODULES:
|
||||
try:
|
||||
callables = callables_from_module(mod)
|
||||
if mod == __name__:
|
||||
callables.pop("protfunc_parser", None)
|
||||
_PROT_FUNCS.update(callables)
|
||||
except ImportError:
|
||||
pass
|
||||
|
||||
|
||||
def protfunc_parser(value, available_functions=None, **kwargs):
|
||||
"""
|
||||
Parse a prototype value string for a protfunc and process it.
|
||||
|
||||
Available protfuncs are specified as callables in one of the modules of
|
||||
`settings.PROTFUNC_MODULES`, or specified on the command line.
|
||||
|
||||
Args:
|
||||
value (any): The value to test for a parseable protfunc. Only strings will be parsed for
|
||||
protfuncs, all other types are returned as-is.
|
||||
available_functions (dict, optional): Mapping of name:protfunction to use for this parsing.
|
||||
|
||||
Kwargs:
|
||||
any (any): Passed on to the inlinefunc.
|
||||
|
||||
Returns:
|
||||
any (any): A structure to replace the string on the prototype level. If this is a
|
||||
callable or a (callable, (args,)) structure, it will be executed as if one had supplied
|
||||
it to the prototype directly. This structure is also passed through literal_eval so one
|
||||
can get actual Python primitives out of it (not just strings). It will also identify
|
||||
eventual object #dbrefs in the output from the protfunc.
|
||||
|
||||
|
||||
"""
|
||||
global _PROTLIB
|
||||
if not _PROTLIB:
|
||||
from evennia.prototypes import prototypes as _PROTLIB
|
||||
|
||||
if not isinstance(value, basestring):
|
||||
return value
|
||||
available_functions = _PROT_FUNCS if available_functions is None else available_functions
|
||||
result = inlinefuncs.parse_inlinefunc(value, available_funcs=available_functions, **kwargs)
|
||||
# at this point we have a string where all procfuncs were parsed
|
||||
try:
|
||||
result = literal_eval(result)
|
||||
except ValueError:
|
||||
# this is due to the string not being valid for literal_eval - keep it a string
|
||||
pass
|
||||
|
||||
result = _PROTLIB.value_to_obj_or_any(result)
|
||||
try:
|
||||
return literal_eval(result)
|
||||
except ValueError:
|
||||
return result
|
||||
|
||||
|
||||
# default protfuncs
|
||||
|
|
@ -180,7 +122,7 @@ def protkey(*args, **kwargs):
|
|||
"""
|
||||
if args:
|
||||
prototype = kwargs['prototype']
|
||||
return prototype[args[0]]
|
||||
return prototype[args[0].strip()]
|
||||
|
||||
|
||||
def add(*args, **kwargs):
|
||||
|
|
@ -193,7 +135,16 @@ def add(*args, **kwargs):
|
|||
"""
|
||||
if len(args) > 1:
|
||||
val1, val2 = args[0], args[1]
|
||||
return literal_eval(val1) + literal_eval(val2)
|
||||
# try to convert to python structures, otherwise, keep as strings
|
||||
try:
|
||||
val1 = literal_eval(val1.strip())
|
||||
except Exception:
|
||||
pass
|
||||
try:
|
||||
val2 = literal_eval(val2.strip())
|
||||
except Exception:
|
||||
pass
|
||||
return val1 + val2
|
||||
raise ValueError("$add requires two arguments.")
|
||||
|
||||
|
||||
|
|
@ -207,11 +158,20 @@ def sub(*args, **kwargs):
|
|||
"""
|
||||
if len(args) > 1:
|
||||
val1, val2 = args[0], args[1]
|
||||
return literal_eval(val1) - literal_eval(val2)
|
||||
# try to convert to python structures, otherwise, keep as strings
|
||||
try:
|
||||
val1 = literal_eval(val1.strip())
|
||||
except Exception:
|
||||
pass
|
||||
try:
|
||||
val2 = literal_eval(val2.strip())
|
||||
except Exception:
|
||||
pass
|
||||
return val1 - val2
|
||||
raise ValueError("$sub requires two arguments.")
|
||||
|
||||
|
||||
def mul(*args, **kwargs):
|
||||
def mult(*args, **kwargs):
|
||||
"""
|
||||
Usage: $mul(val1, val2)
|
||||
Returns the value of val1 * val2. The values must be
|
||||
|
|
@ -221,7 +181,16 @@ def mul(*args, **kwargs):
|
|||
"""
|
||||
if len(args) > 1:
|
||||
val1, val2 = args[0], args[1]
|
||||
return literal_eval(val1) * literal_eval(val2)
|
||||
# try to convert to python structures, otherwise, keep as strings
|
||||
try:
|
||||
val1 = literal_eval(val1.strip())
|
||||
except Exception:
|
||||
pass
|
||||
try:
|
||||
val2 = literal_eval(val2.strip())
|
||||
except Exception:
|
||||
pass
|
||||
return val1 * val2
|
||||
raise ValueError("$mul requires two arguments.")
|
||||
|
||||
|
||||
|
|
@ -234,10 +203,33 @@ def div(*args, **kwargs):
|
|||
"""
|
||||
if len(args) > 1:
|
||||
val1, val2 = args[0], args[1]
|
||||
return literal_eval(val1) / float(literal_eval(val2))
|
||||
# try to convert to python structures, otherwise, keep as strings
|
||||
try:
|
||||
val1 = literal_eval(val1.strip())
|
||||
except Exception:
|
||||
pass
|
||||
try:
|
||||
val2 = literal_eval(val2.strip())
|
||||
except Exception:
|
||||
pass
|
||||
return val1 / float(val2)
|
||||
raise ValueError("$mult requires two arguments.")
|
||||
|
||||
|
||||
def toint(*args, **kwargs):
|
||||
"""
|
||||
Usage: $toint(<number>)
|
||||
Returns <number> as an integer.
|
||||
"""
|
||||
if args:
|
||||
val = args[0]
|
||||
try:
|
||||
return int(literal_eval(val.strip()))
|
||||
except ValueError:
|
||||
return val
|
||||
raise ValueError("$toint requires one argument.")
|
||||
|
||||
|
||||
def eval(*args, **kwargs):
|
||||
"""
|
||||
Usage $eval(<expression>)
|
||||
|
|
@ -247,16 +239,79 @@ def eval(*args, **kwargs):
|
|||
- those will then be evaluated *after* $eval.
|
||||
|
||||
"""
|
||||
string = args[0] if args else ''
|
||||
global _PROTLIB
|
||||
if not _PROTLIB:
|
||||
from evennia.prototypes import prototypes as _PROTLIB
|
||||
|
||||
string = ",".join(args)
|
||||
struct = literal_eval(string)
|
||||
|
||||
if isinstance(struct, basestring):
|
||||
# we must shield the string, otherwise it will be merged as a string and future
|
||||
# literal_evals will pick up e.g. '2' as something that should be converted to a number
|
||||
struct = '"{}"'.format(struct)
|
||||
|
||||
def _recursive_parse(val):
|
||||
# an extra round of recursive parsing, to catch any escaped $$profuncs
|
||||
# an extra round of recursive parsing after literal_eval, to catch any
|
||||
# escaped $$profuncs. This is commonly useful for object references.
|
||||
if is_iter(val):
|
||||
stype = type(val)
|
||||
if stype == dict:
|
||||
return {_recursive_parse(key): _recursive_parse(v) for key, v in val.items()}
|
||||
return stype((_recursive_parse(v) for v in val))
|
||||
return protfunc_parser(val)
|
||||
return _PROTLIB.protfunc_parser(val)
|
||||
|
||||
return _recursive_parse(struct)
|
||||
|
||||
|
||||
def _obj_search(return_list=False, *args, **kwargs):
|
||||
"Helper function to search for an object"
|
||||
|
||||
query = "".join(args)
|
||||
session = kwargs.get("session", None)
|
||||
|
||||
if not session:
|
||||
raise ValueError("$obj called by Evennia without Session. This is not supported.")
|
||||
account = session.account
|
||||
if not account:
|
||||
raise ValueError("$obj requires a logged-in account session.")
|
||||
targets = search.search_object(query)
|
||||
|
||||
if return_list:
|
||||
retlist = []
|
||||
for target in targets:
|
||||
if target.access(account, target, 'control'):
|
||||
retlist.append(target)
|
||||
return retlist
|
||||
else:
|
||||
# single-match
|
||||
if not targets:
|
||||
raise ValueError("$obj: Query '{}' gave no matches.".format(query))
|
||||
if targets.count() > 1:
|
||||
raise ValueError("$obj: Query '{query}' gave {nmatches} matches. Limit your "
|
||||
"query or use $objlist instead.".format(
|
||||
query=query, nmatches=targets.count()))
|
||||
target = target[0]
|
||||
if not target.access(account, target, 'control'):
|
||||
raise ValueError("$obj: Obj {target}(#{dbref} cannot be added - "
|
||||
"Account {account} does not have 'control' access.".format(
|
||||
target=target.key, dbref=target.id, account=account))
|
||||
return target
|
||||
|
||||
|
||||
def obj(*args, **kwargs):
|
||||
"""
|
||||
Usage $obj(<query>)
|
||||
Returns one Object searched globally by key, alias or #dbref. Error if more than one.
|
||||
|
||||
"""
|
||||
return _obj_search(*args, **kwargs)
|
||||
|
||||
|
||||
def objlist(*args, **kwargs):
|
||||
"""
|
||||
Usage $objlist(<query>)
|
||||
Returns list with one or more Objects searched globally by key, alias or #dbref.
|
||||
|
||||
"""
|
||||
return _obj_search(return_list=True, *args, **kwargs)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue