diff --git a/src/grimp/domain/valueobjects.py b/src/grimp/domain/valueobjects.py index a31468a2..63145f17 100644 --- a/src/grimp/domain/valueobjects.py +++ b/src/grimp/domain/valueobjects.py @@ -1,33 +1,15 @@ -from typing import Any +from dataclasses import dataclass +from typing import Set -class ValueObject: - def __repr__(self) -> str: - return "<{}: {}>".format(self.__class__.__name__, self) - - def __eq__(self, other: Any) -> bool: - if isinstance(other, type(self)) or isinstance(self, type(other)): - return hash(self) == hash(other) - else: - return False - - def __hash__(self) -> int: - return hash(str(self)) - - -class Module(ValueObject): +@dataclass(frozen=True) +class Module: """ A Python module. """ - __slots__ = ("name",) - - def __init__(self, name: str) -> None: - """ - Args: - name: The fully qualified name of a Python module, e.g. 'package.foo.bar'. - """ - self.name = name + # The fully qualified name of a Python module, e.g. 'package.foo.bar'. + name: str def __str__(self) -> str: return self.name @@ -61,32 +43,23 @@ def is_descendant_of(self, module: "Module") -> bool: return self.name.startswith(f"{module.name}.") -class DirectImport(ValueObject): +@dataclass(frozen=True) +class DirectImport: """ An import between one module and another. """ - def __init__( - self, - *, - importer: Module, - imported: Module, - line_number: int, - line_contents: str, - ) -> None: - self.importer = importer - self.imported = imported - self.line_number = line_number - self.line_contents = line_contents + importer: Module + imported: Module + line_number: int + line_contents: str def __str__(self) -> str: - return "{} -> {} (l. {})".format(self.importer, self.imported, self.line_number) + return f"{self.importer} -> {self.imported} (l. {self.line_number})" - def __hash__(self) -> int: - return hash((str(self), self.line_contents)) - -class Layer(ValueObject): +@dataclass(frozen=True) +class Layer: """ A layer within a layered architecture. @@ -94,13 +67,14 @@ class Layer(ValueObject): independent. This is the default. """ - def __init__( - self, - *module_tails: str, - independent: bool = True, - ) -> None: - self.module_tails = set(module_tails) - self.independent = independent + module_tails: Set[str] + independent: bool + + # A custom `__init__` is needed since `module_tails` is a variadic argument. + def __init__(self, *module_tails: str, independent: bool = True) -> None: + # `object.__setattr__` is needed since the dataclass is frozen. + object.__setattr__(self, "module_tails", set(module_tails)) + object.__setattr__(self, "independent", independent) def __str__(self) -> str: return f"{self.module_tails}, independent={self.independent}" diff --git a/tests/unit/adaptors/test_importscanner.py b/tests/unit/adaptors/test_importscanner.py index 54d3cccf..f4d1795f 100644 --- a/tests/unit/adaptors/test_importscanner.py +++ b/tests/unit/adaptors/test_importscanner.py @@ -111,20 +111,20 @@ def test_non_ascii(): assert result == { DirectImport( - importer="mypackage.blue", - imported="ñon_ascii_变", + importer=Module("mypackage.blue"), + imported=Module("ñon_ascii_变"), line_number=1, line_contents="from ñon_ascii_变 import *", ), DirectImport( - importer="mypackage.blue", - imported="mypackage.ñon_ascii_变", + importer=Module("mypackage.blue"), + imported=Module("mypackage.ñon_ascii_变"), line_number=2, line_contents="from . import ñon_ascii_变", ), DirectImport( - importer="mypackage.blue", - imported="mypackage.ñon_ascii_变.ラーメン", + importer=Module("mypackage.blue"), + imported=Module("mypackage.ñon_ascii_变.ラーメン"), line_number=3, line_contents="import mypackage.ñon_ascii_变.ラーメン", ), diff --git a/tests/unit/domain/test_valueobjects.py b/tests/unit/domain/test_valueobjects.py index d63d0489..39448b4c 100644 --- a/tests/unit/domain/test_valueobjects.py +++ b/tests/unit/domain/test_valueobjects.py @@ -4,37 +4,9 @@ class TestModule: - def test_repr(self): + def test_str(self): module = Module("foo.bar") - assert repr(module) == "" - - 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" - - def test_equals_obeys_liskov(self): - class SpecialModule(Module): - pass - - module = Module("foo.bar") - special_module = SpecialModule("foo.bar") - - assert module == special_module - assert special_module == module - - 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) + assert str(module) == "foo.bar" @pytest.mark.parametrize( "module, expected", @@ -53,101 +25,11 @@ def test_parent(self, module, expected): class TestDirectImport: - def test_repr(self): + def test_str(self): import_path = DirectImport( importer=Module("foo"), imported=Module("bar"), line_number=10, line_contents="import bar", ) - assert repr(import_path) == " bar (l. 10)>" - - 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" - - 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) + assert str(import_path) == "foo -> bar (l. 10)"