Further cleanup and refactoring
This commit is contained in:
parent
7891987e05
commit
c65c68e4c2
6 changed files with 526 additions and 292 deletions
|
|
@ -803,7 +803,7 @@ class DefaultObject(ObjectDB, metaclass=TypeclassBase):
|
|||
# actor-stance replacements
|
||||
inmessage = _MSG_CONTENTS_PARSER.parse(
|
||||
inmessage, raise_errors=True, return_string=True,
|
||||
you=you, receiver=receiver, mapping=mapping)
|
||||
caller=you, receiver=receiver, mapping=mapping)
|
||||
|
||||
# director-stance replacements
|
||||
outmessage = inmessage.format(
|
||||
|
|
|
|||
|
|
@ -46,11 +46,10 @@ import inspect
|
|||
import random
|
||||
from functools import partial
|
||||
from django.conf import settings
|
||||
from ast import literal_eval
|
||||
from simpleeval import simple_eval
|
||||
from evennia.utils import logger
|
||||
from evennia.utils.utils import (
|
||||
make_iter, callables_from_module, variable_from_module, pad, crop, justify)
|
||||
make_iter, callables_from_module, variable_from_module, pad, crop, justify,
|
||||
safe_convert_to_types)
|
||||
from evennia.utils import search
|
||||
from evennia.utils.verb_conjugation.conjugate import verb_actor_stance_components
|
||||
|
||||
|
|
@ -233,11 +232,15 @@ class FuncParser:
|
|||
f"(available: {available})")
|
||||
return str(parsedfunc)
|
||||
|
||||
nargs = len(args)
|
||||
|
||||
# build kwargs in the proper priority order
|
||||
kwargs = {**self.default_kwargs, **kwargs, **reserved_kwargs}
|
||||
kwargs = {**self.default_kwargs, **kwargs, **reserved_kwargs,
|
||||
**{'funcparser': self, "raise_errors": raise_errors}}
|
||||
|
||||
try:
|
||||
return func(*args, **kwargs)
|
||||
ret = func(*args, **kwargs)
|
||||
return ret
|
||||
except ParsingError:
|
||||
if raise_errors:
|
||||
raise
|
||||
|
|
@ -601,19 +604,8 @@ def funcparser_callable_eval(*args, **kwargs):
|
|||
`$py(3 + 4)`
|
||||
|
||||
"""
|
||||
if not args:
|
||||
return ''
|
||||
inp = args[0]
|
||||
if not isinstance(inp, str):
|
||||
# already converted
|
||||
return inp
|
||||
try:
|
||||
return literal_eval(inp)
|
||||
except Exception:
|
||||
try:
|
||||
return simple_eval(inp)
|
||||
except Exception:
|
||||
return inp
|
||||
args, kwargs = safe_convert_to_types(("py", {}) , *args, **kwargs)
|
||||
return args[0] if args else ''
|
||||
|
||||
|
||||
def funcparser_callable_toint(*args, **kwargs):
|
||||
|
|
@ -640,28 +632,23 @@ def _apply_operation_two_elements(*args, operator="+", **kwargs):
|
|||
better for non-list arithmetic.
|
||||
|
||||
"""
|
||||
args, kwargs = safe_convert_to_types((('py', 'py'), {}), *args, **kwargs)
|
||||
if not len(args) > 1:
|
||||
return ''
|
||||
val1, val2 = args[0], args[1]
|
||||
# try to convert to python structures, otherwise, keep as strings
|
||||
if isinstance(val1, str):
|
||||
try:
|
||||
val1 = literal_eval(val1.strip())
|
||||
except Exception:
|
||||
pass
|
||||
if isinstance(val2, str):
|
||||
try:
|
||||
val2 = literal_eval(val2.strip())
|
||||
except Exception:
|
||||
pass
|
||||
if operator == "+":
|
||||
return val1 + val2
|
||||
elif operator == "-":
|
||||
return val1 - val2
|
||||
elif operator == "*":
|
||||
return val1 * val2
|
||||
elif operator == "/":
|
||||
return val1 / val2
|
||||
try:
|
||||
if operator == "+":
|
||||
return val1 + val2
|
||||
elif operator == "-":
|
||||
return val1 - val2
|
||||
elif operator == "*":
|
||||
return val1 * val2
|
||||
elif operator == "/":
|
||||
return val1 / val2
|
||||
except Exception:
|
||||
if kwargs.get('raise_errors'):
|
||||
raise
|
||||
return ''
|
||||
|
||||
|
||||
def funcparser_callable_add(*args, **kwargs):
|
||||
|
|
@ -705,21 +692,15 @@ def funcparser_callable_round(*args, **kwargs):
|
|||
"""
|
||||
if not args:
|
||||
return ''
|
||||
inp, *significant = args
|
||||
significant = significant[0] if significant else '0'
|
||||
lit_inp = inp
|
||||
if isinstance(inp, str):
|
||||
try:
|
||||
lit_inp = literal_eval(inp)
|
||||
except Exception:
|
||||
return inp
|
||||
args, _ = safe_convert_to_types(((float, int), {}) *args, **kwargs)
|
||||
|
||||
num, *significant = args
|
||||
significant = significant[0] if significant else 0
|
||||
try:
|
||||
int(significant)
|
||||
except Exception:
|
||||
significant = 0
|
||||
try:
|
||||
round(lit_inp, significant)
|
||||
round(num, significant)
|
||||
except Exception:
|
||||
if kwargs.get('raise_errors'):
|
||||
raise
|
||||
return ''
|
||||
|
||||
def funcparser_callable_random(*args, **kwargs):
|
||||
|
|
@ -744,35 +725,32 @@ def funcparser_callable_random(*args, **kwargs):
|
|||
- `$random(5, 10.0)` - random value [5..10] (float)
|
||||
|
||||
"""
|
||||
args, _ = safe_convert_to_types((('py', 'py'), {}), *args, **kwargs)
|
||||
|
||||
nargs = len(args)
|
||||
if nargs == 1:
|
||||
# only maxval given
|
||||
minval, maxval = "0", args[0]
|
||||
minval, maxval = 0, args[0]
|
||||
elif nargs > 1:
|
||||
minval, maxval = args[:2]
|
||||
else:
|
||||
minval, maxval = ("0", "1")
|
||||
minval, maxval = 0, 1
|
||||
|
||||
if "." in minval or "." in maxval:
|
||||
# float mode
|
||||
try:
|
||||
minval, maxval = float(minval), float(maxval)
|
||||
except ValueError:
|
||||
minval, maxval = 0, 1
|
||||
return minval + maxval * random.random()
|
||||
else:
|
||||
# int mode
|
||||
try:
|
||||
minval, maxval = int(minval), int(maxval)
|
||||
except ValueError:
|
||||
minval, maxval = 0, 1
|
||||
return random.randint(minval, maxval)
|
||||
try:
|
||||
if isinstance(minval, float) or isinstance(maxval, float):
|
||||
return minval + maxval * random.random()
|
||||
else:
|
||||
return random.randint(minval, maxval)
|
||||
except Exception:
|
||||
if kwargs.get('raise_errors'):
|
||||
raise
|
||||
return ''
|
||||
|
||||
def funcparser_callable_randint(*args, **kwargs):
|
||||
"""
|
||||
Usage: $randint(start, end):
|
||||
|
||||
Legacy alias - alwas returns integers.
|
||||
Legacy alias - always returns integers.
|
||||
|
||||
"""
|
||||
return int(funcparser_callable_random(*args, **kwargs))
|
||||
|
|
@ -796,10 +774,13 @@ def funcparser_callable_choice(*args, **kwargs):
|
|||
"""
|
||||
if not args:
|
||||
return ''
|
||||
inp = args[0]
|
||||
if not isinstance(inp, str):
|
||||
inp = literal_eval(inp)
|
||||
return random.choice(inp)
|
||||
args, _ = safe_convert_to_types(('py', {}), *args, **kwargs)
|
||||
try:
|
||||
return random.choice(args[0])
|
||||
except Exception:
|
||||
if kwargs.get('raise_errors'):
|
||||
raise
|
||||
return ''
|
||||
|
||||
|
||||
def funcparser_callable_pad(*args, **kwargs):
|
||||
|
|
@ -819,6 +800,9 @@ def funcparser_callable_pad(*args, **kwargs):
|
|||
"""
|
||||
if not args:
|
||||
return ''
|
||||
args, kwargs = safe_convert_to_types(
|
||||
((str, int, str, str), {'width': int, 'align': str, 'fillchar': str}), *args, **kwargs)
|
||||
|
||||
text, *rest = args
|
||||
nrest = len(rest)
|
||||
try:
|
||||
|
|
@ -833,22 +817,6 @@ def funcparser_callable_pad(*args, **kwargs):
|
|||
return pad(str(text), width=width, align=align, fillchar=fillchar)
|
||||
|
||||
|
||||
def funcparser_callable_space(*args, **kwarg):
|
||||
"""
|
||||
Usage: $space(43)
|
||||
|
||||
Insert a length of space.
|
||||
|
||||
"""
|
||||
if not args:
|
||||
return ''
|
||||
try:
|
||||
width = int(args[0])
|
||||
except TypeError:
|
||||
width = 1
|
||||
return " " * width
|
||||
|
||||
|
||||
def funcparser_callable_crop(*args, **kwargs):
|
||||
"""
|
||||
FuncParser callable. Crops ingoing text to given widths.
|
||||
|
|
@ -877,6 +845,22 @@ def funcparser_callable_crop(*args, **kwargs):
|
|||
return crop(str(text), width=width, suffix=str(suffix))
|
||||
|
||||
|
||||
def funcparser_callable_space(*args, **kwarg):
|
||||
"""
|
||||
Usage: $space(43)
|
||||
|
||||
Insert a length of space.
|
||||
|
||||
"""
|
||||
if not args:
|
||||
return ''
|
||||
try:
|
||||
width = int(args[0])
|
||||
except TypeError:
|
||||
width = 1
|
||||
return " " * width
|
||||
|
||||
|
||||
def funcparser_callable_justify(*args, **kwargs):
|
||||
"""
|
||||
Justify text across a width, default across screen width.
|
||||
|
|
@ -948,6 +932,7 @@ def funcparser_callable_clr(*args, **kwargs):
|
|||
"""
|
||||
if not args:
|
||||
return ''
|
||||
|
||||
startclr, text, endclr = '', '', ''
|
||||
if len(args) > 1:
|
||||
# $clr(pre, text, post))
|
||||
|
|
@ -1045,7 +1030,7 @@ def funcparser_callable_search_list(*args, caller=None, access="control", **kwar
|
|||
return_list=True, **kwargs)
|
||||
|
||||
|
||||
def funcparser_callable_you(*args, you=None, receiver=None, mapping=None, capitalize=False, **kwargs):
|
||||
def funcparser_callable_you(*args, caller=None, receiver=None, mapping=None, capitalize=False, **kwargs):
|
||||
"""
|
||||
Usage: $you() or $you(key)
|
||||
|
||||
|
|
@ -1053,19 +1038,19 @@ def funcparser_callable_you(*args, you=None, receiver=None, mapping=None, capita
|
|||
of the caller for others.
|
||||
|
||||
Kwargs:
|
||||
you (Object): The 'you' in the string. This is used unless another
|
||||
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.
|
||||
mapping (dict, optional): This is a mapping `{key:Object, ...}` and is
|
||||
used to find which object `$you(key)` refers to. If not given, the
|
||||
`you` kwarg is used.
|
||||
`caller` kwarg is used.
|
||||
capitalize (bool): Passed by the You helper, to capitalize you.
|
||||
|
||||
Returns:
|
||||
str: The parsed string.
|
||||
|
||||
Raises:
|
||||
ParsingError: If `you` and `receiver` were not supplied.
|
||||
ParsingError: If `caller` and `receiver` were not supplied.
|
||||
|
||||
Notes:
|
||||
The kwargs should be passed the to parser directly.
|
||||
|
|
@ -1076,7 +1061,7 @@ def funcparser_callable_you(*args, you=None, receiver=None, mapping=None, capita
|
|||
|
||||
- `With a grin, $you() $conj(jump) at $you(tommy).`
|
||||
|
||||
The You-object will see "With a grin, you jump at Tommy."
|
||||
The caller-object will see "With a grin, you jump at Tommy."
|
||||
Tommy will see "With a grin, CharName jumps at you."
|
||||
Others will see "With a grin, CharName jumps at Tommy."
|
||||
|
||||
|
|
@ -1084,17 +1069,17 @@ def funcparser_callable_you(*args, you=None, receiver=None, mapping=None, capita
|
|||
if args and mapping:
|
||||
# this would mean a $you(key) form
|
||||
try:
|
||||
you = mapping.get(args[0])
|
||||
caller = mapping.get(args[0])
|
||||
except KeyError:
|
||||
pass
|
||||
|
||||
if not (you and receiver):
|
||||
raise ParsingError("No you-object or receiver supplied to $you callable.")
|
||||
if not (caller and receiver):
|
||||
raise ParsingError("No caller or receiver supplied to $you callable.")
|
||||
|
||||
capitalize = bool(capitalize)
|
||||
if you == receiver:
|
||||
if caller == receiver:
|
||||
return "You" if capitalize else "you"
|
||||
return you.get_display_name(looker=receiver) if hasattr(you, "get_display_name") else str(you)
|
||||
return caller.get_display_name(looker=receiver) if hasattr(caller, "get_display_name") else str(caller)
|
||||
|
||||
|
||||
def funcparser_callable_You(*args, you=None, receiver=None, mapping=None, capitalize=True, **kwargs):
|
||||
|
|
@ -1106,14 +1091,14 @@ def funcparser_callable_You(*args, you=None, receiver=None, mapping=None, capita
|
|||
*args, you=you, receiver=receiver, mapping=mapping, capitalize=capitalize, **kwargs)
|
||||
|
||||
|
||||
def funcparser_callable_conjugate(*args, you=None, receiver=None, **kwargs):
|
||||
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:
|
||||
you_obj (Object): The object who represents 'you' in the string.
|
||||
you_target (Object): The recipient of the string.
|
||||
caller (Object): The object who represents 'you' in the string.
|
||||
receiver (Object): The recipient of the string.
|
||||
|
||||
Returns:
|
||||
str: The parsed string.
|
||||
|
|
@ -1139,11 +1124,11 @@ def funcparser_callable_conjugate(*args, you=None, receiver=None, **kwargs):
|
|||
"""
|
||||
if not args:
|
||||
return ''
|
||||
if not (you and receiver):
|
||||
raise ParsingError("No youj/receiver supplied to $conj callable")
|
||||
if not (caller and receiver):
|
||||
raise ParsingError("No caller/receiver supplied to $conj callable")
|
||||
|
||||
second_person_str, third_person_str = verb_actor_stance_components(args[0])
|
||||
return second_person_str if you == receiver else third_person_str
|
||||
return second_person_str if caller == receiver else third_person_str
|
||||
|
||||
|
||||
# these are made available as callables by adding 'evennia.utils.funcparser' as
|
||||
|
|
|
|||
|
|
@ -14,6 +14,8 @@ from evennia.utils import funcparser, test_resources
|
|||
|
||||
|
||||
def _test_callable(*args, **kwargs):
|
||||
kwargs.pop('funcparser', None)
|
||||
kwargs.pop('raise_errors', None)
|
||||
argstr = ", ".join(args)
|
||||
kwargstr = ""
|
||||
if kwargs:
|
||||
|
|
@ -311,10 +313,10 @@ class TestDefaultCallables(TestCase):
|
|||
|
||||
"""
|
||||
mapping = {"char1": self.obj1, "char2": self.obj2}
|
||||
ret = self.parser.parse(string, you=self.obj1, receiver=self.obj1, mapping=mapping,
|
||||
ret = self.parser.parse(string, caller=self.obj1, receiver=self.obj1, mapping=mapping,
|
||||
raise_errors=True)
|
||||
self.assertEqual(expected_you, ret)
|
||||
ret = self.parser.parse(string, you=self.obj1, receiver=self.obj2, mapping=mapping,
|
||||
ret = self.parser.parse(string, caller=self.obj1, receiver=self.obj2, mapping=mapping,
|
||||
raise_errors=True)
|
||||
self.assertEqual(expected_them, ret)
|
||||
|
||||
|
|
@ -346,10 +348,26 @@ class TestDefaultCallables(TestCase):
|
|||
|
||||
def test_random(self):
|
||||
string = "$random(1,10)"
|
||||
ret = self.parser.parse(string, raise_errors=True)
|
||||
ret = int(ret)
|
||||
ret = self.parser.parse_to_any(string, raise_errors=True)
|
||||
self.assertTrue(1 <= ret <= 10)
|
||||
|
||||
string = "$random()"
|
||||
ret = self.parser.parse_to_any(string, raise_errors=True)
|
||||
self.assertTrue(0 <= ret <= 1)
|
||||
|
||||
string = "$random(1.0, 3.0)"
|
||||
for i in range(1000):
|
||||
ret = self.parser.parse_to_any(string, raise_errors=True)
|
||||
self.assertTrue(isinstance(ret, float))
|
||||
print("ret:", ret)
|
||||
self.assertTrue(1.0 <= ret <= 3.0)
|
||||
|
||||
def test_randint(self):
|
||||
string = "$randint(1.0, 3.0)"
|
||||
ret = self.parser.parse_to_any(string, raise_errors=True)
|
||||
self.assertTrue(isinstance(ret, int))
|
||||
self.assertTrue(1.0 <= ret <= 3.0)
|
||||
|
||||
def test_nofunc(self):
|
||||
self.assertEqual(
|
||||
self.parser.parse("as$382ewrw w we w werw,|44943}"),
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ TODO: Not nearly all utilities are covered yet.
|
|||
import os.path
|
||||
import random
|
||||
|
||||
from parameterized import parameterized
|
||||
import mock
|
||||
from django.test import TestCase
|
||||
from datetime import datetime
|
||||
|
|
@ -385,3 +386,43 @@ class TestPercent(TestCase):
|
|||
self.assertEqual(utils.percent(3, 1, 1), "0.0%")
|
||||
self.assertEqual(utils.percent(3, 0, 1), "100.0%")
|
||||
self.assertEqual(utils.percent(-3, 0, 1), "0.0%")
|
||||
|
||||
|
||||
class TestSafeConvert(TestCase):
|
||||
"""
|
||||
Test evennia.utils.utils.safe_convert_to_types
|
||||
|
||||
"""
|
||||
|
||||
@parameterized.expand([
|
||||
(('1', '2', 3, 4, '5'), {'a': 1, 'b': '2', 'c': 3},
|
||||
((int, float, str, int), {'a': int, 'b': float}), # "
|
||||
(1, 2.0, '3', 4, '5'), {'a': 1, 'b': 2.0, 'c': 3}),
|
||||
(('1 + 2', '[1, 2, 3]', [3, 4, 5]), {'a': '3 + 4', 'b': 5},
|
||||
(('py', 'py', 'py'), {'a': 'py', 'b': 'py'}),
|
||||
(3, [1, 2, 3], [3, 4, 5]), {'a': 7, 'b': 5}),
|
||||
])
|
||||
def test_conversion(self, args, kwargs, converters, expected_args, expected_kwargs):
|
||||
"""
|
||||
Test the converter with different inputs
|
||||
|
||||
"""
|
||||
result_args, result_kwargs = utils.safe_convert_to_types(
|
||||
converters, *args, raise_errors=True, **kwargs)
|
||||
self.assertEqual(expected_args, result_args)
|
||||
self.assertEqual(expected_kwargs, result_kwargs)
|
||||
|
||||
def test_conversion__fail(self):
|
||||
"""
|
||||
Test failing conversion
|
||||
|
||||
"""
|
||||
from evennia.utils.funcparser import ParsingError
|
||||
|
||||
with self.assertRaises(ValueError):
|
||||
utils.safe_convert_to_types(
|
||||
(int, ), *('foo', ), raise_errors=True)
|
||||
|
||||
with self.assertRaises(ParsingError) as err:
|
||||
utils.safe_convert_to_types(
|
||||
('py', {}), *('foo', ), raise_errors=True)
|
||||
|
|
|
|||
|
|
@ -20,6 +20,8 @@ import traceback
|
|||
import importlib
|
||||
import importlib.util
|
||||
import importlib.machinery
|
||||
from ast import literal_eval
|
||||
from simpleeval import simple_eval
|
||||
from unicodedata import east_asian_width
|
||||
from twisted.internet.task import deferLater
|
||||
from twisted.internet.defer import returnValue # noqa - used as import target
|
||||
|
|
@ -2390,3 +2392,104 @@ def interactive(func):
|
|||
return ret
|
||||
|
||||
return decorator
|
||||
|
||||
|
||||
def safe_convert_to_types(converters, *args, raise_errors=True, **kwargs):
|
||||
"""
|
||||
Helper function to safely convert inputs to expected data types.
|
||||
|
||||
Args:
|
||||
converters (tuple): A tuple `((converter, converter,...), {kwarg: converter, ...})` to
|
||||
match a converter to each element in `*args` and `**kwargs`.
|
||||
Each converter will will be called with the arg/kwarg-value as the only argument.
|
||||
If there are too few converters given, the others will simply not be converter. If the
|
||||
converter is given as the string 'py', it attempts to run
|
||||
`safe_eval`/`literal_eval` on the input arg or kwarg value. It's possible to
|
||||
skip the arg/kwarg part of the tuple, an empty tuple/dict will then be assumed.
|
||||
*args: The arguments to convert with `argtypes`.
|
||||
raise_errors (bool, optional): If set, raise any errors. This will
|
||||
abort the conversion at that arg/kwarg. Otherwise, just skip the
|
||||
conversion of the failing arg/kwarg. This will be set by the FuncParser if
|
||||
this is used as a part of a FuncParser callable.
|
||||
**kwargs: The kwargs to convert with `kwargtypes`
|
||||
|
||||
Returns:
|
||||
tuple: `(args, kwargs)` in converted form.
|
||||
|
||||
Raises:
|
||||
utils.funcparser.ParsingError: If parsing failed in the `'py'`
|
||||
converter. This also makes this compatible with the FuncParser
|
||||
interface.
|
||||
any: Any other exception raised from other converters, if raise_errors is True.
|
||||
|
||||
Notes:
|
||||
This function is often used to validate/convert input from untrusted sources. For
|
||||
security, the "py"-converter is deliberately limited and uses `safe_eval`/`literal_eval`
|
||||
which only supports simple expressions or simple containers with literals. NEVER
|
||||
use the python `eval` or `exec` methods as a converter for any untrusted input! Allowing
|
||||
untrusted sources to execute arbitrary python on your server is a severe security risk,
|
||||
|
||||
Example:
|
||||
::
|
||||
|
||||
$funcname(1, 2, 3.0, c=[1,2,3])
|
||||
|
||||
def _funcname(*args, **kwargs):
|
||||
args, kwargs = safe_convert_input(((int, int, float), {'c': 'py'}), *args, **kwargs)
|
||||
# ...
|
||||
|
||||
"""
|
||||
def _safe_eval(inp):
|
||||
if not inp:
|
||||
return ''
|
||||
if not isinstance(inp, str):
|
||||
# already converted
|
||||
return inp
|
||||
|
||||
try:
|
||||
return literal_eval(inp)
|
||||
except Exception as err:
|
||||
literal_err = f"{err.__class__.__name__}: {err}"
|
||||
try:
|
||||
return simple_eval(inp)
|
||||
except Exception as err:
|
||||
simple_err = f"{str(err.__class__.__name__)}: {err}"
|
||||
pass
|
||||
|
||||
if raise_errors:
|
||||
from evennia.utils.funcparser import ParsingError
|
||||
err = (f"Errors converting '{inp}' to python:\n"
|
||||
f"literal_eval raised {literal_err}\n"
|
||||
f"simple_eval raised {simple_err}")
|
||||
raise ParsingError(err)
|
||||
|
||||
# handle an incomplete/mixed set of input converters
|
||||
if not converters:
|
||||
return args, kwargs
|
||||
arg_converters, *kwarg_converters = converters
|
||||
arg_converters = make_iter(arg_converters)
|
||||
kwarg_converters = kwarg_converters[0] if kwarg_converters else {}
|
||||
|
||||
# apply the converters
|
||||
if args and arg_converters:
|
||||
args = list(args)
|
||||
arg_converters = make_iter(arg_converters)
|
||||
for iarg, arg in enumerate(args[:len(arg_converters)]):
|
||||
converter = arg_converters[iarg]
|
||||
converter = _safe_eval if converter in ('py', 'python') else converter
|
||||
try:
|
||||
args[iarg] = converter(arg)
|
||||
except Exception:
|
||||
if raise_errors:
|
||||
raise
|
||||
args = tuple(args)
|
||||
if kwarg_converters and isinstance(kwarg_converters, dict):
|
||||
for key, converter in kwarg_converters.items():
|
||||
converter = _safe_eval if converter in ('py', 'python') else converter
|
||||
if key in {**kwargs}:
|
||||
try:
|
||||
kwargs[key] = converter(kwargs[key])
|
||||
except Exception:
|
||||
if raise_errors:
|
||||
raise
|
||||
return args, kwargs
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue