1
1
import io
2
- from typing import Union , Optional
3
- from pathlib import Path
2
+ import os
4
3
from ..utils import *
4
+ from pathlib import Path
5
5
from ...entities import *
6
6
from ...graph import Graph
7
+ from typing import Union , Optional
7
8
from ..analyzer import AbstractAnalyzer
8
9
9
10
import tree_sitter_python as tspython
@@ -74,7 +75,7 @@ def process_class_definition(self, node: Node, path: Path) -> tuple[Class, list[
74
75
75
76
return (c , inherited_classes )
76
77
77
- def process_function_definition (self , node : Node , path : Path ) -> Function :
78
+ def process_function_definition (self , node : Node , path : Path , source_code : str ) -> Function :
78
79
"""
79
80
Processes a function definition node from the syntax tree and extracts relevant information.
80
81
@@ -153,7 +154,8 @@ def process_function_definition(self, node: Node, path: Path) -> Function:
153
154
ret_type = return_type .text .decode ('utf-8' ) if return_type else None
154
155
155
156
# Create Function object
156
- f = Function (str (path ), function_name , docstring , ret_type , '' , start_line , end_line )
157
+ src = source_code [node .start_byte :node .end_byte ]
158
+ f = Function (str (path ), function_name , docstring , ret_type , src , start_line , end_line )
157
159
158
160
# Add arguments to Function object
159
161
for arg in args :
@@ -162,7 +164,7 @@ def process_function_definition(self, node: Node, path: Path) -> Function:
162
164
return f
163
165
164
166
def first_pass_traverse (self , parent : Union [File ,Class ,Function ], node : Node ,
165
- path : Path , graph : Graph ) -> None :
167
+ path : Path , graph : Graph , source_code : str ) -> None :
166
168
"""
167
169
Recursively traverses a syntax tree node, processes class and function definitions,
168
170
and connects them in a graph representation.
@@ -197,7 +199,7 @@ def first_pass_traverse(self, parent: Union[File,Class,Function], node: Node,
197
199
graph .add_class (entity )
198
200
199
201
elif node .type == "function_definition" :
200
- entity = self .process_function_definition (node , path )
202
+ entity = self .process_function_definition (node , path , source_code )
201
203
# Add Function object to the graph
202
204
graph .add_function (entity )
203
205
@@ -208,7 +210,7 @@ def first_pass_traverse(self, parent: Union[File,Class,Function], node: Node,
208
210
209
211
# Recursivly visit child nodes
210
212
for child in node .children :
211
- self .first_pass_traverse (parent , child , path , graph )
213
+ self .first_pass_traverse (parent , child , path , graph , source_code )
212
214
213
215
def first_pass (self , path : Path , f : io .TextIOWrapper , graph :Graph ) -> None :
214
216
"""
@@ -226,15 +228,20 @@ def first_pass(self, path: Path, f: io.TextIOWrapper, graph:Graph) -> None:
226
228
logger .info (f"Python Processing { path } " )
227
229
228
230
# Create file entity
229
- file = File (str ( path .parent ), path .name , path .suffix )
231
+ file = File (os . path .dirname ( path ), path .name , path .suffix )
230
232
graph .add_file (file )
231
233
232
234
# Parse file
233
- content = f .read ()
234
- tree = self .parser .parse (content )
235
+ source_code = f .read ()
236
+ tree = self .parser .parse (source_code )
237
+ try :
238
+ source_code = source_code .decode ('utf-8' )
239
+ except Exception as e :
240
+ logger .error (f"Failed decoding source code: { e } " )
241
+ source_code = ''
235
242
236
243
# Walk thought the AST
237
- self .first_pass_traverse (file , tree .root_node , path , graph )
244
+ self .first_pass_traverse (file , tree .root_node , path , graph , source_code )
238
245
239
246
def process_function_call (self , node ) -> Optional [str ]:
240
247
"""
@@ -323,7 +330,7 @@ def process_inheritance(self, cls: Class, super_classes: list[str],
323
330
graph .connect_entities ('INHERITS' , cls .id , _super_class .id )
324
331
325
332
def second_pass_traverse (self , parent : Union [File , Class , Function ],
326
- node : Node , path : Path , graph : Graph ) -> None :
333
+ node : Node , path : Path , graph : Graph , source_code : str ) -> None :
327
334
"""
328
335
Traverse the AST nodes during the second pass and process each node accordingly.
329
336
@@ -340,7 +347,9 @@ def second_pass_traverse(self, parent: Union[File, Class, Function],
340
347
parent = cls
341
348
342
349
elif node .type == "function_definition" :
343
- func = self .process_function_definition (node , path )
350
+ # TODO: simply extract function name, no need to parse entire function
351
+ # see C analyzer
352
+ func = self .process_function_definition (node , path , source_code )
344
353
parent = graph .get_function_by_name (func .name )
345
354
elif node .type == "call" :
346
355
callee = self .process_function_call (node )
@@ -349,7 +358,7 @@ def second_pass_traverse(self, parent: Union[File, Class, Function],
349
358
350
359
# Recursivly visit child nodes
351
360
for child in node .children :
352
- self .second_pass_traverse (parent , child , path , graph )
361
+ self .second_pass_traverse (parent , child , path , graph , source_code )
353
362
354
363
def second_pass (self , path : Path , f : io .TextIOWrapper , graph : Graph ) -> None :
355
364
"""
@@ -367,17 +376,17 @@ def second_pass(self, path: Path, f: io.TextIOWrapper, graph: Graph) -> None:
367
376
logger .info (f"Processing { path } " )
368
377
369
378
# Get file entity
370
- file = graph .get_file (str ( path .parent ), path .name , path .suffix )
379
+ file = graph .get_file (os . path .dirname ( path ), path .name , path .suffix )
371
380
if file is None :
372
381
logger .error (f"File entity not found for: { path } " )
373
382
return
374
383
375
384
try :
376
385
# Parse file
377
- content = f .read ()
378
- tree = self .parser .parse (content )
386
+ source_code = f .read ()
387
+ tree = self .parser .parse (source_code )
379
388
380
389
# Walk thought the AST
381
- self .second_pass_traverse (file , tree .root_node , path , graph )
390
+ self .second_pass_traverse (file , tree .root_node , path , graph , source_code )
382
391
except Exception as e :
383
392
logger .error (f"Failed to process file { path } : { e } " )
0 commit comments