def _detect_fns_free_variables(self, source_code: str, imports_and_functions: str = "", step_parameters: dict = None): """Return the function's free variables. Free variable: _If a variable is used in a code block but not defined there, it is a free variable._ An Example: ``` x = 5 def foo(): print(x) ``` In the example above, `x` is a free variable for function `foo`, because it is defined outside of the context of `foo`. Here we run the PyFlakes report over the function body to get all the missing names (i.e. free variables), excluding the function arguments. Args: source_code: Multiline Python source code imports_and_functions: Multiline Python source that is prepended to every pipeline step. It should contain the code cells that where tagged as `import` and `functions`. We prepend this code to the function body because it will always be present in any pipeline step. step_parameters: Step parameters names. The step parameters are removed from the pyflakes report, as these names will always be available in the step's context. Returns (dict): A dictionary with the name of the function as key and a list of variables names + consumed pipeline parameters as values. """ fns_free_vars = dict() # now check the functions' bodies for free variables. fns is a # dict function_name -> function_source fns = astutils.parse_functions(source_code) for fn_name, fn in fns.items(): code = imports_and_functions + "\n" + fn free_vars = flakeutils.pyflakes_report(code=code) # the pipeline parameters that are used in the function consumed_params = {} if step_parameters: consumed_params = free_vars.intersection( step_parameters.keys()) # remove the used parameters form the free variables, as they # need to be handled differently. free_vars.difference_update(consumed_params) fns_free_vars[fn_name] = (free_vars, consumed_params) return fns_free_vars
def test_parse_functions(): """Test that the body of the function is retrieved correctly.""" code = ''' x = 5 def foo(): print('hello') print(x) ''' target = {"foo": "def foo():\n print('hello')\n print(x)\n"} assert kale_ast.parse_functions(code) == target