diff --git a/evennia/prototypes/protfuncs.py b/evennia/prototypes/protfuncs.py index 057f5f770f..01859452b7 100644 --- a/evennia/prototypes/protfuncs.py +++ b/evennia/prototypes/protfuncs.py @@ -25,6 +25,9 @@ where *args are the arguments given in the prototype, and **kwargs are inserted - 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. + - 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 @@ -32,9 +35,14 @@ 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.prototypes.prototypes import value_to_obj_or_any _PROTOTYPEFUNCS = {} @@ -57,7 +65,8 @@ def protfunc_parser(value, available_functions=None, **kwargs): `settings.PROTOTYPEFUNC_MODULES`, or specified on the command line. Args: - value (string): The value to test for a parseable protfunc. + 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: @@ -66,13 +75,179 @@ def protfunc_parser(value, available_functions=None, **kwargs): 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. + 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. + """ if not isinstance(value, basestring): return value available_functions = _PROTOTYPEFUNCS if available_functions is None else available_functions - return inlinefuncs.parse_inlinefunc(value, _available_funcs=available_functions, **kwargs) + result = inlinefuncs.parse_inlinefunc(value, _available_funcs=available_functions, **kwargs) + result = value_to_obj_or_any(result) + try: + return literal_eval(result) + except ValueError: + return result + # default protfuncs + +def random(*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() + Returns left-justified. + + """ + if args: + return base_justify(args[0], align='l') + return "" + + +def right_justify(*args, **kwargs): + """ + Usage: $right_justify() + Returns right-justified across screen width. + + """ + if args: + return base_justify(args[0], align='r') + return "" + + +def center_justify(*args, **kwargs): + + """ + Usage: $center_justify() + Returns centered in screen width. + + """ + if args: + return base_justify(args[0], align='c') + return "" + + +def full_justify(*args, **kwargs): + + """ + Usage: $full_justify() + Returns filling up screen width by adding extra space. + + """ + if args: + return base_justify(args[0], align='f') + return "" + + +def protkey(*args, **kwargs): + """ + Usage: $protkey() + 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]] + + +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] + return literal_eval(val1) + literal_eval(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] + return literal_eval(val1) - literal_eval(val2) + raise ValueError("$sub requires two arguments.") + + +def mul(*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] + return literal_eval(val1) * literal_eval(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] + return literal_eval(val1) / float(literal_eval(val2)) + raise ValueError("$mult requires two arguments.") + + +def eval(*args, **kwargs): + """ + Usage $eval() + 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. + + """ + string = args[0] if args else '' + struct = literal_eval(string) + + def _recursive_parse(val): + # an extra round of recursive parsing, to catch any escaped $$profuncs + 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 _recursive_parse(struct)