def identify_consumers(func, env): """ Identify consumers of generators, that is find the loops that iterate over a generator. """ generator_objects = find_generators(func, env) #print("generators", generator_objects) if not generator_objects: # We can stop now return loop_forest = loop_detection.find_natural_loops(func) loops = loop_detection.flatloops(loop_forest) heads = dict((loop.head, loop) for loop in loops) expect_call = partial(expect_single_call, func, env) #print("loops", loops, "heads", heads) for generator_obj in generator_objects: # Check for a nesting of next(iter(my_generator())) iter = expect_call(generator_obj, builtins.iter) next = expect_call(iter, builtins.next) if iter and next and next.block in heads: loop = heads[next.block] yield Consumer(generator_obj, iter, next, loop)
def test_unnested(self): loops = loop_detection.find_natural_loops(self.f) assert len(loops) == 2 assert len(loops[0].children) == 0 assert len(loops[1].children) == 0 assert len(loops[0].blocks) >= 4 assert len(loops[1].blocks) >= 2
def assert_inlinable(func, call, callee, uses): """ Verify that a function call can be inlined. We can inline generators if they are consumed in a single loop: - iter(g) must be in a loop header - next(g) must be in the loop body :return: None if inlineable, or an exception with a message """ if not isinstance(callee, Function): return CompileError("Cannot inline external function: %s" % (callee,)) yields = findallops(callee, 'yield') if yields: for use in uses[call]: if use.opcode not in ('iter', 'next'): return CompileError( "Cannot inline generator with use %s" % (use,)) if len(uses[call]) != 2: return CompileError("Can only") loops = loop_detection.find_natural_loops(func)
def test_nested(self): loops = loop_detection.find_natural_loops(self.f) for i in range(3): loop, = loops loops = loop.children