Clean up docs and more funcparser fixes
This commit is contained in:
parent
c65c68e4c2
commit
c9d9e9c6f8
21 changed files with 438 additions and 512 deletions
|
|
@ -1,7 +1,5 @@
|
|||
# The Inline Function Parser
|
||||
|
||||
## Introduction
|
||||
|
||||
The [FuncParser](api:evennia.utils.funcparser#evennia.utils.funcparser.FuncParser) extracts and executes
|
||||
'inline functions'
|
||||
embedded in a string on the form `$funcname(args, kwargs)`. Under the hood, this will
|
||||
|
|
@ -11,26 +9,25 @@ the return from the function.
|
|||
```python
|
||||
from evennia.utils.funcparser import FuncParser
|
||||
|
||||
def _square_callable(*args, **kwargs):
|
||||
"""This will be callable as $square(number) in string"""
|
||||
return float(args[0]) ** 2
|
||||
def _power_callable(*args, **kwargs):
|
||||
"""This will be callable as $square(number, power=<num>) in string"""
|
||||
pow = int(kwargs.get('power', 2))
|
||||
return float(args[0]) ** pow
|
||||
|
||||
parser = FuncParser({"square": _square_callable})
|
||||
parser = FuncParser({"pow": _power_callable})
|
||||
|
||||
```
|
||||
Next, just pass a string into the parser, optionally containing `$func(...)` markers:
|
||||
|
||||
```python
|
||||
|
||||
parser.parse("We have that 4 x 4 is $square(4).")
|
||||
"We have that 4 x 4 is 16."
|
||||
|
||||
parser.parse("We have that 4 x 4 x 4 is $pow(4, power=3).")
|
||||
"We have that 4 x 4 x 4 is 64."
|
||||
```
|
||||
|
||||
Normally the return is always converted to a string but you can also get the actual data type from the call:
|
||||
|
||||
```python
|
||||
parser.parse_to_any("$square(4)")
|
||||
parser.parse_to_any("$pow(4)")
|
||||
16
|
||||
```
|
||||
|
||||
|
|
@ -38,8 +35,8 @@ To show a `$func()` verbatim in your code without parsing it, escape it as eithe
|
|||
|
||||
|
||||
```python
|
||||
parser.parse("This is an escaped $$square(4) and so is this \$square(3)")
|
||||
"This is an escaped $square(4) and so is this $square(3)"
|
||||
parser.parse("This is an escaped $$pow(4) and so is this \$pow(3)")
|
||||
"This is an escaped $pow(4) and so is this $pow(3)"
|
||||
```
|
||||
|
||||
## Uses in default Evennia
|
||||
|
|
@ -47,14 +44,14 @@ parser.parse("This is an escaped $$square(4) and so is this \$square(3)")
|
|||
The FuncParser can be applied to any string. Out of the box it's applied in a few situations:
|
||||
|
||||
- _Outgoing messages_. All messages sent from the server is processed through FuncParser and every
|
||||
callable is provided the [Session](Sessions) of the object receiving the message. This potentially
|
||||
callable is provided the [Session](./Sessions) of the object receiving the message. This potentially
|
||||
allows a message to be modified on the fly to look different for different recipients.
|
||||
- _Prototype values_. A [Prototype](Prototypes) dict's values are run through the parser such that every
|
||||
- _Prototype values_. A [Prototype](./Prototypes) dict's values are run through the parser such that every
|
||||
callable gets a reference to the rest of the prototype. In the Prototype ORM, this would allow builders
|
||||
to safely call functions to set non-string values to prototype values, get random values, reference
|
||||
other fields of the prototype, and more.
|
||||
- _Actor-stance in messages to others_. In the
|
||||
[Object.msg_contents](api:evennia.objects.objects#objects.objects.DefaultObject.msg_contents) method,
|
||||
[Object.msg_contents](api:evennia.objects.objects#DefaultObject.msg_contents) method,
|
||||
the outgoing string is parsed for special `$You()` and `$conj()` callables to decide if a given recipient
|
||||
should see "You" or the character's name.
|
||||
|
||||
|
|
@ -115,20 +112,18 @@ The other arguments to the parser:
|
|||
Here's an example of using the default/reserved keywords:
|
||||
|
||||
```python
|
||||
|
||||
def _test(*args, **kwargs):
|
||||
# do stuff
|
||||
return something
|
||||
|
||||
parser = funcparser.FuncParser({"test": _test}, mydefault=2)
|
||||
result = parser.parse("$test(foo, bar=4)", myreserved=[1, 2, 3])
|
||||
|
||||
```
|
||||
Here the callable will be called as
|
||||
|
||||
```python
|
||||
_test('foo', bar='4', mydefault=2, myreserved=[1, 2, 3],
|
||||
funcparser=<FuncParser>, raise_errrors=False)
|
||||
_test('foo', bar='4', mydefault=2, myreserved=[1, 2, 3],
|
||||
funcparser=<FuncParser>, raise_errrors=False)
|
||||
```
|
||||
|
||||
The `mydefault=2` kwarg could be overwritten if we made the call as `$test(mydefault=...)`
|
||||
|
|
@ -154,8 +149,8 @@ an example of an `$toint` function; it converts numbers to integers.
|
|||
"There's a $toint(22.0)% chance of survival."
|
||||
|
||||
What will enter the `$toint` callable (as `args[0]`) is the _string_ `"22.0"`. The function is responsible
|
||||
for converting this to a float so it can operate on it. And also to properly handle invalid inputs (like
|
||||
non-numbers). Common is to just return the input as-is or return the empty string.
|
||||
for converting this to a number so that we can convert it to an integer. We must also properly handle invalid
|
||||
inputs (like non-numbers).
|
||||
|
||||
If you want to mark an error, raise `evennia.utils.funcparser.ParsingError`. This stops the entire parsing
|
||||
of the string and may or may not raise the exception depending on what you set `raise_errors` to when you
|
||||
|
|
@ -167,12 +162,12 @@ Python's `literal_eval` and/or `simple_eval`.
|
|||
|
||||
"There's a $toint($eval(10 * 2.2))% chance of survival."
|
||||
|
||||
Since the `$eval` is the innermost call, it will get a sring as input - the string `"10 * 2.2"`.
|
||||
Since the `$eval` is the innermost call, it will get a string as input - the string `"10 * 2.2"`.
|
||||
It evaluates this and returns the `float` `22.0`. This time the outermost `$toint` will be called with
|
||||
this `float` instead of with a string.
|
||||
|
||||
> Since you don't know in which order users will nest or not nest your callables, it's important to
|
||||
> safely validate your inputs. See the next section for useful tools to do this.
|
||||
> It's important to safely validate your inputs since users may end up nesting your callables in any order.
|
||||
> See the next section for useful tools to help with this.
|
||||
|
||||
In these examples, the result will be embedded in the larger string, so the result of the entire parsing
|
||||
will be a string:
|
||||
|
|
@ -195,16 +190,16 @@ parser.parse_to_any("$toint($eval(10 * 2.2)")
|
|||
### Safe convertion of inputs
|
||||
|
||||
Since you don't know in which order users may use your callables, they should always check the types
|
||||
of its inputs and convert it to type the callable needs. Note also that this limits what inputs you can
|
||||
support since some things (such as complex classes/callables etc) are just not safe/possible to
|
||||
convert from string representation.
|
||||
of its inputs and convert to the type the callable needs. Note also that when converting from strings,
|
||||
there are limits what inputs you can support. This is because FunctionParser strings are often used by
|
||||
non-developer players/builders and some things (such as complex classes/callables etc) are just not
|
||||
safe/possible to convert from string representation.
|
||||
|
||||
In `evennia.utils.utils` is a helper called
|
||||
[safe_convert_to_types](api.evennia.utils.utils#evennia.utils.utils.safe_convert_to_types). This function
|
||||
[safe_convert_to_types](api:evennia.utils.utils#evennia.utils.utils.safe_convert_to_types). This function
|
||||
automates the conversion of simple data types in a safe way:
|
||||
|
||||
```python
|
||||
|
||||
from evennia.utils.utils import safe_convert_to_types
|
||||
|
||||
def _process_callable(*args, **kwargs):
|
||||
|
|
@ -225,7 +220,6 @@ def _process_callable(*args, **kwargs):
|
|||
In other words,
|
||||
|
||||
```python
|
||||
|
||||
args, kwargs = safe_convert_to_type(
|
||||
(tuple_of_arg_converters, dict_of_kwarg_converters), *args, **kwargs)
|
||||
```
|
||||
|
|
@ -245,12 +239,12 @@ following tools (which you may also find useful to experiment with on your own):
|
|||
containers like lists/dicts etc, so this and `literal_eval` are complementary to each other.
|
||||
|
||||
```warning::
|
||||
It may be tempting to run use Python's in-built `eval()` or `exec()` functions as converters since these
|
||||
are able to convert any valid Python source code to Python. NEVER DO THIS unless you really, really
|
||||
know ONLY developers will ever send input to the callable. The parser is intended
|
||||
It may be tempting to run use Python's in-built ``eval()`` or ``exec()`` functions as converters since
|
||||
these are able to convert any valid Python source code to Python. NEVER DO THIS unless you really, really
|
||||
know that ONLY developers will ever modify the string going into the callable. The parser is intended
|
||||
for untrusted users (if you were trusted you'd have access to Python already). Letting untrusted users
|
||||
pass strings to eval/exec is a MAJOR security risk. It allows the caller to effectively run arbitrary
|
||||
Python code on your server. This is the way to maliciously deleted hard drives. Just don't do it and
|
||||
pass strings to ``eval``/``exec`` is a MAJOR security risk. It allows the caller to run arbitrary
|
||||
Python code on your server. This is the path to maliciously deleted hard drives. Just don't do it and
|
||||
sleep better at night.
|
||||
```
|
||||
|
||||
|
|
@ -264,16 +258,16 @@ more to them when you create your `FuncParser` instance to have those callables
|
|||
|
||||
These are the 'base' callables.
|
||||
|
||||
- `$eval(expression)` ([code](api:evennia.utils.funcparser#evennia.utils.funcparser.funcparser_callable_eval) -
|
||||
- `$eval(expression)` ([code](api:evennia.utils.funcparser#evennia.utils.funcparser.funcparser_callable_eval)) -
|
||||
this uses `literal_eval` and `simple_eval` (see previous section) attemt to convert a string expression
|
||||
to a python object. This handles e.g. lists of literals `[1, 2, 3]` and simple expressions like `"1 + 2"`.
|
||||
- `$toint(number)` ([code](api:evennia.utils.funcparser#evennia.utils.funcparser.funcparser_callable_toint)) -
|
||||
always converts an output to an integer, if possible.
|
||||
- `$add/sub/mult/div(obj1, obj2)` ([code](api:evennia.utils.funcparser#evennia.utils.funcparser.funcparser_callable_add))) -
|
||||
- `$add/sub/mult/div(obj1, obj2)` ([code](api:evennia.utils.funcparser#evennia.utils.funcparser.funcparser_callable_add)) -
|
||||
this adds/subtracts/multiplies and divides to elements together. While simple addition could be done with
|
||||
`$eval`, this could for example be used also to add two lists together, which is not possible with `eval`;
|
||||
for example `$add($eval([1,2,3]), $eval([4,5,6])) -> [1, 2, 3, 4, 5, 6]`.
|
||||
- `$round(float, significant)` ([code](api:evennia.utils.funcparser#evennia.utils.funcparser.funcparser_callable_round) -
|
||||
- `$round(float, significant)` ([code](api:evennia.utils.funcparser#evennia.utils.funcparser.funcparser_callable_round)) -
|
||||
rounds an input float into the number of provided significant digits. For example `$round(3.54343, 3) -> 3.543`.
|
||||
- `$random([start, [end]])` ([code](api:evennia.utils.funcparser#evennia.utils.funcparser.funcparser_callable_random)) -
|
||||
this works like the Python `random()` function, but will randomize to an integer value if both start/end are
|
||||
|
|
@ -296,7 +290,7 @@ These are the 'base' callables.
|
|||
- `$ljust` - shortcut to justify-left. Takes all other kwarg of `$just`.
|
||||
- `$rjust` - shortcut to right justify.
|
||||
- `$cjust` - shortcut to center justify.
|
||||
- `$clr(startcolor, text[, endcolor])`([code](api:evennia.utils.funcparser#evennia.utils.funcparser.funcparser_callable_clr)) -
|
||||
- `$clr(startcolor, text[, endcolor])` ([code](api:evennia.utils.funcparser#evennia.utils.funcparser.funcparser_callable_clr)) -
|
||||
color text. The color is given with one or two characters without the preceeding `|`. If no endcolor is
|
||||
given, the string will go back to neutral, so `$clr(r, Hello)` is equivalent to `|rHello|n`.
|
||||
|
||||
|
|
@ -310,7 +304,7 @@ parser.parse_to_any(string, caller=<object or account>, access="control", ...)`
|
|||
|
||||
```
|
||||
The `caller` is required, it's the the object to do the access-check for. The `access` kwarg is the
|
||||
[lock type](Locks) to check, default being `"control"`.
|
||||
[lock type](./Locks) to check, default being `"control"`.
|
||||
|
||||
- `$search(query,type=account|script,return_list=False)` ([code](api:evennia.utils.funcparser#evennia.utils.funcparser.funcparser_callable_search)) -
|
||||
this will look up and try to match an object by key or alias. Use the `type` kwarg to
|
||||
|
|
@ -342,9 +336,9 @@ references to other objects accessible via these callables.
|
|||
result of `you_obj.get_display_name(looker=receiver)`. This allows for a single string to echo differently
|
||||
depending on who sees it, and also to reference other people in the same way.
|
||||
- `$You([key])` - same as `$you` but always capitalized.
|
||||
- `$conj(verb)` - conjugates a verb between 2nd person presens to 3rd person presence depending on who
|
||||
sees the string. For example `$You() $conj(smiles).` will show as "You smile." and "Tom smiles." depending
|
||||
on who sees it. This makes use of the tools in [evennia.utils.verb_conjugation](api.evennia.utils.verb_conjugation)
|
||||
- `$conj(verb)` ([code](api:evennia.utils.funcparser#evennia.utils.funcparser.funcparser_callable_conjugate)) -- conjugates a verb between 4nd person presens to 3rd person presence depending on who
|
||||
sees the string. For example `"$You() $conj(smiles)".` will show as "You smile." and "Tom smiles." depending
|
||||
on who sees it. This makes use of the tools in [evennia.utils.verb_conjugation](api:evennia.utils.verb_conjugation)
|
||||
to do this, and only works for English verbs.
|
||||
|
||||
### Example
|
||||
|
|
@ -384,7 +378,5 @@ all the defaults (like `$toint()`).
|
|||
|
||||
The parsed result of the above would be something like this:
|
||||
|
||||
```
|
||||
This is the current uptime:
|
||||
------- 343 seconds -------
|
||||
```
|
||||
This is the current uptime:
|
||||
------- 343 seconds -------
|
||||
Loading…
Add table
Add a link
Reference in a new issue