def test_imports(self): 'restrict blocks containing imports' # Test 'from' syntax b = Block('from math import sin, pi\n'\ 'b=sin(pi/a)\n' \ 'd = c * 3.3') sub_block = b.restrict(inputs=('a')) self.assertEqual(sub_block.inputs, set(['a'])) self.assertEqual(sub_block.outputs, set(['b'])) self.assertEqual(sub_block.fromimports, set(['pi', 'sin'])) context = {'a':2, 'c':0.0} sub_block.execute(context) self.assertTrue(context.has_key('b')) self.assertEqual(context['b'], 1.0) # Test 'import' syntax b = Block('import math\n'\ 'b=math.sin(math.pi/a)\n' \ 'd = c * 3.3') sub_block = b.restrict(inputs=('a')) self.assertEqual(sub_block.inputs, set(['a'])) self.assertEqual(sub_block.outputs, set(['b'])) self.assertEqual(sub_block.fromimports, set(['math'])) context = {'a':2, 'c':0.0} sub_block.execute(context) self.assertTrue(context.has_key('b')) self.assertEqual(context['b'], 1.0)
def test_impure_execute(): code = """ import os # module and function names are discarded by default. def ff(): global y # will not be retained but will be available in the code block. y = a + x b.append(4) x = a b.append(3) ff() z = y _x = x # names beginning with underscore are discarded by default a = 99 """ context = DataContext(subcontext=dict(a=1, b=[2])) block = Block(code) # by default, clean shadow after execution: shadow = block.execute_impure(context) assert_equal(set(context.keys()), set(['a', 'b'])) # names unchanged assert_equal(context['b'], [2, 3, 4]) # mutable object was changed in context assert_equal(set(shadow.keys()), set(['x', 'z', 'a'])) assert_equal(context['a'], 1) # original mutable object does not change, assert_equal(shadow['a'], 99) # but the new object is in the shadow dict. # do not clean shadow after execution: shadow = block.execute_impure(context, clean_shadow=False) assert_equal(set(shadow.keys()), set(['x', 'z', 'a', '_x', 'os', 'ff']))
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, e: test(tracebacks()[-1], 1, 'foo/a.py')
def test_impure_execute(): code=""" import os # module and function names are discarded by default. def ff(): global y # will not be retained but will be available in the code block. y = a + x b.append(4) x = a b.append(3) ff() z = y _x = x # names beginning with underscore are discarded by default a = 99 """ context = DataContext(subcontext=dict(a=1,b=[2])) block = Block(code) # by default, clean shadow after execution: shadow = block.execute_impure(context) assert_equal(set(context.keys()), set(['a', 'b'])) # names unchanged assert_equal(context['b'], [2,3,4]) # mutable object was changed in context assert_equal(set(shadow.keys()), set(['x', 'z', 'a'])) assert_equal(context['a'], 1) # original mutable object does not change, assert_equal(shadow['a'], 99) # but the new object is in the shadow dict. # do not clean shadow after execution: shadow = block.execute_impure(context, clean_shadow=False) assert_equal(set(shadow.keys()), set(['x', 'z', 'a', '_x', 'os', 'ff']))
def test_restrict_on_nested_blocks(self): b1 = Block('a=0;b=a+1') b2 = Block('x=99;z=x-1') composite=Block([b1,b2]) self.assertSimilar( Block('a=0'), composite.restrict(outputs=['a'])) self.assertSimilar( Block('x=99'), composite.restrict(outputs=['x']))
def test_restrict_outputs(): """Test a basic use of the restrict(outputs=(...)) method.""" code = 'x = a + b\ny = b - c\nz = c**2' b = Block(code) br = b.restrict(outputs=('z',)) names = dict(c=5) br.execute(names) assert_equal(sorted(names), ['c', 'z']) assert_equal(names['c'], 5) assert_equal(names['z'], 25)
def test_restrict_outputs(): """Test a basic use of the restrict(outputs=(...)) method.""" code = 'x = a + b\ny = b - c\nz = c**2' b = Block(code) br = b.restrict(outputs=('z', )) names = dict(c=5) br.execute(names) assert_equal(sorted(names), ['c', 'z']) assert_equal(names['c'], 5) assert_equal(names['z'], 25)
def test_basic_02(): """Another test of the basic use of a Block.""" code = 'y = x + 1' b = Block(code) assert_equal(b.inputs, set(['x'])) assert_equal(b.outputs, set(['y'])) names = dict(x=100) b.execute(names) assert_equal(sorted(names), ['x', 'y']) assert_equal(names['x'], 100) assert_equal(names['y'], 101)
def test_basic_01(): """Test basic use of a Block.""" code = 'x = 100\ny = x + 1' b = Block(code) assert_equal(b.inputs, set([])) assert_equal(b.outputs, set(['x','y'])) names = dict() b.execute(names) assert_equal(sorted(names), ['x', 'y']) assert_equal(names['x'], 100) assert_equal(names['y'], 101)
def test_bound_inputs(self): code = "c = a * b\n" \ "x = 3 + c\n" block = Block(code) context = DataContext() context['a'] = 1 context['b'] = 2 sub_block = block.restrict(inputs=('c')) self.assertEqual(sub_block.inputs, set(['c']))
def setUp(self): code = "from blockcanvas.debug.my_operator import add, mul\n" \ "c = add(a,b)\n" \ "d = mul(c, 2)\n" \ "e = mul(c, 3)\n" \ "f = add(d,e)" self.block = Block(code) # Context setup. self.context = MultiContext(DataContext(name='Data'), {}) self.context['a'] = 1 self.context['b'] = 2
def test_basic_01(): """Test basic use of a Block.""" code = 'x = 100\ny = x + 1' b = Block(code) assert_equal(b.inputs, set([])) assert_equal(b.outputs, set(['x', 'y'])) assert_equal(b.const_assign, (set(['x']), ['x = 100'])) names = dict() b.execute(names) assert_equal(sorted(names), ['x', 'y']) assert_equal(names['x'], 100) assert_equal(names['y'], 101)
def test_restrict_on_nested_blocks(self): b1 = Block('a=0;b=a+1') b2 = Block('x=99;z=x-1') composite = Block([b1, b2]) self.assertSimilar(Block('a=0'), composite.restrict(outputs=['a'])) self.assertSimilar(Block('x=99'), composite.restrict(outputs=['x']))
def test_restrict_inputs(): """Test a basic use of the restrict(inputs=(...)) method.""" code = 'x = a + b\ny = b - c\nz = c**2' b = Block(code) assert_equal(b.inputs, set(['a','b','c'])) assert_equal(b.outputs, set(['x','y','z'])) br = b.restrict(inputs=('a',)) names = dict(a=100, b=200) br.execute(names) assert_equal(sorted(names), ['a', 'b', 'x']) assert_equal(names['a'], 100) assert_equal(names['b'], 200) assert_equal(names['x'], 300)
def test_block_composition(self): 'Composing Blocks' self.assertSimilar(Block('a; b; c'), Block(('a', 'b', 'c'))) self.assertSimilar(Block('a'), Block(('a'))) self.assertSimilar(Block(''), Block(())) self.assertSimilar( Block('if t: a = b\nc = f(a)'), Block(('if t: a = b', 'c = f(a)')))
def test_restrict_inputs(): """Test a basic use of the restrict(inputs=(...)) method.""" code = 'x = a + b\ny = b - c\nz = c**2' b = Block(code) assert_equal(b.inputs, set(['a', 'b', 'c'])) assert_equal(b.outputs, set(['x', 'y', 'z'])) br = b.restrict(inputs=('a', )) names = dict(a=100, b=200) br.execute(names) assert_equal(sorted(names), ['a', 'b', 'x']) assert_equal(names['a'], 100) assert_equal(names['b'], 200) assert_equal(names['x'], 300)
def test_optimization_no_filenames_in_tracebacks(self): 'Optimization: No filenames in tracebacks' b = Block('import operator\n' 'x,d = 2,{}\n' 'for i in range(10):\n' ' d[i] = repr(i)\n' ' x *= 2\n' 'sum = lambda l, add=operator.add: reduce(add, l)\n' 'd = (sum(d.keys()), sum(d.values()))\n', no_filenames_in_tracebacks = True) c = {} b.execute(c) assert c['x'] == 2**11 assert c['d'] == (9*10/2, '0123456789')
def __init__(self, **kwtraits): if 'external_block' in kwtraits: self.external_block = kwtraits.pop('external_block') super(FormulaExecutingContext, self).__init__(**kwtraits) self._regenerate_expression_block() if self.external_block is None: self._external_code_changed(self.external_code) else: self.external_block = Block( [self.external_block, Block(self.external_code)]) self._regenerate_composite_block()
def test_intermediate_inputs(self): """ restrict blocks with inputs which are intermediates """ block = Block('c = a + b\n'\ 'd = c * 3') sub_block = block.restrict(inputs=('c')) self.assertEqual(sub_block.inputs, set(['c'])) self.assertEqual(sub_block.outputs, set(['d'])) context = {'a': 1, 'b': 2} block.execute(context) self.assertEqual(context['c'], 3) self.assertEqual(context['d'], 9) context = {'c': 10} sub_block.execute(context) self.assertEqual(context['c'], 10) self.assertEqual(context['d'], 30) context = {'d': 15} sub_block = block.restrict(inputs=('d')) self.assertEqual(sub_block.inputs, set([])) self.assertEqual(sub_block.outputs, set([])) sub_block.execute(context) self.assertEqual(context['d'], 15)
def __getitem__(self, key): if key in self.underlying_context.keys(): return self.underlying_context[key] else: try: # FIXME imports need to be more configurable # FIXME we may want to have cache rules on sizes so that we # don't keep around huge values that aren't needed anymore # however, there is a time/space tradeoff that really should be # used. We currently support del so that is available if the user # wants to manually manage this. eval_globals = {} for lib in (__builtins__, __import__('numpy')): for sym in dir(lib): if not key.startswith('__'): eval_globals[sym] = getattr(lib, sym) result = eval(key, eval_globals, self.underlying_context) self._expressions[key] = result for dep in Block(key).inputs: self._dependencies.setdefault(dep, list()) self._dependencies[dep].append(key) return result except: return None
def test_intermediate_inputs_with_highly_connected_graph(self): """ restrict blocks with inputs which are intermediates on a highly connected graph""" code = "c = a + b\n" \ "d = c * 3\n" \ "e = a * c\n" \ "f = d + e\n" \ "g = e + c\n" \ "h = a * 3" block = Block(code) sub_block = block.restrict(inputs=('c')) self.assertEqual(sub_block.inputs, set(['a', 'c'])) self.assertEqual(sub_block.outputs, set(['d', 'e', 'f', 'g'])) context = {'a': 1, 'b': 2} block.execute(context) self.assertEqual(context['c'], 3) self.assertEqual(context['d'], 9) self.assertEqual(context['e'], 3) self.assertEqual(context['f'], 12) self.assertEqual(context['g'], 6) context = {'a': 1, 'c': 10} sub_block.execute(context) self.assertEqual(context['c'], 10) self.assertEqual(context['d'], 30) self.assertEqual(context['e'], 10) self.assertEqual(context['f'], 40) self.assertEqual(context['g'], 20)
def test_intermediate_inputs_and_outputs(self): """ restrict blocks with inputs and outputs which are intermediates """ code = "c = a + b\n" \ "d = c * 3\n" \ "e = a * c\n" \ "f = d + e\n" \ "g = e + c\n" \ "h = a * 3" block = Block(code) sub_block = block.restrict(inputs=('c'), outputs=('e', 'g')) self.assertEqual(sub_block.inputs, set(['a', 'c'])) self.assertEqual(sub_block.outputs, set(['e', 'g'])) context = {'a': 1, 'b': 2} block.execute(context) self.assertEqual(context['c'], 3) self.assertEqual(context['e'], 3) self.assertEqual(context['g'], 6) context = {'a': 1, 'c': 10} sub_block.execute(context) self.assertEqual(context['c'], 10) self.assertEqual(context['e'], 10) self.assertEqual(context['g'], 20)
def _regenerate_expression_block(self): exprs = [ '%s = %s' % (var, expr) for var, expr in list(self._expressions.items()) ] expression_code = '\n'.join(exprs) + '\n' self._expression_block = Block(expression_code)
def test3(self): """ Same sized array assignments within 'with' block """ file_object = open(os.path.join(self.code_dir, 'test3.py'), 'r') code = file_object.read() file_object.close() b = Block(code) b.execute(self.context) desired_vp = arange(0., 10., 1.) desired_vs = arange(0., 100., 10.) # Check equal self.assertTrue((desired_vp == self.context['vp']).all()) self.assertTrue((desired_vs == self.context['vs']).all())
def test_inputs_are_dependent_outputs(self): """ restrict blocks with inputs which are intermediates and outputs""" code = "t2 = b * 2\n" \ "t3 = t2 + 3\n" block = Block(code) sub_block = block.restrict(inputs=['t2', 't3'])
def test_simple_output_reduction(self): code = 'from blockcanvas.debug.my_operator import add, mul\n' \ 'c = add(a,b)\n' \ 'd = mul(a,16)\n' block = Block(code) context = DataContext() context['a'] = 1 context['b'] = 2 sub_block = block.restrict(outputs=('d')) self.assertEqual(sub_block.inputs, set(['a'])) sub_block.execute(context) self.assertTrue(context.has_key('d')) self.assertEqual(context['d'], 16)
def test_block_name_replacer(self): """ Does BlockNameReplacer work? """ code = "x = x(x, x)\nx\n" desired = "y = y(y, y)\ny\n" b = Block(code) rename_variable(b.ast, 'x', 'y') self.assertEqual(desired, unparse(b.ast))
def test3(self): """ Same sized array assignments within 'with' block """ file_object = open(os.path.join(self.code_dir,'test3.py'), 'r') code = file_object.read() file_object.close() b = Block(code) b.execute(self.context) desired_vp = arange(0., 10., 1.) desired_vs = arange(0., 100., 10.) # Check equal self.assertTrue((desired_vp == self.context['vp']).all()) self.assertTrue((desired_vs == self.context['vs']).all())
def test_load_global_ranges_with_project_ranges(self): """ Do global ranges get overwritten by the project settings ? """ code = "from blockcanvas.debug.my_operator import add,mul\n"\ "d = mul(a, b)\n" \ "e = mul(c, 3)\n" \ "f = add(d,e)" self.block = Block(code) self.context['c'] = 2.0 # This is to be done because the interactor doesn't get updated with # change in block. i_config = InteractorConfig(var_configs=[ VariableConfig(name='a', type='Shadow'), VariableConfig(name='b', type='Parametric'), VariableConfig(name='c', type='Shadow'), VariableConfig(name='d', type='Parametric') ]) self.interactor = ConfigurableInteractor(block=self.block, context=self.context, interactor_config=i_config) child_int = self.interactor.interactor_shadow global_file_path = os.path.join(os.path.dirname(__file__), 'data', 'global_user_range.txt') project_file_path = os.path.join(os.path.dirname(__file__), 'data', 'project_ranges.txt') self.interactor.load_ranges_from_files({'shadow': project_file_path}, {'shadow': global_file_path}) child_int._view_items() # Range of a is obtained from the project settings and not the # global preferences self.assertAlmostEqual(child_int.input_a__low, 3.4) self.assertAlmostEqual(child_int.input_a__high, 5.0) self.assertNotEqual(child_int.input_a__low, 0.15) self.assertNotEqual(child_int.input_a__high, 9.3) # Range of c is obtained from the global preferences self.assertAlmostEqual(child_int.input_c__low, 2.0) self.assertAlmostEqual(child_int.input_c__high, 7.0)
def test_dep_graph_exists_for_line_of_code(self): """ Does block treat 1 func blocks like multi-func blocks. It doesn't appear that simple blocks are forcing updates to the dep_graph. One func graphs Should be the same as multi-line ones (I think). Without this, we have to always check for None and special case that code path in all the processing tools. fixme: I (eric) haven't examined this very deeply, it just cropped up in some of my code. This test is a reminder that we need to either fix it or verify that we don't want to fix it. """ block = Block('b = foo(a)\nc=bar(b)\n') self.assertTrue(block._dep_graph is not None) block = Block('b = foo(a)\n') self.assertTrue(block._dep_graph is not None)
def test_import_and_rename(self): code = "from blockcanvas.debug.my_operator import add as add1\n" \ "a = add1(1,2)\n" \ "b = add1(a,3)" foo_block = Block(code) info = find_functions(foo_block.ast) foo_call = FunctionCall.from_ast(foo_block.sub_blocks[1].ast, info) desired = 'result = add(1, 2)' self.assertEqual(foo_call.call_signature, desired)
def test_function_names(self): """ Does function_names find all the functions in an AST? """ code = "x = func1(func2(1),2)\n" \ "y = func3()\n" desired = ["func1", "func2", "func3"] b = Block(code) actual = sorted(function_names(b)) self.assertEqual(actual, desired)
def test_local_function_preprocessing(self): code = "def foo(a,b):\n" \ "\tx,y=a,b\n" \ "\treturn x,y\n" \ "i,j = foo(1,2)\n" foo_block = Block(code) info = find_functions(foo_block.ast) assert 'foo' in info assert info['foo']
def test_cyclic_dep_graph(self): "Regression: Don't make cyclic dep graphs" # When an input and output name are the same, we used to represent them # identically in the dep graph, making it cyclic (and wrong anyway) try: # (';' appends an empty 'Discard' statement to the block, which # gives it multiple sub-blocks, which forces 'restrict' to run # non-trivially) Block('a = a;').restrict(inputs=['a']) except graph.CyclicGraph: self.fail()
def __delitem__(self, key): """Delete an item from the ExpressionContext -- either in the underlying context, or if an already cached expression, delete it.""" # if item is an expression, delete it from the list of dependencies, otherwise pass it down # to underlying if key in self._expressions: self._expressions.remove(key) for dep in list(Block(key).inputs): self._dependencies[dep].remove(key) else: del self.underlying_context[key]
def test2(self): """ Different sized arrays """ file_object = open(os.path.join(self.code_dir,'test2.py'), 'r') code = file_object.read() file_object.close() b = Block(code) b.execute(self.context) depth = arange(0., 10000., 1000.) desired_vp = zeros(depth.shape) desired_vs = zeros(depth.shape) desired_vp[(depth<4000.0) & (depth > 1000.0)] = 1.0 desired_vs[(depth<4000.0) & (depth > 1000.0)] = 1.5 # Check equal self.assertTrue((desired_vp == self.context['vp']).all()) self.assertTrue((desired_vs == self.context['vs']).all())
def test_local_def(self): code = "def foo(a):\n" \ " b = a\n" \ " return b\n" \ "y = foo(2)\n" foo_block = Block(code) info = find_functions(foo_block.ast) foo_call = FunctionCall.from_ast(foo_block.sub_blocks[-1].ast, info) desired = 'b = foo(2)' self.assertEqual(foo_call.call_signature, desired)
def test2(self): """ Different sized arrays """ file_object = open(os.path.join(self.code_dir, 'test2.py'), 'r') code = file_object.read() file_object.close() b = Block(code) b.execute(self.context) depth = arange(0., 10000., 1000.) desired_vp = zeros(depth.shape) desired_vs = zeros(depth.shape) desired_vp[(depth < 4000.0) & (depth > 1000.0)] = 1.0 desired_vs[(depth < 4000.0) & (depth > 1000.0)] = 1.5 # Check equal self.assertTrue((desired_vp == self.context['vp']).all()) self.assertTrue((desired_vs == self.context['vs']).all())
def test_block_events(self): import numpy from codetools.blocks.api import Block context = DataContext(name="data") context.on_trait_change(self.event_listener, 'items_modified') context.defer_events = True context['a'] = 4 context['b'] = numpy.array((1,2,3)) context.defer_events = False self.assertEqual(self.event_count, 1) multi_context = MultiContext(context, name="multi") multi_context.on_trait_change(self.event_listener, 'items_modified') block = Block("c = a * b") block.execute(multi_context) # we expect one event from data context, one from multi context self.assertEqual(self.event_count, 3)
def test_unbound_inputs(self): code = 'from blockcanvas.debug.my_operator import add, mul\n' \ "c = add(a,b)\n" \ "d = mul(c,a)\n" \ "z = add(b, 2)" block = Block(code) context = DataContext() context['a'] = 2 context['b'] = 1 sub_block = block.restrict(inputs=('a')) self.assertEqual(sub_block.inputs, set(['a', 'b'])) self.assertEqual(sub_block.outputs, set(['c', 'd', 'add', 'mul'])) sub_block.execute(context) self.assertTrue(context.has_key('c')) self.assertEqual(context['c'], 3) self.assertTrue(context.has_key('d')) self.assertEqual(context['d'], 6)
def test_ast_policy(self): 'Policy: Keep tidy ASTs' a = Discard(Name('a')) empty = Stmt([]) self.assertEqual(empty, Block('').ast) self.assertEqual(empty, Block(empty).ast) self.assertEqual(empty, Block(Module(None, empty)).ast) self.assertEqual(a, Block('a').ast) self.assertEqual(a, Block(a).ast) self.assertEqual(a, Block(Stmt([a])).ast) self.assertEqual(a, Block(Module(None, Stmt([a]))).ast) # Similar, except we don't use strings since Block does its own parsing b = Block() b.ast = empty self.assertEqual(b.ast, empty) b.ast = Module(None, empty) self.assertEqual(b.ast, empty) b.ast = a self.assertEqual(b.ast, a) b.ast = Stmt([a]) self.assertEqual(b.ast, a) b.ast = Module(None, Stmt([a])) self.assertEqual(b.ast, a)
def setUp(self): code = "from blockcanvas.debug.my_operator import add, mul\n" \ "c = add(a,b)\n" \ "d = mul(c, 2)\n" \ "e = mul(c, 3)\n" \ "f = add(d,e)" self.block=Block(code) # Context setup. self.context = MultiContext(DataContext(name='Data'),{}) self.context['a'] = 1 self.context['b'] = 2
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_load_global_ranges_with_project_ranges(self): """ Do global ranges get overwritten by the project settings ? """ code = "from blockcanvas.debug.my_operator import add,mul\n"\ "d = mul(a, b)\n" \ "e = mul(c, 3)\n" \ "f = add(d,e)" self.block = Block(code) self.context['c'] = 2.0 # This is to be done because the interactor doesn't get updated with # change in block. i_config = InteractorConfig(var_configs=[VariableConfig(name='a', type='Shadow'), VariableConfig(name='b', type='Parametric'), VariableConfig(name='c', type='Shadow'), VariableConfig(name='d', type='Parametric')]) self.interactor = ConfigurableInteractor(block=self.block, context=self.context, interactor_config=i_config) child_int = self.interactor.interactor_shadow global_file_path = os.path.join(os.path.dirname(__file__), 'data', 'global_user_range.txt') project_file_path = os.path.join(os.path.dirname(__file__), 'data', 'project_ranges.txt') self.interactor.load_ranges_from_files({'shadow':project_file_path}, {'shadow':global_file_path}) child_int._view_items() # Range of a is obtained from the project settings and not the # global preferences self.assertAlmostEqual(child_int.input_a__low, 3.4) self.assertAlmostEqual(child_int.input_a__high, 5.0) self.assertNotEqual(child_int.input_a__low, 0.15) self.assertNotEqual(child_int.input_a__high, 9.3) # Range of c is obtained from the global preferences self.assertAlmostEqual(child_int.input_c__low, 2.0) self.assertAlmostEqual(child_int.input_c__high, 7.0)
def setUp(self): code = "from blockcanvas.debug.my_operator import add,mul\n"\ "c = add(a, b)\n" \ "d = mul(c, 2)\n" \ "e = mul(c, 3)\n" \ "f = add(d, e)\n" \ "g = add(f, 3)\n" \ "h = mul(g, f)\n" \ "i = mul(g,h)" self.block=Block(code) # Context setup. self.context = MultiContext(DataContext(name='Data'),{}) self.context['a'] = 1.0 self.context['b'] = 2 self.block.execute(self.context) # ConfigurableInteractor setup i_config = InteractorConfig(var_configs=[VariableConfig(name='a', type='Shadow'), VariableConfig(name='b', type='Parametric'), VariableConfig(name='c', type='Shadow'), VariableConfig(name='d', type='Parametric'), VariableConfig(name='e', type='Stochastic: Constant'), VariableConfig(name='f', type='Stochastic: Gaussian'), VariableConfig(name='g', type='Stochastic: Triangular'), VariableConfig(name='h', type='Stochastic: Uniform'), ]) self.interactor = ConfigurableInteractor(interactor_config=i_config, block=self.block, context=self.context) # Temp dir self.temp_dir = tempfile.gettempdir()
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)
a = Block(File('foo/a.py', 'y = x')) try: a.execute({}) except NameError, 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, 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, e: test(tracebacks()[-1], 3, 'foo/b.py') #del b # (use below in 'Compose') # Compose c = Block((a,b)) try: c.execute({}) except NameError, e: test(tracebacks()[-1], 2, 'foo/a.py') try: c.execute({'x':0}) except IndexError, e:
'from numpy import zeros\n'\ 'from codetools.contexts.with_mask import Mask\n'\ 'with Mask((depth < 4000.0) & (depth > 1000.0)):vp=1.5 ; vs=1.0' ## # Expanded form of with statement taken from PEP 343. This is just for testing ## code = 'from numpy import zeros\n'\ ## 'array_len = depth.shape\n'\ ## 'vp = zeros(array_len)\n'\ ## 'vs = zeros(array_len)\n'\ ## 'from codetools.contexts.with_mask import Mask\n'\ ## 'mgr = Mask((depth < 4000.0) & (depth > 1000.0))\n'\ ## 'exit_code = mgr.__exit__\n'\ ## 'mgr.__enter__()\n'\ ## 'exc = True\n'\ ## 'try:\n'\ ## ' try:\n'\ ## ' vp = 1.0\n'\ ## ' vs = 1.5\n'\ ## ' except:\n'\ ## ' exc = False\n'\ ## 'finally:\n'\ ## ' if exc:\n'\ ## ' exit_code(None, None, None)' b = Block(code) b.execute(context) print 'vp', context['vp'] print 'vs', context['vs'] ### EOF ------------------------------------------------------------------------
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
# Test if __name__ == '__main__': from codetools.blocks.api import Block from codetools.contexts.api import DataContext, MultiContext # from context.api import GeoContext # from scimath.units.api import UnitArray # from scimath.units.length import meter code = "from blockcanvas.debug.my_operator import add, mul\n" \ "c = add(a,b)\n" \ "d = mul(c, 2)\n" \ "e = mul(c, 3)\n" \ "f = add(d,e)" block=Block(code) # context = ParametricContext(GeoContext(), {}) # context['a'] = UnitArray((1,2,3), units=meter) # context['b'] = UnitArray((2,3,4), units=meter) # # interactor = ParametricInteractor(context=context, block = block) # interactor.configure_traits() # Context setup. context = MultiContext(DataContext(name='Data'),{}) context['a'] = 1 context['b'] = 2 interactor = ParametricInteractor(context=context, block = block) interactor.configure_traits()
class ConfigurableInteractorTestCase(unittest.TestCase): """ Unit testing for ConfigurableInteractor """ def setUp(self): code = "from blockcanvas.debug.my_operator import add,mul\n"\ "c = add(a, b)\n" \ "d = mul(c, 2)\n" \ "e = mul(c, 3)\n" \ "f = add(d, e)\n" \ "g = add(f, 3)\n" \ "h = mul(g, f)\n" \ "i = mul(g,h)" self.block=Block(code) # Context setup. self.context = MultiContext(DataContext(name='Data'),{}) self.context['a'] = 1.0 self.context['b'] = 2 self.block.execute(self.context) # ConfigurableInteractor setup i_config = InteractorConfig(var_configs=[VariableConfig(name='a', type='Shadow'), VariableConfig(name='b', type='Parametric'), VariableConfig(name='c', type='Shadow'), VariableConfig(name='d', type='Parametric'), VariableConfig(name='e', type='Stochastic: Constant'), VariableConfig(name='f', type='Stochastic: Gaussian'), VariableConfig(name='g', type='Stochastic: Triangular'), VariableConfig(name='h', type='Stochastic: Uniform'), ]) self.interactor = ConfigurableInteractor(interactor_config=i_config, block=self.block, context=self.context) # Temp dir self.temp_dir = tempfile.gettempdir() def test_build_vars(self): """ Does the interactor create the right traits for the types of of interactors """ self.failUnless(isinstance(self.interactor.interactor_shadow, ShadowInteractor)) self.failUnless(isinstance(self.interactor.interactor_parametric, ParametricInteractor)) self.failUnless(isinstance( self.interactor.interactor_stochastic_constant,StochasticInteractor)) self.failUnless(isinstance( self.interactor.interactor_stochastic_gaussian,StochasticInteractor)) self.failUnless(isinstance( self.interactor.interactor_stochastic_triangular, StochasticInteractor)) self.failUnless(isinstance( self.interactor.interactor_stochastic_uniform, StochasticInteractor)) def test_load_project_ranges_for_shadow_interactor(self): """ Does loading ranges from file work for shadow interactor ? """ child_int = self.interactor.interactor_shadow file_path = os.path.join(os.path.dirname(__file__), 'data', 'project_ranges.txt') # Applying the ranges to the shadow interactor in the configurable # interactor self.interactor.load_ranges_from_files({'shadow':file_path}) child_int._view_items() self.assertEqual(len(child_int.ranges.keys()), 42) self.assertAlmostEqual(child_int.input_a__high, 5) self.assertAlmostEqual(child_int.input_a__low, 3.4) self.assertEqual(child_int.input_a, self.context['a']) def test_save_project_ranges_for_shadow_interactor(self): """ Does saving ranges to file work for shadow interactor ? """ child_int = self.interactor.interactor_shadow f_path = os.path.join(self.temp_dir,'test_save_ranges_for_shadow.txt') child_int._view_items() child_int.input_a__high = 5.0 self.interactor.save_ranges_to_files({'shadow': f_path}) # Read the saved file file_object = open(f_path, 'r') line = file_object.read() line = [name.strip() for name in line.split()] file_object.close() os.remove(f_path) # Check results self.assertEqual(len(line), 6) self.assertEqual(line[2], str(5.0)) def test_load_project_ranges_for_parametric_interactor(self): """ Does loading ranges from file work for parametric interactor ? """ child_int = self.interactor.interactor_parametric file_path = os.path.join(os.path.dirname(__file__), 'data', 'project_ranges.txt') self.interactor.load_ranges_from_files({'parametric': file_path}) child_int._view_items() self.assertEqual(len(child_int.ranges.keys()), 42) self.assertAlmostEqual(child_int.input_b.low, 1.0) self.assertAlmostEqual(child_int.input_b.high, 6.0) self.assertEqual(child_int.input_b.step, 0.0) self.assertEqual(child_int.input_b.input_value, self.context['b']) def test_save_project_ranges_for_parametric_interactor(self): """ Does saving ranges to file work for parametric interactor ? """ file_path = os.path.join(self.temp_dir, 'test_save_ranges_for_parametric.txt') child_int = self.interactor.interactor_parametric child_int._view_items() child_int.input_b.low = 2 child_int.input_b.high = 3 child_int.input_b.step = 0.5 self.interactor.save_ranges_to_files({'parametric':file_path}) # Read the saved file file_object = open(file_path, 'r') lines = file_object.readlines() lines = [line.split() for line in lines] file_object.close() os.remove(file_path) self.assertEqual(len(lines), 2) self.assertEqual(len(lines[0]), 4) self.assertEqual(len(lines[1]), 4) self.assertEqual(lines[0][1], str(2)) self.assertEqual(lines[0][2], str(3)) self.assertEqual(lines[0][3], str(0)) def test_load_project_ranges_for_stochastic_constant_interactor(self): """ Does loading parameters from file work for stochastic interactor with constant distribution ? """ child_int = self.interactor.interactor_stochastic_constant file_path = os.path.join(os.path.dirname(__file__), 'data', 'stochastic_constant_ranges.txt') self.interactor.load_ranges_from_files({'stochastic_constant': file_path}) child_int._view_items() self.assertEqual(child_int.input_e.distribution.value, 45) def test_save_project_ranges_for_stochastic_constant_interactor(self): """ Does saving parameters to file work for stochastic interactor with constant distribution ? """ file_path = os.path.join(self.temp_dir, 'test_save_ranges_for_stoch_constant.txt') child_int = self.interactor.interactor_stochastic_constant child_int._view_items() child_int.input_e.distribution.value = 37.5 self.interactor.save_ranges_to_files({'stochastic_constant': file_path}) # Read the saved file file_object = open(file_path, 'r') lines = file_object.readlines() lines = [line.split() for line in lines] file_object.close() os.remove(file_path) self.assertEqual(len(lines), 1) self.assertEqual(len(lines[0]), 2) self.assertEqual(lines[0][1], str(37.5)) def test_load_project_ranges_for_stochastic_gaussian_interactor(self): """ Does loading parameters from file work for stochastic interactor with gaussian distribution ? """ child_int = self.interactor.interactor_stochastic_gaussian file_path = os.path.join(os.path.dirname(__file__), 'data', 'stochastic_gaussian_ranges.txt') self.interactor.load_ranges_from_files({'stochastic_gaussian': file_path}) child_int._view_items() self.assertEqual(child_int.input_f.distribution.mean, 39) self.assertEqual(child_int.input_f.distribution.std, 3) def test_save_project_ranges_for_stochastic_gaussian_interactor(self): """ Does saving parameters to file work for stochastic interactor with gaussian distribution ? """ file_path = os.path.join(self.temp_dir, 'test_save_ranges_for_stoch_gaussian.txt') child_int = self.interactor.interactor_stochastic_gaussian child_int._view_items() child_int.input_f.distribution.mean = 200 child_int.input_f.distribution.std = 0.5 self.interactor.save_ranges_to_files({'stochastic_gaussian': file_path}) # Read the saved file file_object = open(file_path, 'r') lines = file_object.readlines() lines = [line.split() for line in lines] file_object.close() os.remove(file_path) self.assertEqual(len(lines), 1) self.assertEqual(len(lines[0]), 3) self.assertEqual(lines[0][1], str(200.0)) self.assertEqual(lines[0][2], str(0.5)) def test_load_project_ranges_for_stochastic_triangular_interactor(self): """ Does loading parameters from file work for triangular interactor with triangular distribution ? """ child_int = self.interactor.interactor_stochastic_triangular file_path = os.path.join(os.path.dirname(__file__), 'data', 'stochastic_triangular_ranges.txt') self.interactor.load_ranges_from_files({'stochastic_triangular': file_path}) child_int._view_items() self.assertEqual(child_int.input_g.distribution.mode, 44) self.assertEqual(child_int.input_g.distribution.low, 39) self.assertEqual(child_int.input_g.distribution.high, 49) def test_save_project_ranges_for_stochastic_triangular_interactor(self): """ Does saving parameters to file work for stochastic interactor with triangular distribution ? """ file_path = os.path.join(self.temp_dir, 'test_save_ranges_for_stoch_triangular.txt') child_int = self.interactor.interactor_stochastic_triangular child_int._view_items() child_int.input_g.distribution.mode = 40.0 child_int.input_g.distribution.low = 30.0 child_int.input_g.distribution.high = 50.0 self.interactor.save_ranges_to_files({'stochastic_triangular': file_path}) # Read the saved file file_object = open(file_path, 'r') lines = file_object.readlines() lines = [line.split() for line in lines] file_object.close() os.remove(file_path) self.assertEqual(len(lines), 1) self.assertEqual(len(lines[0]), 4) self.assertEqual(lines[0][1], str(40.0)) self.assertEqual(lines[0][2], str(30.0)) self.assertEqual(lines[0][3], str(50.0)) def test_load_project_ranges_for_stochastic_uniform_interactor(self): """ Does loading parameters from file work for stochastic interactor with uniform distribution ? """ child_int = self.interactor.interactor_stochastic_uniform file_path = os.path.join(os.path.dirname(__file__), 'data', 'stochastic_uniform_ranges.txt') self.interactor.load_ranges_from_files({'stochastic_uniform':file_path}) child_int._view_items() self.assertEqual(child_int.input_h.distribution.low, 40) self.assertEqual(child_int.input_h.distribution.high, 58) def test_save_project_ranges_for_stochastic_uniform_interactor(self): """ Does saving parameters to file work for stochastic interactor with uniform distribution ? """ file_path = os.path.join(self.temp_dir, 'test_save_ranges_for_stoch_uniform.txt') child_int = self.interactor.interactor_stochastic_uniform child_int._view_items() child_int.input_h.distribution.low = 30.0 child_int.input_h.distribution.high = 50.0 self.interactor.save_ranges_to_files({'stochastic_uniform': file_path}) # Read the saved file file_object = open(file_path, 'r') lines = file_object.readlines() lines = [line.split() for line in lines] file_object.close() os.remove(file_path) self.assertEqual(len(lines), 1) self.assertEqual(len(lines[0]), 3) self.assertEqual(lines[0][1], str(30.0)) self.assertEqual(lines[0][2], str(50.0)) def test_load_global_ranges_with_project_ranges(self): """ Do global ranges get overwritten by the project settings ? """ code = "from blockcanvas.debug.my_operator import add,mul\n"\ "d = mul(a, b)\n" \ "e = mul(c, 3)\n" \ "f = add(d,e)" self.block = Block(code) self.context['c'] = 2.0 # This is to be done because the interactor doesn't get updated with # change in block. i_config = InteractorConfig(var_configs=[VariableConfig(name='a', type='Shadow'), VariableConfig(name='b', type='Parametric'), VariableConfig(name='c', type='Shadow'), VariableConfig(name='d', type='Parametric')]) self.interactor = ConfigurableInteractor(block=self.block, context=self.context, interactor_config=i_config) child_int = self.interactor.interactor_shadow global_file_path = os.path.join(os.path.dirname(__file__), 'data', 'global_user_range.txt') project_file_path = os.path.join(os.path.dirname(__file__), 'data', 'project_ranges.txt') self.interactor.load_ranges_from_files({'shadow':project_file_path}, {'shadow':global_file_path}) child_int._view_items() # Range of a is obtained from the project settings and not the # global preferences self.assertAlmostEqual(child_int.input_a__low, 3.4) self.assertAlmostEqual(child_int.input_a__high, 5.0) self.assertNotEqual(child_int.input_a__low, 0.15) self.assertNotEqual(child_int.input_a__high, 9.3) # Range of c is obtained from the global preferences self.assertAlmostEqual(child_int.input_c__low, 2.0) self.assertAlmostEqual(child_int.input_c__high, 7.0) def test_load_global_ranges_without_project_ranges(self): """ Do global ranges get used when there are no project settings ? """ code = "from blockcanvas.debug.my_operator import add,mul\n"\ "d = mul(a, b)\n" \ "e = mul(c, 3)\n" \ "f = add(d,e)" self.block = Block(code) self.context['c'] = 2.0 # This is to be done because the interactor doesn't get updated with # change in block. i_config = InteractorConfig(var_configs=[VariableConfig(name='a', type='Shadow'), VariableConfig(name='b', type='Parametric'), VariableConfig(name='c', type='Shadow'), VariableConfig(name='d', type='Parametric')]) self.interactor = ConfigurableInteractor(block=self.block, context=self.context, interactor_config=i_config) child_int = self.interactor.interactor_shadow global_file_path = os.path.join(os.path.dirname(__file__), 'data', 'global_user_range.txt') self.interactor.load_ranges_from_files({}, {'shadow':global_file_path}) child_int._view_items() # Range of a is obtained from the project settings and not the # global preferences self.assertAlmostEqual(child_int.input_a__low, 0.15) self.assertAlmostEqual(child_int.input_a__high, 9.3) # Range of c is obtained from the global preferences self.assertAlmostEqual(child_int.input_c__low, 2.0) self.assertAlmostEqual(child_int.input_c__high, 7.0)
class ParametricInteractorTestCase(unittest.TestCase): """ Unit testing for ParametricInteractor """ def setUp(self): code = "from blockcanvas.debug.my_operator import add, mul\n" \ "c = add(a,b)\n" \ "d = mul(c, 2)\n" \ "e = mul(c, 3)\n" \ "f = add(d,e)" self.block=Block(code) # Context setup. self.context = MultiContext(DataContext(name='Data'),{}) self.context['a'] = 1 self.context['b'] = 2 def test_attributes(self): """ Test if creation of attributes is working correctly """ interactor = ParametricInteractor(context=self.context,block=self.block) self.assertTrue(hasattr(interactor, interactor._input_prefix + "a")) self.assertTrue(hasattr(interactor, interactor._input_prefix + "b")) # Check if the parameter items were written correctly attribute_a = getattr(interactor, interactor._input_prefix+'a') attribute_b = getattr(interactor, interactor._input_prefix+'b') self.assertEqual(attribute_a.step, 0) self.assertEqual(attribute_a.low, 1) self.assertEqual(attribute_a.high, 1) self.assertEqual(attribute_b.step, 0) self.assertEqual(attribute_b.low, 2) self.assertEqual(attribute_b.high, 2) # Check if the inputs in the interactor are right self.assertEqual(interactor.inputs, list(self.block.inputs)) def test_create_shadows(self): """ Test if creating shadows is working correctly """ interactor = ParametricInteractor(context=self.context,block=self.block) # Change the parameters for the attributes attribute_a = getattr(interactor, interactor._input_prefix+'a') attribute_b = getattr(interactor, interactor._input_prefix+'b') attribute_a.high = 4 attribute_a.step = 1 attribute_b.high = 3 attribute_b.step = 1 # Create the shadows. interactor._update_contexts_button_changed() # Check if the shadows were created correctly. self.assertEqual(len(self.context.shadows), len(attribute_a.output_list)*len(attribute_b.output_list)) # Check if the shadow context gives desired result. self.block.execute(self.context.shadows[-1]) self.assertEqual(self.context.shadows[-1]['f'], 5*(self.context.shadows[-1]['a']+self.context.shadows[-1]['b']))