def test_not_functions(self): """Test make_function error conditions.""" with self.assertRaises(ValueError): make_function('1+1') with self.assertRaises(ValueError): make_function('def one(): pass\ndef two(): pass')
def make_steps(cls, step_container, steps, is_background, outline=None): """ Construct either a scenario or a background calling the specified steps. The method will have debugging information corresponding to the lines in the feature file. """ assert len(steps) > 0 step_definitions = [cls.prepare_step(step) for step in steps] source = 'def run_steps(self):\n' if not is_background: source += ' self.background()\n' source += '\n'.join( ' func{i}(step{i}, *args{i}, **kwargs{i})'.format(i=i) for i in range(len(step_definitions))) source = ast.parse(source) # Set locations of the steps for step, step_call in zip(steps, source.body[0].body[1:]): for node in ast.walk(step_call): node.lineno = step.described_at.line # Supply all the step functions and arguments context = { k + str(i): v for i, definition in enumerate(step_definitions) for k, v in definition.items() } if is_background: func_name = 'background' else: func_name = step_container.name run_steps = make_function( source=source, context=context, source_file=step_container.described_at.file, name=func_name, ) try: tags = step_container.tags except AttributeError: tags = () for tag in tags: run_steps = attr(tag)(run_steps) if not is_background: run_steps = CALLBACK_REGISTRY.wrap('example', run_steps, step_container, outline, steps) return run_steps
def make_steps(cls, step_container, steps, is_background, outline=None): """ Construct either a scenario or a background calling the specified steps. The method will have debugging information corresponding to the lines in the feature file. """ assert len(steps) > 0 step_definitions = [ cls.prepare_step(step) for step in steps ] source = 'def run_steps(self):\n' if not is_background: source += ' self.background()\n' source += '\n'.join( ' func{i}(step{i}, *args{i}, **kwargs{i})'.format(i=i) for i in range(len(step_definitions)) ) source = ast.parse(source) # Set locations of the steps for step, step_call in zip(steps, source.body[0].body[1:]): for node in ast.walk(step_call): node.lineno = step.line # Supply all the step functions and arguments context = { k + str(i): v for i, definition in enumerate(step_definitions) for k, v in definition.items() } if is_background: func_name = 'background' else: func_name = step_container.name run_steps = make_function( source=source, context=context, source_file=step_container.filename, name=func_name, ) if not is_background: run_steps = CALLBACK_REGISTRY.wrap('example', run_steps, step_container, outline, steps) return run_steps
def make_background(cls, background): """ Construct a method running the background steps. """ if background is None: result = make_function('def background(self): pass') else: result = cls.make_steps(background, background.steps, is_background=True) return result
def make_examples(cls, scenario, index): """ Construct methods for running all the examples of a scenario. index is the 1-based number of the scenario in the feature. """ if scenario.outlines: for i, (outline, steps) in enumerate(scenario.evaluated, 1): # Create a function calling the real scenario example to show # the right location in the outline source = """ def run_example(self): outline(self) """ source = ast.parse(source) # Set location of the call for node in ast.walk(source.body[0].body[0]): node.lineno = outline.line context = { 'outline': cls.make_steps(scenario, steps, is_background=False, outline=outline) } yield cls.make_example( make_function( source=source, context=context, source_file=scenario.feature.filename, name='{}: Example {}'.format(scenario.name, i), ), scenario, index, ) else: yield cls.make_example( cls.make_steps( scenario, scenario.steps, is_background=False, ), scenario, index, )
def test_functions(self): """Test generating functions.""" def inner(val): """A function to pass to the generated one.""" return 2 * val adder = make_function( 'def add(x, y): return inner(x) + y', context={ 'inner': inner, }, name='adder', ) self.assertEqual(adder(10, 3), 23) self.assertEqual(adder.__name__, 'adder')
def make_scenario(cls, scenario, index): """ Construct a method running the scenario steps. index is the 1-based number of the scenario in the feature. """ if scenario.outlines: source = 'def run_outlines(self):\n' + '\n'.join( ' outline{i}(self)'.format(i=i) for i in range(len(scenario.outlines)) ) source = ast.parse(source) # Set locations of the steps for outline, outline_call in \ zip(scenario.outlines, source.body[0].body): for node in ast.walk(outline_call): node.lineno = outline.line context = { 'outline' + str(i): cls.make_steps(scenario, steps, is_background=False, outline=outline) for i, (outline, steps) in enumerate(scenario.evaluated) } result = make_function( source=source, context=context, source_file=scenario.feature.filename, name=scenario.name, ) else: result = cls.make_steps(scenario, scenario.steps, is_background=False) result.is_scenario = True result.scenario = scenario result.scenario_index = index for tag in scenario.tags: result = attr(tag)(result) return result
def make_scenario(cls, scenario, index): """ Construct a method running the scenario steps. index is the 1-based number of the scenario in the feature. """ if scenario.outlines: source = 'def run_outlines(self):\n' + '\n'.join( ' outline{i}(self)'.format(i=i) for i in range(len(scenario.outlines)) ) context = { 'outline' + str(i): cls.make_steps(scenario, steps, is_background=False, outline=outline) for i, (outline, steps) in enumerate(scenario.evaluated) } # TODO: Line numbers of the outline lines aren't preserved, because # the Outline tokens don't store the information result = make_function( source=source, context=context, source_file=scenario.feature.described_at.file, name=scenario.name, ) else: result = cls.make_steps(scenario, scenario.steps, is_background=False) result.is_scenario = True result.scenario_index = index return result
def make_scenario(cls, scenario, index): """ Construct a method running the scenario steps. index is the 1-based number of the scenario in the feature. """ if scenario.outlines: source = 'def run_outlines(self):\n' + '\n'.join( ' outline{i}(self)'.format(i=i) for i in range(len(scenario.outlines))) context = { 'outline' + str(i): cls.make_steps(scenario, steps, is_background=False, outline=outline) for i, (outline, steps) in enumerate(scenario.evaluated) } # TODO: Line numbers of the outline lines aren't preserved, because # the Outline tokens don't store the information result = make_function( source=source, context=context, source_file=scenario.feature.described_at.file, name=scenario.name, ) else: result = cls.make_steps(scenario, scenario.steps, is_background=False) result.is_scenario = True result.scenario_index = index return result
def make_steps(cls, step_container, steps, is_background, outline=None): """ Construct either a scenario or a background calling the specified steps. The method will have debugging information corresponding to the lines in the feature file. """ assert steps step_definitions = [cls.prepare_step(step) for step in steps] source = 'def run_steps(self):\n' if not is_background: source += ' self.background()\n' source += '\n'.join( # This has to be a single statement, in order to set its source # location as a whole below """ try: step{i}.test = self func{i}(step{i}, *args{i}, **kwargs{i}) finally: step{i}.test = None """.format(i=i) for i in range(len(step_definitions))) source = ast.parse(source) # Set locations of the steps step_source = source.body[0].body if not is_background: # There is no source for the background() call step_source = step_source[1:] for step, step_call in zip(steps, step_source): for node in ast.walk(step_call): node.lineno = step.line # Supply all the step functions and arguments context = { k + str(i): v for i, definition in enumerate(step_definitions) for k, v in definition.items() } if is_background: func_name = 'background' else: func_name = step_container.name run_steps = make_function( source=source, context=context, source_file=step_container.filename, name=func_name, ) if not is_background: run_steps = CALLBACK_REGISTRY.wrap('example', run_steps, step_container, outline, steps) return run_steps
def make_examples(cls, scenario, index): """ Construct methods for running all the examples of a scenario. index is the 1-based number of the scenario in the feature. """ if scenario.outlines: outline_example = [] for i, (outline, steps) in enumerate(scenario.evaluated, 1): # Create a function calling the real scenario example to show # the right location in the outline source = """ def run_example(self): outline(self) """ source = ast.parse(source) # Set location of the call for node in ast.walk(source.body[0].body[0]): node.lineno = outline.line context = { 'outline': cls.make_steps(scenario, steps, is_background=False, is_outline=True, outline=outline) } outline_example.append( cls.make_example( make_function( source=source, context=context, source_file=scenario.feature.filename, name='{}: Example {}'.format(scenario.name, i), ), scenario, index, )) source = """ def run_example(self): for outline in outlines: outline(self) """ context = {'outlines': outline_example} yield (cls.make_example( CALLBACK_REGISTRY.wrap( 'example', make_function( source=source, context=context, source_file=scenario.feature.filename, name='{}: Example {}'.format(scenario.name, index), ), scenario, None, scenario.steps), scenario, index, )) if scenario.keyword == 'Scenario Outline': pass elif scenario.outline_header is None: yield cls.make_example( cls.make_steps( scenario, scenario.steps, is_background=False, ), scenario, index, )