def test_if(self): suite = Suite("""if foo == 42: x = True """) data = {'foo': 42} suite.execute(data) self.assertEqual(True, data['x'])
def test_return(self): suite = Suite(""" def f(): return v assert f() == 42 """) suite.execute({"v": 42})
def test_assign_to_attribute(self): class Something(object): pass something = Something() suite = Suite("obj.attr = 'foo'") data = {"obj": something} suite.execute(data) self.assertEqual('foo', something.attr)
def test_delete(self): suite = Suite("""foo = 42 del foo """) data = {} suite.execute(data) assert 'foo' not in data
def test_class_with_methods(self): suite = Suite("""class plain(object): def donothing(): pass """) data = {} suite.execute(data) assert 'plain' in data
def test_for(self): suite = Suite("""x = [] for i in range(3): x.append(i**2) """) data = {} suite.execute(data) self.assertEqual([0, 1, 4], data['x'])
def test_slice_attribute(self): class ValueHolder: def __init__(self): self.value = 3 suite = Suite("x = numbers[obj.value]") data = {"numbers": [0, 1, 2, 3, 4], "obj": ValueHolder()} suite.execute(data) self.assertEqual(3, data["x"])
def test_slice_call(self): def f(): return 2 suite = Suite("x = numbers[f()]") data = {"numbers": [0, 1, 2, 3, 4], "f": f} suite.execute(data) self.assertEqual(2, data["x"])
def test_import_in_def(self): suite = Suite("""def fun(): from itertools import repeat return repeat(1, 3) """) data = Context() suite.execute(data) assert 'repeat' not in data self.assertEqual([1, 1, 1], list(data['fun']()))
def test_def_kwargs(self): suite = Suite(""" def smash(**kw): return [''.join(i) for i in kw.items()] x = smash(foo='abc', bar='def') """) data = {} suite.execute(data) self.assertEqual(['bardef', 'fooabc'], sorted(data['x']))
def test_def_kwonlyarg(self): suite = Suite(""" def kwonly(*args, k): return k x = kwonly(k="foo") """) data = {} suite.execute(data) self.assertEqual("foo", data['x'])
def test_import_in_def(self): suite = Suite("""def fun(): from itertools import ifilter return ifilter(None, range(3)) """) data = Context() suite.execute(data) assert 'ifilter' not in data self.assertEqual([1, 2], list(data['fun']()))
def test_def_kwargs(self): suite = Suite(""" def smash(**kw): return [''.join(i) for i in kw.items()] x = smash(foo='abc', bar='def') """) data = {} suite.execute(data) self.assertEqual(['fooabc', 'bardef'], data['x'])
def test_finally(self): suite = Suite("""try: x = 2 finally: x = None """) data = {} suite.execute(data) self.assertEqual(None, data['x'])
def test_def_with_multiple_statements(self): suite = Suite(""" def donothing(): if True: return foo """) data = {'foo': 'bar'} suite.execute(data) assert 'donothing' in data self.assertEqual('bar', data['donothing']())
def test_while_break(self): suite = Suite("""x = 0 while x < 5: x += step if x == 4: break """) data = {'step': 2} suite.execute(data) self.assertEqual(4, data['x'])
def test_try_except(self): suite = Suite("""try: import somemod except ImportError: somemod = None else: somemod.dosth()""") data = {} suite.execute(data) self.assertEqual(None, data['somemod'])
def test_def_kwonlyarg_with_default(self): suite = Suite(""" def kwonly(*args, k="bar"): return k x = kwonly(k="foo") y = kwonly() """) data = {} suite.execute(data) self.assertEqual("foo", data['x']) self.assertEqual("bar", data['y'])
def test_def_some_defaults(self): suite = Suite(""" def difference(v1, v2=10): return v1 - v2 x = difference(20, 19) y = difference(20) """) data = {} suite.execute(data) self.assertEqual(1, data['x']) self.assertEqual(10, data['y'])
def test_for_in_def(self): suite = Suite("""def loop(): for i in range(10): if i == 5: break return i """) data = {} suite.execute(data) assert 'loop' in data self.assertEqual(5, data['loop']())
def test_internal_shadowing(self): # The context itself is stored in the global execution scope of a suite # It used to get stored under the name 'data', which meant the # following test would fail, as the user defined 'data' variable # shadowed the Genshi one. We now use the name '__data__' to avoid # conflicts suite = Suite("""data = [] bar = foo """) data = {'foo': 42} suite.execute(data) self.assertEqual(42, data['bar'])
def test_def_vararg(self): suite = Suite(""" def mysum(*others): rv = 0 for n in others: rv = rv + n return rv x = mysum(1, 2, 3) """) data = {} suite.execute(data) self.assertEqual(6, data['x'])
def test_class_in_def(self): suite = Suite(""" def create(): class Foobar(object): def __str__(self): return 'foobar' return Foobar() x = create() """) data = {} suite.execute(data) self.assertEqual('foobar', str(data['x']))
def test_def_using_nonlocal(self): suite = Suite(""" values = [] def add(value): if value not in values: values.append(value) add('foo') add('bar') """) data = {} suite.execute(data) self.assertEqual(['foo', 'bar'], data['values'])
def test_def_with_decorator(self): suite = Suite(""" def lower(fun): return lambda: fun().lower() @lower def say_hi(): return 'Hi!' result = say_hi() """) data = {} suite.execute(data) self.assertEqual('hi!', data['result'])
def test_def_nested(self): suite = Suite(""" def doit(): values = [] def add(value): if value not in values: values.append(value) add('foo') add('bar') return values x = doit() """) data = {} suite.execute(data) self.assertEqual(['foo', 'bar'], data['x'])
def test_delattr(self): class Something(object): def __init__(self): self.attr = 'foo' obj = Something() Suite("del obj.attr").execute({'obj': obj}) self.assertFalse(hasattr(obj, 'attr'))
def _parse(self, source, encoding): if not isinstance(source, Stream): source = XMLParser(source, filename=self.filename, encoding=encoding) stream = [] for kind, data, pos in source: if kind is TEXT: for kind, data, pos in interpolate(data, self.filepath, pos[1], pos[2], lookup=self.lookup): stream.append((kind, data, pos)) elif kind is PI and data[0] == 'python': if not self.allow_exec: raise TemplateSyntaxError('Python code blocks not allowed', self.filepath, *pos[1:]) try: suite = Suite(data[1], self.filepath, pos[1], lookup=self.lookup) except SyntaxError, err: raise TemplateSyntaxError(err, self.filepath, pos[1] + (err.lineno or 1) - 1, pos[2] + (err.offset or 0)) stream.append((EXEC, suite, pos)) elif kind is COMMENT: if not data.lstrip().startswith('!'): stream.append((kind, data, pos))
def _parse(self, source, encoding): """Parse the template from text input.""" stream = [] # list of events of the "compiled" template dirmap = {} # temporary mapping of directives to elements depth = 0 source = source.read() if not isinstance(source, unicode): source = source.decode(encoding or 'utf-8', 'replace') offset = 0 lineno = 1 _escape_sub = self._escape_re.sub def _escape_repl(mo): groups = [g for g in mo.groups() if g] if not groups: return '' return groups[0] for idx, mo in enumerate(self._directive_re.finditer(source)): start, end = mo.span(1) if start > offset: text = _escape_sub(_escape_repl, source[offset:start]) for kind, data, pos in interpolate(text, self.filepath, lineno, lookup=self.lookup): stream.append((kind, data, pos)) lineno += len(text.splitlines()) lineno += len(source[start:end].splitlines()) command, value = mo.group(2, 3) if command == 'include': pos = (self.filename, lineno, 0) value = list(interpolate(value, self.filepath, lineno, 0, lookup=self.lookup)) if len(value) == 1 and value[0][0] is TEXT: value = value[0][1] stream.append((INCLUDE, (value, None, []), pos)) elif command == 'python': if not self.allow_exec: raise TemplateSyntaxError('Python code blocks not allowed', self.filepath, lineno) try: suite = Suite(value, self.filepath, lineno, lookup=self.lookup) except SyntaxError, err: raise TemplateSyntaxError(err, self.filepath, lineno + (err.lineno or 1) - 1) pos = (self.filename, lineno, 0) stream.append((EXEC, suite, pos)) elif command == 'end': depth -= 1 if depth in dirmap: directive, start_offset = dirmap.pop(depth) substream = stream[start_offset:] stream[start_offset:] = [(SUB, ([directive], substream), (self.filepath, lineno, 0))]
def test_augmented_assign_in_def(self): d = {} Suite("""def foo(): i = 1 i += 1 return i x = foo()""").execute(d) self.assertEqual(2, d['x'])
def test_augmented_assign_in_loop_in_def(self): d = {} Suite("""def foo(): i = 0 for n in range(5): i += n return i x = foo()""").execute(d) self.assertEqual(10, d['x'])
def test_yield_expression(self): d = {} suite = Suite("""results = [] def counter(maximum): i = 0 while i < maximum: val = (yield i) if val is not None: i = val else: i += 1 it = counter(5) results.append(it.next()) results.append(it.send(3)) results.append(it.next()) """) suite.execute(d) self.assertEqual([0, 3, 4], d['results'])
def test_pickle(self): suite = Suite('foo = 42') buf = StringIO() pickle.dump(suite, buf, 2) buf.seek(0) unpickled = pickle.load(buf) data = {} unpickled.execute(data) self.assertEqual(42, data['foo'])
def test_with_statement(self): fd, path = mkstemp() f = os.fdopen(fd, "w") try: f.write('foo\nbar\n') f.seek(0) f.close() d = {'path': path} suite = Suite("""from __future__ import with_statement lines = [] with open(path) as file: for line in file: lines.append(line) """) suite.execute(d) self.assertEqual(['foo\n', 'bar\n'], d['lines']) finally: os.remove(path)
def test_assign_in_list(self): suite = Suite("[d['k']] = 'foo',; assert d['k'] == 'foo'") d = {"k": "bar"} suite.execute({"d": d}) self.assertEqual("foo", d["k"])
def test_class(self): suite = Suite("class plain(object): pass") data = {} suite.execute(data) assert 'plain' in data
def test_import(self): suite = Suite("from itertools import ifilter") data = {} suite.execute(data) assert 'ifilter' in data
def test_import_star(self): suite = Suite("from itertools import *") data = Context() suite.execute(data) assert 'ifilter' in data
def test_augmented_attribute_assignment(self): suite = Suite("d['k'] += 42") d = {"k": 1} suite.execute({"d": d}) self.assertEqual(43, d["k"])
def test_exec(self): suite = Suite("x = 1; exec d['k']; assert x == 42, x") suite.execute({"d": {"k": "x = 42"}})
def test_assign_to_dict_item(self): suite = Suite("d['k'] = 'foo'") data = {'d': {}} suite.execute(data) self.assertEqual('foo', data['d']['k'])