def check_process_pipes_are_pure( cls, pipeline_classdef: ast.ClassDef, ast_tree: ast.Module, pyfilepath: str, ) -> Generator[Tuple[int, int, str], None, None]: for pipe_funcdef in get_all_pipes_from(pipeline_classdef): if not has_any_decorator(pipe_funcdef, {cls.PROCESS_DECORATOR_NAME}): continue set_parents(ast_tree) is_pure, errors = is_function_pure( pipe_funcdef, ast_tree, pyfilepath=pyfilepath, with_errors=True, recursive=True, ) if not is_pure: yield ( pipe_funcdef.lineno, pipe_funcdef.col_offset, (f'SME004 Pipe {pipe_funcdef.name} is of process type and is not ' f'pure ({", ".join(errors)})'), )
def get_not_pure_internal_calls( funcdef_node: AnyFuncdef, file_ast_tree: ast.Module, pyfilepath: str, ) -> List[str]: from mr_proper.public_api import is_function_pure not_pure_calls: List[str] = [] for call_node in get_nodes_from_funcdef_body(funcdef_node, [ast.Call]): if not isinstance(call_node.func, ast.Name): continue # recursively check only functions calls, not methods/attrs calls if ( call_node.func.id.lower() == call_node.func.id # check for only snake_case calls and call_node.func.id not in BUILTINS_LIST ): import_info = get_name_import_path(call_node.func, pyfilepath) imported_funcdef_node = get_funcdef_by(import_info) if import_info else None if imported_funcdef_node and import_info: filepath = cast(str, import_info['file_path']) is_call_clean: Optional[bool] = is_function_pure( imported_funcdef_node, file_ast_tree=file_ast_tree, recursive=False, pyfilepath=filepath, with_errors=False, ) else: is_call_clean = None if is_call_clean is False: not_pure_calls.append(call_node.func.id) return not_pure_calls
def test_ok_for_destructive_assignment(): funcdef = ast.parse(""" def foo(a): b, c = a return b * c """.strip()).body[0] assert is_function_pure(funcdef)
def check_purity_of_functions(func_def: AnyFuncdef) -> Tuple[int, int, str]: if 'pure' in func_def.name.split('_') and not is_function_pure(func_def): return ( func_def.lineno, func_def.col_offset, f'CFQ003 Function "{func_def.name}" is not pure.', )
def test_is_function_pure_fail_case_for_recursive(): test_file_path = os.path.join(os.path.dirname(__file__), 'test_files/test.py') ast_tree = get_ast_tree(test_file_path) foo_node = ast_tree.body[0] assert not is_function_pure( foo_node, file_ast_tree=ast_tree, pyfilepath=test_file_path, recursive=True, )
def check_file(path_to_file: str, recursive: bool) -> None: ast_tree = get_ast_tree(path_to_file) if not ast_tree: sys.stdout.write('Error parsing ast tree\n') return sys.stdout.write(f'{path_to_file}:\n') for funcdef_node in get_all_funcdefs_from(ast_tree): function_name = funcdef_node.name is_pure, pureness_errors = is_function_pure( funcdef_node, ast_tree, with_errors=True, recursive=recursive, pyfilepath=path_to_file, ) if is_pure: sys.stdout.write(f'{function_name} is pure!\n') else: sys.stdout.write(f'{function_name} is not pure because of:\n') for error in pureness_errors: sys.stdout.write(f'\t{error}\n')
def test_is_function_pure_fail_case(): funcdef = ast.parse(""" def foo(a): print(a) """.strip()).body[0] assert not is_function_pure(funcdef)