Homogenize funcparser calls
This commit is contained in:
parent
adb370b1d3
commit
a3a57314a1
11 changed files with 293 additions and 993 deletions
|
|
@ -1,33 +1,28 @@
|
|||
"""
|
||||
Protfuncs are function-strings embedded in a prototype and allows for a builder to create a
|
||||
prototype with custom logics without having access to Python. The Protfunc is parsed using the
|
||||
inlinefunc parser but is fired at the moment the spawning happens, using the creating object's
|
||||
session as input.
|
||||
Protfuncs are FuncParser-callables that can be embedded in a prototype to
|
||||
provide custom logic without having access to Python. The protfunc is parsed at
|
||||
the time of spawning, using the creating object's session as input. If the
|
||||
protfunc returns a non-string, this is what will be added to the prototype.
|
||||
|
||||
In the prototype dict, the protfunc is specified as a string inside the prototype, e.g.:
|
||||
|
||||
{ ...
|
||||
|
||||
"key": "$funcname(arg1, arg2, ...)"
|
||||
"key": "$funcname(args, kwargs)"
|
||||
|
||||
... }
|
||||
|
||||
and multiple functions can be nested (no keyword args are supported). The result will be used as the
|
||||
value for that prototype key for that individual spawn.
|
||||
|
||||
Available protfuncs are callables in one of the modules of `settings.PROT_FUNC_MODULES`. They
|
||||
are specified as functions
|
||||
Available protfuncs are either all callables in one of the modules of `settings.PROT_FUNC_MODULES`
|
||||
or all callables added to a dict FUNCPARSER_CALLABLES in such a module.
|
||||
|
||||
def funcname (*args, **kwargs)
|
||||
|
||||
where *args are the arguments given in the prototype, and **kwargs are inserted by Evennia:
|
||||
At spawn-time the spawner passes the following extra kwargs into each callable (in addition to
|
||||
what is added in the call itself):
|
||||
|
||||
- session (Session): The Session of the entity spawning using this prototype.
|
||||
- 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.
|
||||
|
||||
Any traceback raised by this function will be handled at the time of spawning and abort the spawn
|
||||
before any object is created/updated. It must otherwise return the value to store for the specified
|
||||
|
|
@ -35,312 +30,26 @@ 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, choice as base_choice
|
||||
import re
|
||||
|
||||
from evennia.utils import search
|
||||
from evennia.utils.utils import justify as base_justify, is_iter, to_str
|
||||
|
||||
_PROTLIB = None
|
||||
|
||||
_RE_DBREF = re.compile(r"\#[0-9]+")
|
||||
from evennia.utils import funcparser
|
||||
|
||||
|
||||
# default protfuncs
|
||||
|
||||
|
||||
def random(*args, **kwargs):
|
||||
def protfunc_callable_protkey(*args, **kwargs):
|
||||
"""
|
||||
Usage: $random()
|
||||
Returns a random value in the interval [0, 1)
|
||||
|
||||
"""
|
||||
return base_random()
|
||||
|
||||
|
||||
def randint(*args, **kwargs):
|
||||
"""
|
||||
Usage: $randint(start, end)
|
||||
Returns random integer in interval [start, end]
|
||||
|
||||
"""
|
||||
if len(args) != 2:
|
||||
raise TypeError("$randint needs two arguments - start and end.")
|
||||
start, end = int(args[0]), int(args[1])
|
||||
return base_randint(start, end)
|
||||
|
||||
|
||||
def left_justify(*args, **kwargs):
|
||||
"""
|
||||
Usage: $left_justify(<text>)
|
||||
Returns <text> left-justified.
|
||||
|
||||
"""
|
||||
if args:
|
||||
return base_justify(args[0], align="l")
|
||||
return ""
|
||||
|
||||
|
||||
def right_justify(*args, **kwargs):
|
||||
"""
|
||||
Usage: $right_justify(<text>)
|
||||
Returns <text> right-justified across screen width.
|
||||
|
||||
"""
|
||||
if args:
|
||||
return base_justify(args[0], align="r")
|
||||
return ""
|
||||
|
||||
|
||||
def center_justify(*args, **kwargs):
|
||||
|
||||
"""
|
||||
Usage: $center_justify(<text>)
|
||||
Returns <text> centered in screen width.
|
||||
|
||||
"""
|
||||
if args:
|
||||
return base_justify(args[0], align="c")
|
||||
return ""
|
||||
|
||||
|
||||
def choice(*args, **kwargs):
|
||||
"""
|
||||
Usage: $choice(val, val, val, ...)
|
||||
Returns one of the values randomly
|
||||
"""
|
||||
if args:
|
||||
return base_choice(args)
|
||||
return ""
|
||||
|
||||
|
||||
def full_justify(*args, **kwargs):
|
||||
|
||||
"""
|
||||
Usage: $full_justify(<text>)
|
||||
Returns <text> filling up screen width by adding extra space.
|
||||
|
||||
"""
|
||||
if args:
|
||||
return base_justify(args[0], align="f")
|
||||
return ""
|
||||
|
||||
|
||||
def protkey(*args, **kwargs):
|
||||
"""
|
||||
Usage: $protkey(<key>)
|
||||
Usage: $protkey(keyname)
|
||||
Returns the value of another key in this prototoype. Will raise an error if
|
||||
the key is not found in this prototype.
|
||||
|
||||
"""
|
||||
if args:
|
||||
prototype = kwargs["prototype"]
|
||||
return prototype[args[0].strip()]
|
||||
if not args:
|
||||
return ""
|
||||
|
||||
prototype = kwargs.get("prototype", {})
|
||||
return prototype[args[0].strip()]
|
||||
|
||||
|
||||
def add(*args, **kwargs):
|
||||
"""
|
||||
Usage: $add(val1, val2)
|
||||
Returns the result of val1 + val2. Values must be
|
||||
valid simple Python structures possible to add,
|
||||
such as numbers, lists etc.
|
||||
|
||||
"""
|
||||
if len(args) > 1:
|
||||
val1, val2 = args[0], args[1]
|
||||
# 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.")
|
||||
|
||||
|
||||
def sub(*args, **kwargs):
|
||||
"""
|
||||
Usage: $del(val1, val2)
|
||||
Returns the value of val1 - val2. Values must be
|
||||
valid simple Python structures possible to
|
||||
subtract.
|
||||
|
||||
"""
|
||||
if len(args) > 1:
|
||||
val1, val2 = args[0], args[1]
|
||||
# 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 mult(*args, **kwargs):
|
||||
"""
|
||||
Usage: $mul(val1, val2)
|
||||
Returns the value of val1 * val2. The values must be
|
||||
valid simple Python structures possible to
|
||||
multiply, like strings and/or numbers.
|
||||
|
||||
"""
|
||||
if len(args) > 1:
|
||||
val1, val2 = args[0], args[1]
|
||||
# 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.")
|
||||
|
||||
|
||||
def div(*args, **kwargs):
|
||||
"""
|
||||
Usage: $div(val1, val2)
|
||||
Returns the value of val1 / val2. Values must be numbers and
|
||||
the result is always a float.
|
||||
|
||||
"""
|
||||
if len(args) > 1:
|
||||
val1, val2 = args[0], args[1]
|
||||
# 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>)
|
||||
Returns evaluation of a simple Python expression. The string may *only* consist of the following
|
||||
Python literal structures: strings, numbers, tuples, lists, dicts, booleans,
|
||||
and None. The strings can also contain #dbrefs. Escape embedded protfuncs as $$protfunc(..)
|
||||
- those will then be evaluated *after* $eval.
|
||||
|
||||
"""
|
||||
global _PROTLIB
|
||||
if not _PROTLIB:
|
||||
from evennia.prototypes import prototypes as _PROTLIB
|
||||
|
||||
string = ",".join(args)
|
||||
struct = literal_eval(string)
|
||||
|
||||
if isinstance(struct, str):
|
||||
# we must shield the string, otherwise it will be merged as a string and future
|
||||
# literal_evas will pick up e.g. '2' as something that should be converted to a number
|
||||
struct = '"{}"'.format(struct)
|
||||
|
||||
# convert any #dbrefs to objects (also in nested structures)
|
||||
struct = _PROTLIB.value_to_obj_or_any(struct)
|
||||
|
||||
return struct
|
||||
|
||||
|
||||
def _obj_search(*args, **kwargs):
|
||||
"Helper function to search for an object"
|
||||
|
||||
query = "".join(args)
|
||||
session = kwargs.get("session", None)
|
||||
return_list = kwargs.pop("return_list", False)
|
||||
account = None
|
||||
|
||||
if session:
|
||||
account = session.account
|
||||
|
||||
targets = search.search_object(query)
|
||||
|
||||
if return_list:
|
||||
retlist = []
|
||||
if account:
|
||||
for target in targets:
|
||||
if target.access(account, target, "control"):
|
||||
retlist.append(target)
|
||||
else:
|
||||
retlist = targets
|
||||
return retlist
|
||||
else:
|
||||
# single-match
|
||||
if not targets:
|
||||
raise ValueError("$obj: Query '{}' gave no matches.".format(query))
|
||||
if len(targets) > 1:
|
||||
raise ValueError(
|
||||
"$obj: Query '{query}' gave {nmatches} matches. Limit your "
|
||||
"query or use $objlist instead.".format(query=query, nmatches=len(targets))
|
||||
)
|
||||
target = targets[0]
|
||||
if account:
|
||||
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.
|
||||
|
||||
"""
|
||||
obj = _obj_search(return_list=False, *args, **kwargs)
|
||||
if obj:
|
||||
return "#{}".format(obj.id)
|
||||
return "".join(args)
|
||||
|
||||
|
||||
def objlist(*args, **kwargs):
|
||||
"""
|
||||
Usage $objlist(<query>)
|
||||
Returns list with one or more Objects searched globally by key, alias or #dbref.
|
||||
|
||||
"""
|
||||
return ["#{}".format(obj.id) for obj in _obj_search(return_list=True, *args, **kwargs)]
|
||||
|
||||
|
||||
def dbref(*args, **kwargs):
|
||||
"""
|
||||
Usage $dbref(<#dbref>)
|
||||
Validate that a #dbref input is valid.
|
||||
"""
|
||||
if not args or len(args) < 1 or _RE_DBREF.match(args[0]) is None:
|
||||
raise ValueError("$dbref requires a valid #dbref argument.")
|
||||
|
||||
return obj(args[0])
|
||||
# this is picked up by FuncParser
|
||||
FUNCPARSER_CALLABLES = {
|
||||
"protkey": protfunc_callable_protkey,
|
||||
**funcparser.FUNCPARSER_CALLABLES,
|
||||
**funcparser.SEARCHING_CALLABLES,
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue