def test_filter_chaining(): template = '''{{ (xs|first|last).gsom|sort|length }}''' ast = parse(template).find(nodes.Filter) rtype, struct = visit_filter(ast, get_scalar_context(ast)) expected_struct = Dictionary({ 'xs': List(List(Dictionary( { 'gsom': List(Unknown(), label='gsom', linenos=[1]), }, linenos=[1]), linenos=[1]), label='xs', linenos=[1]), }) assert struct == expected_struct template = '''{{ x|list|sort|first }}''' ast = parse(template).find(nodes.Filter) rtype, struct = visit_filter(ast, get_scalar_context(ast)) expected_struct = Dictionary({ 'x': Scalar(label='x', linenos=[1]), }) assert struct == expected_struct template = '''{{ x|first|list }}''' ast = parse(template).find(nodes.Filter) with pytest.raises(UnexpectedExpression): visit_filter(ast, get_scalar_context(ast))
def test_getitem_1(): template = '''{{ a['b']['c'][1]['d'][x] }}''' ast = parse(template).find(nodes.Getitem) config = Config() config.TYPE_OF_VARIABLE_INDEXED_WITH_VARIABLE_TYPE = 'list' rtype, struct = visit_getitem(ast, get_scalar_context(ast), {}, config) expected_struct = Dictionary({ 'a': Dictionary( { 'b': Dictionary( { 'c': List(Dictionary( { 'd': List(Scalar(linenos=[1]), label='d', linenos=[1]) }, linenos=[1]), label='c', linenos=[1]), }, label='b', linenos=[1]), }, label='a', linenos=[1]), 'x': Scalar(label='x', linenos=[1]), }) assert struct == expected_struct
def test_basics_15(): # 1. Accept no unknown filter (default behavior) config = Config() template = ''' {% for item in items %} {{ item.attr1|lower }} {{ item.attr2|bar }} {% endfor %} ''' with pytest.raises(InvalidExpression) as e: infer(template, config) assert 'line 4: unknown filter "bar"' == str(e.value) # 2. Accept all unknown filters config = Config() config.IGNORE_UNKNOWN_FILTERS = True template = ''' {% for item in items %} {{ item.attr1|lower }} {{ item.attr2|bar }} {% endfor %} ''' struct = infer(template, config) expected_struct = Dictionary({ 'items': List(Dictionary({ 'attr1': String(label='attr1', linenos=[3]), 'attr2': Unknown(label='attr2', linenos=[4]) }, label='item', linenos=[3, 4]), label='items', linenos=[2]), }) assert struct == expected_struct
def test_length_filter(): ast = parse('{{ xs|length }}').find(nodes.Filter) rtype, struct = visit_filter(ast, get_scalar_context(ast)) assert rtype == Number(label='xs', linenos=[1]) assert struct == Dictionary({ 'xs': List(Unknown(), label='xs', linenos=[1]), })
def test_str_method_calls(): template = '''{{ x.endswith(suffix) }}''' call_ast = parse(template).find(nodes.Call) rtype, struct = visit_call(call_ast, get_scalar_context(call_ast)) expected_rtype = Boolean() assert rtype == expected_rtype expected_struct = Dictionary({ 'x': String(label='x', linenos=[1]), # TODO suffix must be in struct too }) assert struct == expected_struct template = '''{{ x.split(separator) }}''' call_ast = parse(template).find(nodes.Call) ctx = Context(return_struct_cls=Unknown, predicted_struct=Unknown.from_ast(call_ast)) rtype, struct = visit_call(call_ast, ctx) expected_rtype = List(String()) assert rtype == expected_rtype expected_struct = Dictionary({ 'x': String(label='x', linenos=[1]), 'separator': String(label='separator', linenos=[1]), }) assert struct == expected_struct
def test_getattr_1(): template = '{{ (x or y).field.subfield[2].a }}' ast = parse(template).find(nodes.Getattr) rtype, struct = visit_getattr(ast, get_scalar_context(ast)) x_or_y_dict = { 'field': Dictionary( { 'subfield': List(Dictionary({'a': Scalar(label='a', linenos=[1])}, linenos=[1]), label='subfield', linenos=[1]), }, label='field', linenos=[1]), } expected_struct = Dictionary({ 'x': Dictionary(x_or_y_dict, label='x', linenos=[1]), 'y': Dictionary(x_or_y_dict, label='y', linenos=[1]) }) assert struct == expected_struct
def test_basics_13(): config = Config() config.TYPE_OF_VARIABLE_INDEXED_WITH_INTEGER_TYPE = 'tuple' template = ''' {% for x in xs %} {{ x[2] }} {{ x[3] }} {% endfor %} ''' struct = infer(template, config) expected_struct = Dictionary({ 'xs': List(Tuple(( Unknown(label=None, linenos=[]), Unknown(label=None, linenos=[]), Scalar(label=None, linenos=[3]), Scalar(label=None, linenos=[4]), ), label='x', linenos=[3, 4]), label='xs', linenos=[2]) }) assert struct == expected_struct
def test_list_becomes_dict(): template = ''' {% for r in PrjList %} <tr><td><a class='btn btn-primary' href='/part/prj/{{ r[0] }}'>Select</a></td> <td> {{ r[1] }} [{{ r[0] }}] {{ ("<a href='mailto:"+r['email']+"'>") |safe if r['email'] }}</td> <td>{{ r['samplecount'] }}</td> </tr> {% endfor %} ''' struct = infer(template) expected_struct = Dictionary({ 'PrjList': List(Dictionary( { 0: Scalar(), 1: Scalar(), 'email': String(label="email", linenos=[4]), 'samplecount': Scalar(label="samplecount", linenos=[5]), }, label="r", linenos=[3, 4, 5]), label="PrjList", linenos=[2]), }) assert struct == expected_struct
def test_basics_8(): template = ''' {% for row in items|batch(3, ' ')|batch(1) %} {{ row[1].name }} {% endfor %} ''' with pytest.raises(UnexpectedExpression) as excinfo: infer(template) e = excinfo.value assert isinstance(e.actual_ast, nodes.Filter) assert e.expected_struct == List( Dictionary({ 'name': Scalar(label='name', constant=False, linenos=[3]) }, constant=False, linenos=[3]), label='row', constant=False, linenos=[2, 3] ) assert e.actual_struct == List(List(Unknown()))
def test_slice(): template = '''{{ xs[a:2:b] }}''' ast = parse(template).find(nodes.Getitem) rtype, struct = visit_getitem(ast, get_scalar_context(ast)) assert struct == Dictionary({ 'xs': List(Scalar(linenos=[1]), label='xs', linenos=[1]), 'a': Number(label='a', linenos=[1]), 'b': Number(label='b', linenos=[1]), })
def test_for_2(): template = ''' {% for x in xs %} {{ x }} {% for y in ys %} {{ loop.index0 }} {% endfor %} {{ loop.length }} {% endfor %} ''' ast = parse(template) struct = infer_from_ast(ast) expected_struct = Dictionary({ 'xs': List(Scalar(label='x', linenos=[3]), label='xs', linenos=[2]), 'ys': List(Unknown(linenos=[4]), label='ys', linenos=[4]), }) assert struct == expected_struct
def test_basics_6(): template = ''' {% for row in items|batch(3, ' ') %} {% endfor %} ''' struct = infer(template) expected_struct = Dictionary({ 'items': List(Unknown(), label='items', linenos=[2]), }) assert struct == expected_struct
def test_basics_9(): template = ''' {% set xs = items|batch(3, ' ') %} {{ xs[0][0] }} ''' struct = infer(template) expected_struct = Dictionary({ 'items': List(Unknown(), label='items', linenos=[2]), # TODO it should be Scalar }) assert struct == expected_struct
def test_join_filter(): ast = parse('{{ xs|join(separator|default("|")) }}').find(nodes.Filter) rtype, struct = visit_filter(ast, get_scalar_context(ast)) assert rtype == String(label='xs', linenos=[1]) assert struct == Dictionary({ 'xs': List(String(), label='xs', linenos=[1]), 'separator': String(label='separator', linenos=[1], used_with_default=True), })
def test_max_min_filter(): for filter in ('max', 'min'): template = '{{ values|' + filter + ' }}' ast = parse(template).find(nodes.Filter) rtype, struct = visit_filter(ast, get_scalar_context(ast)) assert rtype == Scalar(label='values', linenos=[1]) assert struct == Dictionary({ 'values': List(Scalar(linenos=[1]), label='values', linenos=[1]), })
def test_length_filter(): for filter in ('count', 'length'): template = '{{ xs|' + filter + ' }}' ast = parse(template).find(nodes.Filter) rtype, struct = visit_filter(ast, get_scalar_context(ast)) assert rtype == Number(label='xs', linenos=[1]) assert struct == Dictionary({ 'xs': List(Unknown(), label='xs', linenos=[1]), })
def test_sum_filter(): template = '{{ x|sum }}' ast = parse(template).find(nodes.Filter) rtype, struct = visit_filter(ast, get_scalar_context(ast)) assert rtype == Scalar(label='x', linenos=[1]) expected_struct = Dictionary({ 'x': List(Scalar(), label='x', linenos=[1]), }) assert struct == expected_struct
def test_unique_filter(): template = '{{ values|unique }}' ast = parse(template).find(nodes.Filter) unknown_ctx = Context(predicted_struct=Unknown.from_ast(ast)) rtype, struct = visit_filter(ast, unknown_ctx) assert rtype == Unknown(label='values', linenos=[1]) assert struct == Dictionary({ 'values': List(Unknown(), label='values', linenos=[1]), })
def test_compare_2(): template = '{{ a + b[1] - c == 4 == x }}' ast = parse(template).find(nodes.Compare) rtype, struct = visit_compare(ast, get_scalar_context(ast)) # TODO make customizable expected_struct = Dictionary({ 'a': Unknown(label='a', linenos=[1]), 'b': List(Unknown(linenos=[1]), label='b', linenos=[1]), 'c': Unknown(label='c', linenos=[1]), 'x': Unknown(label='x', linenos=[1]), }) assert struct == expected_struct
def test_getattr_3(): template = '''{{ a[z][1:\nn][1].x }}''' ast = parse(template).find(nodes.Getattr) config = Config() config.TYPE_OF_VARIABLE_INDEXED_WITH_VARIABLE_TYPE = 'list' rtype, struct = visit_getattr(ast, get_scalar_context(ast), {}, config) expected_struct = Dictionary({ 'a': List(List(List(Dictionary({'x': Scalar(label='x', linenos=[2])}, linenos=[2]), linenos=[2]), linenos=[1]), label='a', linenos=[1]), 'z': Scalar(label='z', linenos=[1]), 'n': Number(label='n', linenos=[2]) }) assert struct == expected_struct
def test_basics_11(): template = ''' {{ a|xmlattr }} {{ a.attr1|join(',') }} {{ a.attr2|default([])|first }} {{ a.attr3|default('gsom') }} {% for x in xs|rejectattr('is_active') %} {{ x }} {% endfor %} ''' struct = infer(template) expected_struct = Dictionary({ 'a': Dictionary( { 'attr1': List(String(), label='attr1', linenos=[3]), 'attr2': List(Scalar(linenos=[4]), label='attr2', linenos=[4], used_with_default=True), 'attr3': String(label='attr3', linenos=[5], used_with_default=True, value='gsom') }, label='a', linenos=[2, 3, 4, 5]), 'xs': List( Scalar(label='x', linenos=[ 7 ]), # TODO it should be Dictionary({'is_active': Unknown()}) label='xs', linenos=[6]), }) assert struct == expected_struct
def test_basics_7(): template = ''' {% for row in items|batch(3, ' ')|batch(1) %} {{ row[1][1].name }} {% endfor %} ''' struct = infer(template) expected_struct = Dictionary({ 'items': List(Dictionary({ 'name': Scalar(label='name', linenos=[3]), }, linenos=[3]), label='items', linenos=[2, 3]), }) assert struct == expected_struct
def test_range_call(): template = '{{ range(n) }}' ast = parse(template).find(nodes.Call) rtype, struct = visit_call(ast, Context(predicted_struct=Unknown())) expected_rtype = List(Number()) assert rtype == expected_rtype expected_struct = Dictionary({ 'n': Number(label='n', linenos=[1]), }) assert struct == expected_struct
def test_for_1(): template = ''' {% for x in a.b %} {{ x }} {% endfor %} ''' ast = parse(template).find(nodes.For) struct = visit_for(ast) expected_struct = Dictionary({ 'a': Dictionary({ 'b': List(Scalar(label='x', linenos=[3]), label='b', linenos=[2]) }, label='a', linenos=[2]), }) assert struct == expected_struct
def test_filter_chaining(): template = '''{{ (xs|first|last).gsom|sort|length }}''' ast = parse(template).find(nodes.Filter) rtype, struct = visit_filter(ast, get_scalar_context(ast)) expected_struct = Dictionary({ 'xs': List(List(Dictionary( { 'gsom': List(Unknown(), label='gsom', linenos=[1]), }, linenos=[1]), linenos=[1]), label='xs', linenos=[1]), }) assert struct == expected_struct template = '''{{ x|list|sort|first }}''' ast = parse(template).find(nodes.Filter) rtype, struct = visit_filter(ast, get_scalar_context(ast)) expected_struct = Dictionary({ 'x': Scalar(label='x', linenos=[1]), }) assert struct == expected_struct template = '''{{ x|first|list }}''' ast = parse(template).find(nodes.Filter) with pytest.raises(UnexpectedExpression) as e: visit_filter(ast, get_scalar_context(ast)) expected = "conflict on the line 1\n\ got: AST node jinja2.nodes.Filter of structure [<scalar>]\n\ expected structure: <scalar>" assert expected == str(e.value)
def test_batch_and_slice_filters(): for filter in ('batch', 'slice'): template = '{{ items|' + filter + '(3, " ") }}' ast = parse(template).find(nodes.Filter) unknown_ctx = Context(predicted_struct=Unknown.from_ast(ast)) rtype, struct = visit_filter(ast, unknown_ctx) expected_rtype = List(List(Unknown(), linenos=[1]), linenos=[1]) assert rtype == expected_rtype expected_struct = Dictionary({ 'items': List(Unknown(), label='items', linenos=[1]), }) assert struct == expected_struct scalar_ctx = Context(predicted_struct=Scalar.from_ast(ast)) with pytest.raises(UnexpectedExpression) as e: visit_filter(ast, scalar_ctx) assert str(e.value) == ( 'conflict on the line 1\n' 'got: AST node jinja2.nodes.Filter of structure [[<unknown>]]\n' 'expected structure: <scalar>')
def test_basics_5(): template = ''' {% for row in items|batch(3, ' ') %} {% for column in row %} {{ column.x }} {% endfor %} {% endfor %} ''' struct = infer(template) expected_struct = Dictionary({ 'items': List(Dictionary({ 'x': Scalar(label='x', linenos=[4]) }, label='column', linenos=[4]), label='items', linenos=[2, 3]), }) assert struct == expected_struct
def test_assign_5(): template = ''' {%- set weights = [ ('A', {'data': 0.3}), ('B', {'data': 0.9}), ] %} ''' ast = parse(template).find(nodes.Assign) struct = visit_assign(ast) expected_struct = Dictionary({ 'weights': List(Tuple([ String(linenos=[3, 4], constant=True), Dictionary({ 'data': Number(linenos=[3, 4], constant=True) }, linenos=[3, 4], constant=True), ], linenos=[3, 4], constant=True), label='weights', linenos=[2], constant=True) }) assert struct == expected_struct
def test_for_3(): template = ''' {% for a, b in list %} {{ a.field }} {{ b }} {% endfor %} ''' ast = parse(template).find(nodes.For) struct = visit_for(ast) expected_struct = Dictionary({ 'list': List(Tuple(( Dictionary({ 'field': Scalar(label='field', linenos=[3]) }, label='a', linenos=[3]), Scalar(label='b', linenos=[4]) ), linenos=[2]), label='list', linenos=[2]) }) assert struct == expected_struct
def test_for(): template = ''' {% for number in range(10 - users|length) %} {{ number }} {% endfor %} ''' struct = infer(template) expected_struct = Dictionary({ 'users': List(Unknown(), label='users', linenos=[2]), }) assert struct == expected_struct template = ''' {% for number in range(10 - users|length) %} {{ number.field }} {% endfor %} ''' with pytest.raises(MergeException): infer(template) template = '{{ range(10 - users|length) }}' with pytest.raises(UnexpectedExpression): infer(template) template = ''' {% for number in lipsum(n=10) %} {% endfor %} ''' with pytest.raises(UnexpectedExpression): infer(template) template = ''' {% for k, v in data|dictsort %} {{ k.x }} {{ v }} {% endfor %} ''' with pytest.raises(UnexpectedExpression): infer(template)