diff --git a/.templates/leetcode/examples/linked_list.json5 b/.templates/leetcode/examples/linked_list.json5 index 0485c53..f0a77b2 100644 --- a/.templates/leetcode/examples/linked_list.json5 +++ b/.templates/leetcode/examples/linked_list.json5 @@ -41,8 +41,8 @@ solution_methods: [ { name: "reverse_between", // snake_case method name - parameters: "head: ListNode | None, left: int, right: int", // Use ListNode | None for nullable parameters - return_type: "ListNode | None", // Modern union syntax + parameters: "head: ListNode[int] | None, left: int, right: int", // Use ListNode[int] | None for nullable parameters + return_type: "ListNode[int] | None", // Modern union syntax with explicit generic type dummy_return: "None", // None for linked list problems }, ], @@ -65,14 +65,14 @@ parametrize_typed: "head_list: list[int], left: int, right: int, expected_list: list[int]", test_cases: "[([1, 2, 3, 4, 5], 2, 4, [1, 4, 3, 2, 5]), ([5], 1, 1, [5])]", // IMPORTANT: Linked list test body converts arrays to ListNode and compares objects directly - body: "head = ListNode.from_list(head_list)\nexpected = ListNode.from_list(expected_list)\nresult = self.solution.reverse_between(head, left, right)\nassert result == expected", + body: "head = ListNode[int].from_list(head_list)\nexpected = ListNode[int].from_list(expected_list)\nresult = self.solution.reverse_between(head, left, right)\nassert result == expected", }, ], // === PLAYGROUND NOTEBOOK === // IMPORTANT: Linked list playground needs ListNode import and conversion playground_imports: "from solution import Solution\n\nfrom leetcode_py import ListNode", - playground_test_case: "# Example test case\nhead_list = [1, 2, 3, 4, 5]\nhead = ListNode.from_list(head_list)\nleft, right = 2, 4\nexpected = ListNode.from_list([1, 4, 3, 2, 5])", + playground_test_case: "# Example test case\nhead_list = [1, 2, 3, 4, 5]\nhead = ListNode[int].from_list(head_list)\nleft, right = 2, 4\nexpected = ListNode[int].from_list([1, 4, 3, 2, 5])", playground_execution: "result = Solution().reverse_between(head, left, right)\nresult", playground_assertion: "assert result == expected", } diff --git a/.templates/leetcode/examples/tree.json5 b/.templates/leetcode/examples/tree.json5 index ecbcd92..fd5cf13 100644 --- a/.templates/leetcode/examples/tree.json5 +++ b/.templates/leetcode/examples/tree.json5 @@ -45,8 +45,8 @@ solution_methods: [ { name: "invert_tree", // snake_case method name - parameters: "root: TreeNode | None", // Use TreeNode | None for nullable tree parameters - return_type: "TreeNode | None", // Modern union syntax (not Optional[TreeNode]) + parameters: "root: TreeNode[int] | None", // Use TreeNode[int] | None for nullable tree parameters + return_type: "TreeNode[int] | None", // Modern union syntax with explicit generic type dummy_return: "None", // None for tree problems }, ], @@ -69,14 +69,14 @@ parametrize_typed: "root_list: list[int | None], expected_list: list[int | None]", test_cases: "[([4, 2, 7, 1, 3, 6, 9], [4, 7, 2, 9, 6, 3, 1]), ([2, 1, 3], [2, 3, 1]), ([], [])]", // IMPORTANT: Tree test body converts arrays to TreeNode and compares objects directly - body: "root = TreeNode.from_list(root_list)\nexpected = TreeNode.from_list(expected_list)\nresult = self.solution.invert_tree(root)\nassert result == expected", + body: "root = TreeNode[int].from_list(root_list)\nexpected = TreeNode[int].from_list(expected_list)\nresult = self.solution.invert_tree(root)\nassert result == expected", }, ], // === PLAYGROUND NOTEBOOK === // IMPORTANT: Tree playground needs TreeNode import and conversion playground_imports: "from solution import Solution\n\nfrom leetcode_py import TreeNode", - playground_test_case: "# Example test case\nroot_list: list[int | None] = [4, 2, 7, 1, 3, 6, 9]\nroot = TreeNode.from_list(root_list)\nexpected = TreeNode.from_list([4, 7, 2, 9, 6, 3, 1])", + playground_test_case: "# Example test case\nroot_list: list[int | None] = [4, 2, 7, 1, 3, 6, 9]\nroot = TreeNode[int].from_list(root_list)\nexpected = TreeNode[int].from_list([4, 7, 2, 9, 6, 3, 1])", playground_execution: "result = Solution().invert_tree(root)\nresult", playground_assertion: "assert result == expected", } diff --git a/.templates/leetcode/json/invert_binary_tree.json b/.templates/leetcode/json/invert_binary_tree.json index 3abdcd5..8e1f324 100644 --- a/.templates/leetcode/json/invert_binary_tree.json +++ b/.templates/leetcode/json/invert_binary_tree.json @@ -18,8 +18,8 @@ "solution_methods": [ { "name": "invert_tree", - "parameters": "root: TreeNode | None", - "return_type": "TreeNode | None", + "parameters": "root: TreeNode[int] | None", + "return_type": "TreeNode[int] | None", "dummy_return": "None" } ], @@ -34,11 +34,11 @@ "parametrize": "root_list, expected_list", "parametrize_typed": "root_list: list[int | None], expected_list: list[int | None]", "test_cases": "[([4, 2, 7, 1, 3, 6, 9], [4, 7, 2, 9, 6, 3, 1]), ([2, 1, 3], [2, 3, 1]), ([], [])]", - "body": "root = TreeNode.from_list(root_list)\nexpected = TreeNode.from_list(expected_list)\nresult = self.solution.invert_tree(root)\nassert result == expected" + "body": "root = TreeNode[int].from_list(root_list)\nexpected = TreeNode[int].from_list(expected_list)\nresult = self.solution.invert_tree(root)\nassert result == expected" } ], "playground_imports": "from solution import Solution\n\nfrom leetcode_py import TreeNode", - "playground_test_case": "# Example test case\nroot_list: list[int | None] = [4, 2, 7, 1, 3, 6, 9]\nroot = TreeNode.from_list(root_list)\nexpected = TreeNode.from_list([4, 7, 2, 9, 6, 3, 1])", + "playground_test_case": "# Example test case\nroot_list: list[int | None] = [4, 2, 7, 1, 3, 6, 9]\nroot = TreeNode[int].from_list(root_list)\nexpected = TreeNode[int].from_list([4, 7, 2, 9, 6, 3, 1])", "playground_execution": "result = Solution().invert_tree(root)\nresult", "playground_assertion": "assert result == expected" } diff --git a/.templates/leetcode/json/reverse_linked_list_ii.json b/.templates/leetcode/json/reverse_linked_list_ii.json index 8c66969..7c904e2 100644 --- a/.templates/leetcode/json/reverse_linked_list_ii.json +++ b/.templates/leetcode/json/reverse_linked_list_ii.json @@ -17,8 +17,8 @@ "solution_methods": [ { "name": "reverse_between", - "parameters": "head: ListNode | None, left: int, right: int", - "return_type": "ListNode | None", + "parameters": "head: ListNode[int] | None, left: int, right: int", + "return_type": "ListNode[int] | None", "dummy_return": "None" } ], @@ -33,11 +33,11 @@ "parametrize": "head_list, left, right, expected_list", "parametrize_typed": "head_list: list[int], left: int, right: int, expected_list: list[int]", "test_cases": "[([1, 2, 3, 4, 5], 2, 4, [1, 4, 3, 2, 5]), ([5], 1, 1, [5])]", - "body": "head = ListNode.from_list(head_list)\nexpected = ListNode.from_list(expected_list)\nresult = self.solution.reverse_between(head, left, right)\nassert result == expected" + "body": "head = ListNode[int].from_list(head_list)\nexpected = ListNode[int].from_list(expected_list)\nresult = self.solution.reverse_between(head, left, right)\nassert result == expected" } ], "playground_imports": "from solution import Solution\n\nfrom leetcode_py import ListNode", - "playground_test_case": "# Example test case\nhead_list = [1, 2, 3, 4, 5]\nhead = ListNode.from_list(head_list)\nleft, right = 2, 4\nexpected = ListNode.from_list([1, 4, 3, 2, 5])", + "playground_test_case": "# Example test case\nhead_list = [1, 2, 3, 4, 5]\nhead = ListNode[int].from_list(head_list)\nleft, right = 2, 4\nexpected = ListNode[int].from_list([1, 4, 3, 2, 5])", "playground_execution": "result = Solution().reverse_between(head, left, right)\nresult", "playground_assertion": "assert result == expected" } diff --git a/leetcode/invert_binary_tree/playground.ipynb b/leetcode/invert_binary_tree/playground.ipynb index b63bfcd..452ecf4 100644 --- a/leetcode/invert_binary_tree/playground.ipynb +++ b/leetcode/invert_binary_tree/playground.ipynb @@ -14,7 +14,7 @@ "id": "setup", "metadata": {}, "outputs": [], - "source": ["# Example test case\nroot_list: list[int | None] = [4, 2, 7, 1, 3, 6, 9]\nroot = TreeNode.from_list(root_list)\nexpected = TreeNode.from_list([4, 7, 2, 9, 6, 3, 1])"] + "source": ["# Example test case\nroot_list: list[int | None] = [4, 2, 7, 1, 3, 6, 9]\nroot = TreeNode[int].from_list(root_list)\nexpected = TreeNode[int].from_list([4, 7, 2, 9, 6, 3, 1])"] }, { "cell_type": "code", diff --git a/leetcode/invert_binary_tree/solution.py b/leetcode/invert_binary_tree/solution.py index 452457c..1e11b18 100644 --- a/leetcode/invert_binary_tree/solution.py +++ b/leetcode/invert_binary_tree/solution.py @@ -10,7 +10,7 @@ class Solution: # DFS recursive # Time: O(n) # Space: O(h) where h is height of tree - def invert_tree(self, root: TreeNode | None) -> TreeNode | None: + def invert_tree(self, root: TreeNode[int] | None) -> TreeNode[int] | None: if not root: return None @@ -22,11 +22,11 @@ class SolutionDFS: # DFS iterative # Time: O(n) # Space: O(h) where h is height of tree - def invert_tree(self, root: TreeNode | None) -> TreeNode | None: + def invert_tree(self, root: TreeNode[int] | None) -> TreeNode[int] | None: if not root: return None - stack: list[TreeNode | None] = [root] + stack: list[TreeNode[int] | None] = [root] while stack: node = stack.pop() if node is None: @@ -42,11 +42,11 @@ def invert_tree(self, root: TreeNode | None) -> TreeNode | None: class SolutionBFS: # Time: O(n) # Space: O(w) where w is maximum width of tree - def invert_tree(self, root: TreeNode | None) -> TreeNode | None: + def invert_tree(self, root: TreeNode[int] | None) -> TreeNode[int] | None: if not root: return None - queue: deque[TreeNode | None] = deque([root]) + queue: deque[TreeNode[int] | None] = deque([root]) while queue: node = queue.popleft() if node is None: diff --git a/leetcode/invert_binary_tree/tests.py b/leetcode/invert_binary_tree/tests.py index 5bf066f..dff6608 100644 --- a/leetcode/invert_binary_tree/tests.py +++ b/leetcode/invert_binary_tree/tests.py @@ -20,7 +20,7 @@ def test_invert_tree( solution_class: type[Solution | SolutionDFS | SolutionBFS], ): solution = solution_class() - root = TreeNode.from_list(root_list) - expected = TreeNode.from_list(expected_list) + root = TreeNode[int].from_list(root_list) + expected = TreeNode[int].from_list(expected_list) result = solution.invert_tree(root) assert result == expected diff --git a/leetcode/lru_cache/solution.py b/leetcode/lru_cache/solution.py index 817b786..33cdbb6 100644 --- a/leetcode/lru_cache/solution.py +++ b/leetcode/lru_cache/solution.py @@ -3,7 +3,7 @@ class LRUCache: # Space: O(capacity) - def __init__(self, capacity: int): + def __init__(self, capacity: int) -> None: self.capacity = capacity self.cache: OrderedDict[int, int] = OrderedDict() diff --git a/leetcode/reverse_linked_list_ii/playground.ipynb b/leetcode/reverse_linked_list_ii/playground.ipynb index 87d95ac..8fdc5e9 100644 --- a/leetcode/reverse_linked_list_ii/playground.ipynb +++ b/leetcode/reverse_linked_list_ii/playground.ipynb @@ -14,7 +14,7 @@ "id": "setup", "metadata": {}, "outputs": [], - "source": ["# Example test case\nhead_list = [1, 2, 3, 4, 5]\nhead = ListNode.from_list(head_list)\nleft, right = 2, 4\nexpected = ListNode.from_list([1, 4, 3, 2, 5])"] + "source": ["# Example test case\nhead_list = [1, 2, 3, 4, 5]\nhead = ListNode[int].from_list(head_list)\nleft, right = 2, 4\nexpected = ListNode[int].from_list([1, 4, 3, 2, 5])"] }, { "cell_type": "code", diff --git a/leetcode/reverse_linked_list_ii/solution.py b/leetcode/reverse_linked_list_ii/solution.py index 7db8da6..db9a3a0 100644 --- a/leetcode/reverse_linked_list_ii/solution.py +++ b/leetcode/reverse_linked_list_ii/solution.py @@ -4,11 +4,11 @@ class Solution: # Time: O(n) # Space: O(1) - def reverse_between(self, head: ListNode | None, left: int, right: int) -> ListNode | None: + def reverse_between(self, head: ListNode[int] | None, left: int, right: int) -> ListNode[int] | None: if not head or left == right: return head - dummy = ListNode(0) + dummy = ListNode[int](0) dummy.next = head prev = dummy diff --git a/leetcode/reverse_linked_list_ii/tests.py b/leetcode/reverse_linked_list_ii/tests.py index 546cd35..e3ea5b7 100644 --- a/leetcode/reverse_linked_list_ii/tests.py +++ b/leetcode/reverse_linked_list_ii/tests.py @@ -18,7 +18,7 @@ def setup_method(self): def test_reverse_between( self, head_list: list[int], left: int, right: int, expected_list: list[int] ): - head = ListNode.from_list(head_list) - expected = ListNode.from_list(expected_list) + head = ListNode[int].from_list(head_list) + expected = ListNode[int].from_list(expected_list) result = self.solution.reverse_between(head, left, right) assert result == expected diff --git a/leetcode_py/data_structures/list_node.py b/leetcode_py/data_structures/list_node.py index 6759952..c5b5409 100644 --- a/leetcode_py/data_structures/list_node.py +++ b/leetcode_py/data_structures/list_node.py @@ -1,10 +1,16 @@ -class ListNode: - def __init__(self, val: int = 0, next: "ListNode | None" = None): +from typing import Generic, TypeVar + +# TODO: Remove TypeVar when minimum Python version is 3.12+ (use class ListNode[T]: syntax) +T = TypeVar("T") + + +class ListNode(Generic[T]): + def __init__(self, val: T, next: "ListNode[T] | None" = None): self.val = val self.next = next @classmethod - def from_list(cls, arr: list[int]) -> "ListNode | None": + def from_list(cls, arr: list[T]) -> "ListNode[T] | None": if not arr: return None head = cls(arr[0]) @@ -14,9 +20,9 @@ def from_list(cls, arr: list[int]) -> "ListNode | None": current = current.next return head - def to_list(self) -> list[int]: + def to_list(self) -> list[T]: result = [] - current: "ListNode | None" = self + current: "ListNode[T] | None" = self while current: result.append(current.val) current = current.next diff --git a/leetcode_py/data_structures/tree_node.py b/leetcode_py/data_structures/tree_node.py index 796c9f3..ca96921 100644 --- a/leetcode_py/data_structures/tree_node.py +++ b/leetcode_py/data_structures/tree_node.py @@ -1,8 +1,13 @@ +from typing import Any, Generic, TypeVar + import graphviz from anytree import Node, RenderTree +# TODO: Remove TypeVar when minimum Python version is 3.12+ (use class TreeNode[T]: syntax) +T = TypeVar("T") + -def build_anytree(node: "TreeNode | None", parent: Node | None = None) -> Node | None: +def build_anytree(node: "TreeNode[Any] | None", parent: Node | None = None) -> Node | None: if not node: return Node("None", parent=parent) if parent else None current = Node(str(node.val), parent=parent) @@ -12,7 +17,7 @@ def build_anytree(node: "TreeNode | None", parent: Node | None = None) -> Node | return current -def add_nodes(dot: graphviz.Digraph, node: "TreeNode | None", node_id: int = 0) -> int: +def add_nodes(dot: graphviz.Digraph, node: "TreeNode[Any] | None", node_id: int = 0) -> int: if not node: return node_id @@ -31,14 +36,14 @@ def add_nodes(dot: graphviz.Digraph, node: "TreeNode | None", node_id: int = 0) return next_id - 1 -class TreeNode: - def __init__(self, val: int = 0, left: "TreeNode | None" = None, right: "TreeNode | None" = None): +class TreeNode(Generic[T]): + def __init__(self, val: T, left: "TreeNode[T] | None" = None, right: "TreeNode[T] | None" = None): self.val = val self.left = left self.right = right @classmethod - def from_list(cls, arr: list[int | None]) -> "TreeNode | None": + def from_list(cls, arr: list[T | None]) -> "TreeNode[T] | None": """Convert array representation to binary tree.""" if not arr or arr[0] is None: return None @@ -66,10 +71,10 @@ def from_list(cls, arr: list[int | None]) -> "TreeNode | None": return root - def to_list(self) -> list[int | None]: + def to_list(self) -> list[T | None]: """Convert binary tree to array representation.""" - result: list[int | None] = [] - queue: list[TreeNode | None] = [self] + result: list[T | None] = [] + queue: list[TreeNode[T] | None] = [self] while queue: node = queue.pop(0) diff --git a/tests/data_structures/test_list_node.py b/tests/data_structures/test_list_node.py index 236f188..cc1bb9d 100644 --- a/tests/data_structures/test_list_node.py +++ b/tests/data_structures/test_list_node.py @@ -1,3 +1,5 @@ +from typing import Any + import pytest from leetcode_py import ListNode @@ -5,32 +7,33 @@ class TestListNode: @pytest.mark.parametrize( - "val,expected_val,expected_next", + "val, expected_val, expected_next", [ - (None, 0, None), # default (5, 5, None), # with value + ("hello", "hello", None), # string value ], ) - def test_init(self, val, expected_val, expected_next): - node = ListNode() if val is None else ListNode(val) + def test_init(self, val: Any, expected_val: Any, expected_next: Any) -> None: + node = ListNode(val) assert node.val == expected_val assert node.next == expected_next - def test_init_with_next(self): - next_node = ListNode(2) - node = ListNode(1, next_node) + def test_init_with_next(self) -> None: + next_node = ListNode[int](2) + node = ListNode[int](1, next_node) assert node.val == 1 assert node.next == next_node @pytest.mark.parametrize( - "input_list,expected_result", + "input_list, expected_result", [ ([], None), ([1], "single_node"), ([1, 2, 3], "multiple_nodes"), + (["a", "b"], "string_nodes"), ], ) - def test_from_list(self, input_list, expected_result): + def test_from_list(self, input_list: list[Any], expected_result: str | None) -> None: result = ListNode.from_list(input_list) if expected_result is None: @@ -47,26 +50,36 @@ def test_from_list(self, input_list, expected_result): assert result.next.next is not None assert result.next.next.val == 3 assert result.next.next.next is None + elif expected_result == "string_nodes": + assert result is not None + assert result.val == "a" + assert result.next is not None + assert result.next.val == "b" + assert result.next.next is None @pytest.mark.parametrize( - "input_list,expected_output", + "input_list, expected_output", [ ([1], [1]), ([1, 2, 3], [1, 2, 3]), + (["x", "y"], ["x", "y"]), ], ) - def test_to_list(self, input_list, expected_output): + def test_to_list(self, input_list: list[Any], expected_output: list[Any]) -> None: node = ListNode.from_list(input_list) assert node is not None assert node.to_list() == expected_output @pytest.mark.parametrize( - "input_list,expected_str,expected_repr", + "input_list, expected_str, expected_repr", [ ([1, 2, 3], "1 -> 2 -> 3", "ListNode([1, 2, 3])"), + (["a", "b"], "a -> b", "ListNode(['a', 'b'])"), ], ) - def test_string_representations(self, input_list, expected_str, expected_repr): + def test_string_representations( + self, input_list: list[Any], expected_str: str, expected_repr: str + ) -> None: node = ListNode.from_list(input_list) assert node is not None assert str(node) == expected_str @@ -74,20 +87,20 @@ def test_string_representations(self, input_list, expected_str, expected_repr): assert node._repr_html_() == expected_str @pytest.mark.parametrize( - "list1,list2,should_equal", + "list1,list2, should_equal", [ ([1, 2, 3], [1, 2, 3], True), ([1, 2, 3], [1, 2, 4], False), ], ) - def test_equality(self, list1, list2, should_equal): + def test_equality(self, list1: list[int], list2: list[int], should_equal: bool) -> None: node1 = ListNode.from_list(list1) node2 = ListNode.from_list(list2) assert (node1 == node2) == should_equal @pytest.mark.parametrize("other_value", [[1], "1"]) - def test_equality_different_types(self, other_value): - node = ListNode(1) + def test_equality_different_types(self, other_value: Any) -> None: + node = ListNode[int](1) assert node != other_value @pytest.mark.parametrize( @@ -96,9 +109,11 @@ def test_equality_different_types(self, other_value): [1, 2, 3, 4, 5], [1], [10, 20, 30], + ["hello", "world"], + [True, False, True], ], ) - def test_roundtrip_conversion(self, test_list): + def test_roundtrip_conversion(self, test_list: list[Any]) -> None: node = ListNode.from_list(test_list) assert node is not None result = node.to_list() diff --git a/tests/data_structures/test_tree_node.py b/tests/data_structures/test_tree_node.py index 4d07f9f..992dec7 100644 --- a/tests/data_structures/test_tree_node.py +++ b/tests/data_structures/test_tree_node.py @@ -1,3 +1,5 @@ +from typing import Any + import pytest from leetcode_py import TreeNode @@ -6,42 +8,42 @@ class TestTreeNode: @pytest.mark.parametrize( - "val,expected_val", + "val, expected_val", [ - (None, 0), # default - (5, 5), # with value + (5, 5), # integer value + ("hello", "hello"), # string value ], ) - def test_init(self, val, expected_val): - node = TreeNode() if val is None else TreeNode(val) + def test_init(self, val: Any, expected_val: Any) -> None: + node = TreeNode(val) assert node.val == expected_val assert node.left is None assert node.right is None - def test_init_with_children(self): - left = TreeNode(1) - right = TreeNode(2) - node = TreeNode(0, left, right) + def test_init_with_children(self) -> None: + left = TreeNode[int](1) + right = TreeNode[int](2) + node = TreeNode[int](0, left, right) assert node.val == 0 assert node.left == left assert node.right == right - def test_from_list_empty(self): - result = TreeNode.from_list([]) + def test_from_list_empty(self) -> None: + result: TreeNode[Any] | None = TreeNode.from_list([]) assert result is None - def test_from_list_none_root(self): + def test_from_list_none_root(self) -> None: result = TreeNode.from_list([None]) assert result is None - def test_from_list_single(self): + def test_from_list_single(self) -> None: result = TreeNode.from_list([1]) assert result is not None assert result.val == 1 assert result.left is None assert result.right is None - def test_from_list_complete_tree(self): + def test_from_list_complete_tree(self) -> None: result = TreeNode.from_list([1, 2, 3, 4, 5, 6, 7]) assert result is not None assert result.val == 1 @@ -58,7 +60,7 @@ def test_from_list_complete_tree(self): assert result.right.right is not None assert result.right.right.val == 7 - def test_from_list_sparse_tree(self): + def test_from_list_sparse_tree(self) -> None: result = TreeNode.from_list([1, None, 2]) assert result is not None assert result.val == 1 @@ -69,38 +71,40 @@ def test_from_list_sparse_tree(self): assert result.right.right is None @pytest.mark.parametrize( - "input_list,expected_output", + "input_list, expected_output", [ ([1], [1]), ([1, 2, 3, 4, 5, 6, 7], [1, 2, 3, 4, 5, 6, 7]), ([1, None, 2], [1, None, 2]), + (["a", "b", "c"], ["a", "b", "c"]), ], ) - def test_to_list(self, input_list, expected_output): + def test_to_list(self, input_list: list[Any], expected_output: list[Any]) -> None: node = TreeNode.from_list(input_list) assert node is not None assert node.to_list() == expected_output @pytest.mark.parametrize( - "input_list,expected_values", + "input_list, expected_values", [ ([1], ["1"]), ([1, 2, 3], ["1", "2", "3"]), + (["x", "y"], ["x", "y"]), ], ) - def test_str_representation(self, input_list, expected_values): + def test_str_representation(self, input_list: list[Any], expected_values: list[str]) -> None: node = TreeNode.from_list(input_list) assert node is not None result = str(node) for val in expected_values: assert val in result - def test_repr_representation(self): + def test_repr_representation(self) -> None: node = TreeNode.from_list([1, 2, 3]) assert node is not None assert repr(node) == "TreeNode([1, 2, 3])" - def test_repr_html_generates_svg(self): + def test_repr_html_generates_svg(self) -> None: node = TreeNode.from_list([1, 2, 3]) assert node is not None result = node._repr_html_() @@ -108,13 +112,15 @@ def test_repr_html_generates_svg(self): assert "svg" in result.lower() @pytest.mark.parametrize( - "list1,list2,should_equal", + "list1,list2, should_equal", [ ([1, 2, 3], [1, 2, 3], True), ([1, 2, 3], [1, 3, 2], False), ], ) - def test_equality(self, list1, list2, should_equal): + def test_equality( + self, list1: list[int | None], list2: list[int | None], should_equal: bool + ) -> None: node1 = TreeNode.from_list(list1) node2 = TreeNode.from_list(list2) assert node1 is not None @@ -122,8 +128,8 @@ def test_equality(self, list1, list2, should_equal): assert (node1 == node2) == should_equal @pytest.mark.parametrize("other_value", [[1], "1"]) - def test_equality_different_types(self, other_value): - node = TreeNode(1) + def test_equality_different_types(self, other_value: Any) -> None: + node = TreeNode[int](1) assert node != other_value @pytest.mark.parametrize( @@ -132,26 +138,28 @@ def test_equality_different_types(self, other_value): [1, 2, 3, 4, 5, None, 6], [1], [1, None, 2], + ["root", "left", "right"], + [True, False, None, True], ], ) - def test_roundtrip_conversion(self, test_list): + def test_roundtrip_conversion(self, test_list: list[Any]) -> None: node = TreeNode.from_list(test_list) assert node is not None result = node.to_list() assert result == test_list - def test_build_anytree_none(self): + def test_build_anytree_none(self) -> None: result = build_anytree(None) assert result is None - def test_build_anytree_single_node(self): - node = TreeNode(1) + def test_build_anytree_single_node(self) -> None: + node = TreeNode[int](1) result = build_anytree(node) assert result is not None assert result.name == "1" assert len(result.children) == 0 - def test_str_with_none_tree(self): + def test_str_with_none_tree(self) -> None: # Create a scenario where build_anytree returns None # This happens when we have a node but build_anytree fails import unittest.mock @@ -159,6 +167,6 @@ def test_str_with_none_tree(self): with unittest.mock.patch( "leetcode_py.data_structures.tree_node.build_anytree", return_value=None ): - node = TreeNode(1) + node = TreeNode[int](1) result = str(node) assert result == "None"