Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 7 additions & 3 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,10 @@ namespaces = false

[tool.ruff]
line-length = 99
exclude = ['tests/assets'] # These are sample packages for tests to run under - we don't want ruff to mess with them.
[tool.ruff.lint]
select = ["E4", "E7", "E9", "F", "UP"]
exclude = [
'tests/assets', # These are sample packages for tests to run under - we don't want ruff to mess with them.
'docs/', # Documentation configuration scripts
]
lint.select = ["A", "C4", "E4", "E7", "E9", "F", "FLY", "FURB", "INP", "PLE", "PLR", "PT", "RUF", "SIM", "UP"]
lint.ignore = ["C408", "PLR2004", "PLR0913", "PT006", "PT007", "SIM108", "SIM118", "SIM300"]
lint.per-file-ignores."tests/**/*.py" = [ "RUF012", "RUF059" ]
4 changes: 2 additions & 2 deletions src/grimp/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,13 @@
from .main import build_graph

__all__ = [
"Module",
"DetailedImport",
"DirectImport",
"Import",
"ImportGraph",
"Layer",
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is sorting __all__ alphabetically

"Module",
"PackageDependency",
"Route",
"build_graph",
"Layer",
]
7 changes: 4 additions & 3 deletions src/grimp/adaptors/modulefinder.py
Original file line number Diff line number Diff line change
Expand Up @@ -147,9 +147,10 @@ def _module_name_from_filename(
"""
internal_filename_and_path = filename_and_path[len(package_directory) :]
internal_filename_and_path_without_extension = internal_filename_and_path[1:-3]
components = [package_name] + internal_filename_and_path_without_extension.split(
self.file_system.sep
)
components = [
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This unpacking syntax is a stylistic choice. Personally I like it but feel free to disagree - I'll revert and ignore the rule.

package_name,
*internal_filename_and_path_without_extension.split(self.file_system.sep),
]
if components[-1] == "__init__":
components.pop()
return ".".join(components)
Expand Down
11 changes: 8 additions & 3 deletions src/grimp/adaptors/packagefinder.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,14 @@ def determine_package_directories(
logger.debug(f"sys.path: {sys.path}")
raise ValueError(f"Could not find package '{package_name}' in your Python path.")

if spec.has_location and spec.origin:
if not self._is_a_package(spec, file_system) or self._has_a_non_namespace_parent(spec):
raise exceptions.NotATopLevelModule
if (
spec.has_location
and spec.origin
and (
not self._is_a_package(spec, file_system) or self._has_a_non_namespace_parent(spec)
)
):
raise exceptions.NotATopLevelModule

assert spec.submodule_search_locations # This should be the case if spec.has_location.
return set(spec.submodule_search_locations)
Expand Down
2 changes: 1 addition & 1 deletion src/grimp/application/usecases.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ def build_graph(

found_packages = _find_packages(
file_system=file_system,
package_names=[package_name] + list(additional_package_names),
package_names=[package_name, *additional_package_names],
)

imports_by_module = _scan_packages(
Expand Down
19 changes: 3 additions & 16 deletions tests/benchmarking/test_benchmarking.py
Original file line number Diff line number Diff line change
Expand Up @@ -54,18 +54,12 @@ def large_graph():
heads=frozenset({"mypackage.domain.7960519247.6215972208"}),
middle=(),
tails=frozenset(
{
"mypackage.application.7537183614.6928774480.5676105139.3275676604"
# noqa:E501
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The latest version of ruff doesn't consider these to be "line-too-long" violations so these rule codes can be removed.

}
{"mypackage.application.7537183614.6928774480.5676105139.3275676604"}
),
),
Route(
heads=frozenset(
{
"mypackage.domain.6928774480.5676105139.1330171288.7588443317.4661445087"
# noqa:E501
}
{"mypackage.domain.6928774480.5676105139.1330171288.7588443317.4661445087"}
),
middle=(),
tails=frozenset({"mypackage.application.7537183614.3430454356.1518604543"}),
Expand All @@ -84,10 +78,7 @@ def large_graph():
),
Route(
heads=frozenset(
{
"mypackage.domain.6928774480.1028759677.7960519247.2888779155.7486857426"
# noqa:E501
}
{"mypackage.domain.6928774480.1028759677.7960519247.2888779155.7486857426"}
),
middle=(),
tails=frozenset({"mypackage.application.7537183614.3430454356.1518604543"}),
Expand Down Expand Up @@ -139,18 +130,14 @@ def large_graph():
heads=frozenset(
{
"mypackage.application.7537183614.2538372545.1153384736.6297289996",
# noqa:E501
"mypackage.application.7537183614.2538372545.1153384736.6404547812.6297289996",
# noqa:E501
}
),
middle=("mypackage.6398020133.9075581450.6529869526.6297289996",),
tails=frozenset(
{
"mypackage.plugins.5634303718.6180716911.7582995238.1039461003.2943193489",
# noqa:E501
"mypackage.plugins.5634303718.6180716911.7582995238.1039461003.6322703811",
# noqa:E501
}
),
)
Expand Down
9 changes: 5 additions & 4 deletions tests/unit/application/graph/test_chains.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import re
import pytest # type: ignore

from grimp.application.graph import ImportGraph
Expand Down Expand Up @@ -112,14 +113,14 @@ def test_find_shortest_chain_returns_none_if_not_exists(self):
def test_raises_value_error_if_importer_not_present(self):
graph = ImportGraph()

with pytest.raises(ValueError, match="Module foo is not present in the graph."):
with pytest.raises(ValueError, match=re.escape("Module foo is not present in the graph.")):
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

match= arguments are regex (which is easy to forget), so characters like . have special meaning. This can lead to subtle bugs. Better to explicitly escape the string.

graph.find_shortest_chain(importer="foo", imported="bar")

def test_raises_value_error_if_imported_not_present(self):
graph = ImportGraph()
graph.add_module("foo")

with pytest.raises(ValueError, match="Module bar is not present in the graph."):
with pytest.raises(ValueError, match=re.escape("Module bar is not present in the graph.")):
graph.find_shortest_chain(importer="foo", imported="bar")

def test_find_shortest_chain_copes_with_cycle(self):
Expand Down Expand Up @@ -164,7 +165,7 @@ def test_demonstrate_nondeterminism_of_equal_chains(self):

one_chain = (source, a, b, c, destination)
other_chain = (source, d, e, f, destination)
assert (result == one_chain) or (result == other_chain)
assert result in (one_chain, other_chain)
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Stylistic (similar comment to before regarding unpackaging syntax)


@pytest.mark.parametrize(
"as_packages, expected_result",
Expand Down Expand Up @@ -201,7 +202,7 @@ def test_modules_with_shared_descendants_raises_value_error_when_as_packages_tru
graph.add_module(importer)
graph.add_module(imported)

with pytest.raises(ValueError, match="Modules have shared descendants."):
with pytest.raises(ValueError, match=re.escape("Modules have shared descendants.")):
graph.find_shortest_chains(importer=importer, imported=imported, as_packages=True)

@pytest.mark.parametrize(
Expand Down
7 changes: 5 additions & 2 deletions tests/unit/application/graph/test_hierarchy.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import re
import pytest # type: ignore

from grimp.application.graph import ImportGraph
Expand Down Expand Up @@ -33,7 +34,7 @@ def test_find_children_raises_exception_for_squashed_module():

graph.add_module(module, is_squashed=True)

with pytest.raises(ValueError, match="Cannot find children of a squashed module."):
with pytest.raises(ValueError, match=re.escape("Cannot find children of a squashed module.")):
graph.find_children(module)


Expand Down Expand Up @@ -75,7 +76,9 @@ def test_find_descendants_raises_exception_for_squashed_module():

graph.add_module(module, is_squashed=True)

with pytest.raises(ValueError, match="Cannot find descendants of a squashed module."):
with pytest.raises(
ValueError, match=re.escape("Cannot find descendants of a squashed module.")
):
graph.find_descendants(module)


Expand Down
12 changes: 6 additions & 6 deletions tests/unit/application/graph/test_layers.py
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ def test_indirect_illegal_within_one_package(
self, specify_container: bool, start: str, end: str, route_middle: list[str]
):
graph = self._build_legal_graph()
import_pairs = _pairwise([start] + route_middle + [end])
import_pairs = _pairwise([start, *route_middle, end])
for importer, imported in import_pairs:
graph.add_import(importer=importer, imported=imported)

Expand Down Expand Up @@ -342,7 +342,7 @@ def test_demonstrate_nondeterminism_with_equal_length_forked_routes(self):
),
}

assert (result == first_option) or (result == second_option)
assert result in (first_option, second_option)

def _build_legal_graph(self):
graph = ImportGraph()
Expand Down Expand Up @@ -479,7 +479,7 @@ def test_indirect_illegal_within_one_package(
self, specify_container: bool, start: str, end: str, route_middle: list[str]
):
graph = self._build_legal_graph()
import_pairs = _pairwise([start] + route_middle + [end])
import_pairs = _pairwise([start, *route_middle, end])
for importer, imported in import_pairs:
graph.add_import(importer=importer, imported=imported)

Expand Down Expand Up @@ -668,7 +668,7 @@ def test_indirect_illegal_across_two_packages(
self, start: str, end: str, route_middle: list[str]
):
graph = self._build_legal_graph()
import_pairs = _pairwise([start] + route_middle + [end])
import_pairs = _pairwise([start, *route_middle, end])
for importer, imported in import_pairs:
graph.add_import(importer=importer, imported=imported)

Expand Down Expand Up @@ -1104,12 +1104,12 @@ def _build_layers(self, layers: Iterable[Layer]):
modules = []
for layer in layers:
assert len(layer.module_tails) == 1
module = list(layer.module_tails)[0]
module = next(iter(layer.module_tails))
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Stylistic (similar comment to before regarding unpackaging syntax)

It's a slight performance hit to instantiate the list in full, so that's the motivation for this slightly less readable syntax. Perhaps should be disabled for the test suite

graph.add_module(module)
modules.append(module)

# Legal imports, from higher layer to immediate lower layer
for higher_module, lower_module in zip(modules[:-1], modules[1:]):
for higher_module, lower_module in itertools.pairwise(modules):
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Stylistic (similar comment to before regarding unpackaging syntax)

I think this one is more readable

graph.add_import(importer=higher_module, imported=lower_module)

return graph
7 changes: 4 additions & 3 deletions tests/unit/application/graph/test_manipulation.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import re
import pytest # type: ignore

from grimp.application.graph import ImportGraph
Expand Down Expand Up @@ -119,7 +120,7 @@ def test_cannot_add_squashed_module_if_already_same_unsquashed_module(self):

with pytest.raises(
ValueError,
match=(
match=re.escape(
"Cannot add a squashed module when it is already present in the graph as an "
"unsquashed module, or vice versa."
),
Expand All @@ -134,7 +135,7 @@ def test_cannot_add_unsquashed_module_if_already_same_squashed_module(self):

with pytest.raises(
ValueError,
match=(
match=re.escape(
"Cannot add a squashed module when it is already present in the graph as an "
"unsquashed module, or vice versa."
),
Expand All @@ -148,7 +149,7 @@ def test_cannot_add_descendant_of_squashed_module(self, module_name):
graph.add_module("mypackage.foo", is_squashed=True)

with pytest.raises(
ValueError, match="Module is a descendant of squashed module mypackage.foo."
ValueError, match=re.escape("Module is a descendant of squashed module mypackage.foo.")
):
graph.add_module(module_name)

Expand Down
7 changes: 0 additions & 7 deletions tests/unit/application/test_scanning.py
Original file line number Diff line number Diff line change
Expand Up @@ -730,13 +730,6 @@ def my_function():
("import namespace.foo.green.alpha.one", "namespace.foo.green"),
("from namespace.foo.green.alpha import one", "namespace.foo.green"),
("from ..green.alpha import one", "namespace.foo.green"),
("from .. import green", "namespace.foo.green"),
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These parametrizations are duplicated.

("import namespace.foo.green.alpha", "namespace.foo.green"),
("from namespace.foo.green import alpha", "namespace.foo.green"),
("from ..green import alpha", "namespace.foo.green"),
("import namespace.foo.green.alpha.one", "namespace.foo.green"),
("from namespace.foo.green.alpha import one", "namespace.foo.green"),
("from ..green.alpha import one", "namespace.foo.green"),
),
)
def test_external_package_imports_for_namespace_packages(statement, expected_module_name):
Expand Down
7 changes: 5 additions & 2 deletions tests/unit/application/test_usecases.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import re
from unittest.mock import sentinel
import pytest # type: ignore

Expand Down Expand Up @@ -63,7 +64,9 @@ class FakePackageFinder(BaseFakePackageFinder):
# Check that the external packages are squashed modules.
if include_external_packages:
for module in ("external", "decimal"):
with pytest.raises(ValueError, match="Cannot find children of a squashed module."):
with pytest.raises(
ValueError, match=re.escape("Cannot find children of a squashed module.")
):
graph.find_children(module)

def test_boolean_additional_package_raises_type_error(self):
Expand All @@ -75,7 +78,7 @@ def test_boolean_additional_package_raises_type_error(self):
as the second argument, and it's possible it might have been called
as a positional argument.
"""
with pytest.raises(TypeError, match="Package names must be strings, got bool."):
with pytest.raises(TypeError, match=re.escape("Package names must be strings, got bool.")):
usecases.build_graph("mypackage", True)

@pytest.mark.parametrize(
Expand Down