def test_model_plugin(): div_elem = MockElement('div', id='test') text_elem = MockElement('#text', id='text') text_elem.text = "{{ c['name'] }}" div_elem <= text_elem input_elem = MockElement('input', model='c["name"]', id='input') div_elem <= input_elem document <= div_elem tpl = Template(document['test']) ctx = Context({}) ctx.c = {'name': ''} elem = tpl.bind_ctx(ctx) text_elem = document['text'] input_elem = document['input'] assert text_elem.text == '' input_elem.setAttribute('value', 'Jonathan') assert text_elem.text == 'Jonathan' assert ctx.c['name'] == 'Jonathan' assert input_elem.value == 'Jonathan' input_elem.value = 'Test' assert text_elem.text == 'Jonathan' assert ctx.c['name'] == 'Jonathan' ctx.c['name'] = 'Test2' assert input_elem.value == 'Test2' assert text_elem.text == 'Test2'
def test_nested_for(): # Test nested loops doc = MockElement('div') div_elem = MockElement('div') div_elem.attributes.append(MockAttr('for', 'c in colours')) div_elem.attributes.append(MockAttr('style', '{{ c["css"] }}')) ch_elem = MockElement('span', id='id-{{name}}') ch_elem.attributes.append(MockAttr('for', 'name in c["names"]')) t_elem = MockElement('#text') t_elem.text = "{{ name }}{{parent_attr}}" ch_elem <= t_elem div_elem <= ch_elem doc <= div_elem plug = _compile(doc) ctx = Context() ctx.parent_attr = "Test" ctx.colours = [{ 'names': ['Red', 'Reddish'], 'css': 'red' }, { 'names': ['Blue'], 'css': 'blue' }] doc = plug.bind_ctx(ctx) nocomment_children = filter_comments(doc.children) assert len(nocomment_children) == 2 assert len(filter_comments(nocomment_children[0].children)) == 2 red_elem = doc._findChild('id-Red') assert red_elem.children[0].text == 'RedTest'
def test_for_plugin(): div_elem = MockElement('div', style='{{ c["css"] }}') text_elem = MockElement('#text') text_elem.text = "{{ c['name'] }}" div_elem <= text_elem plug = For(div_elem, loop_spec="c in colours") ctx = Context({}) elems = plug.bind_ctx(ctx) assert filter_comments(elems) == [] ctx.colours = [{ 'name': 'Red', 'css': 'red' }, { 'name': 'Blue', 'css': 'blue' }] assert plug._dirty_self is True elems = plug.update() nocomment = filter_comments(elems) assert len(nocomment) == 2 assert hasattr(nocomment[0].attributes, 'style') assert nocomment[0].attributes.style == "red" assert nocomment[0].children[0].text == "Red" assert nocomment[1].attributes.style == "blue" assert nocomment[1].children[0].text == "Blue" ctx.colours[0]['name'] = 'Reddish' assert plug._dirty_self is False assert plug._dirty_subtree is True assert plug.update() is None assert filter_comments(nocomment[0].children)[0].text == "Reddish"
def test_future(event_loop): asyncio.set_event_loop(event_loop) ctx = Context() fut = asyncio. async (asyncio.sleep(0.1, result=3)) ctx.test = fut assert hasattr(ctx, 'test') is False event_loop.run_until_complete(fut) assert ctx.test == 3
def test_clone(self): self.prepare("x**2 + x") clone = self.obs.clone() ctx = Context() ctx.x = 0 clone.bind_ctx(ctx) self.ctx.x = 1 assert clone.value == 0 assert self.obs.value == 2
def test_incomplete(): ctx = Context() s = InterpolatedStr("Ahoj {{ name }} {{ surname }}!") assert s.value == "Ahoj !" ctx.name = "Jonathan" s.bind_ctx(ctx) assert s.value == "Ahoj Jonathan !" ctx.surname = "Verner" assert s.value == "Ahoj Jonathan Verner!"
def test_string_interp(): ctx = Context() ctx.name = "James" s = InterpolatedStr("My name is {{ surname }}, {{name}} {{ surname}}.") s.bind_ctx(ctx) t = TObserver(s) assert s.value == "My name is , James ." ctx.surname = "Bond" data = t.events.pop().data assert s.value == "My name is Bond, James Bond." # Should correctly interpolate two immediately succeeding expressions ctx.sur = "B" s = InterpolatedStr('{{name}}{{sur}}') s.bind_ctx(ctx) assert s.value == "JamesB"
def test_text_plugin(): text_elem = MockElement('#text') text_elem.text = "Hello {{ name }}" tp = TextPlugin(text_elem) c = Context({}) elem = tp.bind_ctx(c) assert elem.text == "Hello " c.name = "Jonathan" assert tp._dirty_self is True assert tp.update() is None assert tp._dirty_self is False assert elem.text == "Hello Jonathan" tp2 = tp.clone() c2 = Context({'name':'Ansa'}) elem2 = tp2.bind_ctx(c2) assert elem2.text == "Hello Ansa" assert elem.text == "Hello Jonathan" assert tp._dirty_self is False text_elem = MockElement('#text') text_elem.text = "Hello" tp = _compile(text_elem) c = Context({}) elem = tp.bind_ctx(c) assert elem.text == "Hello" c.name = "Jonathan" assert tp.update() is None assert elem.text == "Hello"
def setAttribute(self, name, value): if name == 'value': self.value = value self.emit('input', Context({'target': self})) else: pos = self._indexAttr(name) if pos > -1: self.attributes[pos].value = value else: self.attributes.append(MockAttr(name, value))
def test_parse_interpolated_string(): ctx = Context() ctx.name = 'Name' asts = exp.parse_interpolated_str( 'Test text {{ 1+3 }} other text {{ "ahoj" }} final text.') val = "".join([ast.evalctx(ctx) for ast in asts]) assert val == 'Test text 4 other text ahoj final text.' asts = exp.parse_interpolated_str( 'Test text {{ 1+3 }} other text {{ name }} final text.') val = "".join([ast.evalctx(ctx) for ast in asts]) assert val == 'Test text 4 other text Name final text.' asts = exp.parse_interpolated_str( 'Test text {{ 1+3 }} other text {{ len(name) }} final text.') val = "".join([ast.evalctx(ctx) for ast in asts]) assert val == 'Test text 4 other text 4 final text.' asts = exp.parse_interpolated_str( 'Test text {{ "{{{{}}{}{}}}" }} other }}') val = "".join([ast.evalctx(ctx) for ast in asts]) assert val == 'Test text {{{{}}{}{}}} other }}'
def test_eval_assignment(): ctx = Context() # Do not allow assigning to non-trivial expressions ast, _ = exp.parse('(1+1*x)*9') with raises(Exception): ast.value = 10 # Do not allow assigning to built-in constants ast, _ = exp.parse('True') with raises(Exception): ast.value = 10 # Do not allow assigning to function calls ast, _ = exp.parse('f(1)') with raises(Exception): ast.value = 10 # Do not allow assigning to constant lists ast, _ = exp.parse("[1,2,3,4]") with raises(Exception): ast.value = 10 # Do not allow assigning to constants ast, _ = exp.parse("'ahoj'") with raises(Exception): ast.value = 10 # Allow assigning to non-existing variables ast, _ = exp.parse('x') ast.bind_ctx(ctx) ast.value = 10 assert ctx.x == 10 # Allow assigning to existing variables ast.value = 20 assert ctx.x == 20 # Allow assigning to list elements ctx.lst = [1, 2, 3] ctx.x = 0 ast, _ = exp.parse("lst[x]") ast.bind_ctx(ctx) ast.value = 20 assert ctx.lst[0] == 20 # Allow assigning to non-existing object attributes ctx.obj = Context() ast, _ = exp.parse('obj.test') ast.bind_ctx(ctx) ast.value = 30987 assert ctx.obj.test == 30987 # Allow assigning to existing object attributes ast.value = 40 assert ctx.obj.test == 40
def test_extension(): base = Context() base.a = 10 base.c = 30 child = Context(base=base) # Child should have access to parent assert child.a == 10 # The _get method should work for accessing parent assert child._get('a') == 10 # Child should not be allowed to modify parent child.a = 20 assert child.a == 20 assert base.a == 10 # Attributes should propagate recursively second_child = Context(base=child) assert second_child.c == 30 assert second_child.a == 20
def test_interpolated_attrs_plugin(): text_elem = MockElement('#text') text_elem.text = "{{ name }}" t2_elem = MockElement('#text') t2_elem.text = "ahoj" div_elem = MockElement('div', id="test") div_elem <= text_elem div_elem <= t2_elem plug = InterpolatedAttrsPlugin(div_elem) ctx = Context({}) elem = plug.bind_ctx(ctx) assert elem.attributes.id == "test" div_elem.setAttribute('id', "{{ id }}") plug = InterpolatedAttrsPlugin(div_elem) ctx = Context({}) elem = plug.bind_ctx(ctx) text_elem = elem.children[0] com_elem = elem.children[1] t2_elem = elem.children[2] assert elem.attributes.id == "" assert com_elem.tagName == 'comment' assert text_elem.text == "" assert t2_elem.text == "ahoj" assert plug._dirty_self is False assert plug._dirty_subtree is False ctx.id = "test_id" assert plug._dirty_self is True assert plug._dirty_subtree is False assert plug.update() is None assert elem.attributes.id == "test_id" ctx.name = "Jonathan" assert plug._dirty_self is False assert plug._dirty_subtree is True assert plug.update() is None assert text_elem.text == "Jonathan" assert plug._dirty_subtree is False div_elem.attributes.append(MockAttr('id', '{{ id }}')) plug = _compile(div_elem) ctx = Context({}) elem = plug.bind_ctx(ctx) assert elem.attributes.id == "" ctx.id = "test_id" assert plug.update() is None assert elem.attributes.id == "test_id" div_elem.attributes.extend([ MockAttr('id', '{{ id }}'), MockAttr('style', '{{ css }}'), MockAttr('name', '???') ]) plug = _compile(div_elem) ctx = Context({}) elem = plug.bind_ctx(ctx) assert elem.attributes.id == "" assert elem.attributes.style == "" assert elem.attributes.name == "???" ctx.css = "border:1px;" assert plug.update() is None assert elem.attributes.style == "border:1px;"
def setup_method(self, method): self.called = False self.ctx = Context() document._reset()
def setup_method(self, method): self.ctx = Context() self.ctx._clear()
class TestExpressionChanges(object): def setup_method(self, method): self.ctx = Context() self.ctx._clear() def prepare(self, expr): self.obs, _ = exp.parse(expr) self.obs.bind_ctx(self.ctx) try: self.obs.eval() except: pass self.t = TObserver(self.obs) def exec_test(self, new): data = self.t.events.pop().data if new is not None: assert self.obs.value == new else: assert self.obs.cache_status is False try: self.obs.eval() except: pass assert self.obs.defined is False assert 'value' not in data def test_clone(self): self.prepare("x**2 + x") clone = self.obs.clone() ctx = Context() ctx.x = 0 clone.bind_ctx(ctx) self.ctx.x = 1 assert clone.value == 0 assert self.obs.value == 2 def test_arithmetic_exp(self): self.ctx.a = 1 self.ctx.b = -2 self.ctx.c = 0.5 self.prepare("a*x**2 + b*x + c*x") assert self.obs.cache_status is False assert self.obs.defined is False self.ctx.d = 10 assert len(self.t.events) == 0 self.ctx.x = 0 self.exec_test(0) self.ctx.x = 1 self.exec_test(-0.5) def test_comprehension(self): self.ctx.lst = [-4, -3, -2, -1, 0, 1, 2, 3, 4] self.prepare("[p+1 for p in lst if p%2 == 0]") assert self.obs.cache_status is True self.ctx.lst.append(4) assert self.obs.cache_status is False self.exec_test([-3, -1, 1, 3, 5, 5]) self.ctx.lst.remove(4) self.exec_test([-3, -1, 1, 3, 5]) self.ctx.lst.clear() self.exec_test([]) def test_attr_acces(self): self.ctx.root = MockObject(depth=3) self.prepare("root.child.child.child.leaf and True") assert self.obs.value is True assert self.obs.cache_status is True self.ctx.root.child.child.child.leaf = False assert self.obs.cache_status is False self.exec_test(False) self.ctx.root.child = None self.exec_test(None) def test_func(self): self.ctx.func = lambda x, y: x + y self.prepare("func(a,b)") assert self.obs.cache_status is False self.ctx.a = 10 assert self.obs.cache_status is False self.ctx.b = 20 assert self.obs.cache_status is False self.exec_test(30) self.ctx.b = 30 self.exec_test(40) self.ctx.func = lambda x, y: x * y self.exec_test(300) del self.ctx.a self.exec_test(None) def test_array_index(self): self.ctx.lst = [[1, 2, 3], 2, 3, 4, 5] self.ctx.a = 0 self.prepare("lst[0][a]") assert self.obs.cache_status is True assert self.obs.value == 1 self.ctx.lst[1] = 2 self.exec_test(1) self.ctx.a = 2 self.exec_test(3) self.ctx.a = 3 self.exec_test(None) self.ctx.lst[0].append(4) self.exec_test(4) self.ctx.lst.pop() self.exec_test(4)
def setup_method(self, method): self.called = False self.ctx = Context()
def test_parse(): ctx = Context() # Test Simple Arithmetic Expressions ast, _ = exp.parse('(1+1*8)*9') assert ast.evalctx(ctx) is 81 # Test Simple Arithmetic Expressions ast, _ = exp.parse('(1-1)') assert ast.evalctx(ctx) is 0 # Test Simple Arithmetic Expressions ast, _ = exp.parse('(-1)') assert ast.evalctx(ctx) is -1 # Test Boolean Expressions ast, _ = exp.parse('True and False') assert ast.evalctx(ctx) is False ast, _ = exp.parse('True and not False') assert ast.evalctx(ctx) is True # Test is ast, _ = exp.parse("1 is None") assert ast.evalctx(ctx) is False ast, _ = exp.parse("None is None") assert ast.evalctx(ctx) is True ast, _ = exp.parse("False is not None") assert ast.evalctx(ctx) is True # Test Slices ctx.s = "abcde" ast, _ = exp.parse('s[-1]') assert ast.evalctx(ctx) == 'e' ast, _ = exp.parse('s[0]') assert ast.evalctx(ctx) == 'a' ast, _ = exp.parse('s[1:3]') assert ast.evalctx(ctx) == 'bc' ast, _ = exp.parse('s[0:-1:2]') assert ast.evalctx(ctx) == 'ac' ast, _ = exp.parse('s[1:]') assert ast.evalctx(ctx) == 'bcde' ast, _ = exp.parse('s[:-1]') assert ast.evalctx(ctx) == 'abcd' # Test Lists ast, _ = exp.parse('[1,2,3,4]') assert ast.evalctx(ctx) == [1, 2, 3, 4] # Test Comprehension ast, _ = exp.parse('[p+1 for p in [1,2,3,4]]') assert ast.evalctx(ctx) == [2, 3, 4, 5] ast, _ = exp.parse('[p+1 for p in [1,2,3,4] if p%2==0]') assert ast.evalctx(ctx) == [3, 5] # Test Builtins ast, _ = exp.parse("str(10)") assert ast.evalctx(ctx) == "10" ast, _ = exp.parse("int('21')") assert ast.evalctx(ctx) == 21 ast, _ = exp.parse("len([1,2,3])") assert ast.evalctx(ctx) == 3 ctx.str = lambda x: "str(" + str(x) + ")" ast, _ = exp.parse("str(10)") assert str(ast) == "str(10)" del ctx.str # Test Object Access ctx.obj = Context() ctx.obj.a = 10 ctx.obj.b = Context() ctx.obj.b.c = 20 ctx.obj.d = [Context({'a': 30})] ast, _ = exp.parse('obj.a') assert ast.evalctx(ctx) == 10 ast, _ = exp.parse('obj.b.c') assert ast.evalctx(ctx) == 20 ast, _ = exp.parse('obj.d[0].a') assert ast.evalctx(ctx) == 30 # Test Array Access ast, _ = exp.parse('mylst[0][1][2]') ctx.mylst = [[None, [None, None, "Ahoj"]]] assert ast.evalctx(ctx) == "Ahoj" # Test String slices ast, _ = exp.parse('"ahoj"[1:]') assert ast.evalctx(ctx) == "hoj" ast, _ = exp.parse('"ahoj"[:1]') assert ast.evalctx(ctx) == "a" ast, _ = exp.parse('"ahoj"[-1]') assert ast.evalctx(ctx) == "j" # Test array concatenation ast, _ = exp.parse('([0]+["mixin"])[1]') assert ast.evalctx(ctx) == "mixin" # Test Function Calls ast, _ = exp.parse('"a,b,c,d".split(",")') assert ast.evalctx(ctx) == ['a', 'b', 'c', 'd'] ctx.func = lambda x, ev: str(x + 10) + ev ctx.ch = 20 ctx.s = 'Hi' ast, _ = exp.parse("func(ch,ev=s)") ast.bind_ctx(ctx) ctx.s = 'Hello' assert ast.eval() == '30Hello' assert ast.evalctx(ctx) == '30Hello' # Test Complex Expressions expr = '(1+2*obj.a - 10)' ast, _ = exp.parse(expr) assert ast.evalctx(ctx) == 11 expr = '[(1+2*a[1+3] - 10) for a in [[2,1,2,3,4,5],[1,2],[2,2,2,2,2,2,2]] if a[0] % 2 == 0]' ast, _ = exp.parse(expr) assert ast.evalctx(ctx) == [-1, -5] # Test parse cache for i in range(10): expr = '[(1+2*a[1+3] - 10) for a in [[2,1,2,3,4,5],[1,2],[2,2,2,2,2,2,2]] if a[0] % 2 == 0]' ast, _ = exp.parse(expr) assert ast.evalctx(ctx) == [-1, -5]