Skip to content

Commit

Permalink
Add pytest configuration and update tree data structure (#16)
Browse files Browse the repository at this point in the history
  • Loading branch information
FlavioAmurrioCS authored Mar 4, 2024
1 parent de58455 commit 1d9d899
Show file tree
Hide file tree
Showing 3 changed files with 51 additions and 23 deletions.
7 changes: 6 additions & 1 deletion .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,5 +22,10 @@
"reorder-python-imports.args": [
"--application-directories=.:src",
"--add-import 'from __future__ import annotations'",
]
],
"python.testing.pytestArgs": [
"tests"
],
"python.testing.unittestEnabled": false,
"python.testing.pytestEnabled": true
}
61 changes: 42 additions & 19 deletions src/dev_toolbox/data_structures/tree.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,45 +4,52 @@
from dataclasses import dataclass
from dataclasses import field
from functools import lru_cache
from typing import Callable
from typing import Generator
from typing import Generic
from typing import Hashable
from typing import Sequence
from typing import TYPE_CHECKING
from typing import TypeVar


if TYPE_CHECKING:
from typing_extensions import Self

_T = TypeVar("_T", bound=Hashable)


@dataclass(unsafe_hash=True)
class Node:
class TreeNode(Generic[_T]):
"""Node for tree."""

name: str = field(hash=True)
data: _T = field(hash=True)
parent: Self | None = field(default=None, hash=False)
children: list[Self] = field(default_factory=list, hash=False)

@classmethod
def build_tree(cls, parent_child_connections: list[tuple[str, str]]) -> list[Self]:
def build_tree(cls, parent_child_connections: list[tuple[_T, _T]]) -> list[Self]:
"""Build tree from connections."""
nodes: dict[str, Self] = {}
nodes: dict[_T, Self] = {}
for parent, child in parent_child_connections:
if parent not in nodes:
nodes[parent] = cls(name=parent)
nodes[parent] = cls(data=parent)
if child not in nodes:
nodes[child] = cls(name=child)
nodes[child] = cls(data=child)
nodes[parent].children.append(nodes[child])
nodes[child].parent = nodes[parent]

for node in nodes.values():
node.children = sorted(node.children, key=lambda x: (cls.children_count(x), x.name))
node.children = sorted(node.children, key=lambda x: (cls.children_count(x), x.data))
return sorted(
(v for v in nodes.values() if v.parent is None),
key=lambda x: (cls.children_count(x), x.name),
key=lambda x: (cls.children_count(x), x.data),
)

def connections(self) -> Generator[tuple[str, str], None, None]:
def connections(self) -> Generator[tuple[_T, _T], None, None]:
"""Get connections."""
for child in self.children:
yield (self.name, child.name)
yield (self.data, child.data)
yield from child.connections()

def nodes(self) -> Generator[Self, None, None]:
Expand All @@ -57,19 +64,35 @@ def children_count(cls, node: Self) -> int:
"""Count children."""
return sum(1 + cls.children_count(child) for child in node.children)

def print_node(self, level: int = 0) -> None:
def print_node(self, level: int = 0, _repr: Callable[[_T], str] = str) -> None:
"""Print node."""
print(" " * level + "- " + self.name)
print(" " * level + "- " + _repr(self.data))
for child in self.children:
child.print_node(level + 1)
child.print_node(level + 1, _repr=_repr)

def print_tree(self, *, prefix: str = "") -> None:
def print_tree(self, *, prefix: str = "", _repr: Callable[[_T], str] = str) -> None:
if not prefix:
print(self.name)
print(_repr(self.data))
for i, child in enumerate(self.children):
if i == len(self.children) - 1:
print(f"{prefix}└── {child.name}")
child.print_tree(prefix=prefix + " ")
print(f"{prefix}└── {_repr(child.data)}")
child.print_tree(prefix=prefix + " ", _repr=_repr)
else:
print(f"{prefix}β”œβ”€β”€ {child.name}")
child.print_tree(prefix=f"{prefix}β”‚ ")
print(f"{prefix}β”œβ”€β”€ {_repr(child.data)}")
child.print_tree(prefix=f"{prefix}β”‚ ", _repr=_repr)

@classmethod
def parse_indent_hierarchy(cls, lines: Sequence[str]) -> list[Self]:
"""Parse indent hierarchy."""
parent_child_connections: list[tuple[str, str]] = []
stack: list[tuple[int, str]] = []
for line in lines:
indent = len(line) - len(line.lstrip())
while stack and indent <= stack[-1][0]:
stack.pop()
if stack:
parent = stack[-1][1]
child = line.strip()
parent_child_connections.append((parent, child))
stack.append((indent, line.strip()))
return cls.build_tree(parent_child_connections) # type: ignore[arg-type]
6 changes: 3 additions & 3 deletions src/dev_toolbox/reflection_tools/class_hierarchy.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
from typing import Sequence
from typing import TYPE_CHECKING

from dev_toolbox.data_structures.tree import Node
from dev_toolbox.data_structures.tree import TreeNode


if TYPE_CHECKING:
Expand Down Expand Up @@ -72,8 +72,8 @@ def main(argv: Sequence[str] | None = None) -> int:
else:
relations = get_relations(args.files_or_modules)

tree_top_nodes = Node.build_tree(list(relations))
Node(name="args.root", children=tree_top_nodes).print_tree()
tree_top_nodes = TreeNode.build_tree(list(relations))
TreeNode(data="args.root", children=tree_top_nodes).print_tree()
return 0


Expand Down

0 comments on commit 1d9d899

Please sign in to comment.