bupa1018 commited on
Commit
2fd774b
·
1 Parent(s): ef8026c

Update chunk_python_code.py

Browse files
Files changed (1) hide show
  1. chunk_python_code.py +94 -47
chunk_python_code.py CHANGED
@@ -1,65 +1,75 @@
1
  import ast
2
  from langchain.schema import Document
3
 
4
- def chunk_python_code_with_metadata(source_code, source):
5
  """
6
  Entry point method to process the Python file.
7
  It invokes the iterate_ast function.
8
  """
9
  documents = []
10
- #print(f"Processing file: {source}")
11
 
12
- iterate_ast(source_code, documents, source)
13
- # Determine usage based on the source path
14
- if source.startswith("kadi_apy/lib/"):
15
  usage = "kadi-apy python library"
16
- elif source.startswith("kadi_apy/cli/"):
17
  usage = "kadi-apy python cli library"
18
  else:
19
  usage = "undefined"
20
 
21
  # Add metadata for usage to all documents
22
  for doc in documents:
23
- doc.metadata["source"] = source
24
  doc.metadata["usage"] = usage # Add the determined usage metadata
25
  #print(doc)
26
  return documents
27
 
28
 
29
- def iterate_ast(source_code, documents, source):
30
  """
31
  Parses the AST of the given Python file and delegates
32
  handling to specific methods based on node types.
33
  """
34
- # Parse the source code into an abstract syntax tree (AST)
35
- tree = ast.parse(source_code, filename=source)
36
 
37
  first_level_nodes = list(ast.iter_child_nodes(tree))
38
 
39
  # Check if there are no first-level nodes
40
  if not first_level_nodes:
41
- handle_no_first_level_node_found(documents, source_code, source)
 
42
  return
43
 
44
  all_imports = all(isinstance(node, (ast.Import, ast.ImportFrom)) for node in first_level_nodes)
45
  if all_imports:
46
- handle_first_level_imports_only(documents, source_code, source)
 
47
 
48
  # Iterate over first-level nodes
49
  for first_level_node in ast.iter_child_nodes(tree):
 
50
  if isinstance(first_level_node, ast.ClassDef):
51
- handle_first_level_class(first_level_node, documents, source_code)
 
52
  elif isinstance(first_level_node, ast.FunctionDef):
53
- handle_first_level_func(first_level_node, documents, source_code)
 
54
  elif isinstance(first_level_node, ast.Assign):
55
- handle_first_level_assign(first_level_node, documents, source_code)
56
-
57
-
58
- def handle_first_level_imports_only(documents, source_code, source):
 
 
 
 
59
  """
60
  Handles cases where the first-level nodes are only imports.
61
  """
62
- if source.endswith("__init__.py"):
 
63
  type = "__init__-file"
64
  else:
65
  type = "undefined"
@@ -69,17 +79,46 @@ def handle_first_level_imports_only(documents, source_code, source):
69
 
70
  # Create and store a Document with the full source code
71
  doc = Document(
72
- page_content=source_code,
73
  metadata=metadata
74
  )
75
  documents.append(doc)
 
 
 
76
 
77
 
78
- def handle_no_first_level_node_found(documents, source_code, source):
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
79
  """
80
  Handles cases where no top-level nodes are found in the AST.
81
  """
82
- if source.endswith("__init__.py"):
 
83
  type = "__init__-file"
84
  else:
85
  type = "undefined"
@@ -89,19 +128,22 @@ def handle_no_first_level_node_found(documents, source_code, source):
89
 
90
  # Create and store a Document with the full source code
91
  doc = Document(
92
- page_content=source_code,
93
  metadata=metadata
94
  )
95
  documents.append(doc)
96
 
 
97
 
98
- def handle_first_level_assign(assign_node, documents, source_code):
 
99
  """
100
  Handles assignment statements at the first level of the AST.
101
  """
102
- assign_start_line = assign_node.lineno
103
- assign_end_line = assign_node.end_lineno
104
- assign_source = '\n'.join(source_code.splitlines()[assign_start_line-1:assign_end_line])
 
105
 
106
  # Create metadata without imports
107
  metadata = {"type": "Assign"}
@@ -113,20 +155,21 @@ def handle_first_level_assign(assign_node, documents, source_code):
113
  )
114
  documents.append(doc)
115
 
 
116
 
117
- def handle_first_level_class(class_node, documents, source_code):
118
  """
119
  Handles classes at the first level of the AST.
120
  """
121
- class_start_line = class_node.lineno
122
- class_body_lines = [child.lineno for child in class_node.body if isinstance(child, ast.FunctionDef)]
123
- class_end_line = min(class_body_lines, default=class_node.end_lineno) - 1
124
- class_source = '\n'.join(source_code.splitlines()[class_start_line-1:class_end_line])
 
125
 
