- 
                Notifications
    You must be signed in to change notification settings 
- Fork 2
Graphs | Vertex & Graph implementations #77
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
| WalkthroughThis pull request refactors the graph data structures module by converting a monolithic edge implementation into a package-based architecture with an abstract base Edge class and concrete subclasses for different edge types. The module facade is updated to re-export from submodules, and a new Graph class implementing adjacency-list operations is introduced. The Vertex class is enhanced with additional properties and methods. Changes
 Sequence Diagram(s)sequenceDiagram
    participant Client
    participant Graph
    participant Vertex
    participant Edge Subclass
    participant EdgeType
    Client->>Graph: add(source_node, destination_node)
    Graph->>Vertex: access source/destination properties
    Graph->>Edge Subclass: create edge (DirectedEdge, UndirectedEdge, etc.)
    Edge Subclass->>EdgeType: return edge_type()
    Graph->>Vertex: add_adjacent_vertex(other)
    Vertex->>Vertex: update adjacent_vertices
    Graph->>Graph: update adjacency_list and edge_list
    Client->>Graph: find_path(node_one, node_two)
    Graph->>Vertex: access adjacent_vertices
    Graph->>Graph: recursive traversal via adjacent_vertices
    Graph-->>Client: return path or None
    Client->>Graph: topological_sorted_order()
    Graph->>Graph: DFS with white/gray/black marking
    Graph->>Graph: cycle detection
    Graph-->>Client: return sorted order or empty
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes The changes involve multiple interrelated files with new abstraction patterns, generic type parameters, and updated data structures. The refactoring from monolithic to package-based architecture with concrete subclasses requires careful verification of inheritance relationships and method contracts. The Graph and Vertex class modifications introduce significant behavioral changes and new public APIs. The logic density in path-finding and topological sort methods requires scrutiny. While changes follow a consistent pattern, the heterogeneous scope across edge types, graph operations, and vertex updates demands independent reasoning for several sections. Poem
 Pre-merge checks and finishing touches❌ Failed checks (2 warnings)
 ✅ Passed checks (1 passed)
 ✨ Finishing touches
 🧪 Generate unit tests (beta)
 Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment  | 
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 19
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️  Outside diff range comments (2)
datastructures/graphs/vertex.py (2)
72-91: Degree calculation is incorrect.
- Early return triggers when either incoming or outgoing is empty; should be when both are empty.
- Use Edge.is_unweighted() per API; don’t treat weight 0 as unweighted.- degrees = 0 - - if len(self.incoming_edges) == 0 or len(self.outgoing_edges) == 0: - return degrees - - seen_edges: Set = set() - - for edge in self.edges: - if edge not in seen_edges: - seen_edges.add(edge) - if not edge.weight: - degrees += 1 - - return degrees + if not self.edges: + return 0 + return sum(1 for edge in self.edges if edge.is_unweighted())
101-109: Directed-degree checks reference non-existent attributes.Use DirectedEdge and its source/destination fields (or edge.edge_type()). Current code uses edge.type and node_one/node_two, which don’t exist for DirectedEdge.
- if len(self.edges) == 0: + if not self.edges: return in_degrees - - for edge in self.edges: - if edge.type == EdgeType.DIRECTED and edge.node_two == self: - in_degrees += 1 + for edge in self.edges: + if isinstance(edge, DirectedEdge) and edge.destination == self: + in_degrees += 1 @@ - if len(self.edges) == 0: + if not self.edges: return out_degrees - - for edge in self.edges: - if edge.type == EdgeType.DIRECTED and edge.node_one == self: - out_degrees += 1 + for edge in self.edges: + if isinstance(edge, DirectedEdge) and edge.source == self: + out_degrees += 1Also applies to: 123-127
🧹 Nitpick comments (7)
datastructures/graphs/vertex.py (1)
58-69: Minor: simplify neighbours.No behavior change.
- nodes = [] - for vertex in self.adjacent_vertices.values(): - nodes.append(vertex) - - return nodes + return list(self.adjacent_vertices.values())datastructures/graphs/edge/edge_type.py (1)
4-10: Enum looks good; considerauto()and future-proofing.Current members are fine. Optional: use
auto()to avoid manual numbering and reduce churn when adding new types. If HyperEdge won’t encode direction, introduce a genericHYPERmember and align uses accordingly.from enum import Enum, unique @unique class EdgeType(Enum): - UNDIRECTED = 1 - DIRECTED = 2 - SELF = 3 - HYPER_DIRECTED = 4 - HYPER_UNDIRECTED = 5 + UNDIRECTED = auto() + DIRECTED = auto() + SELF = auto() + HYPER_DIRECTED = auto() + HYPER_UNDIRECTED = auto()datastructures/graphs/edge/edge_hyper.py (1)
13-26: Tighten generics: useList[T]where possible.
HyperEdgeisGeneric[T]but currently usesList[Any]. PreferList[T]fornodesand return type.- def __init__(self, nodes: List[Any], ... + def __init__(self, nodes: List[T], ... ... - def vertices(self) -> List[Any]: + def vertices(self) -> List[T]: return self.nodesdatastructures/graphs/edge/edge_self.py (1)
19-26: Minor: property name clarity.For a single node, consider
self.nodeoverself.node_onefor clarity.- self.node_one = node + self.node = node ... - return f"{super().__str__()}, Node: {self.node_one}" + return f"{super().__str__()}, Node: {self.node}" ... - def vertices(self) -> List[Any]: - return [self.node_one] + def vertices(self) -> List[Any]: + return [self.node]datastructures/graphs/edge/edge_directed.py (1)
27-28: Tighten generics: returnList[T].Since the edge is
Generic[T], return a typed list to improve correctness.- def vertices(self) -> List[Any]: + def vertices(self) -> List[T]: return [self.source, self.destination]datastructures/graphs/edge/edge_undirected.py (1)
8-11: Optional: Remove redundant Generic inheritance.Since
Edgealready inherits fromGeneric[T], explicitly inheriting fromGeneric[T]again inUndirectedEdgeis redundant. While not incorrect, it can be simplified.Apply this diff to simplify the inheritance:
-class UndirectedEdge(Edge, Generic[T]): +class UndirectedEdge(Edge[T]):datastructures/graphs/edge/edge.py (1)
1-7: Optional: Consolidate typing imports for better organization.The typing imports are split across lines 2 and 4, which slightly reduces readability.
Apply this diff to consolidate the imports:
from abc import ABC, abstractmethod -from typing import AnyStr, Union +from typing import AnyStr, Union, Any, Dict, Optional, Generic, TypeVar, List from .edge_type import EdgeType -from typing import Any, Dict, Optional, Generic, TypeVar, List from uuid import uuid4
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (13)
- DIRECTORY.md(1 hunks)
- datastructures/graphs/__init__.py(1 hunks)
- datastructures/graphs/edge.py(0 hunks)
- datastructures/graphs/edge/__init__.py(1 hunks)
- datastructures/graphs/edge/edge.py(1 hunks)
- datastructures/graphs/edge/edge_directed.py(1 hunks)
- datastructures/graphs/edge/edge_hyper.py(1 hunks)
- datastructures/graphs/edge/edge_self.py(1 hunks)
- datastructures/graphs/edge/edge_type.py(1 hunks)
- datastructures/graphs/edge/edge_undirected.py(1 hunks)
- datastructures/graphs/graph.py(1 hunks)
- datastructures/graphs/test_vertex.py(1 hunks)
- datastructures/graphs/vertex.py(5 hunks)
💤 Files with no reviewable changes (1)
- datastructures/graphs/edge.py
🧰 Additional context used
🧬 Code graph analysis (10)
datastructures/graphs/edge/edge_self.py (5)
datastructures/graphs/edge/edge.py (3)
edge_type(28-29)
Edge(9-36)
vertices(35-36)datastructures/graphs/edge/edge_directed.py (2)
edge_type(24-25)
vertices(27-28)datastructures/graphs/edge/edge_hyper.py (2)
edge_type(22-23)
vertices(25-26)datastructures/graphs/edge/edge_undirected.py (2)
edge_type(23-24)
vertices(26-27)datastructures/graphs/edge/edge_type.py (1)
EdgeType(5-10)
datastructures/graphs/edge/__init__.py (6)
datastructures/graphs/edge/edge.py (2)
Edge(9-36)
edge_type(28-29)datastructures/graphs/edge/edge_directed.py (2)
edge_type(24-25)
DirectedEdge(8-28)datastructures/graphs/edge/edge_hyper.py (2)
edge_type(22-23)
HyperEdge(8-26)datastructures/graphs/edge/edge_self.py (2)
edge_type(22-23)
SelfEdge(8-26)datastructures/graphs/edge/edge_undirected.py (2)
edge_type(23-24)
UndirectedEdge(8-27)datastructures/graphs/edge/edge_type.py (1)
EdgeType(5-10)
datastructures/graphs/edge/edge.py (5)
datastructures/graphs/edge/edge_directed.py (2)
edge_type(24-25)
vertices(27-28)datastructures/graphs/edge/edge_hyper.py (2)
edge_type(22-23)
vertices(25-26)datastructures/graphs/edge/edge_self.py (2)
edge_type(22-23)
vertices(25-26)datastructures/graphs/edge/edge_undirected.py (2)
edge_type(23-24)
vertices(26-27)datastructures/graphs/edge/edge_type.py (1)
EdgeType(5-10)
datastructures/graphs/graph.py (3)
datastructures/stacks/__init__.py (1)
Stack(7-48)datastructures/graphs/vertex.py (2)
Vertex(8-127)
neighbours(59-69)datastructures/graphs/edge/edge.py (1)
Edge(9-36)
datastructures/graphs/edge/edge_hyper.py (5)
datastructures/graphs/edge/edge.py (3)
edge_type(28-29)
Edge(9-36)
vertices(35-36)datastructures/graphs/edge/edge_directed.py (2)
edge_type(24-25)
vertices(27-28)datastructures/graphs/edge/edge_self.py (2)
edge_type(22-23)
vertices(25-26)datastructures/graphs/edge/edge_undirected.py (2)
edge_type(23-24)
vertices(26-27)datastructures/graphs/edge/edge_type.py (1)
EdgeType(5-10)
datastructures/graphs/test_vertex.py (2)
datastructures/graphs/vertex.py (1)
Vertex(8-127)datastructures/graphs/edge/edge_undirected.py (1)
UndirectedEdge(8-27)
datastructures/graphs/edge/edge_directed.py (2)
datastructures/graphs/edge/edge.py (3)
edge_type(28-29)
Edge(9-36)
vertices(35-36)datastructures/graphs/edge/edge_type.py (1)
EdgeType(5-10)
datastructures/graphs/__init__.py (7)
datastructures/graphs/edge/edge.py (1)
Edge(9-36)datastructures/graphs/edge/edge_type.py (1)
EdgeType(5-10)datastructures/graphs/edge/edge_directed.py (1)
DirectedEdge(8-28)datastructures/graphs/edge/edge_undirected.py (1)
UndirectedEdge(8-27)datastructures/graphs/edge/edge_self.py (1)
SelfEdge(8-26)datastructures/graphs/edge/edge_hyper.py (1)
HyperEdge(8-26)datastructures/graphs/graph.py (1)
Graph(12-230)
datastructures/graphs/edge/edge_undirected.py (5)
datastructures/graphs/edge/edge.py (3)
edge_type(28-29)
Edge(9-36)
vertices(35-36)datastructures/graphs/edge/edge_directed.py (2)
edge_type(24-25)
vertices(27-28)datastructures/graphs/edge/edge_hyper.py (2)
edge_type(22-23)
vertices(25-26)datastructures/graphs/edge/edge_self.py (2)
edge_type(22-23)
vertices(25-26)datastructures/graphs/edge/edge_type.py (1)
EdgeType(5-10)
datastructures/graphs/vertex.py (2)
datastructures/graphs/edge/edge.py (1)
Edge(9-36)datastructures/graphs/edge/edge_type.py (1)
EdgeType(5-10)
🪛 markdownlint-cli2 (0.18.1)
DIRECTORY.md
198-198: Unordered list indentation
Expected: 2; Actual: 4
(MD007, ul-indent)
199-199: Unordered list indentation
Expected: 4; Actual: 6
(MD007, ul-indent)
200-200: Unordered list indentation
Expected: 4; Actual: 6
(MD007, ul-indent)
201-201: Unordered list indentation
Expected: 4; Actual: 6
(MD007, ul-indent)
202-202: Unordered list indentation
Expected: 4; Actual: 6
(MD007, ul-indent)
203-203: Unordered list indentation
Expected: 4; Actual: 6
(MD007, ul-indent)
204-204: Unordered list indentation
Expected: 4; Actual: 6
(MD007, ul-indent)
205-205: Unordered list indentation
Expected: 2; Actual: 4
(MD007, ul-indent)
206-206: Unordered list indentation
Expected: 2; Actual: 4
(MD007, ul-indent)
🔇 Additional comments (8)
datastructures/graphs/__init__.py (1)
1-14: Public API facade looks good.Re-exports are clear and usable. No action needed.
datastructures/graphs/edge/__init__.py (1)
1-6: Public exports OK; verify enum usage across modules.Re-exports look correct. Cross-check
edge_undirected.py: snippet showsEdgeType.Undirected(wrong case). Should beEdgeType.UNDIRECTEDor code will break.Suggested fix in
datastructures/graphs/edge/edge_undirected.py:- def edge_type(self) -> EdgeType: - return EdgeType.Undirected + def edge_type(self) -> EdgeType: + return EdgeType.UNDIRECTEDdatastructures/graphs/edge/edge_undirected.py (3)
1-6: Imports and type variable are set up correctly.The imports are well-organized and the TypeVar declaration follows best practices for generic classes.
20-21: LGTM!The string representation correctly extends the base class and includes the undirected edge's node information.
26-27: LGTM!The
verticesmethod correctly returns both nodes of the undirected edge, consistent with the pattern used in other edge implementations.datastructures/graphs/edge/edge.py (3)
24-25: LGTM!The string representation provides clear, informative output for debugging and logging.
27-29: LGTM!The abstract methods
edge_typeandverticescorrectly define the interface contract that all edge subclasses must implement. The use of@abstractmethodensures subclasses provide concrete implementations.Also applies to: 34-36
31-32: LGTM!The
is_unweightedhelper method provides a clean API for checking whether an edge has a weight, which is useful for graph algorithms that need to distinguish between weighted and unweighted edges.
| def __init__(self, source: Any, destination: Any, weight: Optional[Union[int, float]] = None, | ||
| properties: Optional[Dict[str, Any]] = None, | ||
| identifier: AnyStr = uuid4()): | ||
| super().__init__(weight, properties, identifier) | ||
| self.source = source | 
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Bug: uuid4() as default arg; use late binding and UUID typing.
Same issue as other edges.
-from typing import AnyStr, Union, Dict, Optional, Generic, TypeVar, List, Any
-from uuid import uuid4
+from typing import Union, Dict, Optional, Generic, TypeVar, List, Any
+from uuid import uuid4, UUID
 ...
-    def __init__(self, source: Any, destination: Any, weight: Optional[Union[int, float]] = None,
-                 properties: Optional[Dict[str, Any]] = None,
-                 identifier: AnyStr = uuid4()):
-        super().__init__(weight, properties, identifier)
+    def __init__(self, source: Any, destination: Any, weight: Optional[Union[int, float]] = None,
+                 properties: Optional[Dict[str, Any]] = None,
+                 identifier: Optional[UUID] = None):
+        super().__init__(weight, properties, identifier or uuid4())Committable suggestion skipped: line range outside the PR's diff.
🤖 Prompt for AI Agents
In datastructures/graphs/edge/edge_directed.py around lines 14 to 18, the
constructor uses uuid4() as a default argument which causes a single UUID to be
shared across calls and the identifier typing should be a UUID; change the
signature to accept identifier: Optional[UUID] = None (import UUID from uuid or
typing), and inside __init__ set self.identifier = identifier or uuid4(); keep
calling super() with the resolved identifier and ensure imports and typing match
other edge classes for consistency.
| def __init__(self, nodes: List[Any], weight: Optional[Union[int, float]] = None, | ||
| properties: Optional[Dict[str, Any]] = None, | ||
| identifier: AnyStr = uuid4()): | ||
| super().__init__(weight, properties, identifier) | 
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Bug: uuid4() used as a default argument (evaluated at import time).
This gives the same id for all instances created without an explicit identifier.
-from typing import AnyStr, Union, Dict, Optional, Generic, TypeVar, List, Any
-from uuid import uuid4
+from typing import Union, Dict, Optional, Generic, TypeVar, List, Any
+from uuid import uuid4, UUID
 ...
-    def __init__(self, nodes: List[Any], weight: Optional[Union[int, float]] = None,
-                 properties: Optional[Dict[str, Any]] = None,
-                 identifier: AnyStr = uuid4()):
-        super().__init__(weight, properties, identifier)
+    def __init__(self, nodes: List[Any], weight: Optional[Union[int, float]] = None,
+                 properties: Optional[Dict[str, Any]] = None,
+                 identifier: Optional[UUID] = None):
+        super().__init__(weight, properties, identifier or uuid4())Also align base Edge to accept Optional[UUID] (see follow‑up note below).
Committable suggestion skipped: line range outside the PR's diff.
🤖 Prompt for AI Agents
In datastructures/graphs/edge/edge_hyper.py around lines 13 to 16, the
constructor uses uuid4() as a default argument causing the same UUID to be
shared across all instances; change the signature so identifier: Optional[UUID]
= None and inside __init__ set identifier = identifier or uuid4() before calling
super().__init__; also update the base Edge type annotations and signature to
accept Optional[UUID] (not just AnyStr) so the propagated identifier type is
Optional[UUID] and maintain existing behavior when an identifier is provided.
| def edge_type(self) -> EdgeType: | ||
| return EdgeType.SELF | 
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Bug: HyperEdge returns the wrong EdgeType.
edge_type() returns EdgeType.SELF. Use a hyper variant (likely undirected unless you carry direction).
-    def edge_type(self) -> EdgeType:
-        return EdgeType.SELF
+    def edge_type(self) -> EdgeType:
+        return EdgeType.HYPER_UNDIRECTED📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| def edge_type(self) -> EdgeType: | |
| return EdgeType.SELF | |
| def edge_type(self) -> EdgeType: | |
| return EdgeType.HYPER_UNDIRECTED | 
🤖 Prompt for AI Agents
In datastructures/graphs/edge/edge_hyper.py around lines 22-23, the edge_type()
method incorrectly returns EdgeType.SELF; change it to return the hyper variant
(e.g., EdgeType.HYPER or EdgeType.UNDIRECTED_HYPER depending on the enum name
used in the codebase) so HyperEdge reports the correct type; update any imports
or references if necessary and run tests to ensure consumers expecting hyper
edges now see the correct enum value.
| def __init__(self, node: Any, weight: Optional[Union[int, float]] = None, | ||
| properties: Optional[Dict[str, Any]] = None, | ||
| identifier: AnyStr = uuid4()): | ||
| super().__init__(weight, properties, identifier) | ||
| self.node_one = node | 
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Bug: uuid4() as default arg; switch to Optional[UUID] and late binding.
Avoid import-time evaluation and align identifier typing to UUID.
-from typing import AnyStr, Union, Dict, Optional, Generic, TypeVar, List, Any
-from uuid import uuid4
+from typing import Union, Dict, Optional, Generic, TypeVar, List, Any
+from uuid import uuid4, UUID
 ...
-    def __init__(self, node: Any, weight: Optional[Union[int, float]] = None,
-                 properties: Optional[Dict[str, Any]] = None,
-                 identifier: AnyStr = uuid4()):
-        super().__init__(weight, properties, identifier)
+    def __init__(self, node: Any, weight: Optional[Union[int, float]] = None,
+                 properties: Optional[Dict[str, Any]] = None,
+                 identifier: Optional[UUID] = None):
+        super().__init__(weight, properties, identifier or uuid4())🤖 Prompt for AI Agents
In datastructures/graphs/edge/edge_self.py around lines 13 to 17, the
constructor uses uuid4() as a default argument which evaluates at import time
and the identifier type should be a UUID; change the signature to accept
identifier: Optional[UUID] = None (import UUID from uuid or typing) and in the
body do identifier = uuid4() if identifier is None, then pass that UUID to
super().__init__; also update the type hints and any imports accordingly so
identifier is typed as UUID and default is lazily generated.
| def __init__(self, node_one: Any, node_two: Any, weight: Optional[Union[int, float]] = None, | ||
| properties: Optional[Dict[str, Any]] = None, | ||
| identifier: AnyStr = uuid4()): | ||
| super().__init__(weight, properties, identifier) | ||
| self.node_one = node_one | ||
| self.node_two = node_two | 
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Critical: Mutable default argument and type mismatch for identifier parameter.
Two issues with the identifier parameter:
- 
Mutable default: uuid4()is called once at function definition time, causing all instances created without an explicit identifier to share the same UUID. This will lead to incorrect edge identification and potential data corruption.
- 
Type mismatch: The parameter is typed as AnyStr(which isstr | bytes), butuuid4()returns aUUIDobject.
Apply this diff to fix both issues:
-    def __init__(self, node_one: Any, node_two: Any, weight: Optional[Union[int, float]] = None,
-                 properties: Optional[Dict[str, Any]] = None,
-                 identifier: AnyStr = uuid4()):
+    def __init__(self, node_one: Any, node_two: Any, weight: Optional[Union[int, float]] = None,
+                 properties: Optional[Dict[str, Any]] = None,
+                 identifier: Optional[Union[str, UUID]] = None):
+        if identifier is None:
+            identifier = uuid4()
         super().__init__(weight, properties, identifier)Note: The base Edge class has the same issue and should be fixed there as well.
🤖 Prompt for AI Agents
In datastructures/graphs/edge/edge_undirected.py lines 13-18, the constructor
currently uses uuid4() as a default argument and types identifier as AnyStr;
change the signature to accept identifier: Optional[UUID] = None (import UUID
from uuid), and inside __init__ do if identifier is None: identifier = uuid4()
before calling super().__init__; this avoids a single shared UUID and matches
the UUID return type; also make the same change in the base Edge class (use
Optional[UUID] = None and assign uuid4() inside the constructor).
| def find_all_paths( | ||
| self, node_one: Vertex, node_two: Vertex, path: List = None | ||
| ) -> list: | ||
| """ | ||
| Finds all paths between node_one and node_two, where node_one is the start & node_two is the end | ||
| :param node_one Graph Node | ||
| :param node_two Graph Node | ||
| :param path | ||
| """ | ||
| if path is None: | ||
| path = [] | ||
| path = path + [node_one] | ||
|  | ||
| if node_one.data == node_two.data: | ||
| return [path] | ||
|  | ||
| if node_one.data not in self.adjacency_list: | ||
| return [] | ||
|  | ||
| paths = [] | ||
|  | ||
| for node in self.adjacency_list[node_one.data]: | ||
| if node not in path: | ||
| newpaths = self.find_all_paths(Vertex(node), node_two, path) | ||
| for newpath in newpaths: | ||
| paths.append(newpath) | ||
|  | ||
| return paths | 
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
find_all_paths(): indexes with node_one.data and constructs new Vertex() erroneously.
-        if path is None:
-            path = []
-        path = path + [node_one]
-
-        if node_one.data == node_two.data:
+        if path is None:
+            path = []
+        path = path + [node_one]
+
+        if node_one == node_two:
             return [path]
-
-        if node_one.data not in self.adjacency_list:
-            return []
-
-        paths = []
-
-        for node in self.adjacency_list[node_one.data]:
-            if node not in path:
-                newpaths = self.find_all_paths(Vertex(node), node_two, path)
-                for newpath in newpaths:
-                    paths.append(newpath)
-
-        return paths
+        paths: List[List[Vertex]] = []
+        for neighbour in self.adjacency_list.get(node_one, []):
+            if neighbour not in path:
+                new_paths = self.find_all_paths(neighbour, node_two, path)
+                paths.extend(new_paths)
+        return paths🤖 Prompt for AI Agents
In datastructures/graphs/graph.py around lines 169-196, find_all_paths is
indexing the adjacency_list with node_one.data and constructing new Vertex(node)
when recursing; instead, use the actual Vertex objects stored in the
adjacency_list (or consistently map between data->Vertex first) so you don't
rebuild vertices or mix data keys with Vertex keys. Update the function to look
up neighbors with self.adjacency_list.get(node_one) (or translate node_one.data
to the Vertex once at the top), iterate neighbor Vertex instances, compare
neighbor != node (or compare their unique id/identity rather than .data), and
pass the neighbor Vertex directly into the recursive call (no new Vertex(...)).
Ensure adjacency_list usage is consistent (either keys/values are Vertex objects
or you convert data->Vertex once) and adjust membership checks against path to
use Vertex objects.
| def find_shortest_path( | ||
| self, node_one: Vertex, node_two: Vertex, path: List = None | ||
| ) -> Union[List, None]: | ||
| """ | ||
| Finds the shortest path between 2 nodes in the graph | ||
| """ | ||
| if path is None: | ||
| path = [] | ||
|  | ||
| path = path + [node_one] | ||
|  | ||
| if node_one.data == node_two.data: | ||
| return path | ||
|  | ||
| if node_one.data not in self.adjacency_list: | ||
| return None | ||
|  | ||
| shortest = None | ||
|  | ||
| for node in self.adjacency_list[node_one]: | ||
| if node.data not in path: | ||
| newpath = self.find_shortest_path(node, node_two, path) | ||
| if newpath: | ||
| if not shortest or len(newpath) < len(shortest): | ||
| shortest = newpath | ||
|  | ||
| return shortest | 
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
find_shortest_path(): same keying issues as above.
-        if path is None:
-            path = []
-
-        path = path + [node_one]
-
-        if node_one.data == node_two.data:
+        if path is None:
+            path = []
+        path = path + [node_one]
+
+        if node_one == node_two:
             return path
-
-        if node_one.data not in self.adjacency_list:
-            return None
-
-        shortest = None
-
-        for node in self.adjacency_list[node_one]:
-            if node.data not in path:
-                newpath = self.find_shortest_path(node, node_two, path)
-                if newpath:
-                    if not shortest or len(newpath) < len(shortest):
-                        shortest = newpath
-
-        return shortest
+        shortest: Union[List[Vertex], None] = None
+        for neighbour in self.adjacency_list.get(node_one, []):
+            if neighbour not in path:
+                new_path = self.find_shortest_path(neighbour, node_two, path)
+                if new_path and (shortest is None or len(new_path) < len(shortest)):
+                    shortest = new_path
+        return shortest📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| def find_shortest_path( | |
| self, node_one: Vertex, node_two: Vertex, path: List = None | |
| ) -> Union[List, None]: | |
| """ | |
| Finds the shortest path between 2 nodes in the graph | |
| """ | |
| if path is None: | |
| path = [] | |
| path = path + [node_one] | |
| if node_one.data == node_two.data: | |
| return path | |
| if node_one.data not in self.adjacency_list: | |
| return None | |
| shortest = None | |
| for node in self.adjacency_list[node_one]: | |
| if node.data not in path: | |
| newpath = self.find_shortest_path(node, node_two, path) | |
| if newpath: | |
| if not shortest or len(newpath) < len(shortest): | |
| shortest = newpath | |
| return shortest | |
| def find_shortest_path( | |
| self, node_one: Vertex, node_two: Vertex, path: List = None | |
| ) -> Union[List, None]: | |
| """ | |
| Finds the shortest path between 2 nodes in the graph | |
| """ | |
| if path is None: | |
| path = [] | |
| path = path + [node_one] | |
| if node_one == node_two: | |
| return path | |
| shortest: Union[List[Vertex], None] = None | |
| for neighbour in self.adjacency_list.get(node_one, []): | |
| if neighbour not in path: | |
| new_path = self.find_shortest_path(neighbour, node_two, path) | |
| if new_path and (shortest is None or len(new_path) < len(shortest)): | |
| shortest = new_path | |
| return shortest | 
🤖 Prompt for AI Agents
In datastructures/graphs/graph.py around lines 198-224, the method mixes Vertex
objects and their .data keys (e.g. checking node_one.data in adjacency_list but
indexing self.adjacency_list[node_one], and comparing node.data to items in
path), causing incorrect membership/equality checks; fix by treating vertices
consistently as Vertex objects: check membership with node_one in
self.adjacency_list, compare nodes with equality on the Vertex (e.g. node_one ==
node_two), store Vertex instances in path (no .data), and use node not node.data
when checking if a neighbor is already in path or when recursing/indexing
adjacency_list.
| class VertexTestCases(unittest.TestCase): | ||
| def test_1(self): | ||
| node_one = Vertex(data=1) | ||
| node_two = Vertex(data=2) | ||
| edge = UndirectedEdge(node_one=node_one, node_two=node_two) | ||
|  | ||
| self.assertEqual(True, False) # add assertion here | ||
|  | ||
|  | ||
| if __name__ == '__main__': | ||
| unittest.main() | 
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Replace placeholder failing test with real Vertex checks.
Add adjacency and equality/hash tests.
-class VertexTestCases(unittest.TestCase):
-    def test_1(self):
-        node_one = Vertex(data=1)
-        node_two = Vertex(data=2)
-        edge = UndirectedEdge(node_one=node_one, node_two=node_two)
-
-        self.assertEqual(True, False)  # add assertion here
+class VertexTestCases(unittest.TestCase):
+    def test_add_adjacent_vertex_is_mutual(self):
+        v1 = Vertex(data=1)
+        v2 = Vertex(data=2)
+        v1.add_adjacent_vertex(v2)
+        self.assertIn(v2, v1.neighbours)
+        self.assertIn(v1, v2.neighbours)
+
+    def test_eq_and_hash_by_id(self):
+        v1 = Vertex(data=1)
+        v2 = Vertex(data=99, identifier=v1.id)
+        self.assertEqual(v1, v2)
+        self.assertIn(v2, {v1})📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| class VertexTestCases(unittest.TestCase): | |
| def test_1(self): | |
| node_one = Vertex(data=1) | |
| node_two = Vertex(data=2) | |
| edge = UndirectedEdge(node_one=node_one, node_two=node_two) | |
| self.assertEqual(True, False) # add assertion here | |
| if __name__ == '__main__': | |
| unittest.main() | |
| class VertexTestCases(unittest.TestCase): | |
| def test_add_adjacent_vertex_is_mutual(self): | |
| v1 = Vertex(data=1) | |
| v2 = Vertex(data=2) | |
| v1.add_adjacent_vertex(v2) | |
| self.assertIn(v2, v1.neighbours) | |
| self.assertIn(v1, v2.neighbours) | |
| def test_eq_and_hash_by_id(self): | |
| v1 = Vertex(data=1) | |
| v2 = Vertex(data=99, identifier=v1.id) | |
| self.assertEqual(v1, v2) | |
| self.assertIn(v2, {v1}) | |
| if __name__ == '__main__': | |
| unittest.main() | 
🤖 Prompt for AI Agents
In datastructures/graphs/test_vertex.py around lines 6 to 16, replace the
failing placeholder assertion with real tests: create two Vertex instances and
an UndirectedEdge between them, then assert that each vertex's
adjacency/container includes the other (and the edge if your API stores edges),
assert that two Vertex instances constructed with the same data compare equal
and produce the same hash, and assert that vertices with different data are not
equal; use assertTrue/assertFalse/assertEqual/assertIn/assertNotIn as
appropriate to validate adjacency and equality/hash behavior.
| def __init__( | ||
| self, | ||
| data: T, | ||
| incoming_edges: Set[Edge], | ||
| outgoing_edges: Set[Edge], | ||
| properties: Optional[Dict[str, Any]] = None, | ||
| identifier: Any = uuid4(), | ||
| properties: Optional[Dict[str, Any]] = None, | ||
| incoming_edges: Optional[Set[Edge]] = None, | ||
| outgoing_edges: Optional[Set[Edge]] = None, | ||
| ): | ||
| if outgoing_edges is None: | ||
| outgoing_edges = set() | ||
| if incoming_edges is None: | ||
| incoming_edges = set() | ||
| self.id = identifier | ||
| self.data = data | ||
| self.incoming_edges = incoming_edges | ||
| self.outgoing_edges = outgoing_edges | ||
| self.edges = self.incoming_edges.union(self.outgoing_edges) | ||
| self.adjacent_vertices: Dict[str, 'Vertex'] = {} | ||
| self.properties = properties | ||
|  | 
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Default-id bug, stale edges, and key typing. Make Vertex hashable.
- Avoid uuid4() as a default arg (same id across instances).
- Don’t cache edges; compute union on demand.
- adjacent_vertices key type should match id type.
- Add hash to use Vertex as dict key (Graph uses it).
-from .edge import Edge, EdgeType
+from .edge import Edge, EdgeType, DirectedEdge
@@
-    def __init__(
+    def __init__(
         self,
         data: T,
-        identifier: Any = uuid4(),
+        identifier: Any = None,
         properties: Optional[Dict[str, Any]] = None,
         incoming_edges: Optional[Set[Edge]] = None,
         outgoing_edges: Optional[Set[Edge]] = None,
     ):
@@
-        self.id = identifier
+        self.id = uuid4() if identifier is None else identifier
         self.data = data
@@
-        self.edges = self.incoming_edges.union(self.outgoing_edges)
-        self.adjacent_vertices: Dict[str, 'Vertex'] = {}
+        self.adjacent_vertices: Dict[Any, 'Vertex'] = {}
         self.properties = properties
+
+    @property
+    def edges(self) -> Set[Edge]:
+        return self.incoming_edges | self.outgoing_edges
@@
-    def __eq__(self, other: "Vertex") -> bool:
-        return self.id == other.id
+    def __eq__(self, other: object) -> bool:
+        if not isinstance(other, Vertex):
+            return NotImplemented
+        return self.id == other.id
+
+    def __hash__(self) -> int:
+        return hash(self.id)Committable suggestion skipped: line range outside the PR's diff.
| * Edge | ||
| * [Edge](https://github.com/BrianLusina/PythonSnips/blob/master/datastructures/graphs/edge/edge.py) | ||
| * [Edge Directed](https://github.com/BrianLusina/PythonSnips/blob/master/datastructures/graphs/edge/edge_directed.py) | ||
| * [Edge Hyper](https://github.com/BrianLusina/PythonSnips/blob/master/datastructures/graphs/edge/edge_hyper.py) | ||
| * [Edge Self](https://github.com/BrianLusina/PythonSnips/blob/master/datastructures/graphs/edge/edge_self.py) | ||
| * [Edge Type](https://github.com/BrianLusina/PythonSnips/blob/master/datastructures/graphs/edge/edge_type.py) | ||
| * [Edge Undirected](https://github.com/BrianLusina/PythonSnips/blob/master/datastructures/graphs/edge/edge_undirected.py) | ||
| * [Graph](https://github.com/BrianLusina/PythonSnips/blob/master/datastructures/graphs/graph.py) | ||
| * [Test Vertex](https://github.com/BrianLusina/PythonSnips/blob/master/datastructures/graphs/test_vertex.py) | 
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fix list indentation to satisfy MD007 and match surrounding style.
Current bullets under “Graphs > Edge” are over‑indented. Apply:
-    * Edge
-      * [Edge](https://github.com/BrianLusina/PythonSnips/blob/master/datastructures/graphs/edge/edge.py)
-      * [Edge Directed](https://github.com/BrianLusina/PythonSnips/blob/master/datastructures/graphs/edge/edge_directed.py)
-      * [Edge Hyper](https://github.com/BrianLusina/PythonSnips/blob/master/datastructures/graphs/edge/edge_hyper.py)
-      * [Edge Self](https://github.com/BrianLusina/PythonSnips/blob/master/datastructures/graphs/edge/edge_self.py)
-      * [Edge Type](https://github.com/BrianLusina/PythonSnips/blob/master/datastructures/graphs/edge/edge_type.py)
-      * [Edge Undirected](https://github.com/BrianLusina/PythonSnips/blob/master/datastructures/graphs/edge/edge_undirected.py)
-    * [Graph](https://github.com/BrianLusina/PythonSnips/blob/master/datastructures/graphs/graph.py)
-    * [Test Vertex](https://github.com/BrianLusina/PythonSnips/blob/master/datastructures/graphs/test_vertex.py)
+  * Edge
+    * [Edge](https://github.com/BrianLusina/PythonSnips/blob/master/datastructures/graphs/edge/edge.py)
+    * [Edge Directed](https://github.com/BrianLusina/PythonSnips/blob/master/datastructures/graphs/edge/edge_directed.py)
+    * [Edge Hyper](https://github.com/BrianLusina/PythonSnips/blob/master/datastructures/graphs/edge/edge_hyper.py)
+    * [Edge Self](https://github.com/BrianLusina/PythonSnips/blob/master/datastructures/graphs/edge/edge_self.py)
+    * [Edge Type](https://github.com/BrianLusina/PythonSnips/blob/master/datastructures/graphs/edge/edge_type.py)
+    * [Edge Undirected](https://github.com/BrianLusina/PythonSnips/blob/master/datastructures/graphs/edge/edge_undirected.py)
+  * [Graph](https://github.com/BrianLusina/PythonSnips/blob/master/datastructures/graphs/graph.py)
+  * [Test Vertex](https://github.com/BrianLusina/PythonSnips/blob/master/datastructures/graphs/test_vertex.py)📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| * Edge | |
| * [Edge](https://github.com/BrianLusina/PythonSnips/blob/master/datastructures/graphs/edge/edge.py) | |
| * [Edge Directed](https://github.com/BrianLusina/PythonSnips/blob/master/datastructures/graphs/edge/edge_directed.py) | |
| * [Edge Hyper](https://github.com/BrianLusina/PythonSnips/blob/master/datastructures/graphs/edge/edge_hyper.py) | |
| * [Edge Self](https://github.com/BrianLusina/PythonSnips/blob/master/datastructures/graphs/edge/edge_self.py) | |
| * [Edge Type](https://github.com/BrianLusina/PythonSnips/blob/master/datastructures/graphs/edge/edge_type.py) | |
| * [Edge Undirected](https://github.com/BrianLusina/PythonSnips/blob/master/datastructures/graphs/edge/edge_undirected.py) | |
| * [Graph](https://github.com/BrianLusina/PythonSnips/blob/master/datastructures/graphs/graph.py) | |
| * [Test Vertex](https://github.com/BrianLusina/PythonSnips/blob/master/datastructures/graphs/test_vertex.py) | |
| * Edge | |
| * [Edge](https://github.com/BrianLusina/PythonSnips/blob/master/datastructures/graphs/edge/edge.py) | |
| * [Edge Directed](https://github.com/BrianLusina/PythonSnips/blob/master/datastructures/graphs/edge/edge_directed.py) | |
| * [Edge Hyper](https://github.com/BrianLusina/PythonSnips/blob/master/datastructures/graphs/edge/edge_hyper.py) | |
| * [Edge Self](https://github.com/BrianLusina/PythonSnips/blob/master/datastructures/graphs/edge/edge_self.py) | |
| * [Edge Type](https://github.com/BrianLusina/PythonSnips/blob/master/datastructures/graphs/edge/edge_type.py) | |
| * [Edge Undirected](https://github.com/BrianLusina/PythonSnips/blob/master/datastructures/graphs/edge/edge_undirected.py) | |
| * [Graph](https://github.com/BrianLusina/PythonSnips/blob/master/datastructures/graphs/graph.py) | |
| * [Test Vertex](https://github.com/BrianLusina/PythonSnips/blob/master/datastructures/graphs/test_vertex.py) | 
🧰 Tools
🪛 markdownlint-cli2 (0.18.1)
198-198: Unordered list indentation
Expected: 2; Actual: 4
(MD007, ul-indent)
199-199: Unordered list indentation
Expected: 4; Actual: 6
(MD007, ul-indent)
200-200: Unordered list indentation
Expected: 4; Actual: 6
(MD007, ul-indent)
201-201: Unordered list indentation
Expected: 4; Actual: 6
(MD007, ul-indent)
202-202: Unordered list indentation
Expected: 4; Actual: 6
(MD007, ul-indent)
203-203: Unordered list indentation
Expected: 4; Actual: 6
(MD007, ul-indent)
204-204: Unordered list indentation
Expected: 4; Actual: 6
(MD007, ul-indent)
205-205: Unordered list indentation
Expected: 2; Actual: 4
(MD007, ul-indent)
206-206: Unordered list indentation
Expected: 2; Actual: 4
(MD007, ul-indent)
🤖 Prompt for AI Agents
In DIRECTORY.md around lines 198 to 206, the six bullet links under the "Edge"
subheading are over‑indented and break MD007; unindent those lines so the list
items for Edge are at the same indentation level as the following top-level list
entries (e.g., Graph and Test Vertex). Ensure the bullets use the same number of
leading spaces/tabs as the surrounding lists (consistent indentation style) so
the nested list structure and MD007 lint rule are satisfied.
Vertex & Graph implementations
Checklist:
Summary by CodeRabbit
Release Notes
New Features
Refactor
Tests