Example #1
0
    def test_hash(self):
        a = Module("foo.bar")
        b = Module("foo.bar")
        c = Module("foo.bar.baz")

        assert hash(a) == hash(b)
        assert hash(a) != hash(c)
Example #2
0
def test_ignores_hidden_directories():
    module_finder = ModuleFinder()

    file_system = FakeFileSystem(
        contents="""
                /path/to/mypackage/
                    __init__.py
                    two/
                        __init__.py
                        green.py
                    .hidden/
                        green.py
                        orphan/
                            __init__.py
                            red.py
                """
    )

    result = module_finder.find_modules(
        package_name="mypackage",
        package_directory="/path/to/mypackage",
        file_system=file_system,
    )

    expected_modules = {
        Module("mypackage"),
        Module("mypackage.two"),
        Module("mypackage.two.green"),
    }
    assert set(result) == expected_modules
Example #3
0
def test_ignores_orphaned_python_files():
    # Python files in directories that don't contain an __init__.py should not be discovered.
    module_finder = ModuleFinder()

    file_system = FakeFileSystem(
        contents="""
            /path/to/mypackage/
                __init__.py
                two/
                    __init__.py
                    green.py
                noinitpackage/
                    green.py
                    orphan/
                        __init__.py
                        red.py
            """
    )

    result = module_finder.find_modules(
        package_name="mypackage",
        package_directory="/path/to/mypackage",
        file_system=file_system,
    )

    expected_modules = {
        Module("mypackage"),
        Module("mypackage.two"),
        Module("mypackage.two.green"),
    }
    assert set(result) == expected_modules
Example #4
0
 def test_repr(self):
     import_path = DirectImport(
         importer=Module("foo"),
         imported=Module("bar"),
         line_number=10,
         line_contents="import bar",
     )
     assert repr(import_path) == "<DirectImport: foo -> bar (l. 10)>"
Example #5
0
    def determine_imported_modules(
        self, include_external_packages: bool
    ) -> Set[Module]:
        imported_modules: Set[Module] = set()
        assert isinstance(self.node, self.node_class)  # For type checker.
        assert isinstance(self.node.level, int)  # For type checker.

        if self.node.level == 0:
            # Absolute import.
            # Let the type checker know we expect node.module to be set here.
            assert isinstance(self.node.module, str)
            node_module = Module(self.node.module)
            if not self._is_internal_module(node_module):
                if include_external_packages:
                    # Just return the top level package of the external module.
                    return {Module(node_module.package_name)}
                else:
                    return set()
            # Don't include imports of modules outside this package.

            module_base = self.node.module
        elif self.node.level >= 1:
            # Relative import. The level corresponds to how high up the tree it goes;
            # for example 'from ... import foo' would be level 3.
            importing_module_components = self.module.name.split(".")
            # TODO: handle level that is too high.
            # Trim the base module by the number of levels.
            if self.module_is_package:
                # If the scanned module an __init__.py file, we don't want
                # to go up an extra level.
                number_of_levels_to_trim_by = self.node.level - 1
            else:
                number_of_levels_to_trim_by = self.node.level

            if number_of_levels_to_trim_by:
                module_base = ".".join(
                    importing_module_components[:-number_of_levels_to_trim_by]
                )
            else:
                module_base = ".".join(importing_module_components)
            if self.node.module:
                module_base = ".".join([module_base, self.node.module])

        # node.names corresponds to 'a', 'b' and 'c' in 'from x import a, b, c'.
        for alias in self.node.names:
            full_module_name = ".".join([module_base, alias.name])
            try:
                imported_module = self._trim_to_internal_module(
                    untrimmed_module=Module(full_module_name)
                )
            except FileNotFoundError:
                logger.warning(
                    f"Could not find {full_module_name} when scanning {self.module}. "
                    "This may be due to a missing __init__.py file in the parent package."
                )
            else:
                imported_modules.add(imported_module)
        return imported_modules
Example #6
0
    def test_equals(self):
        a = Module("foo.bar")
        b = Module("foo.bar")
        c = Module("foo.bar.baz")

        assert a == b
        assert a != c
        # Also non-Module instances should not be treated as equal.
        assert a != "foo"
Example #7
0
    def find_children(self, module: str) -> Set[str]:
        # It doesn't make sense to find the children of a squashed module, as we don't store
        # the children in the graph.
        if self.is_module_squashed(module):
            raise ValueError("Cannot find children of a squashed module.")

        children = set()
        for potential_child in self.modules:
            if Module(potential_child).is_child_of(Module(module)):
                children.add(potential_child)
        return children
