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_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('b' in context)
        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('b' in context)
        self.assertEqual(context['b'], 1.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
 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_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 __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 _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)
Ejemplo n.º 8
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_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 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)
Ejemplo n.º 11
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_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)
Ejemplo n.º 13
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')
Ejemplo n.º 14
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_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_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)
Ejemplo n.º 17
0
    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)
Ejemplo n.º 18
0
    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']
Ejemplo n.º 19
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_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()
Ejemplo n.º 21
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]
Ejemplo n.º 22
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)
Ejemplo n.º 23
0
    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)
Ejemplo n.º 24
0
    def _base(self, code, inputs, outputs, *results):

        # Convert results to a string if necessary
        def try_join(x):
            if not isinstance(x, basestring):
                return '\n'.join(x)
            else:
                return x
        results = map(try_join, results)

        # Avoid empty discard statements at the end of 'results'
        results = [ re.sub(';*$', '', r) for r in results ]

        # Make sure code's sub-block is one of the given results (restrict
        # isn't deterministic on parallelizable code)
        restricted = Block(code).restrict(inputs=inputs, outputs=outputs)
        if results == ['']:
            self.assertSimilar(restricted, Block(()))
        else:
            self.assertTrue(restricted.ast in [Block(r).ast for r in results])
 def _base(self, code, expected):
     context = default_context()
     Block(code).execute(context)
     for k in expected:
         self.assert_(k in context)
         if isinstance(expected[k], ndarray) or \
            isinstance(context[k], ndarray):
             self.assert_(all(expected[k] == context[k]),
                          'expected = %s, dict(context) = %s' % \
                              (expected, dict(context)))
         else:
             self.assertEqual(expected[k], context[k])
Ejemplo n.º 26
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)
    def test_errors(self):
        'Errors for block restriction'

        # Note: 'a' can be passed as a input, which should return
        # a trivial subblock. This is due to supporting intermediate
        # inputs

        b = Block('a = b')
        self.assertRaises(ValueError, b.restrict, outputs='b')
        self.assertRaises(ValueError, b.restrict, inputs='z')
        self.assertRaises(ValueError, b.restrict, outputs='z')
        self.assertRaises(ValueError, b.restrict)
    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']))
Ejemplo n.º 29
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
    def test_pickle(self):
        'Pickling'

        strings = [
            ''
            'a=b',
            'a=b;c=d',
            'a=f(z); b=g(y); c=h(a,b); d=k(b)',
        ]

        for s in strings:
            b = Block(s)
            self.assertEqual(loads(dumps(b)), b, '(where s = %r)' % s)