126
- # Create metadata without imports
127
  metadata = {
128
  "type": "class",
129
- "class": class_node.name,
130
  "visibility": "public"
131
  }
132
 
@@ -138,14 +181,14 @@ def handle_first_level_class(class_node, documents, source_code):
138
  documents.append(doc)
139
 
140
  # Handle methods within the class
141
- for second_level_node in ast.iter_child_nodes(class_node):
142
  if isinstance(second_level_node, ast.FunctionDef):
143
  method_start_line = (
144
  second_level_node.decorator_list[0].lineno
145
  if second_level_node.decorator_list else second_level_node.lineno
146
  )
147
  method_end_line = second_level_node.end_lineno
148
- method_source = '\n'.join(source_code.splitlines()[method_start_line-1:method_end_line])
149
 
150
  visibility = "internal" if second_level_node.name.startswith("_") else "public"
151
 
@@ -155,28 +198,30 @@ def handle_first_level_class(class_node, documents, source_code):
155
  "type": "method",
156
  "method": second_level_node.name,
157
  "visibility": visibility,
158
- "class": class_node.name
159
  }
160
  )
161
  documents.append(doc)
162
 
 
163
 
164
- def handle_first_level_func(function_node, documents, source_code):
165
  """
166
  Handles functions at the first level of the AST.
167
  """
 
168
  function_start_line = (
169
- function_node.decorator_list[0].lineno
170
- if function_node.decorator_list else function_node.lineno
171
  )
172
- function_end_line = function_node.end_lineno
173
- function_source = '\n'.join(source_code.splitlines()[function_start_line-1:function_end_line])
174
 
175
- visibility = "internal" if function_node.name.startswith("_") else "public"
176
 
177
  is_command = any(
178
  decorator.id == "apy_command"
179
- for decorator in function_node.decorator_list
180
  if hasattr(decorator, "id")
181
  )
182
 
@@ -185,12 +230,14 @@ def handle_first_level_func(function_node, documents, source_code):
185
  "visibility": visibility
186
  }
187
  if is_command:
188
- metadata["command"] = function_node.name
189
  else:
190
- metadata["method"] = function_node.name
191
 
192
  doc = Document(
193
  page_content=function_source,
194
  metadata=metadata
195
  )
196
- documents.append(doc)
 
 
 
1
  import ast
2
  from langchain.schema import Document
3
 
4
+ def chunk_python_code_with_metadata(python_code, file_path):
5
  """
6
  Entry point method to process the Python file.
7
  It invokes the iterate_ast function.
8
  """
9
  documents = []
10
+ #print(f"Processing file: {file_path}")
11
 
12
+ _iterate_ast(python_code, documents, file_path)
13
+ # Determine usage based on the file_path
14
+ if file_path.startswith("kadi_apy/lib/"):
15
  usage = "kadi-apy python library"
16
+ elif file_path.startswith("kadi_apy/cli/"):
17
  usage = "kadi-apy python cli library"
18
  else:
19
  usage = "undefined"
20
 
21
  # Add metadata for usage to all documents
22
  for doc in documents:
23
+ doc.metadata["source"] = file_path
24
  doc.metadata["usage"] = usage # Add the determined usage metadata
25
  #print(doc)
26
  return documents
27
 
28
 
29
+ def _iterate_ast(python_code, documents, file_path):
30
  """
31
  Parses the AST of the given Python file and delegates
32
  handling to specific methods based on node types.
33
  """
34
+ documents = []
35
+ tree = ast.parse(python_code, filename=file_path)
36
 
37
  first_level_nodes = list(ast.iter_child_nodes(tree))
38
 
39
  # Check if there are no first-level nodes
40
  if not first_level_nodes:
41
+ documents.extend(
42
+ _chunk_nodeless_python_code(documents, python_code, file_path))
43
  return
44
 
45
  all_imports = all(isinstance(node, (ast.Import, ast.ImportFrom)) for node in first_level_nodes)
46
  if all_imports:
47
+ docuements.extend(
48
+ _chunk_import_only_python_code(documents, python_code, file_path))
49
 
50
  # Iterate over first-level nodes
51
  for first_level_node in ast.iter_child_nodes(tree):
52
+
53
  if isinstance(first_level_node, ast.ClassDef):
54
+ documents.extend(
55
+ = handle_first_level_class(first_level_node, documents, python_code))
56
  elif isinstance(first_level_node, ast.FunctionDef):
57
+ documents.extend(
58
+ _chunk_first_level_func_node(first_level_node, documents, python_code))
59
  elif isinstance(first_level_node, ast.Assign):
60
+ documents.extend(
61
+ _chunk_first_level_assign_node(first_level_node, documents, python_code))
62
+ else:
63
+ documents.extend(
64
+ handle_notdefined_case(python_code))
65
+
66
+
67
+ def _chunk_import_only_python_code(python_code, file_path):
68
  """
69
  Handles cases where the first-level nodes are only imports.
70
  """
