|
30 | 30 | "source": [ |
31 | 31 | "from codegen import Codebase\n", |
32 | 32 | "import networkx as nx\n", |
33 | | - "from utils import visualize_graph # utility function to visualize a networkx graph" |
| 33 | + "from utils import visualize_graph # utility function to visualize a networkx graph" |
34 | 34 | ] |
35 | 35 | }, |
36 | 36 | { |
|
74 | 74 | "# Add all edges to the graph\n", |
75 | 75 | "for imp in codebase.imports:\n", |
76 | 76 | " if imp.from_file and imp.to_file:\n", |
77 | | - " edge_color = 'red' if imp.is_dynamic else 'black'\n", |
78 | | - " edge_label = 'dynamic' if imp.is_dynamic else 'static'\n", |
79 | | - " \n", |
| 77 | + " edge_color = \"red\" if imp.is_dynamic else \"black\"\n", |
| 78 | + " edge_label = \"dynamic\" if imp.is_dynamic else \"static\"\n", |
| 79 | + "\n", |
80 | 80 | " # Store the import statement and its metadata\n", |
81 | 81 | " G.add_edge(\n", |
82 | 82 | " imp.to_file.filepath,\n", |
|
85 | 85 | " label=edge_label,\n", |
86 | 86 | " is_dynamic=imp.is_dynamic,\n", |
87 | 87 | " import_statement=imp, # Store the whole import object\n", |
88 | | - " key=id(imp.import_statement)\n", |
| 88 | + " key=id(imp.import_statement),\n", |
89 | 89 | " )\n", |
90 | 90 | "# Find strongly connected components\n", |
91 | 91 | "cycles = [scc for scc in nx.strongly_connected_components(G) if len(scc) > 1]\n", |
|
97 | 97 | "\n", |
98 | 98 | " # Create subgraph for this cycle to count edges\n", |
99 | 99 | " cycle_subgraph = G.subgraph(cycle)\n", |
100 | | - " \n", |
| 100 | + "\n", |
101 | 101 | " # Count total edges\n", |
102 | 102 | " total_edges = cycle_subgraph.number_of_edges()\n", |
103 | 103 | " print(f\"Total number of imports in cycle: {total_edges}\")\n", |
104 | | - " \n", |
| 104 | + "\n", |
105 | 105 | " # Count dynamic and static imports separately\n", |
106 | | - " dynamic_imports = sum(1 for u, v, data in cycle_subgraph.edges(data=True) \n", |
107 | | - " if data.get('color') == 'red')\n", |
108 | | - " static_imports = sum(1 for u, v, data in cycle_subgraph.edges(data=True) \n", |
109 | | - " if data.get('color') == 'black')\n", |
110 | | - " \n", |
| 106 | + " dynamic_imports = sum(1 for u, v, data in cycle_subgraph.edges(data=True) if data.get(\"color\") == \"red\")\n", |
| 107 | + " static_imports = sum(1 for u, v, data in cycle_subgraph.edges(data=True) if data.get(\"color\") == \"black\")\n", |
| 108 | + "\n", |
111 | 109 | " print(f\"Number of dynamic imports: {dynamic_imports}\")\n", |
112 | 110 | " print(f\"Number of static imports: {static_imports}\")" |
113 | 111 | ] |
|
133 | 131 | "import_loop = cycles[0]\n", |
134 | 132 | "cycle_list = list(import_loop)\n", |
135 | 133 | "\n", |
| 134 | + "\n", |
136 | 135 | "def create_single_loop_graph(cycle):\n", |
137 | 136 | " cycle_graph = nx.MultiDiGraph() # Changed to MultiDiGraph to support multiple edges\n", |
138 | 137 | " cycle = list(cycle)\n", |
|
144 | 143 | " # For each edge between these nodes\n", |
145 | 144 | " for edge_key, edge_data in edge_data_dict.items():\n", |
146 | 145 | " # Add edge with all its attributes to cycle graph\n", |
147 | | - " cycle_graph.add_edge(cycle[i], \n", |
148 | | - " cycle[j], \n", |
149 | | - " **edge_data)\n", |
| 146 | + " cycle_graph.add_edge(cycle[i], cycle[j], **edge_data)\n", |
150 | 147 | " return cycle_graph\n", |
| 148 | + "\n", |
| 149 | + "\n", |
151 | 150 | "cycle_graph = create_single_loop_graph(cycle_list)\n", |
152 | 151 | "visualize_graph(cycle_graph)" |
153 | 152 | ] |
|
239 | 238 | "def find_problematic_import_loops(G, sccs):\n", |
240 | 239 | " \"\"\"Find cycles where files have both static and dynamic imports between them.\"\"\"\n", |
241 | 240 | " problematic_cycles = []\n", |
242 | | - " \n", |
| 241 | + "\n", |
243 | 242 | " for i, scc in enumerate(sccs):\n", |
244 | | - " if i == 2: # skipping the second import loop as it's incredibly long (it's also invalid)\n", |
| 243 | + " if i == 2: # skipping the second import loop as it's incredibly long (it's also invalid)\n", |
245 | 244 | " continue\n", |
246 | 245 | " mixed_import_files = {} # (from_file, to_file) -> {dynamic: count, static: count}\n", |
247 | | - " \n", |
| 246 | + "\n", |
248 | 247 | " # Check all file pairs in the cycle\n", |
249 | 248 | " for from_file in scc:\n", |
250 | 249 | " for to_file in scc:\n", |
251 | 250 | " if G.has_edge(from_file, to_file):\n", |
252 | 251 | " # Get all edges between these files\n", |
253 | 252 | " edges = G.get_edge_data(from_file, to_file)\n", |
254 | | - " \n", |
| 253 | + "\n", |
255 | 254 | " # Count imports by type\n", |
256 | | - " dynamic_count = sum(1 for e in edges.values() if e['color'] == 'red')\n", |
257 | | - " static_count = sum(1 for e in edges.values() if e['color'] == 'black')\n", |
258 | | - " \n", |
| 255 | + " dynamic_count = sum(1 for e in edges.values() if e[\"color\"] == \"red\")\n", |
| 256 | + " static_count = sum(1 for e in edges.values() if e[\"color\"] == \"black\")\n", |
| 257 | + "\n", |
259 | 258 | " # If we have both types between same files, this is problematic\n", |
260 | 259 | " if dynamic_count > 0 and static_count > 0:\n", |
261 | | - " mixed_import_files[(from_file, to_file)] = {\n", |
262 | | - " 'dynamic': dynamic_count,\n", |
263 | | - " 'static': static_count,\n", |
264 | | - " 'edges': edges\n", |
265 | | - " }\n", |
266 | | - " \n", |
| 260 | + " mixed_import_files[(from_file, to_file)] = {\"dynamic\": dynamic_count, \"static\": static_count, \"edges\": edges}\n", |
| 261 | + "\n", |
267 | 262 | " if mixed_import_files:\n", |
268 | | - " problematic_cycles.append({\n", |
269 | | - " 'files': scc,\n", |
270 | | - " 'mixed_imports': mixed_import_files,\n", |
271 | | - " 'index': i\n", |
272 | | - " })\n", |
273 | | - " \n", |
| 263 | + " problematic_cycles.append({\"files\": scc, \"mixed_imports\": mixed_import_files, \"index\": i})\n", |
| 264 | + "\n", |
274 | 265 | " # Print findings\n", |
275 | 266 | " print(f\"Found {len(problematic_cycles)} cycles with mixed imports:\")\n", |
276 | 267 | " for i, cycle in enumerate(problematic_cycles):\n", |
277 | 268 | " print(f\"\\n⚠️ Problematic Cycle #{i + 1}:\")\n", |
278 | | - " print(f\"\\n⚠️ Index #{cycle[\"index\"]}:\")\n", |
| 269 | + " print(f\"\\n⚠️ Index #{cycle['index']}:\")\n", |
279 | 270 | " print(f\"Size: {len(cycle['files'])} files\")\n", |
280 | | - " \n", |
281 | | - " for (from_file, to_file), data in cycle['mixed_imports'].items():\n", |
| 271 | + "\n", |
| 272 | + " for (from_file, to_file), data in cycle[\"mixed_imports\"].items():\n", |
282 | 273 | " print(\"\\n📁 Mixed imports detected:\")\n", |
283 | 274 | " print(f\" From: {from_file}\")\n", |
284 | 275 | " print(f\" To: {to_file}\")\n", |
285 | 276 | " print(f\" Dynamic imports: {data['dynamic']}\")\n", |
286 | 277 | " print(f\" Static imports: {data['static']}\")\n", |
287 | | - " \n", |
| 278 | + "\n", |
288 | 279 | " return problematic_cycles\n", |
289 | 280 | "\n", |
| 281 | + "\n", |
290 | 282 | "problematic_loops = find_problematic_import_loops(G, cycles)" |
291 | 283 | ] |
292 | 284 | }, |
|
388 | 380 | " if imp.imported_symbol:\n", |
389 | 381 | " symbols_to_move.add(imp.imported_symbol)\n", |
390 | 382 | "\n", |
391 | | - "#Move identified symbols to utils file\n", |
| 383 | + "# Move identified symbols to utils file\n", |
392 | 384 | "for symbol in symbols_to_move:\n", |
393 | 385 | " symbol.move_to_file(utils_file)\n", |
394 | 386 | "\n", |
|
0 commit comments