Example #8
0
    def find_descendants(self, module: str) -> Set[str]:
        # It doesn't make sense to find the descendants of a squashed module, as we don't store
        # the descendants in the graph.
        if self.is_module_squashed(module):
            raise ValueError("Cannot find descendants of a squashed module.")

        descendants = set()
        for potential_descendant in self.modules:
            if Module(potential_descendant).is_descendant_of(Module(module)):
                descendants.add(potential_descendant)
        return descendants
Example #9
0
def test_trims_to_known_modules(import_source):
    all_modules = {
        Module("foo"),
        Module("foo.one"),
        Module("foo.two"),
        Module("foo.two.yellow"),
    }
    file_system = FakeFileSystem(
        contents="""
                /path/to/foo/
                    __init__.py
                    one.py
                    two/
                        __init__.py
                        yellow.py
            """,
        content_map={"/path/to/foo/one.py": import_source},
    )

    import_scanner = ImportScanner(
        modules_by_package_directory={"/path/to/foo": all_modules},
        file_system=file_system,
    )

    result = import_scanner.scan_for_imports(Module("foo.one"))

    assert result == {
        DirectImport(
            importer=Module("foo.one"),
            imported=Module("foo.two.yellow"),
            line_number=1,
            line_contents=import_source,
        )
    }
Example #10
0
    def determine_imported_modules(
        self, include_external_packages: bool
    ) -> Set[Module]:
        imported_modules: Set[Module] = set()

        assert isinstance(self.node, self.node_class)  # For type checker.
        for alias in self.node.names:
            module_from_alias = Module(alias.name)

            if self._is_internal_module(module_from_alias):
                imported_module = module_from_alias
            else:
                if include_external_packages:
                    imported_module = Module(module_from_alias.package_name)
                else:
                    continue

            imported_modules.add(imported_module)

        return imported_modules
Example #11
0
def test_happy_path():
    module_finder = ModuleFinder()

    file_system = FakeFileSystem(
        contents="""
        /path/to/mypackage/
            __init__.py
            not-a-python-file.txt
            .hidden
            foo/
                __init__.py
                one.py
                two/
                    __init__.py
                    green.py
                    blue.py
        """
    )

    result = module_finder.find_modules(
        package_name="mypackage",
        package_directory="/path/to/mypackage",
        file_system=file_system,
    )

    expected_modules = {
        Module("mypackage"),
        Module("mypackage.foo"),
        Module("mypackage.foo.one"),
        Module("mypackage.foo.two"),
        Module("mypackage.foo.two.green"),
        Module("mypackage.foo.two.blue"),
    }
    assert set(result) == expected_modules
Example #12
0
def test_trims_whitespace_from_start_of_line_contents():
    all_modules = {Module("foo"), Module("foo.one"), Module("foo.two")}
    file_system = FakeFileSystem(
        contents="""
                    /path/to/foo/
                        __init__.py
                        one.py
                        two.py
                """,
        content_map={
            "/path/to/foo/one.py":
            """
            def my_function():
                from . import two
            """
        },
    )

    import_scanner = ImportScanner(
        modules_by_package_directory={"/path/to/foo": all_modules},
        file_system=file_system,
    )

    result = import_scanner.scan_for_imports(Module("foo.one"))

    assert result == {
        DirectImport(
            importer=Module("foo.one"),
            imported=Module("foo.two"),
            line_number=2,
            line_contents="from . import two",
        )
    }
Example #13
0
    def find_modules(self, package_name: str, package_directory: str,
                     file_system: AbstractFileSystem) -> Iterable[Module]:
        self.file_system = file_system

        modules: List[Module] = []

        for module_filename in self._get_python_files_inside_package(
                package_directory):
            module_name = self._module_name_from_filename(
                module_filename, package_directory)
            modules.append(Module(module_name))

        return modules
Example #14
0
def test_absolute_imports(include_external_packages, expected_result):
    all_modules = {Module("foo.one"), Module("foo.two")}
    file_system = FakeFileSystem(
        content_map={
            "/path/to/foo/one.py":
            """
                import foo.two
                import externalone
                import externaltwo.subpackage
                arbitrary_expression = 1
            """
        })

    import_scanner = ImportScanner(
        modules_by_package_directory={"/path/to/foo": all_modules},
        file_system=file_system,
        include_external_packages=include_external_packages,
    )

    result = import_scanner.scan_for_imports(Module("foo.one"))

    assert expected_result == result
