Skip to content

Commit d34d262

Browse files
CG-10430: removes call_graph_successors (#35)
# Motivation Simplifies API # Content removed `call_graph_successors()` method of `Callable` # Testing Updated Pytest # Please check the following before marking your PR as ready for review - [x] I have added tests for my changes - [x] I have updated the documentation or added new documentation as needed - [x] I have read and agree to the [Contributor License Agreement](../CLA.md)
1 parent 9fc2c52 commit d34d262

File tree

5 files changed

+34
-411
lines changed

5 files changed

+34
-411
lines changed

src/graph_sitter/core/interfaces/callable.py

Lines changed: 1 addition & 95 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
from dataclasses import dataclass
2-
from typing import TYPE_CHECKING, Generic, Literal, Self, TypeVar, overload
2+
from typing import TYPE_CHECKING, Generic, Self, TypeVar
33

44
from graph_sitter.core.autocommit import reader
55
from graph_sitter.core.detached_symbols.function_call import FunctionCall
@@ -125,97 +125,3 @@ def get_parameter_by_type(self, type: "Symbol") -> TParameter | None:
125125
if self._parameters is None:
126126
return None
127127
return next((x for x in self._parameters if x.type == type), None)
128-
129-
@overload
130-
def call_graph_successors(
131-
self,
132-
*,
133-
include_classes: Literal[False],
134-
include_external: Literal[False],
135-
) -> list["Function"]: ...
136-
137-
@overload
138-
def call_graph_successors(
139-
self,
140-
*,
141-
include_classes: Literal[False],
142-
include_external: Literal[True] = ...,
143-
) -> list["Function | ExternalModule"]: ...
144-
145-
@overload
146-
def call_graph_successors(
147-
self,
148-
*,
149-
include_classes: Literal[True] = ...,
150-
include_external: Literal[False],
151-
) -> list["Function | Class"]: ...
152-
153-
@overload
154-
def call_graph_successors(
155-
self,
156-
*,
157-
include_classes: Literal[True] = ...,
158-
include_external: Literal[True] = ...,
159-
) -> list["Function | Class | ExternalModule"]: ...
160-
161-
@reader
162-
def call_graph_successors(
163-
self,
164-
*,
165-
include_classes: bool = True,
166-
include_external: bool = True,
167-
) -> list[FunctionCallDefinition]:
168-
"""Returns all function call definitions that are reachable from this callable.
169-
170-
Analyzes the callable's implementation to find all function calls and their corresponding definitions. For classes, if a constructor exists,
171-
returns the call graph successors of the constructor; otherwise returns an empty list.
172-
173-
Args:
174-
include_classes (bool): If True, includes class definitions in the results. Defaults to True.
175-
include_external (bool): If True, includes external module definitions in the results. Defaults to True.
176-
177-
Returns:
178-
list[FunctionCallDefinition]: A list of FunctionCallDefinition objects, each containing a function call and its
179-
possible callable definitions (Functions, Classes, or ExternalModules based on include flags). Returns empty list
180-
for non-block symbols or classes without constructors.
181-
"""
182-
from graph_sitter.core.class_definition import Class
183-
from graph_sitter.core.external_module import ExternalModule
184-
from graph_sitter.core.function import Function
185-
from graph_sitter.core.interfaces.has_block import HasBlock
186-
187-
call_graph_successors: list[FunctionCallDefinition] = []
188-
189-
# Check if Callable has function_calls:
190-
if isinstance(self, HasBlock):
191-
# Special handling for classes.
192-
# Classes with no constructors are not included in the code paths. Else, the code path of the constructor is included.
193-
if isinstance(self, Class):
194-
if self.constructor:
195-
return self.constructor.call_graph_successors(include_classes=include_classes, include_external=include_external)
196-
else:
197-
return []
198-
199-
for call in self.function_calls:
200-
call_graph_successor = FunctionCallDefinition(call, [])
201-
# Extract function definition
202-
for call_func in call.function_definitions:
203-
# Case - Function with definition
204-
if isinstance(call_func, Function):
205-
call_graph_successor.callables.append(call_func)
206-
207-
# =====[ Extract `__init__` from classes ]=====
208-
elif isinstance(call_func, Class):
209-
if include_classes:
210-
call_graph_successor.callables.append(call_func)
211-
# Case - external module (leaf node)
212-
elif isinstance(call_func, ExternalModule) and include_external:
213-
call_graph_successor.callables.append(call_func)
214-
215-
if len(call_graph_successor.callables) > 0:
216-
call_graph_successors.append(call_graph_successor)
217-
else:
218-
# Non-block symbols will not have any function calls
219-
return []
220-
221-
return call_graph_successors

src/graph_sitter/skills/graph_viz/graph_viz_call_graph.py

Lines changed: 33 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,12 @@
22

33
import networkx as nx
44

5+
from graph_sitter.core.class_definition import Class
56
from graph_sitter.core.codebase import CodebaseType
7+
from graph_sitter.core.detached_symbols.function_call import FunctionCall
68
from graph_sitter.core.external_module import ExternalModule
79
from graph_sitter.core.function import Function
8-
from graph_sitter.core.interfaces.callable import Callable, FunctionCallDefinition
10+
from graph_sitter.core.interfaces.callable import Callable
911
from graph_sitter.enums import ProgrammingLanguage
1012
from graph_sitter.skills.core.skill import Skill
1113
from graph_sitter.skills.core.skill_test import SkillTestCase, SkillTestCasePyFile
@@ -69,7 +71,7 @@ def skill_func(codebase: CodebaseType):
6971
# ===== [ Maximum Recursive Depth ] =====
7072
MAX_DEPTH = 5
7173

72-
def create_downstream_call_trace(parent: FunctionCallDefinition | Function | None = None, depth: int = 0):
74+
def create_downstream_call_trace(parent: FunctionCall | Function | None = None, depth: int = 0):
7375
"""Creates call graph for parent
7476
7577
This function recurses through the call graph of a function and creates a visualization
@@ -82,20 +84,14 @@ def create_downstream_call_trace(parent: FunctionCallDefinition | Function | Non
8284
# if the maximum recursive depth has been exceeded return
8385
if MAX_DEPTH <= depth:
8486
return
85-
# if parent is of type Function
86-
if isinstance(parent, Function):
87-
# set both src_call, src_func to parent
88-
src_call, src_func = parent, parent
87+
if isinstance(parent, FunctionCall):
88+
src_call, src_func = parent, parent.function_definition
8989
else:
90-
# get the first callable of parent
91-
src_func = parent.callables[0]
92-
src_call = parent.call
90+
src_call, src_func = parent, parent
9391
# Iterate over all call paths of the symbol
94-
for func_call_def in src_func.call_graph_successors():
95-
# the call of a function
96-
call = func_call_def.call
92+
for call in src_func.function_calls:
9793
# the symbol being called
98-
func = func_call_def.callables[0]
94+
func = call.function_definition
9995

10096
# ignore direct recursive calls
10197
if func.name == src_func.name:
@@ -108,7 +104,7 @@ def create_downstream_call_trace(parent: FunctionCallDefinition | Function | Non
108104
G.add_edge(src_call, call)
109105

110106
# recursive call to function call
111-
create_downstream_call_trace(func_call_def, depth + 1)
107+
create_downstream_call_trace(call, depth + 1)
112108
elif GRAPH_EXERNAL_MODULE_CALLS:
113109
# add `call` to the graph and an edge from `src_call` to `call`
114110
G.add_node(call)
@@ -187,12 +183,12 @@ def function_to_trace():
187183
class CallGraphFilter(Skill, ABC):
188184
"""This skill shows a visualization of the call graph from a given function or symbol.
189185
It iterates through the usages of the starting function and its subsequent calls,
190-
creating a directed graph of function calls. The skill filters out test files and
191-
includes only methods with specific names (post, get, patch, delete).
192-
By default, the call graph uses red for the starting node, yellow for class methods,
186+
creating a directed graph of function calls. The skill filters out test files and class declarations
187+
and includes only methods with specific names (post, get, patch, delete).
188+
The call graph uses red for the starting node, yellow for class methods,
193189
and can be customized based on user requests. The graph is limited to a specified depth
194-
to manage complexity. In its current form,
195-
it ignores recursive calls and external modules but can be modified trivially to include them
190+
to manage complexity. In its current form, it ignores recursive calls and external modules
191+
but can be modified trivially to include them
196192
"""
197193

198194
@staticmethod
@@ -211,30 +207,30 @@ def skill_func(codebase: CodebaseType):
211207
# ===== [ Maximum Recursive Depth ] =====
212208
MAX_DEPTH = 5
213209

210+
SKIP_CLASS_DECLARATIONS = True
211+
214212
cls = codebase.get_class("MyClass")
215213

216214
# Define a recursive function to traverse function calls
217-
def create_filtered_downstream_call_trace(parent_func: FunctionCallDefinition | Function, current_depth, max_depth):
215+
def create_filtered_downstream_call_trace(parent: FunctionCall | Function, current_depth, max_depth):
218216
if current_depth > max_depth:
219217
return
220218

221219
# if parent is of type Function
222-
if isinstance(parent_func, Function):
220+
if isinstance(parent, Function):
223221
# set both src_call, src_func to parent
224-
src_call, src_func = parent_func, parent_func
222+
src_call, src_func = parent, parent
225223
else:
226224
# get the first callable of parent
227-
src_func = parent_func.callables[0]
228-
src_call = parent_func.call
225+
src_call, src_func = parent, parent.function_definition
229226

230227
# Iterate over all call paths of the symbol
231-
for func_call_def in src_func.call_graph_successors():
232-
# the call of a function
233-
call = func_call_def.call
228+
for call in src_func.function_calls:
234229
# the symbol being called
235-
func = func_call_def.callables[0]
230+
func = call.function_definition
236231

237-
# Skip the successor if the file name starts with 'test'
232+
if SKIP_CLASS_DECLARATIONS and isinstance(func, Class):
233+
continue
238234

239235
# if the function being called is not from an external module and is not defined in a test file
240236
if not isinstance(func, ExternalModule) and not func.file.filepath.startswith("test"):
@@ -247,7 +243,7 @@ def create_filtered_downstream_call_trace(parent_func: FunctionCallDefinition |
247243
G.add_edge(src_call, call, symbol=cls) # Add edge from current to successor
248244

249245
# Recursively add successors of the current symbol
250-
create_filtered_downstream_call_trace(func_call_def, current_depth + 1, max_depth)
246+
create_filtered_downstream_call_trace(call, current_depth + 1, max_depth)
251247

252248
# Start the recursive traversal
253249
create_filtered_downstream_call_trace(func_to_trace, 1, MAX_DEPTH)
@@ -301,25 +297,22 @@ def skill_func(codebase: CodebaseType):
301297
MAX_DEPTH = 5
302298

303299
# Define a recursive function to traverse usages
304-
def create_downstream_call_trace(parent_func: FunctionCallDefinition | Function, end: Callable, current_depth, max_depth):
300+
def create_downstream_call_trace(parent: FunctionCall | Function, end: Callable, current_depth, max_depth):
305301
if current_depth > max_depth:
306302
return
307303

308304
# if parent is of type Function
309-
if isinstance(parent_func, Function):
305+
if isinstance(parent, Function):
310306
# set both src_call, src_func to parent
311-
src_call, src_func = parent_func, parent_func
307+
src_call, src_func = parent, parent
312308
else:
313309
# get the first callable of parent
314-
src_func = parent_func.callables[0]
315-
src_call = parent_func.call
310+
src_call, src_func = parent, parent.function_definition
316311

317312
# Iterate over all call paths of the symbol
318-
for func_call_def in src_func.call_graph_successors():
319-
# the call of a function
320-
call = func_call_def.call
313+
for call in src_func.function_calls:
321314
# the symbol being called
322-
func = func_call_def.callables[0]
315+
func = call.function_definition
323316

324317
# ignore direct recursive calls
325318
if func.name == src_func.name:
@@ -335,7 +328,7 @@ def create_downstream_call_trace(parent_func: FunctionCallDefinition | Function,
335328
G.add_edge(call, end)
336329
return
337330
# recursive call to function call
338-
create_downstream_call_trace(func_call_def, end, current_depth + 1, max_depth)
331+
create_downstream_call_trace(call, end, current_depth + 1, max_depth)
339332

340333
# Get the start and end function
341334
start = codebase.get_function("start_func")

tests/unit/python/function/test_function_call_graph_successors.py

Lines changed: 0 additions & 115 deletions
This file was deleted.

0 commit comments

Comments
 (0)