def test_caching_code(self): "Caching: '_code'" b,c = Block('a=2'), {} b._code b.ast = Block('a=3').ast b.execute(c) self.assertEqual(c['a'], 3) b,c = Block('a=2'), {} b._code b.sub_blocks = [Block('a=3')] b.execute(c) self.assertEqual(c['a'], 3) b,c = Block('a=3; a=2'), {} b._code b.sub_blocks.pop() b.execute(c) self.assertEqual(c['a'], 3) b,c = Block(''), {} b._code b.sub_blocks.append(Block('a=3')) b.sub_blocks = [Block('a=3')] b.execute(c) assert 'a' in c
def test_caching_code(self): "Caching: '_code'" b, c = Block('a=2'), {} b._code b.ast = Block('a=3').ast b.execute(c) self.assertEqual(c['a'], 3) b, c = Block('a=2'), {} b._code b.sub_blocks = [Block('a=3')] b.execute(c) self.assertEqual(c['a'], 3) b, c = Block('a=3; a=2'), {} b._code b.sub_blocks.pop() b.execute(c) self.assertEqual(c['a'], 3) b, c = Block(''), {} b._code b.sub_blocks.append(Block('a=3')) b.sub_blocks = [Block('a=3')] b.execute(c) assert 'a' in c
def test_sub_block_manipulation(self): 'Sub-block manipulation' # A block's structure responds to changes in the 'sub_blocks' list b = Block('a; b') b.sub_blocks.append(Block('c')) self.assertSimilar(b, Block('a; b; c')) del b.sub_blocks[1] self.assertSimilar(b, Block('a; c')) b.sub_blocks.reverse() self.assertSimilar(b, Block('c; a')) b.sub_blocks[0] = Block('b') self.assertSimilar(b, Block('b; a')) b.sub_blocks = [] self.assertSimilar(b, Block()) b.sub_blocks = [] self.assertSimilar(b, Block()) # But if we end up with a block that doesn't decompose, 'sub_blocks' # goes away! b = Block('a; b; c') b.sub_blocks.pop() self.assertSimilar(b, Block('a; b')) self.assertEqual(len(b.sub_blocks), 2) b.sub_blocks.pop() self.assertSimilar(b, Block('a')) self.assertEqual(len(b.sub_blocks), 1) b.sub_blocks = [Block('b')] self.assertSimilar(b, Block('b')) self.assertEqual(len(b.sub_blocks), 1) b.sub_blocks = [Block('a'), Block('b')] self.assertSimilar(b, Block('a; b')) self.assertEqual(len(b.sub_blocks), 2) # Note that some seemingly large things don't (currently) decompose: block = Block('for x in l:\n a = f(x)\n b = g(a)\n if t: h()') self.assertEqual(len(block.sub_blocks), 1)
def test_sub_block_manipulation(self): 'Sub-block manipulation' # A block's structure responds to changes in the 'sub_blocks' list b = Block('a; b') b.sub_blocks.append(Block('c')) self.assertSimilar(b, Block('a; b; c')) del b.sub_blocks[1] self.assertSimilar(b, Block('a; c')) b.sub_blocks.reverse() self.assertSimilar(b, Block('c; a')) b.sub_blocks[0] = Block('b') self.assertSimilar(b, Block('b; a')) b.sub_blocks = [] self.assertSimilar(b, Block()) b.sub_blocks = [] self.assertSimilar(b, Block()) # But if we end up with a block that doesn't decompose, 'sub_blocks' # goes away! b = Block('a; b; c') b.sub_blocks.pop() self.assertSimilar(b, Block('a; b')) self.assertEqual(len(b.sub_blocks), 2) b.sub_blocks.pop() self.assertSimilar(b, Block('a')) self.assertEqual(len(b.sub_blocks), 1) b.sub_blocks = [Block('b')] self.assertSimilar(b, Block('b')) self.assertEqual(len(b.sub_blocks), 1) b.sub_blocks = [Block('a'), Block('b')] self.assertSimilar(b, Block('a; b')) self.assertEqual(len(b.sub_blocks), 2) # Note that some seemingly large things don't (currently) decompose: block = Block('for x in l:\n a = f(x)\n b = g(a)\n if t: h()') self.assertEqual(len(block.sub_blocks), 1)
def test_caching_dep_graph_restrict(self): "Caching: '_dep_graph', 'restrict'" # '_dep_graph' determines the behavior of 'restrict', so we can test # them together. b = Block('a=2; b=a; z=a') self.assertSimilar(b.restrict(outputs='z'), Block('a=2; z=a')) b.sub_blocks = [Block('z=0')] self.assertSimilar(b.restrict(outputs='z'), Block('z=0')) b = Block('a=2; b=a; z=a') self.assertSimilar(b.restrict(outputs='z'), Block('a=2; z=a')) b.ast = Block('z=0').ast self.assertSimilar(b.restrict(outputs='z'), Block('z=0'))
def test_caching_dep_graph_restrict(self): "Caching: '_dep_graph', 'restrict'" # '_dep_graph' determines the behavior of 'restrict', so we can test # them together. b = Block('a=2; b=a; z=a') self.assertSimilar(b.restrict(outputs='z'), Block('a=2; z=a')) b.sub_blocks = [Block('z=0')] self.assertSimilar(b.restrict(outputs='z'), Block('z=0')) b = Block('a=2; b=a; z=a') self.assertSimilar(b.restrict(outputs='z'), Block('a=2; z=a')) b.ast = Block('z=0').ast self.assertSimilar(b.restrict(outputs='z'), Block('z=0'))
def test_tracebacks(self): 'Tracebacks have correct file names and line numbers' # If we want tracebacks to make sense, then the reported file names and # line numbers need to associate with the code being executed # regardless which block represents and executes the code. def test(tb, lineno, filename): self.assertEqual(tb.tb_lineno, lineno + 1) self.assertEqual(tb.tb_frame.f_code.co_filename, filename) def tracebacks(): "A list of the current exception's traceback objects." tb = sys.exc_info()[2] l = [tb] while tb.tb_next is not None: tb = tb.tb_next l.append(tb) return l class File(StringIO, object): "Extend StringIO with a 'name' attribute." def __init__(self, name, *args, **kw): super(File, self).__init__(*args, **kw) self.name = name a = Block(File('foo/a.py', 'y = x')) try: a.execute({}) except NameError as e: test(tracebacks()[-1], 1, 'foo/a.py') del a a = Block(File('foo/a.py', 'import sys\ny = x')) try: a.execute({}) except NameError as e: test(tracebacks()[-1], 2, 'foo/a.py') #del a # (use below in 'Compose') b = Block( File('foo/b.py', "import re\nl=re.findall('a(.*?)a', 'abacada')\nx = l[2]")) try: b.execute({}) except IndexError as e: test(tracebacks()[-1], 3, 'foo/b.py') #del b # (use below in 'Compose') # Compose c = Block((a, b)) try: c.execute({}) except NameError as e: test(tracebacks()[-1], 2, 'foo/a.py') try: c.execute({'x': 0}) except IndexError as e: test(tracebacks()[-1], 3, 'foo/b.py') del a, b, c # Restrict a = Block(File('foo/a.py', 'a = 0\nb = 1\nc = 2/a')) try: a.restrict(outputs='c').execute({}) except ZeroDivisionError as e: test(tracebacks()[-1], 3, 'foo/a.py') del a # Throw out a sub-block a = Block(File('foo/a.py', 'a = 0\nb = 1\nc = 2/a')) a.sub_blocks.pop(1) try: a.execute({}) except ZeroDivisionError as e: test(tracebacks()[-1], 3, 'foo/a.py') del a # Swap sub-blocks between blocks a = Block(File('foo/a.py', 'a = 0\nb = 1')) b = Block(File('foo/b.py', 'c = 2\nd = x')) a.sub_blocks = b.sub_blocks try: a.execute({}) except NameError as e: test(tracebacks()[-1], 2, 'foo/b.py') a.sub_blocks = b.sub_blocks[:] try: a.execute({}) except NameError as e: test(tracebacks()[-1], 2, 'foo/b.py') del a, b
test(tracebacks()[-1], 3, 'foo/a.py') del a # Throw out a sub-block a = Block(File('foo/a.py', 'a = 0\nb = 1\nc = 2/a')) a.sub_blocks.pop(1) try: a.execute({}) except ZeroDivisionError, e: test(tracebacks()[-1], 3, 'foo/a.py') del a # Swap sub-blocks between blocks a = Block(File('foo/a.py', 'a = 0\nb = 1')) b = Block(File('foo/b.py', 'c = 2\nd = x')) a.sub_blocks = b.sub_blocks try: a.execute({}) except NameError, e: test(tracebacks()[-1], 2, 'foo/b.py') a.sub_blocks = b.sub_blocks[:] try: a.execute({}) except NameError, e: test(tracebacks()[-1], 2, 'foo/b.py') del a,b def test_construction(self): 'Construction' pass # TODO