Example #15
0
    def _find_ancestor_squashed_module(self, module: str) -> Optional[str]:
        """
        Return the name of a squashed module that is an ancestor of the supplied module, or None
        if no such module exists.
        """
        try:
            parent = Module(module).parent.name
        except ValueError:
            # The module has no more ancestors.
            return None

        if parent in self.modules and self.is_module_squashed(parent):
            return parent
        else:
            return self._find_ancestor_squashed_module(parent)
Example #16
0
    def _trim_to_internal_module(self, untrimmed_module: Module) -> Module:
        """
        Raises FileNotFoundError if it could not find a valid module.
        """
        if untrimmed_module in self.internal_modules:
            return untrimmed_module
        else:
            # The module isn't in the internal modules. This is because it's something *within*
            # a module (e.g. a function): the result of something like 'from .subpackage
            # import my_function'. So we trim the components back to the module.
            components = untrimmed_module.name.split(".")[:-1]
            trimmed_module = Module(".".join(components))

            if trimmed_module in self.internal_modules:
                return trimmed_module
            else:
                raise FileNotFoundError()
Example #17
0
def test_absolute_from_imports(include_external_packages, expected_result):
    all_modules = {
        Module("foo.one.blue"),
        Module("foo.one.green"),
        Module("foo.two.brown"),
        Module("foo.two.yellow"),
        Module("foo.three"),
    }
    file_system = FakeFileSystem(
        contents="""
            /path/to/foo/
                __init__.py
                one/
                    __init__.py
                    blue.py
                    green.py
                two/
                    __init__.py
                    brown.py
                    yellow.py
                three.py
        """,
        content_map={
            "/path/to/foo/one/blue.py":
            """
                from foo.one import green
                from foo.two import yellow
                from foo import three
                from external import one
                from external.two import blue
                arbitrary_expression = 1
            """
        },
    )

    import_scanner = ImportScanner(
        modules_by_package_directory={"/path/to/foo": all_modules},
        file_system=file_system,
        include_external_packages=include_external_packages,
    )

    result = import_scanner.scan_for_imports(Module("foo.one.blue"))

    assert expected_result == result
Example #18
0
 def test_package_name(self):
     assert Module("foo.bar.baz").package_name == "foo"
Example #19
0
def test_scans_multiple_packages(statement):
    foo_modules = {Module("foo"), Module("foo.one"), Module("foo.two")}
    bar_modules = {Module("bar"), Module("bar.green"), Module("bar.blue")}
    file_system = FakeFileSystem(
        content_map={
            "/path/to/foo/one.py":
            f"""
                import foo.two
                {statement}
                import externalone

                arbitrary_expression = 1
            """
        })

    import_scanner = ImportScanner(
        modules_by_package_directory={
            "/path/to/foo": foo_modules,
            "/path/to/bar": bar_modules,
        },
        file_system=file_system,
    )

    result = import_scanner.scan_for_imports(Module("foo.one"))

    assert {
        DirectImport(
            importer=Module("foo.one"),
            imported=Module("foo.two"),
            line_number=1,
            line_contents="import foo.two",
        ),
        DirectImport(
            importer=Module("foo.one"),
            imported=Module("bar.blue"),
            line_number=2,
            line_contents=statement,
        ),
    } == result
Example #20
0
    def test_equals(self):
        a = DirectImport(
            importer=Module("foo"),
            imported=Module("bar"),
            line_number=10,
            line_contents="import bar",
        )
        b = DirectImport(
            importer=Module("foo"),
            imported=Module("bar"),
            line_number=10,
            line_contents="import bar",
        )
        c = DirectImport(
            importer=Module("foo"),
            imported=Module("baz"),
            line_number=10,
            line_contents="import bar",
        )
        d = DirectImport(
            importer=Module("foobar"),
            imported=Module("bar"),
            line_number=10,
            line_contents="import bar",
        )
        e = DirectImport(
            importer=Module("foo"),
            imported=Module("bar"),
            line_number=11,
            line_contents="import bar",
        )
        f = DirectImport(
            importer=Module("foo"),
            imported=Module("bar"),
            line_number=10,
            line_contents="from . import bar",
        )

        assert a == b
        assert a != c
        assert a != d
        assert a != e
        assert a != f
        # Also non-DirectImport instances should not be treated as equal.
        assert a != "foo"
