Adds engine option to make dicts iterable

By default yaql 1.0 will not assume dicts to be iterable.
However because of yaql 0.2 had such behavior and some
users may still want to have it (for example because Python
has it) now in can be turned on. This is done by default
for legacy engine but not for modern one.

Also because SmartType's check method had no access
to engine options additional parameter (engine) was added.
Because previously signature was changed to have *args/**kwargs
backward compatibility retained

Change-Id: Ic3d65d8cb5caff43dee976dd9ba63f4caecf7dff
This commit is contained in:
Stan Lagun 2015-08-18 23:41:50 +03:00
parent df4f77df33
commit 8b9f154676
5 changed files with 25 additions and 10 deletions

View File

@ -85,7 +85,7 @@ def choose_overload(name, candidates, engine, sender, context, args, kwargs):
elif no_kwargs != c.no_kwargs:
raise_ambiguous()
mapping = c.map_args(args, kwargs, context)
mapping = c.map_args(args, kwargs, context, engine)
if mapping is None:
continue
pos, kwd = mapping

View File

@ -192,7 +192,7 @@ class FunctionDefinition(object):
if p.position is not None and p.position >= pd.position:
p.position += 1
def map_args(self, args, kwargs, context):
def map_args(self, args, kwargs, context, engine):
kwargs = dict(kwargs)
positional_args = len(args) * [
self.parameters.get('*', utils.NO_VALUE)]
@ -248,17 +248,18 @@ class FunctionDefinition(object):
value = args[i]
if value is utils.NO_VALUE:
value = positional_args[i].default
if not positional_args[i].value_type.check(value, context):
if not positional_args[i].value_type.check(value, context, engine):
return None
for kwd in six.iterkeys(kwargs):
if not keyword_args[kwd].value_type.check(kwargs[kwd], context):
if not keyword_args[kwd].value_type.check(
kwargs[kwd], context, engine):
return None
return tuple(positional_args), keyword_args
def get_delegate(self, sender, engine, context, args, kwargs):
def checked(val, param):
if not param.value_type.check(val, context):
if not param.value_type.check(val, context, engine):
raise exceptions.ArgumentException(param.name)
def convert_arg_func(context2):

View File

@ -23,7 +23,7 @@ from yaql.language import utils
class HiddenParameterType(object):
# noinspection PyMethodMayBeStatic,PyUnusedLocal
def check(self, value, context, *args, **kwargs):
def check(self, value, context, engine, *args, **kwargs):
return True
@ -35,14 +35,14 @@ class SmartType(object):
def __init__(self, nullable):
self.nullable = nullable
def check(self, value, context, *args, **kwargs):
def check(self, value, context, engine, *args, **kwargs):
if value is None and not self.nullable:
return False
return True
def convert(self, value, sender, context, function_spec, engine,
*args, **kwargs):
if not self.check(value, context, *args, **kwargs):
if not self.check(value, context, engine, *args, **kwargs):
raise exceptions.ArgumentValueException()
utils.limit_memory_usage(engine, (1, value))
@ -56,11 +56,12 @@ class GenericType(SmartType):
self.checker = checker
self.converter = converter
def check(self, value, context, *args, **kwargs):
def check(self, value, context, engine, *args, **kwargs):
if isinstance(value, expressions.Constant):
value = value.value
if not super(GenericType, self).check(value, context, *args, **kwargs):
if not super(GenericType, self).check(
value, context, engine, *args, **kwargs):
return False
if value is None or isinstance(value, expressions.Expression):
return True
@ -144,6 +145,13 @@ class Iterable(PythonType):
lambda t: not isinstance(t, six.string_types + (
utils.MappingType,))] + (validators or []))
def check(self, value, context, engine, *args, **kwargs):
if isinstance(value, utils.MappingType) and engine.options.get(
'yaql.iterableDicts', False):
return True
return super(Iterable, self).check(
value, context, engine, *args, **kwargs)
def convert(self, value, sender, context, function_spec, engine,
*args, **kwargs):
res = super(Iterable, self).convert(

View File

@ -29,6 +29,7 @@ class YaqlFactory(factory.YaqlFactory):
def create(self, options=None):
options = dict(options or {})
options['yaql.convertTuplesToLists'] = False
options['yaql.iterableDicts'] = True
return super(YaqlFactory, self).create(options)

View File

@ -133,3 +133,8 @@ class TestLegacy(TestLegacyNewEngine):
self.assertEqual((1, 2, 3), self.eval('1 => 2 => 3'))
self.assertEqual(((1, 2), 3), self.eval('(1 => 2) => 3'))
self.assertEqual((1, (2, 3)), self.eval('1 => (2 => 3)'))
def test_dicts_are_iterable(self):
data = {'a': 1, 'b': 2}
self.assertTrue(self.eval('a in $', data))
self.assertItemsEqual('ab', self.eval('$.sum()', data))