Clean up docs and more funcparser fixes

This commit is contained in:
Griatch 2021-03-27 23:43:46 +01:00
parent c65c68e4c2
commit c9d9e9c6f8
21 changed files with 438 additions and 512 deletions

View file

@ -2,10 +2,13 @@
Generic function parser for functions embedded in a string, on the form
`$funcname(*args, **kwargs)`, for example:
"A string $foo() with $bar(a, b, c, $moo(), d=23) etc."
```
"A string $foo() with $bar(a, b, c, $moo(), d=23) etc."
```
Each arg/kwarg can also be another nested function. These will be executed from
the deepest-nested first and used as arguments for the higher-level function.
Each arg/kwarg can also be another nested function. These will be executed
inside-out and their return will used as arguments for the enclosing function
(so the same as for regular Python function execution).
This is the base for all forms of embedded func-parsing, like inlinefuncs and
protfuncs. Each function available to use must be registered as a 'safe'
@ -23,7 +26,6 @@ def funcname(*args, **kwargs):
# it must always accept *args and **kwargs.
...
return something
```
Usage:
@ -38,6 +40,7 @@ result = parser.parse("String with $funcname() in it")
The `FuncParser` also accepts a direct dict mapping of `{'name': callable, ...}`.
---
"""
import re
@ -53,20 +56,21 @@ from evennia.utils.utils import (
from evennia.utils import search
from evennia.utils.verb_conjugation.conjugate import verb_actor_stance_components
_CLIENT_DEFAULT_WIDTH = settings.CLIENT_DEFAULT_WIDTH
_MAX_NESTING = 20
# setup
_ESCAPE_CHAR = "\\"
_START_CHAR = "$"
_CLIENT_DEFAULT_WIDTH = settings.CLIENT_DEFAULT_WIDTH
_MAX_NESTING = settings.FUNCPARSER_MAX_NESTING
_START_CHAR = settings.FUNCPARSER_START_CHAR
_ESCAPE_CHAR = settings.FUNCPARSER_ESCAPE_CHAR
@dataclasses.dataclass
class ParsedFunc:
class _ParsedFunc:
"""
Represents a function parsed from the string
"""
prefix: str = "$"
prefix: str = _START_CHAR
funcname: str = ""
args: list = dataclasses.field(default_factory=list)
kwargs: dict = dataclasses.field(default_factory=dict)
@ -98,7 +102,8 @@ class ParsingError(RuntimeError):
class FuncParser:
"""
Sets up a parser for strings containing $funcname(*args, **kwargs) substrings.
Sets up a parser for strings containing `$funcname(*args, **kwargs)`
substrings.
"""
@ -113,26 +118,23 @@ class FuncParser:
Args:
callables (str, module, list or dict): Where to find
'safe' functions to make available in the parser. These modules
can have a dict `FUNCPARSER_CALLABLES = {"funcname": callable, ...}`.
If no such dict exists, all callables in provided modules (whose names
don't start with an underscore) will be loaded as callables. Each
callable will will be available to call as `$funcname(*args, **kwags)`
during parsing. If `callables` is a `str`, this should be the path
to such a module. A `list` can either be a list of paths or module
objects. If a `dict`, this should be a direct mapping
`{"funcname": callable, ...}` to use.
'safe' functions to make available in the parser. If a `dict`,
it should be a direct mapping `{"funcname": callable, ...}`. If
one or mode modules or module-paths, the module(s) are first checked
for a dict `FUNCPARSER_CALLABLES = {"funcname", callable, ...}`. If
no such variable exists, all callables in the module (whose name does
not start with an underscore) will be made available to the parser.
start_char (str, optional): A character used to identify the beginning
of a parseable function. Default is `$`.
escape_char (str, optional): Prepend characters with this to have
them not count as a function. Default is `\\`.
them not count as a function. Default is the backtick, `\\\\`.
max_nesting (int, optional): How many levels of nested function calls
are allowed, to avoid exploitation. Default is 20.
**default_kwargs: These kwargs will be passed into all callables. These
kwargs can be overridden both by kwargs passed direcetly to `.parse` _and_
kwargs can be overridden both by kwargs passed direcetly to `.parse` *and*
by kwargs given directly in the string `$funcname` call. They are
suitable for global defaults that is intended to be changed by the
user. To _guarantee_ a call always gets a particular kwarg, pass it
user. To guarantee a call always gets a particular kwarg, pass it
into `.parse` as `**reserved_kwargs` instead.
"""
@ -194,7 +196,7 @@ class FuncParser:
Execute a parsed function
Args:
parsedfunc (ParsedFunc): This dataclass holds the parsed details
parsedfunc (_ParsedFunc): This dataclass holds the parsed details
of the function.
raise_errors (bool, optional): Raise errors. Otherwise return the
string with the function unparsed.
@ -254,9 +256,9 @@ class FuncParser:
def parse(self, string, raise_errors=False, escape=False,
strip=False, return_str=True, **reserved_kwargs):
"""
Use parser to parse a string that may or may not have `$funcname(*args, **kwargs)`
- style tokens in it. Only the callables used to initiate the parser
will be eligible for parsing, others will remain un-parsed.
Use parser to parse a string that may or may not have
`$funcname(*args, **kwargs)` - style tokens in it. Only the callables
used to initiate the parser will be eligible for parsing.
Args:
string (str): The string to parse.
@ -357,7 +359,7 @@ class FuncParser:
callstack.append(curr_func)
# start a new func
curr_func = ParsedFunc(prefix=char, fullstr=char)
curr_func = _ParsedFunc(prefix=char, fullstr=char)
continue
if not curr_func:
@ -592,16 +594,16 @@ def funcparser_callable_eval(*args, **kwargs):
incoming string into a python object. If it fails, the return will be same
as the input.
Args
Args:
string (str): The string to parse. Only simple literals or operators are allowed.
Returns:
any: The string parsed into its Python form, or the same as input.
Example:
`$py(1)`
`$py([1,2,3,4])`
`$py(3 + 4)`
Examples:
- `$py(1) -> 1`
- `$py([1,2,3,4] -> [1, 2, 3]`
- `$py(3 + 4) -> 7`
"""
args, kwargs = safe_convert_to_types(("py", {}) , *args, **kwargs)
@ -652,22 +654,22 @@ def _apply_operation_two_elements(*args, operator="+", **kwargs):
def funcparser_callable_add(*args, **kwargs):
"""Usage: $add(val1, val2) -> val1 + val2"""
"""Usage: `$add(val1, val2) -> val1 + val2`"""
return _apply_operation_two_elements(*args, operator='+', **kwargs)
def funcparser_callable_sub(*args, **kwargs):
"""Usage: $sub(val1, val2) -> val1 - val2"""
"""Usage: ``$sub(val1, val2) -> val1 - val2`"""
return _apply_operation_two_elements(*args, operator='-', **kwargs)
def funcparser_callable_mult(*args, **kwargs):
"""Usage: $mult(val1, val2) -> val1 * val2"""
"""Usage: `$mult(val1, val2) -> val1 * val2`"""
return _apply_operation_two_elements(*args, operator='*', **kwargs)
def funcparser_callable_div(*args, **kwargs):
"""Usage: $mult(val1, val2) -> val1 / val2"""
"""Usage: `$mult(val1, val2) -> val1 / val2`"""
return _apply_operation_two_elements(*args, operator='/', **kwargs)
@ -686,8 +688,8 @@ def funcparser_callable_round(*args, **kwargs):
any: The rounded value or inp if inp was not a number.
Examples:
- `$round(3.5434343, 3)` - gives 3.543
- `$round($random(), 2)` - rounds random result, e.g 0.22
- `$round(3.5434343, 3) -> 3.543`
- `$round($random(), 2)` - rounds random result, e.g `0.22`
"""
if not args:
@ -738,7 +740,7 @@ def funcparser_callable_random(*args, **kwargs):
try:
if isinstance(minval, float) or isinstance(maxval, float):
return minval + maxval * random.random()
return minval + ((maxval - minval) * random.random())
else:
return random.randint(minval, maxval)
except Exception:
@ -794,8 +796,8 @@ def funcparser_callable_pad(*args, **kwargs):
fillchar (str, optional): Character used for padding. Defaults to a space.
Example:
- `$pad(text, 12, l, ' ')`
- `$pad(text, width=12, align=c, fillchar=-)`
- `$pad(text, 12, r, ' ') -> " text"`
- `$pad(text, width=12, align=c, fillchar=-) -> "----text----"`
"""
if not args:
@ -829,8 +831,8 @@ def funcparser_callable_crop(*args, **kwargs):
of the string was cropped. Defaults to `[...]`.
Example:
`$crop(text, 78, [...])`
`$crop(text, width=78, suffix='[...]')`
- `$crop(A long text, 10, [...]) -> "A lon[...]"`
- `$crop(text, width=11, suffix='[...]) -> "A long[...]"`
"""
if not args:
@ -875,8 +877,8 @@ def funcparser_callable_justify(*args, **kwargs):
str: The justified text.
Examples:
- $just(text, width=40)
- $just(text, align=r, indent=2)
- `$just(text, width=40)`
- `$just(text, align=r, indent=2)`
"""
if not args:
@ -925,9 +927,9 @@ def funcparser_callable_clr(*args, **kwargs):
color (str, optional): If given,
Example:
- `$clr(r, text, n)`
- `$clr(r, text)`
- `$clr(text, start=r, end=n)`
- `$clr(r, text, n) -> "|rtext|n"`
- `$clr(r, text) -> "|rtext|n`
- `$clr(text, start=r, end=n) -> "|rtext|n"`
"""
if not args:
@ -961,7 +963,7 @@ def funcparser_callable_search(*args, caller=None, access="control", **kwargs):
Args:
query (str): The key or dbref to search for.
Kwargs:
Keyword Args:
return_list (bool): If set, return a list of objects with
0, 1 or more matches to `query`. Defaults to False.
type (str): One of 'obj', 'account', 'script'
@ -1037,7 +1039,7 @@ def funcparser_callable_you(*args, caller=None, receiver=None, mapping=None, cap
Replaces with you for the caller of the string, with the display_name
of the caller for others.
Kwargs:
Keyword Args:
caller (Object): The 'you' in the string. This is used unless another
you-key is passed to the callable in combination with `mapping`.
receiver (Object): The recipient of the string.
@ -1093,10 +1095,9 @@ def funcparser_callable_You(*args, you=None, receiver=None, mapping=None, capita
def funcparser_callable_conjugate(*args, caller=None, receiver=None, **kwargs):
"""
$conj(verb)
Conjugate a verb according to if it should be 2nd or third person.
Kwargs:
Keyword Args:
caller (Object): The object who represents 'you' in the string.
receiver (Object): The recipient of the string.
@ -1107,13 +1108,12 @@ def funcparser_callable_conjugate(*args, caller=None, receiver=None, **kwargs):
ParsingError: If `you` and `recipient` were not both supplied.
Notes:
Note that it will not capitalized.
This assumes that the active party (You) is the one performing the verb.
Note that the verb will not be capitalized. It also
assumes that the active party (You) is the one performing the verb.
This automatic conjugation will fail if the active part is another person
than 'you'.
The you/receiver should be passed to the parser directly.
than 'you'. The caller/receiver must be passed to the parser directly.
Exampels:
Examples:
This is often used in combination with the $you/You( callables.
- `With a grin, $you() $conj(jump)`