def test_not_passing_rules_with_import_from() -> None: """ Test result with a set rules that accept files. """ # Given dep_rules = { "module": [ModuleWildcard("module%"), ModuleWildcard("amodule.submodule")] } configuration = Configuration(dep_rules) source_file = SourceFile( Module("module"), SourceCode("from amodule import othermodule, submodule\n") ) report_printer = Mock() use_case = CheckDependenciesUC( configuration, report_printer, PARSER, iter([source_file]) ) # When with pytest.raises(ForbiddenDepencyError): use_case.run() # Then assert set(report_printer.print_report.call_args[0][0]) == set( ( DependencyError( Module("module"), Module("amodule.othermodule"), tuple(sorted(dep_rules["module"])), ), ) )
def test_fold_module(source_files) -> None: """ Test result with a set source files and a module to fold. """ # Given conf_graph = {"fold_modules": ["amodule"]} drawer = Mock() use_case = DrawGraphUC(drawer, PARSER, source_files, conf_graph) # When use_case.run() # Then drawer.write.assert_called() # type: ignore global_dep = drawer.write.call_args[0][0] assert global_dep == { "simple_module": set(( Dependency(Module("module")), Dependency(Module("module.inside.module")), Dependency(Module("amodule")), )), "amodule": set((Dependency(Module("module")), Dependency(Module("module.inside.module")))), }
def test_empty_rules(source_files) -> None: """ Test result with no rule given. """ # Given configuration = Configuration() report_printer = Mock() use_case = CheckDependenciesUC(configuration, report_printer, PARSER, source_files) # When with pytest.raises(ForbiddenDepencyError): use_case.run() # Then assert set(report_printer.print_report.call_args[0][0]) == set( ( DependencyError(SIMPLE_FILE.module, Module("module"), tuple()), DependencyError(SIMPLE_FILE.module, Module("module.inside.module"), tuple()), DependencyError(SIMPLE_FILE.module, Module("amodule.aclass"), tuple()), DependencyError(FILE_WITH_LOCAL_IMPORT.module, Module("module"), tuple()), DependencyError(FILE_WITH_LOCAL_IMPORT.module, Module("module.inside.module"), tuple()), DependencyError(FILE_WITH_LOCAL_IMPORT.module, Module("amodule.aclass"), tuple()), DependencyError(FILE_WITH_LOCAL_IMPORT.module, Module("amodule.inside.aclass"), tuple()), DependencyError(FILE_WITH_STD_IMPORT.module, Module("module"), tuple()), DependencyError(FILE_WITH_STD_IMPORT.module, Module("module.inside.module"), tuple()), ))
def test_nested_module() -> None: """ Test nested case. """ # Given module = Module("toto.tata") # When parent = get_parent(module) # Then assert parent == Module("toto")
def test_long_nested_module() -> None: """ Test long nested case. """ # Given module = Module("toto.titi.tete.tata") # When parent = get_parent(module) # Then assert parent == Module("toto.titi.tete")
def test_not_passing_rules(source_files) -> None: """ Test result with a set rules that not accept files. """ # Given dep_rules = { "simple_module": [ModuleWildcard("module.*"), ModuleWildcard("amodule")], "amodule.local_module": [ ModuleWildcard("module"), ModuleWildcard("module.inside.*"), ModuleWildcard("amod"), ], "amodule.std_module": [ModuleWildcard("mod")], } configuration = Configuration(dependency_rules=dep_rules) report_printer = Mock() use_case = CheckDependenciesUC(configuration, report_printer, PARSER, source_files) # When with pytest.raises(ForbiddenDepencyError): use_case.run() # Then simple = SIMPLE_FILE.module local = FILE_WITH_LOCAL_IMPORT.module std = FILE_WITH_STD_IMPORT.module assert set(report_printer.print_report.call_args[0][0]) == set( ( DependencyError( simple, Module("module"), tuple(sorted(dep_rules["simple_module"])) ), DependencyError( local, Module("amodule.aclass"), tuple(sorted(dep_rules["amodule.local_module"])), ), DependencyError( local, Module("amodule.inside.aclass"), tuple(sorted(dep_rules["amodule.local_module"])), ), DependencyError( std, Module("module"), tuple(sorted(dep_rules["amodule.std_module"])) ), DependencyError( std, Module("module.inside.module"), tuple(sorted(dep_rules["amodule.std_module"])), ), ) )
def test_simple_module() -> None: """ Test simple case. """ # Given module = Module("toto") # When parent = get_parent(module) # Then assert parent == Module("")
def test_pop_empty_module_from_dependencies(source_files) -> None: # Given drawer = Mock() config = {"hide_modules": ["module"]} use_case = DrawGraphUC(drawer, PARSER, source_files, config) # When use_case.run() # Then drawer.write.assert_called_with({ "simple_module": set((Dependency(Module("amodule")), )), "amodule.local_module": set((Dependency(Module("amodule")), Dependency(Module("amodule.inside")))), })
def test_fold_dep() -> None: """ Test result of _fold_dep function with an empty fold module """ # Given global_dep = GLOBAL_DEPENDENCIES fold_module = Module("amodule") # When global_dep = _fold_dep(global_dep, fold_module) # Then assert global_dep == { "simple_module": set(( Dependency(Module("module")), Dependency(Module("module.inside.module")), Dependency(Module("amodule")), )), "amodule": set(( Dependency(Module("module")), Dependency(Module("module.inside.module")), Dependency(Module("amodule")), )), }
def test_hide_nominal(source_files) -> None: # Given drawer = Mock() config = {"hide_modules": ["amodule"]} use_case = DrawGraphUC(drawer, PARSER, source_files, config) # When use_case.run() # Then drawer.write.assert_called() # type: ignore global_dep = drawer.write.call_args[0][0] assert global_dep == { "simple_module": set((Dependency(Module("module")), Dependency(Module("module.inside.module")))) }
def visit(self, node: Any) -> None: modules: FrozenSet[Dependency] = frozenset() if isinstance(node, ast.Import): modules = frozenset( Dependency(Module(alias.name)) for alias in node.names) elif isinstance(node, ast.ImportFrom): module = Module(node.module or "") if node.level: parent_module = ".".join( self.current_module_parts[:-node.level]) if node.module: module = Module("{}.{}".format(parent_module, node.module)) else: module = Module(parent_module) modules = frozenset((Dependency(module), )) self._dependencies |= modules super().visit(node)
def test_multi_imports_after_from() -> None: # Given module = Module("module.toto") source_file = SourceFile( module=module, code=SourceCode( "from module import amodule, othermodule, moduleagain"), ) # When dependencies = get_import_from_dependencies(source_file, PARSER) # Then assert dependencies == set((Dependency( Module("module"), frozenset(( Module("amodule"), Module("othermodule"), Module("moduleagain"), )), ), ))
def test_empty() -> None: """ Test empty case. """ # Given module = Module("") # When parent = get_parent(module) # Then assert parent == ""
def test_local_import_case() -> None: """ Test code with local import case. """ # Given module = Module("module.toto") source_file = SourceFile(module=module, code=SourceCode(_LOCAL_CASE)) # When dependencies = get_dependencies(source_file, PARSER) # Then assert dependencies == _LOCAL_RESULT
def test_simple_case() -> None: """ Test simple code case. """ # Given module = Module("toto_program") source_file = SourceFile(module=module, code=SourceCode(_SIMPLE_CASE)) # When dependencies = get_dependencies(source_file, PARSER) # Then assert dependencies == _SIMPLE_RESULT
def test_fold_dep_empty_dict() -> None: """ Test result of _fold_dep function with an empty dictionary """ # Given global_dep = {} fold_module = Module("module") # When global_dep = _fold_dep(global_dep, fold_module) # Then global_dep = {}
def test_fold_dep_empty_module() -> None: """ Test result of _fold_dep function with an empty fold module """ # Given global_dep = GLOBAL_DEPENDENCIES fold_module = Module("") # When global_dep = _fold_dep(global_dep, fold_module) # Then global_dep = GLOBAL_DEPENDENCIES
def test_empty() -> None: """ Test empty code case. """ # Given module = Module("") source_code = SourceCode("") source_file = SourceFile(module=module, code=source_code) # When dependencies = get_dependencies(source_file, PARSER) # Then assert dependencies == frozenset()
def test_empty() -> None: """ Test empty rules case. """ # Given dependency = Dependency(Module("toto")) authorized_modules: List[ModuleWildcard] = [] # When with raises(NotAllowedDependencyException) as error: check_dependency(PARSER, dependency, authorized_modules) # Then assert error assert error.value.dependency == dependency.main_import assert error.value.authorized_modules == authorized_modules
def check_import_from_dependency(parser: IParser, dependency: Dependency, rules: Rules) -> Rules: used_rules: Rules = set() for import_module in dependency.sub_imports: used_rule = None for module, rule in rules: if re.match( "{}$".format(parser.wildcard_to_regex(rule)), f"{dependency.main_import}.{import_module}", ): used_rule = (module, rule) used_rules.add(used_rule) if not used_rule: raise NotAllowedDependencyException( Module(f"{dependency.main_import}.{import_module}"), [r for _, r in rules], ) return used_rules
def test_not_passing_case() -> None: """ Test a not passing case. """ # Given dependency = Dependency(Module("toto.tata")) rules: Rules = [ (ModuleWildcard("toto.*"), ModuleWildcard("toto")), (ModuleWildcard("toto.*"), ModuleWildcard("te.*")), (ModuleWildcard("toto.*"), ModuleWildcard("titi\\.tata")), ] # When with raises(NotAllowedDependencyException) as error: check_dependency(PARSER, dependency, rules) # Then assert error assert error.value.dependency == dependency.main_import assert error.value.authorized_modules == [r for _, r in rules]
def test_passing_case() -> None: """ Test a passing case. """ # Given dependency = Dependency(Module("toto")) rules: Rules = [ (ModuleWildcard("toto"), ModuleWildcard("to*")), (ModuleWildcard("toto"), ModuleWildcard("titi.tata")), ] # When error = None try: check_dependency(PARSER, dependency, rules) except NotAllowedDependencyException as exception: error = exception # Then assert not error
def source_file_iterator(root_dir: str, file_extension: str) -> Iterator[SourceFile]: """ Iterator of all python source files in a directory. """ if file_extension == "py": project_root = _get_python_project_root(root_dir) separator = "." elif file_extension == "go": project_root = "" separator = "/" with _change_dir(root_dir): for file_path in Path(".").rglob(f"*.{file_extension}"): with open(str(file_path), "r") as stream: content = stream.read() yield SourceFile( Module(project_root + _get_module_from_file_path(file_path, separator)), SourceCode(content), )
def run(self) -> None: global_dependencies: GlobalDependencies = {} for source_file in self.source_files: module = Module(source_file.module.replace(".__init__", "")) dependencies = get_dependencies(source_file, self.parser) dependencies = self.std_lib_filter.filter(dependencies) global_dependencies[module] = dependencies global_dependencies = self._hide(global_dependencies) for fold_module in self.config.get("fold_modules", []): global_dependencies = _fold_dep(global_dependencies, fold_module) # To avoid a module to point itself, and make the graph more readable for module, deps in list(global_dependencies.items()): deps -= {dep for dep in deps if dep.main_import == module} if not global_dependencies[module]: global_dependencies.pop(module, None) self.drawer.write(global_dependencies)
def test_nominal() -> None: source_file = SourceFile( Module("string.string"), SourceCode("""package main import "fmt" import ( "go/parser" "go/module" "othermodule" ) import "amodule" """), ) assert get_dependencies(source_file, PARSER) == { Dependency("fmt"), Dependency("go/parser"), Dependency("go/module"), Dependency("othermodule"), Dependency("amodule"), }
def test_passing_rules_with_import_from() -> None: """ Test result with a set rules that accept files. """ # Given configuration = Configuration( dependency_rules={ "module": [ModuleWildcard("module%"), ModuleWildcard("amodule.submodule")] } ) source_file = SourceFile( Module("module"), SourceCode("from amodule import submodule") ) report_printer = Mock() use_case = CheckDependenciesUC( configuration, report_printer, PARSER, iter([source_file]) ) # When use_case.run() # Then assert report_printer.print_report.call_args[0][0] == []
def _get_module_from_file_path(path: Path, separator: str) -> Module: """ Transform a filename into a corresponding python module. """ path_without_extention = path.parents[0] / path.stem return Module(separator.join(path_without_extention.parts))
""" Tests about get_dependencies function. """ import re from dep_check.dependency_finder import get_dependencies, get_import_from_dependencies from dep_check.infra.python_parser import PythonParser from dep_check.models import Dependency, Module, ModuleWildcard, SourceCode, SourceFile _SIMPLE_CASE = """ import simple import module.inside.module from amodule import aclass """ _SIMPLE_RESULT = frozenset(( Dependency(Module("simple")), Dependency(Module("amodule")), Dependency(Module("module.inside.module")), )) _SIMPLE_RESULT_IMPORT_FROM = frozenset(( Dependency(Module("simple")), Dependency(Module("amodule"), frozenset((Module("aclass"), ))), Dependency(Module("module.inside.module")), )) _LOCAL_CASE = """ import simple from . import aclass from .inside.module import aclass """ _LOCAL_RESULT = set((
from dep_check.models import Dependency, Module, SourceCode, SourceFile SIMPLE_FILE = SourceFile( module=Module("simple_module"), code=SourceCode( """ import module import module.inside.module from amodule import aclass """ ), ) FILE_WITH_LOCAL_IMPORT = SourceFile( module=Module("amodule.local_module"), code=SourceCode( """ import module import module.inside.module from . import aclass from .inside import aclass """ ), ) FILE_WITH_STD_IMPORT = SourceFile( module=Module("amodule.std_module"), code=SourceCode( """ import module import module.inside.module import itertools from abc import ABC