71
+ documents = []
72
+ if file_path.endswith("__init__.py"):
73
  type = "__init__-file"
74
  else:
75
  type = "undefined"
 
79
 
80
  # Create and store a Document with the full source code
81
  doc = Document(
82
+ page_content=python_code,
83
  metadata=metadata
84
  )
85
  documents.append(doc)
86
+ return documents
87
+
88
+ from langchain.text_splitter import RecursiveCharacterTextSplitter
89
 
90
 
91
+ def handle_notdefined_case(python_code):
92
+ documents = []
93
+ documents.extend(
94
+ chunk_python_code_by_character)
95
+
96
+ return documents
97
+
98
+
99
+ def chunk_python_code_by_character(python_code):
100
+ documents = []
101
+ text_splitter = RecursiveCharacterTextSplitter(
102
+ chunk_size=512,
103
+ chunk_overlap=128,
104
+ separators=[]
105
+
106
+ chunks = text_splitter.split_text(text)
107
+ doc = Document(
108
+ page_content=python_code
109
+ )
110
+
111
+ documents.append(doc)
112
+
113
+ return documents
114
+
115
+
116
+ def _chunk_nodeless_python_code(python_code, file_path):
117
  """
118
  Handles cases where no top-level nodes are found in the AST.
119
  """
120
+ documents = []
121
+ if file_path.endswith("__init__.py"):
122
  type = "__init__-file"
123
  else:
124
  type = "undefined"
 
128
 
129
  # Create and store a Document with the full source code
130
  doc = Document(
131
+ page_content=python_code,
132
  metadata=metadata
133
  )
134
  documents.append(doc)
135
 
136
+ return documents
137
 
138
+ def _chunk_first_level_assign_node(ast_node, python_code):
139
+
140
  """
141
  Handles assignment statements at the first level of the AST.
142
  """
143
+ documents = []
144
+ assign_start_line = ast_node.lineno
145
+ assign_end_line = ast_node.end_lineno
146
+ assign_source = '\n'.join(python_code.splitlines()[assign_start_line-1:assign_end_line])
147
 
148
  # Create metadata without imports
149
  metadata = {"type": "Assign"}
 
155
  )
156
  documents.append(doc)
157
 
158
+ return documents
159
 
160
+ def handle_first_level_class(ast_node , python_code):
161
  """
162
  Handles classes at the first level of the AST.
163
  """
164
+ documents = []
165
+ class_start_line = ast_node.lineno
166
+ class_body_lines = [child.lineno for child in ast_node.body if isinstance(child, ast.FunctionDef)]
167
+ class_end_line = min(class_body_lines, default=ast_node.end_lineno) - 1
168
+ class_source = '\n'.join(python_code.splitlines()[class_start_line-1:class_end_line])
169
 
 
170
  metadata = {
171
  "type": "class",
172
+ "class": ast_node.name,
173
  "visibility": "public"
174
  }
175
 
 
181
  documents.append(doc)
182
 
183
  # Handle methods within the class
184
+ for second_level_node in ast.iter_child_nodes(ast_node):
185
  if isinstance(second_level_node, ast.FunctionDef):
186
  method_start_line = (
187
  second_level_node.decorator_list[0].lineno
188
  if second_level_node.decorator_list else second_level_node.lineno
189
  )
190
  method_end_line = second_level_node.end_lineno
191
+ method_source = '\n'.join(python_code.splitlines()[method_start_line-1:method_end_line])
192
 
193
  visibility = "internal" if second_level_node.name.startswith("_") else "public"
194
 
 
198
  "type": "method",
199
  "method": second_level_node.name,
200
  "visibility": visibility,
201
+ "class": ast_node.name
202
  }
203
  )
204
  documents.append(doc)
205
 
206
+ return documents
207
 
208
+ def _chunk_first_level_func_node(ast_node, python_code):
209
  """
210
  Handles functions at the first level of the AST.
211
  """
212
+ documents = []
213
  function_start_line = (
214
+ ast_node.decorator_list[0].lineno
215
+ if ast_node.decorator_list else ast_node.lineno
216
  )
217
+ function_end_line = ast_node.end_lineno
218
+ function_source = '\n'.join(python_code.splitlines()[function_start_line-1:function_end_line])
219
 
220
+ visibility = "internal" if ast_node.name.startswith("_") else "public"
221
 
222
  is_command = any(
223
  decorator.id == "apy_command"
224
+ for decorator in ast_node.decorator_list
225
  if hasattr(decorator, "id")
226
  )
227
 
 
230
  "visibility": visibility
231
  }
232
  if is_command:
233
+ metadata["command"] = ast_node.name
234
  else:
235
+ metadata["method"] = ast_node.name
236
 
237
  doc = Document(
238
  page_content=function_source,
239
  metadata=metadata
240
  )
241
+ documents.append(doc)
242
+
243
+ return documents