Example #21
0
def test_trims_to_known_modules_within_init_file():
    all_modules = {
        Module("foo"),
        Module("foo.one"),
        Module("foo.one.yellow"),
        Module("foo.one.blue"),
        Module("foo.one.blue.alpha"),
    }
    file_system = FakeFileSystem(
        contents="""
                /path/to/foo/
                    __init__.py
                    one/
                        __init__.py
                        yellow.py
                        blue/
                            __init__.py
                            alpha.py
            """,
        content_map={
            "/path/to/foo/one/__init__.py": "from .yellow import my_function",
            "/path/to/foo/one/blue/__init__.py":
            "from .alpha import my_function",
        },
    )

    import_scanner = ImportScanner(
        modules_by_package_directory={"/path/to/foo": all_modules},
        file_system=file_system,
    )

    result = import_scanner.scan_for_imports(Module("foo.one"))

    assert result == {
        DirectImport(
            importer=Module("foo.one"),
            imported=Module("foo.one.yellow"),
            line_number=1,
            line_contents="from .yellow import my_function",
        )
    }

    result = import_scanner.scan_for_imports(Module("foo.one.blue"))

    assert result == {
        DirectImport(
            importer=Module("foo.one.blue"),
            imported=Module("foo.one.blue.alpha"),
            line_number=1,
            line_contents="from .alpha import my_function",
        )
    }
Example #22
0
    def test_hash(self):
        a = DirectImport(
            importer=Module("foo"),
            imported=Module("bar"),
            line_number=10,
            line_contents="import bar",
        )
        b = DirectImport(
            importer=Module("foo"),
            imported=Module("bar"),
            line_number=10,
            line_contents="import bar",
        )
        c = DirectImport(
            importer=Module("foo"),
            imported=Module("baz"),
            line_number=10,
            line_contents="import bar",
        )
        d = DirectImport(
            importer=Module("foobar"),
            imported=Module("bar"),
            line_number=10,
            line_contents="import bar",
        )
        e = DirectImport(
            importer=Module("foo"),
            imported=Module("bar"),
            line_number=11,
            line_contents="import bar",
        )
        f = DirectImport(
            importer=Module("foo"),
            imported=Module("bar"),
            line_number=10,
            line_contents="from . import bar",
        )

        assert hash(a) == hash(b)
        assert hash(a) != hash(c)
        assert hash(a) != hash(d)
        assert hash(a) != hash(e)
        assert hash(a) != hash(f)
Example #23
0
def test_relative_from_imports():
    all_modules = {
        Module("foo.one.blue"),
        Module("foo.one.green"),
        Module("foo.two.brown"),
        Module("foo.two.yellow"),
        Module("foo.three"),
    }
    file_system = FakeFileSystem(
        contents="""
            /path/to/foo/
                __init__.py
                one/
                    __init__.py
                    blue.py
                    green.py
                two/
                    __init__.py
                    brown.py
                    yellow.py
                three.py
        """,
        content_map={
            "/path/to/foo/one/blue.py":
            """
                from . import green
                from ..two import yellow
                from .. import three
                arbitrary_expression = 1
            """
        },
    )

    import_scanner = ImportScanner(
        modules_by_package_directory={"/path/to/foo": all_modules},
        file_system=file_system,
    )

    result = import_scanner.scan_for_imports(Module("foo.one.blue"))

    assert result == {
        DirectImport(
            importer=Module("foo.one.blue"),
            imported=Module("foo.one.green"),
            line_number=1,
            line_contents="from . import green",
        ),
        DirectImport(
            importer=Module("foo.one.blue"),
            imported=Module("foo.two.yellow"),
            line_number=2,
            line_contents="from ..two import yellow",
        ),
        DirectImport(
            importer=Module("foo.one.blue"),
            imported=Module("foo.three"),
            line_number=3,
            line_contents="from .. import three",
        ),
    }
Example #24
0
import pytest  # type: ignore
from grimp.adaptors.importscanner import ImportScanner
from grimp.domain.valueobjects import DirectImport, Module

from tests.adaptors.filesystem import FakeFileSystem


@pytest.mark.parametrize(
    "include_external_packages, expected_result",
    (
        (
            False,
            {
                DirectImport(
                    importer=Module("foo.one"),
                    imported=Module("foo.two"),
                    line_number=1,
                    line_contents="import foo.two",
                )
            },
        ),
        (
            True,
            {
                DirectImport(
                    importer=Module("foo.one"),
                    imported=Module("foo.two"),
                    line_number=1,
                    line_contents="import foo.two",
                ),
                DirectImport(
Example #25
0
 def test_repr(self):
     module = Module("foo.bar")
     assert repr(module) == "<Module: foo.bar>"