Пример #1
0
    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)
Пример #2
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']))
Пример #3
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, e:
            test(tracebacks()[-1], 1, 'foo/a.py')
Пример #4
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']))
Пример #5
0
    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']))
Пример #6
0
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)
Пример #7
0
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)
Пример #8
0
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)
Пример #9
0
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)
Пример #10
0
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 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']))
Пример #13
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)"

        self.block = Block(code)

        # Context setup.
        self.context = MultiContext(DataContext(name='Data'), {})
        self.context['a'] = 1
        self.context['b'] = 2
Пример #14
0
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']))
Пример #16
0
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)')))
Пример #18
0
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)
Пример #19
0
 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)
Пример #22
0
    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)
Пример #26
0
    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)
Пример #29
0
 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 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)
Пример #31
0
    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)
Пример #35
0
    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()
Пример #38
0
 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]
Пример #39
0
    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)
Пример #41
0
    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())
Пример #42
0
    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)
Пример #44
0
    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
Пример #46
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_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()
Пример #49
0
    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)
Пример #50
0
        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:
Пример #51
0
           '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 ------------------------------------------------------------------------
Пример #52
0
    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']))