├── .gitignore ├── LICENSE ├── README.md ├── function_call_main.py ├── main.py ├── playground └── playground.ipynb ├── requirements.txt ├── testing-directories ├── analyzer.py ├── data_processor.py ├── main.py └── utils.py └── testing-files └── basic_python.py /.gitignore: -------------------------------------------------------------------------------- 1 | # Created by https://www.toptal.com/developers/gitignore/api/windows,python,macos,linux 2 | # Edit at https://www.toptal.com/developers/gitignore?templates=windows,python,macos,linux 3 | 4 | ### Linux ### 5 | *~ 6 | 7 | # temporary files which can be created if a process still has a handle open of a deleted file 8 | .fuse_hidden* 9 | 10 | # KDE directory preferences 11 | .directory 12 | 13 | # Linux trash folder which might appear on any partition or disk 14 | .Trash-* 15 | 16 | # .nfs files are created when an open file is removed but is still being accessed 17 | .nfs* 18 | 19 | ### macOS ### 20 | # General 21 | .DS_Store 22 | .AppleDouble 23 | .LSOverride 24 | 25 | # Icon must end with two \r 26 | Icon 27 | 28 | 29 | # Thumbnails 30 | ._* 31 | 32 | # Files that might appear in the root of a volume 33 | .DocumentRevisions-V100 34 | .fseventsd 35 | .Spotlight-V100 36 | .TemporaryItems 37 | .Trashes 38 | .VolumeIcon.icns 39 | .com.apple.timemachine.donotpresent 40 | 41 | # Directories potentially created on remote AFP share 42 | .AppleDB 43 | .AppleDesktop 44 | Network Trash Folder 45 | Temporary Items 46 | .apdisk 47 | 48 | ### macOS Patch ### 49 | # iCloud generated files 50 | *.icloud 51 | 52 | ### Python ### 53 | # Byte-compiled / optimized / DLL files 54 | __pycache__/ 55 | *.py[cod] 56 | *$py.class 57 | 58 | # C extensions 59 | *.so 60 | 61 | # Distribution / packaging 62 | .Python 63 | build/ 64 | develop-eggs/ 65 | dist/ 66 | downloads/ 67 | eggs/ 68 | .eggs/ 69 | lib/ 70 | lib64/ 71 | parts/ 72 | sdist/ 73 | var/ 74 | wheels/ 75 | share/python-wheels/ 76 | *.egg-info/ 77 | .installed.cfg 78 | *.egg 79 | MANIFEST 80 | 81 | # PyInstaller 82 | # Usually these files are written by a python script from a template 83 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 84 | *.manifest 85 | *.spec 86 | 87 | # Installer logs 88 | pip-log.txt 89 | pip-delete-this-directory.txt 90 | 91 | # Unit test / coverage reports 92 | htmlcov/ 93 | .tox/ 94 | .nox/ 95 | .coverage 96 | .coverage.* 97 | .cache 98 | nosetests.xml 99 | coverage.xml 100 | *.cover 101 | *.py,cover 102 | .hypothesis/ 103 | .pytest_cache/ 104 | cover/ 105 | 106 | # Translations 107 | *.mo 108 | *.pot 109 | 110 | # Django stuff: 111 | *.log 112 | local_settings.py 113 | db.sqlite3 114 | db.sqlite3-journal 115 | 116 | # Flask stuff: 117 | instance/ 118 | .webassets-cache 119 | 120 | # Scrapy stuff: 121 | .scrapy 122 | 123 | # Sphinx documentation 124 | docs/_build/ 125 | 126 | # PyBuilder 127 | .pybuilder/ 128 | target/ 129 | 130 | # Jupyter Notebook 131 | .ipynb_checkpoints 132 | 133 | # IPython 134 | profile_default/ 135 | ipython_config.py 136 | 137 | # pyenv 138 | # For a library or package, you might want to ignore these files since the code is 139 | # intended to run in multiple environments; otherwise, check them in: 140 | # .python-version 141 | 142 | # pipenv 143 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. 144 | # However, in case of collaboration, if having platform-specific dependencies or dependencies 145 | # having no cross-platform support, pipenv may install dependencies that don't work, or not 146 | # install all needed dependencies. 147 | #Pipfile.lock 148 | 149 | # poetry 150 | # Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control. 151 | # This is especially recommended for binary packages to ensure reproducibility, and is more 152 | # commonly ignored for libraries. 153 | # https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control 154 | #poetry.lock 155 | 156 | # pdm 157 | # Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control. 158 | #pdm.lock 159 | # pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it 160 | # in version control. 161 | # https://pdm.fming.dev/#use-with-ide 162 | .pdm.toml 163 | 164 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm 165 | __pypackages__/ 166 | 167 | # Celery stuff 168 | celerybeat-schedule 169 | celerybeat.pid 170 | 171 | # SageMath parsed files 172 | *.sage.py 173 | 174 | # Environments 175 | .env 176 | .venv 177 | env/ 178 | venv/ 179 | ENV/ 180 | env.bak/ 181 | venv.bak/ 182 | 183 | # Spyder project settings 184 | .spyderproject 185 | .spyproject 186 | 187 | # Rope project settings 188 | .ropeproject 189 | 190 | # mkdocs documentation 191 | /site 192 | 193 | # mypy 194 | .mypy_cache/ 195 | .dmypy.json 196 | dmypy.json 197 | 198 | # Pyre type checker 199 | .pyre/ 200 | 201 | # pytype static type analyzer 202 | .pytype/ 203 | 204 | # Cython debug symbols 205 | cython_debug/ 206 | 207 | # PyCharm 208 | # JetBrains specific template is maintained in a separate JetBrains.gitignore that can 209 | # be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore 210 | # and can be added to the global gitignore or merged into this file. For a more nuclear 211 | # option (not recommended) you can uncomment the following to ignore the entire idea folder. 212 | #.idea/ 213 | 214 | ### Python Patch ### 215 | # Poetry local configuration file - https://python-poetry.org/docs/configuration/#local-configuration 216 | poetry.toml 217 | 218 | # ruff 219 | .ruff_cache/ 220 | 221 | # LSP config files 222 | pyrightconfig.json 223 | 224 | ### Windows ### 225 | # Windows thumbnail cache files 226 | Thumbs.db 227 | Thumbs.db:encryptable 228 | ehthumbs.db 229 | ehthumbs_vista.db 230 | 231 | # Dump file 232 | *.stackdump 233 | 234 | # Folder config file 235 | [Dd]esktop.ini 236 | 237 | # Recycle Bin used on file shares 238 | $RECYCLE.BIN/ 239 | 240 | # Windows Installer files 241 | *.cab 242 | *.msi 243 | *.msix 244 | *.msm 245 | *.msp 246 | 247 | # Windows shortcuts 248 | *.lnk 249 | 250 | # End of https://www.toptal.com/developers/gitignore/api/windows,python,macos,linux -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2024 Vishal Padia 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # CodeFlowMapper 2 | 3 | CodeFlowMapper is an open-source tool that generates 3D visualizations of Python codebases, helping developers quickly understand and navigate complex projects. 4 | 5 | ## How does it look? 6 | ![CodeFlowMapper](https://github.com/user-attachments/assets/58cf8de9-bf0f-4af7-8c73-8c379ed48320) 7 | 8 | ## Features 9 | 10 | - **3D Visualization**: Generate interactive 3D maps of your Python codebase. 11 | - **File and Function Mapping**: View files as parent nodes and functions as child nodes. 12 | - **Customizable Omissions**: Exclude specific directories from visualization. 13 | - **Obsidian-like Interface**: Familiar and intuitive visualization style. 14 | 15 | ## Quick Start 16 | To run CodeFlowMapper, you can use the following command-line interface: 17 | 1. Clone the repository and navigate to the project directory: 18 | ```bash 19 | $ git clone https://github.com/Vishal-Padia/CodeFlowMapper 20 | $ cd CodeFlowMapper 21 | ``` 22 | 2. Create a virtual environment using the following command: 23 | ```bash 24 | $ python3 -m venv venv 25 | ``` 26 | 3. Activate the virtual environment: 27 | ```bash 28 | $ source venv/bin/activate 29 | ``` 30 | 4. Install the required dependencies: 31 | ```bash 32 | $ pip install -r requirements.txt 33 | ``` 34 | 5. Run the CodeFlowMapper script: 35 | ```bash 36 | $ python main.py 37 | ``` 38 | 6. Follow the prompts to input your project path and exclude directories. 39 | 40 | 7. Access the visualization at `http://localhost:5000`. 41 | 42 | **It takes some time to generate the visualization, so please be patient.** 43 | HAPPY VISUALIZING! 44 | 45 | 46 | ## Usage 47 | 48 | After launching, CodeFlowMapper will: 49 | - Download necessary models (first-time only). 50 | - Prompt for the path to your Python file or directory. 51 | - Ask for directories to exclude from the visualization. 52 | - Start a Flask server and generate the 3D visualization. 53 | 54 | ## Current Limitations 55 | 56 | - Python files and directories only 57 | - Basic Python feature support (no decorators or lambdas) 58 | - Static analysis only 59 | 60 | ## Roadmap 61 | - [ ] Add support for more programming languages 62 | - [ ] Enhance visualization with more features 63 | - [ ] Complex Python feature support 64 | - [x] AI-Powered Code Analysis 65 | 66 | ## Contributing 67 | We welcome contributions to CodeFlowMapper! If you have suggestions for improvements or bug fixes, please feel free to: 68 | 69 | - Fork the repository 70 | - Create a new branch `(git checkout -b feature/AmazingFeature)` 71 | - Commit your changes `(git commit -m 'Add some AmazingFeature')` 72 | - Push to the branch `(git push origin feature/AmazingFeature)` 73 | - Open a Pull Request 74 | 75 | ## License 76 | This project is licensed under the MIT License - see the LICENSE file for details. 77 | 78 | ## Contact 79 | Vishal Padia - vishalpadi9@gmail.com 80 | 81 | Project Link: https://github.com/Vishal-Padia/CodeFlowMapper 82 | -------------------------------------------------------------------------------- /function_call_main.py: -------------------------------------------------------------------------------- 1 | import os 2 | import ast 3 | import flask 4 | import networkx as nx 5 | from flask import Flask, render_template_string, jsonify 6 | 7 | app = Flask(__name__) 8 | 9 | G = None 10 | 11 | 12 | def network_to_visjs(G): 13 | nodes = [ 14 | {"id": node, "label": G.nodes[node].get("label", node)} for node in G.nodes() 15 | ] 16 | edges = [{"from": source, "to": target} for source, target in G.edges()] 17 | return {"nodes": nodes, "edges": edges} 18 | 19 | 20 | def parse_directory(directory_path: str, omit_dirs: list): 21 | python_files = [] 22 | for root, dirs, files in os.walk(directory_path): 23 | dirs[:] = [d for d in dirs if d not in omit_dirs] 24 | 25 | for file in files: 26 | if file.endswith(".py"): 27 | python_files.append(os.path.join(root, file)) 28 | return python_files 29 | 30 | 31 | def parse_file(file_path: str): 32 | with open(file_path, "r", encoding="utf-8") as f: 33 | tree = ast.parse(f.read()) 34 | return tree 35 | 36 | 37 | def extract_functions_and_imports(tree): 38 | functions = {} 39 | imports = set() 40 | for node in ast.walk(tree): 41 | if isinstance(node, ast.FunctionDef): 42 | functions[node.name] = {"calls": [], "line": node.lineno, "file": None} 43 | elif isinstance(node, ast.Import) or isinstance(node, ast.ImportFrom): 44 | for alias in node.names: 45 | imports.add(alias.name.split(".")[0]) # Add imported module names 46 | return functions, imports 47 | 48 | 49 | def analyze_function_calls(tree, functions): 50 | for node in ast.walk(tree): 51 | if isinstance(node, ast.Call): 52 | caller = None 53 | for parent in ast.walk(tree): 54 | if isinstance(parent, ast.FunctionDef) and node in ast.walk(parent): 55 | caller = parent.name 56 | break 57 | if caller and caller in functions: 58 | if isinstance(node.func, ast.Name): 59 | called_func = node.func.id 60 | functions[caller]["calls"].append(called_func) 61 | elif isinstance(node.func, ast.Attribute): 62 | called_func = node.func.attr 63 | if isinstance(node.func.value, ast.Name): 64 | object_name = node.func.value.id 65 | functions[caller]["calls"].append( 66 | f"{object_name}.{called_func}" 67 | ) 68 | else: 69 | functions[caller]["calls"].append(called_func) 70 | else: 71 | if hasattr(node.func, "id"): 72 | functions[caller]["calls"].append(node.func.id) 73 | elif hasattr(node.func, "attr"): 74 | functions[caller]["calls"].append(node.func.attr) 75 | 76 | 77 | def create_graph_with_directory_structure(functions, imports, file_paths): 78 | G = nx.DiGraph() 79 | 80 | directory_nodes = {} 81 | 82 | for file_path in file_paths: 83 | directory = os.path.dirname(file_path) 84 | module_name = os.path.basename(file_path).replace(".py", "") 85 | 86 | # Add directory node 87 | if directory not in directory_nodes: 88 | G.add_node(directory, label=directory, shape="box", color="lightblue") 89 | directory_nodes[directory] = True 90 | 91 | # Add file node 92 | G.add_node(file_path, label=module_name, shape="ellipse", color="lightgreen") 93 | G.add_edge(directory, file_path) 94 | 95 | for func, data in functions.items(): 96 | if data["file"] == file_path: 97 | G.add_node(func, module=module_name) 98 | G.add_edge(file_path, func) 99 | for call in data["calls"]: 100 | if call in functions: 101 | G.add_edge(func, call) 102 | 103 | for module in imports: 104 | G.add_node(module, module="import") 105 | 106 | return G 107 | 108 | 109 | @app.route("/") 110 | def index(): 111 | return render_template_string( 112 | """ 113 | 114 | 115 | 116 | Function Call Graph 117 | 118 | 125 | 126 | 127 |
128 | 171 | 172 | 173 | """ 174 | ) 175 | 176 | 177 | @app.route("/graph_data") 178 | def graph_data(): 179 | return jsonify(network_to_visjs(G)) 180 | 181 | 182 | def run_flask_app(): 183 | print("Starting the web server.") 184 | print( 185 | "Please open a web browser and go to http://127.0.0.1:5000/ to view the graph." 186 | ) 187 | app.run(debug=True) 188 | 189 | 190 | def create_graph_from_directory(directory_path, omit_dirs): 191 | global G 192 | python_files = parse_directory(directory_path, omit_dirs) 193 | 194 | functions = {} 195 | imports = set() 196 | 197 | for i, file_path in enumerate(python_files): 198 | tree = parse_file(file_path) 199 | file_functions, file_imports = extract_functions_and_imports(tree) 200 | 201 | for func_name, func_data in file_functions.items(): 202 | func_data["file"] = file_path 203 | 204 | functions.update(file_functions) 205 | imports.update(file_imports) 206 | analyze_function_calls(tree, functions) 207 | 208 | G = create_graph_with_directory_structure( 209 | functions, imports, python_files[: i + 1] 210 | ) 211 | 212 | print( 213 | f"Processing file {i+1}/{len(python_files)}: {os.path.basename(file_path)}" 214 | ) 215 | 216 | print("Graph creation completed.") 217 | 218 | 219 | def main(): 220 | directory_path = input("Enter the path to the directory: ") 221 | print(f"The input directory is: {directory_path}") 222 | omit_dirs = input("Enter the directories to omit (comma-separated): ").split(",") 223 | omit_list = [func.strip() for func in omit_dirs] 224 | create_graph_from_directory(directory_path, omit_list) 225 | run_flask_app() 226 | 227 | 228 | if __name__ == "__main__": 229 | main() 230 | -------------------------------------------------------------------------------- /main.py: -------------------------------------------------------------------------------- 1 | import os 2 | import ast 3 | import flask 4 | import networkx as nx 5 | from flask import Flask, render_template_string, jsonify, request 6 | from transformers import pipeline 7 | 8 | app = Flask(__name__) 9 | 10 | G = None 11 | code_contents = {} 12 | 13 | # Initialize the code explanation model 14 | explainer = pipeline("text2text-generation", model="facebook/bart-large-cnn") 15 | 16 | 17 | def network_to_visjs(G: nx.DiGraph): 18 | """ 19 | Convert a NetworkX graph to a dictionary format that can be used by vis.js. 20 | """ 21 | nodes = [ 22 | { 23 | "id": node, 24 | "label": G.nodes[node].get("label", node), 25 | "color": G.nodes[node].get("color", "#FFFFFF"), 26 | "shape": G.nodes[node].get("shape", "dot"), 27 | "size": G.nodes[node].get("size", 10), 28 | } 29 | for node in G.nodes() 30 | ] 31 | edges = [ 32 | {"from": source, "to": target, "color": "#FFFFFF"} 33 | for source, target in G.edges() 34 | ] 35 | return {"nodes": nodes, "edges": edges} 36 | 37 | 38 | def parse_directory(directory_path: str, omit_dirs: list): 39 | """ 40 | Parse a directory and return a list of Python files in the directory. 41 | """ 42 | python_files = [] 43 | for root, dirs, files in os.walk(directory_path): 44 | dirs[:] = [d for d in dirs if d not in omit_dirs] 45 | for file in files: 46 | if file.endswith(".py"): 47 | python_files.append(os.path.join(root, file)) 48 | return python_files 49 | 50 | 51 | def parse_file(file_path: str): 52 | """ 53 | Parse a Python file and return the AST (Abstract Syntax Tree) representation of the code. 54 | """ 55 | with open(file_path, "r", encoding="utf-8") as f: 56 | content = f.read() 57 | code_contents[file_path] = content 58 | tree = ast.parse(content) 59 | return tree 60 | 61 | 62 | def extract_functions_and_imports(tree: ast.AST): 63 | """ 64 | Extract function definitions and imports from the AST of a Python file. 65 | Also track function calls for execution path visualization. 66 | """ 67 | functions = {} 68 | imports = set() 69 | function_stack = [] 70 | current_func = None 71 | 72 | for node in ast.walk(tree): 73 | if isinstance(node, ast.FunctionDef): 74 | current_func = node.name 75 | functions[node.name] = {"calls": [], "line": node.lineno, "file": None} 76 | function_stack.append(current_func) 77 | 78 | elif isinstance(node, ast.Call) and current_func: 79 | if isinstance(node.func, ast.Name): 80 | functions[current_func]["calls"].append(node.func.id) 81 | 82 | elif isinstance(node, ast.Import) or isinstance(node, ast.ImportFrom): 83 | for alias in node.names: 84 | imports.add(alias.name.split(".")[0]) 85 | 86 | elif isinstance(node, ast.Return): 87 | if function_stack: 88 | function_stack.pop() 89 | current_func = function_stack[-1] if function_stack else None 90 | 91 | return functions, imports 92 | 93 | 94 | def create_graph_with_directory_structure( 95 | functions: dict, imports: set, file_paths: list 96 | ): 97 | """ 98 | Create a directed graph representing the directory structure of Python files, function calls, and imports. 99 | """ 100 | G = nx.DiGraph() 101 | 102 | for file_path in file_paths: 103 | module_name = os.path.basename(file_path).replace(".py", "") 104 | G.add_node(file_path, label=module_name, color="#FF6B6B", shape="dot", size=15) 105 | 106 | for module in imports: 107 | G.add_node(module, label=module, color="#4ECDC4", shape="dot", size=10) 108 | 109 | for func_name, func_data in functions.items(): 110 | G.add_node(func_name, label=func_name, color="#FFFFFF", shape="dot", size=7) 111 | if func_data["file"]: 112 | G.add_edge(func_data["file"], func_name) 113 | 114 | for called_func in func_data["calls"]: 115 | if called_func in functions: 116 | G.add_edge(func_name, called_func) 117 | 118 | return G 119 | 120 | 121 | @app.route("/") 122 | def index(): 123 | """ 124 | A web page displaying the graph with search and code execution path visualization. 125 | """ 126 | return render_template_string( 127 | """ 128 | 129 | 130 | 131 | Graph Visualization 132 | 133 | 145 | 146 | 147 |
148 | 149 | 150 |
151 |
152 |
153 |
154 | 155 |

156 |                     
157 |                     
158 |
159 |
160 | 286 | 287 | 288 | """ 289 | ) 290 | 291 | 292 | @app.route("/graph_data") 293 | def graph_data(): 294 | """ 295 | Serve the graph data for visualization. 296 | """ 297 | visjs_data = network_to_visjs(G) 298 | return jsonify(visjs_data) 299 | 300 | 301 | @app.route("/get_code") 302 | def get_code(): 303 | """ 304 | Serve the code for a clicked node. 305 | """ 306 | node = request.args.get("node") 307 | return jsonify({"code": code_contents.get(node, "Code not found.")}) 308 | 309 | 310 | @app.route("/explain_code", methods=["POST"]) 311 | def explain_code(): 312 | """ 313 | Explain the code using AI when the user clicks the 'Explain' button. 314 | """ 315 | code = request.json.get("code") 316 | explanation = explainer(code)[0]["summary_text"] 317 | return jsonify({"explanation": explanation}) 318 | 319 | 320 | if __name__ == "__main__": 321 | # Sample directory to parse (replace with your directory) 322 | directory_path = input("Enter the path to the directory to parse: ") 323 | print(f"Parsing directory: {directory_path}") 324 | 325 | omit_dirs = input("Enter the directories to omit (comma-separated): ").split(",") 326 | print(f"Omitting directories: {omit_dirs}") 327 | 328 | python_files = parse_directory(directory_path, omit_dirs) 329 | 330 | # Parse files and extract functions and imports 331 | all_functions = {} 332 | all_imports = set() 333 | for file_path in python_files: 334 | tree = parse_file(file_path) 335 | functions, imports = extract_functions_and_imports(tree) 336 | for func_name, func_data in functions.items(): 337 | func_data["file"] = file_path 338 | all_functions[func_name] = func_data 339 | all_imports.update(imports) 340 | 341 | # Create the graph 342 | G = create_graph_with_directory_structure(all_functions, all_imports, python_files) 343 | 344 | # Run the Flask app 345 | app.run(debug=True) 346 | -------------------------------------------------------------------------------- /playground/playground.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "# Purpose of this file:\n", 8 | "\n", 9 | "The file is just used for debugging purposes. It is used to check if the code is working as expected." 10 | ] 11 | }, 12 | { 13 | "cell_type": "code", 14 | "execution_count": 1, 15 | "metadata": {}, 16 | "outputs": [], 17 | "source": [ 18 | "# importing modules\n", 19 | "import ast\n", 20 | "import networkx as nx\n", 21 | "import matplotlib.pyplot as plt" 22 | ] 23 | }, 24 | { 25 | "cell_type": "code", 26 | "execution_count": 2, 27 | "metadata": {}, 28 | "outputs": [], 29 | "source": [ 30 | "def parse_file(file_path: str):\n", 31 | " \"\"\"\n", 32 | " Parse the file and return the abstract syntax tree\n", 33 | "\n", 34 | " Args:\n", 35 | " file_path: The path to the file to be parsed\n", 36 | "\n", 37 | " Returns:\n", 38 | " The abstract syntax tree of the file\n", 39 | " \"\"\"\n", 40 | " with open(file_path, 'r') as f:\n", 41 | " tree = ast.parse(f.read())\n", 42 | " return tree" 43 | ] 44 | }, 45 | { 46 | "cell_type": "code", 47 | "execution_count": 4, 48 | "metadata": {}, 49 | "outputs": [], 50 | "source": [ 51 | "def extract_functions(tree):\n", 52 | " \"\"\" \n", 53 | " Extract all the functions from the abstract syntax tree\n", 54 | "\n", 55 | " Args:\n", 56 | " tree: The abstract syntax tree of the file\n", 57 | "\n", 58 | " Returns:\n", 59 | " A dictionary of functions with the function name as the key and the function's\n", 60 | " \"\"\"\n", 61 | " functions = {}\n", 62 | " for node in ast.walk(tree):\n", 63 | " if isinstance(node, ast.FunctionDef):\n", 64 | " functions[node.name] = {\n", 65 | " \"calls\" : [],\n", 66 | " 'line' : node.lineno\n", 67 | " }\n", 68 | " return functions" 69 | ] 70 | }, 71 | { 72 | "cell_type": "code", 73 | "execution_count": 8, 74 | "metadata": {}, 75 | "outputs": [], 76 | "source": [ 77 | "def analyze_function_calls(tree, functions):\n", 78 | " \"\"\" \n", 79 | " Analyze the function calls in the abstract syntax tree and update the functions dictionary\n", 80 | "\n", 81 | " Args:\n", 82 | " tree: The abstract syntax tree of the file\n", 83 | " functions: The dictionary of functions with the function name as the key and the function's\n", 84 | "\n", 85 | " Returns:\n", 86 | " None\n", 87 | " \"\"\"\n", 88 | " for node in ast.walk(tree):\n", 89 | " if isinstance(node, ast.Call) and isinstance(node.func, ast.Name):\n", 90 | " caller = None\n", 91 | " for parent in ast.walk(tree):\n", 92 | " if isinstance(parent, ast.FunctionDef) and node in ast.walk(parent):\n", 93 | " caller = parent.name\n", 94 | " break\n", 95 | " if caller and node.func.id in functions:\n", 96 | " functions[caller]['calls'].append(node.func.id)" 97 | ] 98 | }, 99 | { 100 | "cell_type": "code", 101 | "execution_count": 9, 102 | "metadata": {}, 103 | "outputs": [], 104 | "source": [ 105 | "def create_graph(functions):\n", 106 | " \"\"\" \n", 107 | " Create a directed graph of the functions and their calls\n", 108 | "\n", 109 | " Args:\n", 110 | " functions: The dictionary of functions with the function name as the key and the function's\n", 111 | "\n", 112 | " Returns:\n", 113 | " A directed graph of the functions and their calls\n", 114 | " \"\"\"\n", 115 | " G = nx.DiGraph()\n", 116 | " for func, data in functions.items():\n", 117 | " G.add_node(func)\n", 118 | " for call in data['calls']:\n", 119 | " G.add_edge(func, call)\n", 120 | " return G" 121 | ] 122 | }, 123 | { 124 | "cell_type": "code", 125 | "execution_count": 10, 126 | "metadata": {}, 127 | "outputs": [], 128 | "source": [ 129 | "def visualize_graph(G):\n", 130 | " \"\"\" \n", 131 | " Visualize the directed graph of the functions and their calls\n", 132 | "\n", 133 | " Args:\n", 134 | " G: A directed graph of the functions and their calls\n", 135 | "\n", 136 | " Returns:\n", 137 | " None\n", 138 | " \"\"\"\n", 139 | " plt.figure(figsize=(15, 10)) # Increase figure size\n", 140 | " pos = nx.spring_layout(G, k=0.9, iterations=50) # Adjust layout for more spacing\n", 141 | " \n", 142 | " # Draw nodes\n", 143 | " nx.draw_networkx_nodes(G, pos, node_color='lightblue', node_size=3000, alpha=0.8)\n", 144 | " nx.draw_networkx_labels(G, pos, font_size=10, font_weight=\"bold\")\n", 145 | " \n", 146 | " # Draw edges\n", 147 | " nx.draw_networkx_edges(G, pos, edge_color='gray', arrows=True, arrowsize=20)\n", 148 | " \n", 149 | " # Add edge labels (function names)\n", 150 | " edge_labels = {(u, v): u for (u, v) in G.edges()}\n", 151 | " nx.draw_networkx_edge_labels(G, pos, edge_labels=edge_labels, font_size=8)\n", 152 | " \n", 153 | " plt.title(\"Function Call Graph\", fontsize=16)\n", 154 | " plt.axis('off')\n", 155 | " plt.tight_layout()\n", 156 | " plt.show()" 157 | ] 158 | }, 159 | { 160 | "cell_type": "code", 161 | "execution_count": 11, 162 | "metadata": {}, 163 | "outputs": [ 164 | { 165 | "data": { 166 | "image/png": "iVBORw0KGgoAAAANSUhEUgAABdEAAAPdCAYAAABlRyFLAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjkuMiwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy8hTgPZAAAACXBIWXMAAA9hAAAPYQGoP6dpAAEAAElEQVR4nOzdd3yV9fn/8fd9dvZghRXCJiAIEQUHikURR0XFgdY966j60zrqbq2zfnFUq7VaW2tttVj3RAUFBVH2CCNsCCFwTvY4477v3x/hpAQSZpKT8Xo+Sg3n3OO6D3k8zjnvc53rY9i2bQsAAAAAAAAAAOzBEesCAAAAAAAAAABoqQjRAQAAAAAAAABoACE6AAAAAAAAAAANIEQHAAAAAAAAAKABhOgAAAAAAAAAADSAEB0AAAAAAAAAgAYQogMAAAAAAAAA0ABCdAAAAAAAAAAAGkCIDgAAAAAAAABAAwjRAQAA2omsrCwZhrHXP88880ysy2w069evl2EYysrKinUp+8W2bU2dOlUXXnihevfurYSEBPl8PvXs2VNnnHGGXn75ZZWVlTXKuaK/C+vXr69z++WXXy7DMPS3v/3toI5bVVWlF198UT//+c/Vs2dPxcfHKy4uTj169NApp5yixx9/XGvXrj30C2gGre33BwAAAE3HFesCAAAA0LyOPfZY9evXr977Bg8e3MzVHLysrCxt2LBB69ata/VB59q1a3XuuedqwYIFkqTs7GyNHz9eXq9XW7Zs0bRp0/Txxx/r3nvv1U8//aRevXrFuOI9TZs2TZdccom2bdsmh8Oh4cOH66ijjpLH41FBQYG+++47ffHFF7rvvvv05JNP6rbbbot1yQAAAMB+IUQHAABoZ66++mpdfvnlsS6jyXXv3l25ublyu92xLmWvNm7cqKOPPlqFhYU6+uij9dJLL2nYsGF1tikrK9OLL76oRx55REVFRS0uRP/oo4901llnyTRNXXHFFfr973+vbt261dkmHA7rgw8+0KOPPqpVq1bFqFIAAADgwBGiAwAAoE1yu90aNGhQrMvYp4svvliFhYU66qij9PXXX8vn8+2xTVJSku68806dc845SkhIiEGVDfP7/br44otlmqb+3//7f5oyZUq927ndbk2aNElnnXWWFi5c2LxFAgAAAIeAmegAAADYw75mY//tb3+TYRh7dLTventFRYV+85vfqF+/fvJ6vcrIyNBll12mLVu2NHjeLVu26I477tDQoUOVlJSkhIQEDRgwQJdffrm+//77OufYsGGDJKl379515rrPmDFD0r5nWm/evFm/+tWv1L9/f/l8PqWkpOjYY4/Vn//8Z5mmuddrPphrq88333yjmTNnSpJeeumlegP0XfXr109du3at/fv27dv13HPP6bTTTlPv3r0VFxen5ORkjRw5Uk888YSqq6sPqJ6D8cc//lElJSXKyMjQ448/vs/tnU6njjjiiDq37fpvZZqmpkyZohEjRigxMVGGYdRut3z5cj344IM69thj1b17d3k8HnXo0EEnnXSS3n777XrPN2PGDBmGobFjx6qyslL33HOP+vXrJ5/Pp27duumqq67a57+bbdt6+eWXdcQRRyghIUEpKSkaP368Zs+evR+PEAAAAFo7OtEBAADQ6EpKSnTMMcdo48aNGjNmjA477DDNnj1br7/+ur755hstWrRIKSkpdfb56quvdO6556q4uFidO3fWuHHj5PF4tH79er355puSpGOOOUb9+vXTZZddpqlTp6qiokKTJk1SYmJi7XEyMjL2Wd+PP/6oCRMmKBAIKDMzU2eddZZKSko0Y8YMff/993r33Xf1wQcfyOPxNMq1NeT999+XJA0dOlQjRozYr3129fnnn+uWW25R9+7d1a9fP40ePVrbt2/XDz/8oLvvvlvvv/++pk+fLq/Xe8DH3l/Razj//PPrfbwOhG3bOuecc/TZZ59pzJgxys7O1rJly2rvnzJlil599VUNGjRIQ4cOVWpqqjZu3Kjp06frq6++0pw5cxrshA+FQho3bpwWL16ssWPHKicnR7NmzdJf//pXffLJJ/r222/Vv3//eve94oor9Oabb2rMmDE644wztHDhQk2bNk3ffvutvvnmG40aNeqQrhsAAAAtGyE6AAAAGt17772nU045RTNnzlRycrIkqaioSD/72c+0cOFC/elPf9JvfvOb2u03bdqkSZMmqaSkRHfffbd++9vf1glkCwsLa+doH3fccTruuOM0Y8YMVVRU6KmnnjqghUWDwaDOO+88BQIB/fKXv9Rzzz1XOzd97dq1GjdunD7//HP99re/1SOPPHLI17Y3P/30kyTpyCOP3O/6d3XEEUdo9uzZGj16dJ3bi4qKNHnyZH3xxRd67rnndMcddxzU8fclEolo8eLFkg7+Gna1ceNGWZalJUuWaMCAAXvcf8kll+iee+5Rnz596ty+cuVKnXTSSXr66ac1efJkHXXUUXvsO3v2bPXr10+5ubnKzMyUJFVXV+viiy/WO++8o0svvbTezvINGzZoxowZWrp0aW1Npmnq2muv1V//+lc98MAD+vzzzw/52gEAANByMc4FAACgnbniiivqjD+J/hk7dmyjnSMhIUGvvfZabcgsSWlpabr77rslSV9++WWd7adMmaKSkhL9/Oc/12OPPbZHR3Pnzp113HHHNUpt//nPf7RhwwZ169ZNzzzzTJ2FR/v06aOnnnpKUs2YkvrGoRzote3N9u3bJdVc38HIzs7eI0CP1vPHP/5RUs31NpVAICDLsiRJnTp1qnebF154QZdffvkefxry6KOP1hugS9IJJ5ywR4AuSQMHDtT9998vSZo6dWqDx37qqadqA3RJ8vl8+tOf/qT4+HjNmTOndmTQ7v74xz/WqcnpdNZ+wPLNN98oHA43eE4AAAC0fnSiAwAAtDPHHnus+vXrt8ftjbkI58iRI+vM7o7Kzs6WpD1mUH/22WeSpGuvvbbRamhIdGb65MmT6x1zcs455ygtLU1FRUWaN2+ejj322Dr3H+i1NTXTNGvH0GzdulVVVVWybVu2bUuq6dKOpenTp+udd97Z4/aG5u1PmjRpr8crLy/Xp59+qgULFmjHjh0KhUKSpK1bt0pq+HpTU1N15pln7nF7586dNWHCBP33v//VjBkzdMwxx9S53+VyacKECXvsl5GRUft74vf792uMEAAAAFonQnQAAIB25uqrr95rJ3Bj2LXbd1fR7u3dO7yji4Q2ZpDfkGjI3bt373rvNwxDvXv3VlFRUb2B+IFe29506tRJK1asUGFh4X7vs6vVq1fr7LPPrjM3fHelpaUHdez9kZ6eLsMwZNt2bVf97nbtDN+8ebN69uzZ4PE6d+6s+Pj4Bu//8MMPdcUVV8jv9ze4TUPXm5WVVWeR0l1Ffxc2b968x31du3at822FXSUnJ6uoqKhZFnAFAABA7DDOBQAAAAcsOsKjIQ5H232Z2ZjXdsQRR0iqWej0YJx77rlatmyZzjjjDH377be1ndm2bSsYDDZanQ1xuVwaNmyYpP/Ndz8UcXFxDd63ZcsWXXDBBfL7/brzzju1aNEilZSUyDRN2bZdO5c82oF/MOrbty3/LgMAAGD/8IoQAAAAe4jOJC8rK6v3/mjneGOJdnevWLGiUY9bn+7du0uqWUS0IevWrauzbVOZOHGiJGnJkiVasGDBAe27YsUKLV68WJ07d9a7776rMWPGqEOHDrVd06tXr270eusTHZHy9ttvN+ls8A8//FBVVVU6++yz9cQTT2jYsGFKTk6uDbn3db3r16/f5309evRorHIBAADQhhCiAwAAYA/R8Dg3N3eP+2zb1qefftqo54vOnP7LX/6y3/tEg/5IJHJA54ouoPrWW2/VO4bj3XffVVFRkZKSkmo7xZvK2LFja2euX3/99fvsHl+zZk3t7O9AICBJ6tatm1yuPac0vvHGG41cbf1uvvlmJScna+vWrbr33nub7DzR6+3Vq9ce99m2rTfffHOv+xcXF+vDDz/c4/bt27fXzuRvzMV1AQAA0HYQogMAAGAPJ510kiTpH//4h5YvX157ezgc1l133XXQ40cacttttykpKUkffPCB7rvvvj06mgsLCzVr1qw6t0W7hvc2D7w+5513njIzM5Wfn6/bbrutTgi/bt063X777ZKkX/3qV/L5fAdzOQfkjTfeUMeOHfXDDz/oZz/7mZYsWbLHNhUVFZoyZYqOOOIIbdu2TZI0YMAAOZ1OLVmypHax1KgPP/xQTz/9dJPXLkkdO3bU66+/LofDoT/84Q+65ppraoP+Xdm2re++++6gzxNduHXq1Kl1jm+aph544AF9//33+zzG7bffXmfueTAY1I033qiKigodddRReywiCwAAAEgsLAoAAIB6HHvssZo4caLef/99jRw5Uscdd5zi4uI0f/58lZaW6pZbbtGzzz7baOfLzMzU1KlTde655+qRRx7RK6+8oqOPPlput1sbNmzQggULdNFFF+m4446r3WfSpEmaPn26Lr74Yo0fP15paWmSpDvuuEMDBw5s8Fxer1dTp07VhAkT9OKLL+qTTz7R6NGjVVZWpq+//lrV1dU65ZRT9OCDDzba9e1NVlaWZs+erUmTJun777/XsGHDNHjwYA0aNEgej0dbtmzR3LlzFQwG1aVLF6Wnp0uqCa9vuukmPfvssxo3bpzGjBmjbt26aeXKlZo/f77uu+8+/f73v2+Wa5g4caI+/vhjXXrppXrllVf02muvafjw4crKylJcXJz8fr8WLFiggoICORwOXXzxxQd8jp///Oc64ogj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znnvvffqpZde0qmnnqrf//73+vrrr3XOOefo7bff1rRp0zRt2jQlJCToyy+/VGZmpnw+n9LS0nTSSSfp0UcflWEY+xwxY5omQTuAdoUEsgVK9bnVFp6KEhLi5fN6G+wxtyW5nC5ZB9F1b0tK9bKoKAAAANqPgoIC/eMf/9A///lPxcXF6eqrr9akSZP2+1ugaHmiIfqKFSt0+OGHq1u3burYsaMkKT8/X926dZNUE1jarfjbyu2VaZqSpJNPPllPPfWUJOnGG2/UCy+8oEGDBunoo49u9HOuXr1aFRUVWr58uaZMmaKtW7dqyJAhGjp0qMaOHatZs2bJ5/Pptttu06effqrXXntNkvTKK6/o8ssv15FHHqmbb755n+dxOp1yOBwKBAK66qqrVFVV1ejXAgAtCbMwWqAuCV5tKquSadlyOlrLZPT6GEpNTVXh9u2SZdWZj25Icjidqg5WK7itWomJiUpISNyvr7KZli3DkDISvU1WOQAAANBSlJaWavr06Vq4cKE6dOigCy64QAMHDmTR0DYg+m8YFxcnt9utKVOm6LDDDpMkffXVV+rXr98e26L1iHZzX3311br++uuVn5+vU045RY899piOOeaYJjlndXW1Hn/8ca1evVqDBw9WJBLRddddJ5fLpSuvvFK33HKL3n//fY0aNUrjx4/X2LFjtW7dOm3evFlnnXWWQqFQvWODogvfzps3T2+99ZYqKir02GOP6e2339YPP/xQ+02YaHd6Yy6WCgAtgWHzcXaLY9u2Zm7yqzxkyudq/fMMg8Fq+QOBPW7v1LGjnE6nysrKVVlZIYfDocSkJMXHx8vYy4z06oipRI9LY3qm80ISAAAAbVYwGNR3332n2bNny+PxaOzYscrJyWHmeRsTDSdfffVVPf300+rVq5dSUlLkdrt11113afDgwbXboPVat26dUlJSmm1xWL/fr88++0zHHHOMsrKy6vz+zJo1S2+//bZGjBihK664Qv/+97/14osv6ptvvtnrMVevXq2jjz5aDz30kPLz81VdXa0PP/xQt9xyi2666aY9tuf3FkBbQojeQq0rrtTyHWXyOR1t4kmntLREFRUVslXThe71+ZSe9r8XD6YZUVlZmSqrquRyOpWUlLTzk+y6127btqpNS4M7Jql3anyzXgMAAADQHKKLhs6YMUPBYFCjR4/Wsccee1BrCaH1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ALfffnuAvwMR+aaysjIAzUSXM8owDNLS0liwYAFnn302Tqcz0CWJABBstxHmsFPj8YHt+Nt3VH7TItLpIMiuJ6m10DgXEWm16uvrWbBgAUuWLCEoKIhJkyaRnp5OUJAW1hARkfapoaGBP/3pTzgcDuLi4ujduzcXXHABK1euJDU1FYC7776bXbt28fnnn7Nt2zY+++wzrr32WgV0Iq3UwoULKSgo4Ne//nWbnP8rbVdlZSXPPvss559/vkYJSauy+kAVO6vqCXXYj79xB1Xv89MrKpTUhKhAlyKHKUQXkVavurqaOXPmUFRURHh4OFOnTiUtLe1bi6yJiIi0daZpEh0dza233sqzzz4LNIbma9euZe7cuQDs3buXxMREli9frlBEpA347LPP2LNnD3feeWegS5EO6N1336W+vp7bbrst0KWINDtQ62b5vgqCDBt2m04ufpPftPBaJundYogP01UkrYUSKBFp9SIjIznvvPO455576N27N59//jkvvvgia9euRecBRUSkPWh6P7PZbHz00UfNgTnAU089RXFxMV999RUA3bp1Y/v27QrQRdqIsrIyzUOXgElLS2P37t0cOHAg0KWINIsPCyYiyI7XNANdSqvkNU0ighx01oKirYpCdBFpM+Li4rj00ku588476dSpEzNnzuTVV19l8+bNCtNFRKTNcrlcR/192rRpVFRU8OmnnwIQFhbGPffcw6OPPtq8Ta9evc5ojSJy6hSiSyA1rZ9RWFgY6FJEmhmGQc+oMCzQsfw3WJaFBfSMCtUIsFZGIbqItDldu3blmmuu4aabbiIoKIh3332Xt956i127dgW6NBERkRN24MAB3n33XV5++WVqa2uPOoj89NNPue+++5r//qc//Yn58+cHokwR+QG8Xi9VVVVas0ACxm63M3z4cFatWoXP5wt0OSLNukc4CbIZeE2F6EfymhZBNoPuESGBLkW+QSG6iLRZvXv35uabb+bqq6+moaGBv/3tb7z33nu6VFFERFq16upqPvvsM/7yl7/gcrm45JJLCA8PP6rbaOjQofTs2ZN58+YFsFIR+aHKysoAFKJLQKWlpVFfX8+GDRsCXYpIM6fDTreIEPyWpW70wyzLwm9ZdI8IwelQZNvaOAJdgIjID2EYBklJSQwcOJA1a9aQn5/Pyy+/zLBhw8jIyCA2NjbQJYqIiADg8XhYuHAhCxcuxOFwcNZZZzF69Gjsdvsxt58zZ8533iYibUPTuCaNc5FAio+Pp2fPnhQWFjJkyJBAlyPSrEdUKKXVDfgtC4dGl+C3LGyGQWJUaKBLkWNQiC4i7YJhGKSmpjJ48GAKCwuZO3cua9asYdSoUUyZMoWIiIhAlygiIh2UaZoUFxeTn59PfX09Y8eOZfLkyYSEfP9lugrQRdq+srIynE4nYWFhgS5FOri0tDQ+/fRTKioqiImJCXQ5IgDEOB3EhDgoq/diN6wOPQPcsiy8pkVcaBAxTsW1rZF+KiLSrtjtdtLT0xk+fDhLly5lwYIFFBcXM27cOCZMmHDcwEJEROR0sSyLzZs3M3v2bA4cOEBqaipZWVkKL0Q6EJfLRadOnTp0MCStw5AhQ/jqq68oKioiMzMz0OWIAI3NcMmdIlm6pxyvaRFs77ivlV7TwmFrfD70ntE6KUQXkXYpODiYSZMmMWrUKBYsWMCiRYtYtmwZt9xyC507dz7mm5Lf71fXn4iInBb79u0jJyeHrVu30rt3b26//Xa6d+8e6LJE5AwrKyvTKBdpFYKDgxk6dCjFxcVMnToVm03zlqV1iA0Jom90GJvKazEPjzPpaMzDs9AHxoYTGxIU6HLkOyhEF5F2LTQ0lGnTpjF27FiKi4uJj48/5nZutxun0wk0huk2m01nf0VE5KRVVVWRl5fHypUr6dSpE1dddRVJSUl6TxHpoFwuF3379g10GSIAjBw5ksLCQrZs2cLAgQMDXY5Is/6x4Ryoc1Pp9hFi71jH4pZl4fabxDgd9I8JD3Q58j0UootIhxAZGcnkyZOxrG/PWfP5fERHR/PZZ58xadIkQkO1iIeIiJwct9vdfOVTcHAwP/rRjxg5cqSucBLpwNxuN7W1tepEl1aje/fuJCQkUFRUpBBdWhW7zWBIfFSHHOvSNMZlcHwUdlvH+b7bIl2/IyIdyrHOaL/yyivEx8ezdu1ahgwZwvPPP3/U7X6//0yVJyIibYzf72fZsmU899xzLFq0iPHjx3P//feTnp6uAF2kgysrKwOgU6dOAa5EpJFhGIwcOZINGzZQW1sb6HJEjtI01sVvWZiWFehyzoimMS59Y8I0xqUNUCe6iHRoDQ0N/PSnP2Xp0qUMHz6ctLQ0Xn75ZVJTU6mvr+ecc85RCCIiIt9iWRYbN25k9uzZHDp0iOHDh5OVlUVUVFSgSxORVsLlcgGoE11aldTUVHJycli5ciUTJkwIdDkiR+lIY100xqXtUSe6iHRojz76KHfccQfDhw8HGke7fPDBByxbtowPP/yQpKQk1q9ff9R9TNMMRKkiItJK7Nmzh7feeov33nuPyMhI7rzzTi666CIF6CJyFJfLRWhoqEYFSqsSFhZGSkoKRUVFWB2k21faDrvNYHiXaEIdNhr8Zrv9HbUsiwa/SajDxrAu0Rrj0kaoE11EOqyqqioee+wxqqqqmr/2y1/+kl/96lf84he/AODOO++kuLiY5OTk5m2aVrI3TVOr2ouIdCAVFRXk5eWxevVq4uPjueaaaxgwYEC77pISkVNXVlamUS7SKqWlpfH3v/+dXbt20atXr0CXI3KUyGAHI7vGsHxvBQ1+s911pDcF6E6HjZFdY4gMVjTbVij9EZEOy+1289577xEREQHAZ599xrZt2/jTn/4ENIbkhYWFzfMsly1bxoUXXsicOXMAFKCLiHQQDQ0N5OTk8MILL7Bt2zbOP/987rrrLgYOHNiuDupE5PRSiC6tVd++fYmJiaGoqCjQpYgcU0xIEGldowm2G+2qI70pQA+22xjZJZoYzUFvU5QAiUiHFR8fzxVXXNH891tuuYUHHnig+e/vv/8+e/bs4cc//jEAgwYN4sc//jGPPfYY11xzTfOcS4DVq1cza9asM1a7iIi0PL/fz5IlS3juuedYtmwZkyZN4r777mPkyJE6kSoix+VyuTQPXVolwzBIS0tj7dq1uN3uQJcjckydQoMZ2TUGp719jHY5ugM9mrjQ4ECXJCdJn/5FRA77/PPP+f3vfw+A1+vlj3/8I//v//0/AN544w0efPBBduzYwX/+8x9CQkIoLS1tvm+XLl3YvHlzQOoWEZHTy7Is1q1bx0svvcTXX39NcnIy9913HxkZGQQH64BHRI6vvr6e+vp6hejSao0YMQKfz8eaNWtO+D5ev0m910+tx0eV20tlg5cqt5daj496rx+vX2tHyenVKTSY0d1immekm200SDePmIGe3jWGTgrQ2yQN3hEROWzs2LHNf/7444/ZsWMHN910Ey+++CJvvvkmDz74IHPmzOHpp58mOjqaTZs2MXz4cHJycli/fj333Xdf8/39fj92uz0Q34aIiPwApaWlzJo1i127djFgwACuvPJKEhISAl2WiLQxTVcs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tD0K0UVE5LSIj4/niiuuYPfu3eTl5fH++++TmJhIdnY2ffv2DXR5InIEj8fDokWLWLhwITabjenTp5Oenq5xTCIip8DlcpGamhroMiSAzj33XDZv3ozD4SA1NZWQkBCKiorweDzNM71PxZEntU1f435qqqqOe7/62hp+deMV1FRVEuwMYcCQVByOoObRLaZ54jU1jXn5Jq/HQ9D3LNg5++OZvPLYHwDolNCF+G6DqSwvY+/O7UfVMHT0WBL79GX39m0UfP4Jc774FIAZl155UrUcKaZTPLYjnrvHH3+cP/3pTwD07t2brl27Ulpayu7du3/QFbQjRozAeTisb9K7d+9T3p+ItG46UhIRkdMqMTGR66+/nq1bt5Kbm8vbb79N//79ycrKonv37oEuT6RDM02TlStXkp+fT11dHWPGjGHy5MmEhoYGujQRkTbJ6/VSVVWl8VftXHp6OoZhYFkWzz77LOnp6QQHB+NyuaipqWHz5s0APPLII/z6179m+/btJCcnH3e/Y8eOJT8/nw8//JBf/epXDBgwAMuyWL16NcOGDTtqtnbptq307NuPOf/55Lj73bV1CzVVlQD84ok/k3XBJawrWs59l5zzrW2dIaG4G+ppqKtr/lpMp850SezJ/t27GDgkld899yr2wyfad23dwv7du743QAcoKVoOQFhEBO/MXUGw08mzv/sFn7375re2nX7xlbz5zOP88+U/U3bwAEHBTrIuuOSUa7EZBrYjOvebusOTkpLYsGEDfr+fCy64gN27dzdvM2DAAEJCQmhoaODjjz/m8ssv5+DBg+Tn5x+171GjRjX/Ltx000385Cc/ARoXnp0/fz7R0dHf+7yISNulmegiItIi+vXrx2233cYVV1xBZWUlr732Gv/61784dOhQoEsT6ZC2bNnCq6++yqeffkqvXr245557mDFjhgJ0EZEfoLy8HEAhejvXp08f7rnnHgBmzpxJYmIiqampJCYmUlhYSI8ePQD4wx/+QGpqKiNHjjyhq7seffRRgoODKS8vZ8iQIaSmppKQkMDvf/97AAYOHEjPXr0AePzBu/jp1Rfx/B/+57j7jY1PICQ0DID/+9VPuPXsKfz+jhuOuW3P/gMAeP2JP/LjC2fw+pOPAnDrL34LwNwvP+OKcanceW4ml6UP5qbsceR+8t1z3pv0Sx4CNM5gv27qaK6bMpqC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Tz31lNatW6ennnpKZWVlKikpkSRt2rRJ77//vj755BP97W9/06BBgy7ofgEAAAAAAAC0T5ToLWzHjh2Nb0+fPl2SdPXVV+u666771OvdddddkupL9D59+uiOO+7Q7576L2V1ztUZg+gaM3maLMtScmqabhp3uyTpo907m7ztcDisXz34D5o+fKAmXNlVd3/mKtVWV0uSTnxyTJJ0x4xZ8geSdeLYUW1c9abWLn9djuOox+V9NXj4jc3++v2BZE2afb+k+pU1lWWlOnbogCRp2bPzNf6KLhp/RRet+curkqRdWzdLkm64dYx8SUnavPZt3TNsgL597yT9z/99RP7k5Gbfd/3jdGaN3rScnByNHDlSktSvXz9de+21mj17tt5//3117ty5+fcLAAAAAAAAoN3iYNE48ZWvfEUDBgzQSy+9pO3bt2vTpk1avny5+ix6Vr9ftvqSbvuhL87W4aKP5PF61ffqgUryB7T3g+2KhMNyTh7+mZ6ZpTF33a1lixdo2eIFqq2ukiRNmHbfBd1XZnaObPvcz81c1vtyZWXnnPXxSDisvlcP1B//slorXnpBe3ds1/6dO/S3jb/X64vm6X/fWKuuPXrKsuqfTWjILEk1VVV/vyEjnfWMw3msWLFC8+fP19q1a/XBBx/oueee08KFC3X06FF9//vfv6DbAgAAAAAAAND+UKK3sFPXgLz44ou64YYbtHv3bm3btu1Tr/fee+/pmmuu0a233iqp/sDLkSNH6uM9H6qyrPS0y658dak+O2GigrU1Wv/mG5Kkvv0HnvN2K8pKdbjoI0nSA9/5oeZ84x917NABPTDus2dd9q65n9eyxQv07oq/yBgjy7I04Z57m//FS41Fd4OsnM7q2qOXjh8+qKuuuVb//OT/yOOt/213cP8+HT98UL6kJB36aJ9s29b933pQUn2xPn34ANVUVenDbe+ra4+eysrprGOHDjQeflpRWqKt69aefv/nyJSSktL4dk1NTePbxhi98847euCBB/TFL35RkvS1r31Nv/vd77Rq1SpKdAAAAAAAAACU6C2tX79+uvvuu7VkyRL967/+q1588UUdPHhQHo9H0Wi0yes9+eSTWrRokXr27Kns7Gzt3btXkpTT7TKlZWUpFPx7+bv2jdeUd+twBWtrVF5yQpI06+vfOuftZmR1Uu5l3VV89Ij+/MRjevOlF3Ti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", 167 | "text/plain": [ 168 | "
" 169 | ] 170 | }, 171 | "metadata": {}, 172 | "output_type": "display_data" 173 | } 174 | ], 175 | "source": [ 176 | "def main():\n", 177 | " \"\"\" \n", 178 | " Main function to run the program\n", 179 | " \"\"\"\n", 180 | " file_path = input(\"Enter the path to the Python file: \")\n", 181 | " tree = parse_file(file_path)\n", 182 | " functions = extract_functions(tree)\n", 183 | " analyze_function_calls(tree, functions)\n", 184 | " G = create_graph(functions)\n", 185 | " visualize_graph(G)\n", 186 | "\n", 187 | "if __name__ == \"__main__\":\n", 188 | " main()" 189 | ] 190 | }, 191 | { 192 | "cell_type": "markdown", 193 | "metadata": {}, 194 | "source": [ 195 | "## For the whole directory" 196 | ] 197 | }, 198 | { 199 | "cell_type": "code", 200 | "execution_count": 10, 201 | "metadata": {}, 202 | "outputs": [], 203 | "source": [ 204 | "# importing modules\n", 205 | "import os\n", 206 | "import ast\n", 207 | "import time\n", 208 | "import flask\n", 209 | "import numpy as np\n", 210 | "import igraph as ig\n", 211 | "import networkx as nx\n", 212 | "from dash import Dash, html, dcc\n", 213 | "import plotly.graph_objects as go\n", 214 | "from dash.dependencies import Input, Output" 215 | ] 216 | }, 217 | { 218 | "cell_type": "code", 219 | "execution_count": 11, 220 | "metadata": {}, 221 | "outputs": [], 222 | "source": [ 223 | "def parse_directory(directory_path: str):\n", 224 | " \"\"\" \n", 225 | " Parse the directory and return all the python files in that directory\n", 226 | "\n", 227 | " Args:\n", 228 | " directory_path: The path to the directory\n", 229 | "\n", 230 | " Returns:\n", 231 | " A list of all the python files in the directory\n", 232 | " \"\"\"\n", 233 | " python_files = []\n", 234 | " for root, dirs, files in os.walk(directory_path):\n", 235 | " for file in files:\n", 236 | " if file.endswith(\".py\"):\n", 237 | " python_files.append(os.path.join(root, file))\n", 238 | " return python_files" 239 | ] 240 | }, 241 | { 242 | "cell_type": "code", 243 | "execution_count": 12, 244 | "metadata": {}, 245 | "outputs": [], 246 | "source": [ 247 | "def parse_file(file_path: str):\n", 248 | " \"\"\"\n", 249 | " Parse the file and return the abstract syntax tree\n", 250 | "\n", 251 | " Args:\n", 252 | " file_path: The path to the file to be parsed\n", 253 | "\n", 254 | " Returns:\n", 255 | " The abstract syntax tree of the file\n", 256 | " \"\"\"\n", 257 | " with open(file_path, 'r', encoding='utf-8') as f:\n", 258 | " tree = ast.parse(f.read())\n", 259 | " return tree" 260 | ] 261 | }, 262 | { 263 | "cell_type": "code", 264 | "execution_count": 13, 265 | "metadata": {}, 266 | "outputs": [], 267 | "source": [ 268 | "def extract_functions(tree):\n", 269 | " \"\"\" \n", 270 | " Extract all the functions from the abstract syntax tree\n", 271 | "\n", 272 | " Args:\n", 273 | " tree: The abstract syntax tree of the file\n", 274 | "\n", 275 | " Returns:\n", 276 | " A dictionary of functions with the function name as the key and the function's\n", 277 | " \"\"\"\n", 278 | " functions = {}\n", 279 | " for node in ast.walk(tree):\n", 280 | " if isinstance(node, ast.FunctionDef):\n", 281 | " functions[node.name] = {\n", 282 | " \"calls\" : [],\n", 283 | " 'line' : node.lineno,\n", 284 | " 'file': None\n", 285 | " }\n", 286 | " return functions" 287 | ] 288 | }, 289 | { 290 | "cell_type": "code", 291 | "execution_count": 14, 292 | "metadata": {}, 293 | "outputs": [], 294 | "source": [ 295 | "def extract_functions_and_imports(tree):\n", 296 | " \"\"\" \n", 297 | " Extract all the functions and imported modules from the abstract syntax tree\n", 298 | "\n", 299 | " Args:\n", 300 | " tree: The abstract syntax tree of the file\n", 301 | "\n", 302 | " Returns:\n", 303 | " A dictionary of functions and imports with the function name as the key and the function's data\n", 304 | " \"\"\"\n", 305 | " functions = {}\n", 306 | " imports = set()\n", 307 | " for node in ast.walk(tree):\n", 308 | " if isinstance(node, ast.FunctionDef):\n", 309 | " functions[node.name] = {\n", 310 | " \"calls\": [],\n", 311 | " 'line': node.lineno,\n", 312 | " 'file': None\n", 313 | " }\n", 314 | " elif isinstance(node, ast.Import) or isinstance(node, ast.ImportFrom):\n", 315 | " for alias in node.names:\n", 316 | " imports.add(alias.name.split('.')[0]) # Add imported module names\n", 317 | " return functions, imports" 318 | ] 319 | }, 320 | { 321 | "cell_type": "code", 322 | "execution_count": 15, 323 | "metadata": {}, 324 | "outputs": [], 325 | "source": [ 326 | "def analyze_function_calls(tree, functions, omit_list):\n", 327 | " \"\"\" \n", 328 | " Analyze the function calls in the abstract syntax tree and update the functions dictionary\n", 329 | "\n", 330 | " Args:\n", 331 | " tree: The abstract syntax tree of the file\n", 332 | " functions: The dictionary of functions with the function name as the key and the function's\n", 333 | " omit_list: A list of functions to omit from the analysis\n", 334 | "\n", 335 | " Returns:\n", 336 | " None\n", 337 | " \"\"\"\n", 338 | " for node in ast.walk(tree):\n", 339 | " if isinstance(node, ast.Call):\n", 340 | " caller = None\n", 341 | " for parent in ast.walk(tree):\n", 342 | " if isinstance(parent, ast.FunctionDef) and node in ast.walk(parent):\n", 343 | " caller = parent.name\n", 344 | " break\n", 345 | " if caller and caller in functions and caller not in omit_list:\n", 346 | " if isinstance(node.func, ast.Name):\n", 347 | " called_func = node.func.id\n", 348 | " if called_func not in omit_list:\n", 349 | " functions[caller]['calls'].append(called_func)\n", 350 | " elif isinstance(node.func, ast.Attribute):\n", 351 | " called_func = node.func.attr\n", 352 | " if isinstance(node.func.value, ast.Name):\n", 353 | " object_name = node.func.value.id\n", 354 | " if called_func not in omit_list:\n", 355 | " # Append the method call (e.g., object.method)\n", 356 | " functions[caller]['calls'].append(f\"{object_name}.{called_func}\")\n", 357 | " else:\n", 358 | " if called_func not in omit_list:\n", 359 | " functions[caller]['calls'].append(called_func)\n", 360 | " else:\n", 361 | " if hasattr(node.func, 'id') and node.func.id not in omit_list:\n", 362 | " functions[caller]['calls'].append(node.func.id)\n", 363 | " elif hasattr(node.func, 'attr') and node.func.attr not in omit_list:\n", 364 | " functions[caller]['calls'].append(node.func.attr)" 365 | ] 366 | }, 367 | { 368 | "cell_type": "code", 369 | "execution_count": 17, 370 | "metadata": {}, 371 | "outputs": [], 372 | "source": [ 373 | "def create_graph(functions, imports, file_paths, omit_list):\n", 374 | " \"\"\" \n", 375 | " Create a directed graph of the functions, their calls, and imports\n", 376 | "\n", 377 | " Args:\n", 378 | " functions: The dictionary of functions with the function name as the key and the function's data\n", 379 | " imports: The set of imported modules\n", 380 | " file_paths: A list of file paths\n", 381 | " omit_list: A list of functions to omit from the analysis\n", 382 | "\n", 383 | " Returns:\n", 384 | " A directed graph of the functions, their calls, and imports\n", 385 | " \"\"\"\n", 386 | " G = nx.DiGraph()\n", 387 | " \n", 388 | " for file_path in file_paths:\n", 389 | " module_name = os.path.basename(file_path).replace('.py', '')\n", 390 | " for func, data in functions.items():\n", 391 | " if data['file'] == file_path and func not in omit_list:\n", 392 | " G.add_node(func, module=module_name)\n", 393 | " for call in data['calls']:\n", 394 | " if call in functions and call not in omit_list:\n", 395 | " G.add_edge(func, call)\n", 396 | "\n", 397 | " # Add imported modules as isolated nodes\n", 398 | " for module in imports:\n", 399 | " G.add_node(module, module='import')\n", 400 | " \n", 401 | " return G" 402 | ] 403 | }, 404 | { 405 | "cell_type": "code", 406 | "execution_count": 8, 407 | "metadata": {}, 408 | "outputs": [], 409 | "source": [ 410 | "def visualize_graph_3d(G):\n", 411 | " \"\"\"\n", 412 | " Create a 3D directed graph of the functions, their calls, and imports\n", 413 | " \n", 414 | " Args:\n", 415 | " G: A directed graph of the functions, their calls, and imports\n", 416 | " \n", 417 | " Returns:\n", 418 | " A Plotly Figure object\n", 419 | " \"\"\"\n", 420 | " # Convert NetworkX graph to igraph\n", 421 | " ig_graph = ig.Graph.from_networkx(G)\n", 422 | "\n", 423 | " # Get the layout\n", 424 | " layt = ig_graph.layout_fruchterman_reingold(dim=3)\n", 425 | "\n", 426 | " # Extract node positions\n", 427 | " node_x, node_y, node_z = zip(*layt)\n", 428 | "\n", 429 | " # Create node trace\n", 430 | " node_trace = go.Scatter3d(\n", 431 | " x=node_x, y=node_y, z=node_z,\n", 432 | " mode='markers+text',\n", 433 | " text=list(G.nodes()),\n", 434 | " textposition=\"top center\",\n", 435 | " textfont=dict(size=10, color='black'),\n", 436 | " hoverinfo='text',\n", 437 | " marker=dict(\n", 438 | " showscale=True,\n", 439 | " colorscale='YlGnBu',\n", 440 | " reversescale=True,\n", 441 | " color=[],\n", 442 | " size=10,\n", 443 | " colorbar=dict(\n", 444 | " thickness=15,\n", 445 | " title='Node Connections',\n", 446 | " xanchor='left',\n", 447 | " titleside='right'\n", 448 | " ),\n", 449 | " line_width=2))\n", 450 | "\n", 451 | " # Color node points by the number of connections\n", 452 | " node_adjacencies = []\n", 453 | " node_text = []\n", 454 | " for node, adjacencies in G.adjacency():\n", 455 | " node_adjacencies.append(len(adjacencies))\n", 456 | " node_text.append(f\"Function: {node}
# of connections: {len(adjacencies)}\")\n", 457 | "\n", 458 | " node_trace.marker.color = node_adjacencies\n", 459 | " node_trace.hovertext = node_text\n", 460 | "\n", 461 | " # Create edge traces with arrows\n", 462 | " edge_traces = []\n", 463 | " for edge in G.edges():\n", 464 | " start = layt[list(G.nodes()).index(edge[0])]\n", 465 | " end = layt[list(G.nodes()).index(edge[1])]\n", 466 | " x0, y0, z0 = start\n", 467 | " x1, y1, z1 = end\n", 468 | " \n", 469 | " # Calculate the direction vector\n", 470 | " dx, dy, dz = x1 - x0, y1 - y0, z1 - z0\n", 471 | " \n", 472 | " # Normalize the direction vector\n", 473 | " length = np.sqrt(dx**2 + dy**2 + dz**2)\n", 474 | " ux, uy, uz = dx/length, dy/length, dz/length\n", 475 | " \n", 476 | " # Calculate the midpoint\n", 477 | " mx, my, mz = (x0 + x1) / 2, (y0 + y1) / 2, (z0 + z1) / 2\n", 478 | " \n", 479 | " # Create the line trace\n", 480 | " line_trace = go.Scatter3d(\n", 481 | " x=[x0, x1],\n", 482 | " y=[y0, y1],\n", 483 | " z=[z0, z1],\n", 484 | " mode='lines',\n", 485 | " line=dict(color='#888', width=2),\n", 486 | " hoverinfo='none'\n", 487 | " )\n", 488 | " \n", 489 | " # Create the arrow trace\n", 490 | " arrow_trace = go.Cone(\n", 491 | " x=[mx], y=[my], z=[mz],\n", 492 | " u=[ux], v=[uy], w=[uz],\n", 493 | " sizemode=\"absolute\",\n", 494 | " sizeref=0.15,\n", 495 | " showscale=False,\n", 496 | " colorscale=[[0, '#888'], [1, '#888']],\n", 497 | " anchor=\"tip\"\n", 498 | " )\n", 499 | " \n", 500 | " edge_traces.extend([line_trace, arrow_trace])\n", 501 | "\n", 502 | " # Create the figure\n", 503 | " fig = go.Figure(data=[*edge_traces, node_trace],\n", 504 | " layout=go.Layout(\n", 505 | " title='3D Function Call Graph',\n", 506 | " showlegend=False,\n", 507 | " hovermode='closest',\n", 508 | " margin=dict(b=0,l=0,r=0,t=0),\n", 509 | " scene=dict(\n", 510 | " xaxis=dict(showbackground=False, showline=False, zeroline=False, showgrid=False, showticklabels=False, title=''),\n", 511 | " yaxis=dict(showbackground=False, showline=False, zeroline=False, showgrid=False, showticklabels=False, title=''),\n", 512 | " zaxis=dict(showbackground=False, showline=False, zeroline=False, showgrid=False, showticklabels=False, title=''),\n", 513 | " ),\n", 514 | " annotations=[\n", 515 | " dict(\n", 516 | " showarrow=False,\n", 517 | " text=\"\",\n", 518 | " xref=\"paper\",\n", 519 | " yref=\"paper\",\n", 520 | " x=0,\n", 521 | " y=0.1,\n", 522 | " xanchor=\"left\",\n", 523 | " yanchor=\"bottom\",\n", 524 | " font=dict(size=14)\n", 525 | " )\n", 526 | " ]\n", 527 | " )\n", 528 | " )\n", 529 | "\n", 530 | " return fig" 531 | ] 532 | }, 533 | { 534 | "cell_type": "code", 535 | "execution_count": 18, 536 | "metadata": {}, 537 | "outputs": [], 538 | "source": [ 539 | "def create_dash_app(G):\n", 540 | " \"\"\"\n", 541 | " Create a Dash app to serve the 3D graph visualization\n", 542 | " \n", 543 | " Args:\n", 544 | " G: A directed graph of the functions, their calls, and imports\n", 545 | " \n", 546 | " Returns:\n", 547 | " A Dash app object\n", 548 | " \"\"\"\n", 549 | " app = Dash(__name__)\n", 550 | " \n", 551 | " app.layout = html.Div([\n", 552 | " html.H1(\"3D Function Call Graph\"),\n", 553 | " dcc.Graph(id='3d-graph', figure=visualize_graph_3d(G)),\n", 554 | " ])\n", 555 | " \n", 556 | " return app" 557 | ] 558 | }, 559 | { 560 | "cell_type": "code", 561 | "execution_count": 19, 562 | "metadata": {}, 563 | "outputs": [ 564 | { 565 | "name": "stdout", 566 | "output_type": "stream", 567 | "text": [ 568 | "The input directory is: C:\\Users\\Vishal\\Github\\CodeFlowMapper\\testing-directories\n", 569 | "The functions to omit are: ['']\n", 570 | "Processing file 1/4: analyzer.py\n", 571 | "Processing file 2/4: data_processor.py\n", 572 | "Processing file 3/4: main.py\n", 573 | "Processing file 4/4: utils.py\n", 574 | "Starting the web server. Please open a web browser and go to http://127.0.0.1:8050/ to view the graph.\n" 575 | ] 576 | }, 577 | { 578 | "data": { 579 | "text/html": [ 580 | "\n", 581 | " \n", 589 | " " 590 | ], 591 | "text/plain": [ 592 | "" 593 | ] 594 | }, 595 | "metadata": {}, 596 | "output_type": "display_data" 597 | } 598 | ], 599 | "source": [ 600 | "def main():\n", 601 | " directory_path = input(\"Enter the path to the directory: \")\n", 602 | " print(f\"The input directory is: {directory_path}\")\n", 603 | " omit_list = input(\"Enter the functions to omit (comma-separated): \").split(',')\n", 604 | " print(f\"The functions to omit are: {omit_list}\")\n", 605 | "\n", 606 | " omit_list = [func.strip() for func in omit_list]\n", 607 | "\n", 608 | " python_files = parse_directory(directory_path)\n", 609 | "\n", 610 | " functions = {}\n", 611 | " imports = set()\n", 612 | " G = nx.DiGraph()\n", 613 | "\n", 614 | " for i, file_path in enumerate(python_files):\n", 615 | " tree = parse_file(file_path)\n", 616 | " file_functions, file_imports = extract_functions_and_imports(tree)\n", 617 | " \n", 618 | " for func_name, func_data in file_functions.items():\n", 619 | " func_data['file'] = file_path\n", 620 | " \n", 621 | " functions.update(file_functions)\n", 622 | " imports.update(file_imports)\n", 623 | " analyze_function_calls(tree, functions, omit_list)\n", 624 | "\n", 625 | " G = create_graph(functions, imports, python_files[:i+1], omit_list)\n", 626 | " \n", 627 | " print(f\"Processing file {i+1}/{len(python_files)}: {os.path.basename(file_path)}\")\n", 628 | "\n", 629 | " # Pause to simulate processing time (optional)\n", 630 | " time.sleep(0.5)\n", 631 | "\n", 632 | " # Create and run the Dash app\n", 633 | " app = create_dash_app(G)\n", 634 | " print(\"Starting the web server. Please open a web browser and go to http://127.0.0.1:8050/ to view the graph.\")\n", 635 | " app.run_server(debug=True)\n", 636 | "\n", 637 | "if __name__ == \"__main__\":\n", 638 | " main()" 639 | ] 640 | }, 641 | { 642 | "cell_type": "markdown", 643 | "metadata": {}, 644 | "source": [ 645 | "# Visualizing the graph on the web" 646 | ] 647 | }, 648 | { 649 | "cell_type": "code", 650 | "execution_count": 20, 651 | "metadata": {}, 652 | "outputs": [], 653 | "source": [ 654 | "import os\n", 655 | "import ast\n", 656 | "import time\n", 657 | "import flask\n", 658 | "import networkx as nx\n", 659 | "from flask import Flask, render_template_string, jsonify" 660 | ] 661 | }, 662 | { 663 | "cell_type": "code", 664 | "execution_count": 21, 665 | "metadata": {}, 666 | "outputs": [], 667 | "source": [ 668 | "app = Flask(__name__)" 669 | ] 670 | }, 671 | { 672 | "cell_type": "code", 673 | "execution_count": 22, 674 | "metadata": {}, 675 | "outputs": [], 676 | "source": [ 677 | "def network_to_visjs(G):\n", 678 | " nodes = [{\"id\": node, \"label\": node} for node in G.nodes()]\n", 679 | " edges = [{\"from\": source, \"to\": target} for source, target in G.edges()]\n", 680 | " return {\"nodes\": nodes, \"edges\": edges}" 681 | ] 682 | }, 683 | { 684 | "cell_type": "code", 685 | "execution_count": 23, 686 | "metadata": {}, 687 | "outputs": [], 688 | "source": [ 689 | "def parse_directory(directory_path: str):\n", 690 | " \"\"\" \n", 691 | " Parse the directory and return all the python files in that directory\n", 692 | "\n", 693 | " Args:\n", 694 | " directory_path: The path to the directory\n", 695 | "\n", 696 | " Returns:\n", 697 | " A list of all the python files in the directory\n", 698 | " \"\"\"\n", 699 | " python_files = []\n", 700 | " for root, dirs, files in os.walk(directory_path):\n", 701 | " for file in files:\n", 702 | " if file.endswith(\".py\"):\n", 703 | " python_files.append(os.path.join(root, file))\n", 704 | " return python_files\n" 705 | ] 706 | }, 707 | { 708 | "cell_type": "code", 709 | "execution_count": 24, 710 | "metadata": {}, 711 | "outputs": [], 712 | "source": [ 713 | "def parse_file(file_path: str):\n", 714 | " \"\"\"\n", 715 | " Parse the file and return the abstract syntax tree\n", 716 | "\n", 717 | " Args:\n", 718 | " file_path: The path to the file to be parsed\n", 719 | "\n", 720 | " Returns:\n", 721 | " The abstract syntax tree of the file\n", 722 | " \"\"\"\n", 723 | " with open(file_path, 'r', encoding='utf-8') as f:\n", 724 | " tree = ast.parse(f.read())\n", 725 | " return tree" 726 | ] 727 | }, 728 | { 729 | "cell_type": "code", 730 | "execution_count": 25, 731 | "metadata": {}, 732 | "outputs": [], 733 | "source": [ 734 | "def extract_functions_and_imports(tree):\n", 735 | " \"\"\" \n", 736 | " Extract all the functions and imported modules from the abstract syntax tree\n", 737 | "\n", 738 | " Args:\n", 739 | " tree: The abstract syntax tree of the file\n", 740 | "\n", 741 | " Returns:\n", 742 | " A dictionary of functions and imports with the function name as the key and the function's data\n", 743 | " \"\"\"\n", 744 | " functions = {}\n", 745 | " imports = set()\n", 746 | " for node in ast.walk(tree):\n", 747 | " if isinstance(node, ast.FunctionDef):\n", 748 | " functions[node.name] = {\n", 749 | " \"calls\": [],\n", 750 | " 'line': node.lineno,\n", 751 | " 'file': None\n", 752 | " }\n", 753 | " elif isinstance(node, ast.Import) or isinstance(node, ast.ImportFrom):\n", 754 | " for alias in node.names:\n", 755 | " imports.add(alias.name.split('.')[0]) # Add imported module names\n", 756 | " return functions, imports" 757 | ] 758 | }, 759 | { 760 | "cell_type": "code", 761 | "execution_count": 26, 762 | "metadata": {}, 763 | "outputs": [], 764 | "source": [ 765 | "def analyze_function_calls(tree, functions, omit_list):\n", 766 | " \"\"\" \n", 767 | " Analyze the function calls in the abstract syntax tree and update the functions dictionary\n", 768 | "\n", 769 | " Args:\n", 770 | " tree: The abstract syntax tree of the file\n", 771 | " functions: The dictionary of functions with the function name as the key and the function's\n", 772 | " omit_list: A list of functions to omit from the analysis\n", 773 | "\n", 774 | " Returns:\n", 775 | " None\n", 776 | " \"\"\"\n", 777 | " for node in ast.walk(tree):\n", 778 | " if isinstance(node, ast.Call):\n", 779 | " caller = None\n", 780 | " for parent in ast.walk(tree):\n", 781 | " if isinstance(parent, ast.FunctionDef) and node in ast.walk(parent):\n", 782 | " caller = parent.name\n", 783 | " break\n", 784 | " if caller and caller in functions and caller not in omit_list:\n", 785 | " if isinstance(node.func, ast.Name):\n", 786 | " called_func = node.func.id\n", 787 | " if called_func not in omit_list:\n", 788 | " functions[caller]['calls'].append(called_func)\n", 789 | " elif isinstance(node.func, ast.Attribute):\n", 790 | " called_func = node.func.attr\n", 791 | " if isinstance(node.func.value, ast.Name):\n", 792 | " object_name = node.func.value.id\n", 793 | " if called_func not in omit_list:\n", 794 | " # Append the method call (e.g., object.method)\n", 795 | " functions[caller]['calls'].append(f\"{object_name}.{called_func}\")\n", 796 | " else:\n", 797 | " if called_func not in omit_list:\n", 798 | " functions[caller]['calls'].append(called_func)\n", 799 | " else:\n", 800 | " if hasattr(node.func, 'id') and node.func.id not in omit_list:\n", 801 | " functions[caller]['calls'].append(node.func.id)\n", 802 | " elif hasattr(node.func, 'attr') and node.func.attr not in omit_list:\n", 803 | " functions[caller]['calls'].append(node.func.attr)" 804 | ] 805 | }, 806 | { 807 | "cell_type": "code", 808 | "execution_count": 27, 809 | "metadata": {}, 810 | "outputs": [], 811 | "source": [ 812 | "def create_graph(functions, imports, file_paths, omit_list):\n", 813 | " \"\"\" \n", 814 | " Create a directed graph of the functions, their calls, and imports\n", 815 | "\n", 816 | " Args:\n", 817 | " functions: The dictionary of functions with the function name as the key and the function's data\n", 818 | " imports: The set of imported modules\n", 819 | " file_paths: A list of file paths\n", 820 | " omit_list: A list of functions to omit from the analysis\n", 821 | "\n", 822 | " Returns:\n", 823 | " A directed graph of the functions, their calls, and imports\n", 824 | " \"\"\"\n", 825 | " G = nx.DiGraph()\n", 826 | " \n", 827 | " for file_path in file_paths:\n", 828 | " module_name = os.path.basename(file_path).replace('.py', '')\n", 829 | " for func, data in functions.items():\n", 830 | " if data['file'] == file_path and func not in omit_list:\n", 831 | " G.add_node(func, module=module_name)\n", 832 | " for call in data['calls']:\n", 833 | " if call in functions and call not in omit_list:\n", 834 | " G.add_edge(func, call)\n", 835 | "\n", 836 | " # Add imported modules as isolated nodes\n", 837 | " for module in imports:\n", 838 | " G.add_node(module, module='import')\n", 839 | " \n", 840 | " return G" 841 | ] 842 | }, 843 | { 844 | "cell_type": "code", 845 | "execution_count": 29, 846 | "metadata": {}, 847 | "outputs": [], 848 | "source": [ 849 | "@app.route('/')\n", 850 | "def index():\n", 851 | " return render_template_string('''\n", 852 | " \n", 853 | " \n", 854 | " \n", 855 | " Function Call Graph\n", 856 | " \n", 857 | " \n", 864 | " \n", 865 | " \n", 866 | "
\n", 867 | " \n", 904 | " \n", 905 | " \n", 906 | " ''')" 907 | ] 908 | }, 909 | { 910 | "cell_type": "code", 911 | "execution_count": 30, 912 | "metadata": {}, 913 | "outputs": [], 914 | "source": [ 915 | "@app.route('/graph_data')\n", 916 | "def graph_data():\n", 917 | " return jsonify(network_to_visjs(G))" 918 | ] 919 | }, 920 | { 921 | "cell_type": "code", 922 | "execution_count": 32, 923 | "metadata": {}, 924 | "outputs": [], 925 | "source": [ 926 | "def create_graph_from_directory(directory_path, omit_list):\n", 927 | " global G\n", 928 | " python_files = parse_directory(directory_path)\n", 929 | "\n", 930 | " functions = {}\n", 931 | " imports = set()\n", 932 | "\n", 933 | " for i, file_path in enumerate(python_files):\n", 934 | " tree = parse_file(file_path)\n", 935 | " file_functions, file_imports = extract_functions_and_imports(tree)\n", 936 | " \n", 937 | " for func_name, func_data in file_functions.items():\n", 938 | " func_data['file'] = file_path\n", 939 | " \n", 940 | " functions.update(file_functions)\n", 941 | " imports.update(file_imports)\n", 942 | " analyze_function_calls(tree, functions, omit_list)\n", 943 | "\n", 944 | " G = create_graph(functions, imports, python_files[:i+1], omit_list)\n", 945 | " \n", 946 | " print(f\"Processing file {i+1}/{len(python_files)}: {os.path.basename(file_path)}\")\n", 947 | "\n", 948 | " print(\"Graph creation completed.\")" 949 | ] 950 | }, 951 | { 952 | "cell_type": "code", 953 | "execution_count": 33, 954 | "metadata": {}, 955 | "outputs": [], 956 | "source": [ 957 | "def run_flask_app():\n", 958 | " print(\"Starting the web server.\")\n", 959 | " print(\"Please open a web browser and go to http://127.0.0.1:5000/ to view the graph.\")\n", 960 | " app.run(debug=True)" 961 | ] 962 | }, 963 | { 964 | "cell_type": "code", 965 | "execution_count": 34, 966 | "metadata": {}, 967 | "outputs": [ 968 | { 969 | "name": "stdout", 970 | "output_type": "stream", 971 | "text": [ 972 | "The input directory is: C:\\Users\\Vishal\\Github\\CodeFlowMapper\\testing-directories\n", 973 | "The functions to omit are: ['']\n", 974 | "Processing file 1/4: analyzer.py\n", 975 | "Processing file 2/4: data_processor.py\n", 976 | "Processing file 3/4: main.py\n", 977 | "Processing file 4/4: utils.py\n", 978 | "Graph creation completed.\n", 979 | "Starting the web server.\n", 980 | "Please open a web browser and go to http://127.0.0.1:5000/ to view the graph.\n", 981 | " * Serving Flask app '__main__'\n", 982 | " * Debug mode: on\n" 983 | ] 984 | }, 985 | { 986 | "ename": "SystemExit", 987 | "evalue": "1", 988 | "output_type": "error", 989 | "traceback": [ 990 | "An exception has occurred, use %tb to see the full traceback.\n", 991 | "\u001b[1;31mSystemExit\u001b[0m\u001b[1;31m:\u001b[0m 1\n" 992 | ] 993 | } 994 | ], 995 | "source": [ 996 | "if __name__ == \"__main__\":\n", 997 | " directory_path = input(\"Enter the path to the directory: \")\n", 998 | " print(f\"The input directory is: {directory_path}\")\n", 999 | " omit_list = input(\"Enter the functions to omit (comma-separated): \").split(',')\n", 1000 | " print(f\"The functions to omit are: {omit_list}\")\n", 1001 | "\n", 1002 | " omit_list = [func.strip() for func in omit_list]\n", 1003 | "\n", 1004 | " create_graph_from_directory(directory_path, omit_list)\n", 1005 | " run_flask_app()" 1006 | ] 1007 | }, 1008 | { 1009 | "cell_type": "code", 1010 | "execution_count": null, 1011 | "metadata": {}, 1012 | "outputs": [], 1013 | "source": [] 1014 | } 1015 | ], 1016 | "metadata": { 1017 | "kernelspec": { 1018 | "display_name": "venv", 1019 | "language": "python", 1020 | "name": "python3" 1021 | }, 1022 | "language_info": { 1023 | "codemirror_mode": { 1024 | "name": "ipython", 1025 | "version": 3 1026 | }, 1027 | "file_extension": ".py", 1028 | "mimetype": "text/x-python", 1029 | "name": "python", 1030 | "nbconvert_exporter": "python", 1031 | "pygments_lexer": "ipython3", 1032 | "version": "3.11.9" 1033 | } 1034 | }, 1035 | "nbformat": 4, 1036 | "nbformat_minor": 2 1037 | } 1038 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | asttokens==2.4.1 2 | attrs==24.2.0 3 | blinker==1.8.2 4 | certifi==2024.8.30 5 | charset-normalizer==3.3.2 6 | click==8.1.7 7 | colorama==0.4.6 8 | comm==0.2.2 9 | contourpy==1.2.1 10 | cycler==0.12.1 11 | dash==2.17.1 12 | dash-core-components==2.0.0 13 | dash-html-components==2.0.0 14 | dash-table==5.0.0 15 | debugpy==1.8.5 16 | decorator==5.1.1 17 | executing==2.0.1 18 | fastjsonschema==2.20.0 19 | filelock==3.15.4 20 | Flask==3.0.3 21 | fonttools==4.53.1 22 | fsspec==2024.6.1 23 | huggingface-hub==0.24.6 24 | idna==3.8 25 | igraph==0.11.6 26 | importlib_metadata==8.4.0 27 | ipykernel==6.29.5 28 | ipython==8.26.0 29 | itsdangerous==2.2.0 30 | jedi==0.19.1 31 | Jinja2==3.1.4 32 | jsonschema==4.23.0 33 | jsonschema-specifications==2023.12.1 34 | jupyter_client==8.6.2 35 | jupyter_core==5.7.2 36 | kiwisolver==1.4.5 37 | MarkupSafe==2.1.5 38 | matplotlib==3.9.2 39 | matplotlib-inline==0.1.7 40 | mpmath==1.3.0 41 | nbformat==5.10.4 42 | nest-asyncio==1.6.0 43 | networkx==3.3 44 | numpy==1.26.4 45 | packaging==24.1 46 | pandas==2.2.2 47 | parso==0.8.4 48 | pillow==10.4.0 49 | platformdirs==4.2.2 50 | plotly==5.23.0 51 | prompt_toolkit==3.0.47 52 | psutil==6.0.0 53 | pure_eval==0.2.3 54 | Pygments==2.18.0 55 | pyparsing==3.1.2 56 | python-dateutil==2.9.0.post0 57 | pytz==2024.1 58 | pywin32==306 59 | PyYAML==6.0.2 60 | pyzmq==26.1.0 61 | referencing==0.35.1 62 | regex==2024.7.24 63 | requests==2.32.3 64 | retrying==1.3.4 65 | rpds-py==0.20.0 66 | safetensors==0.4.4 67 | scipy==1.14.0 68 | six==1.16.0 69 | stack-data==0.6.3 70 | sympy==1.13.2 71 | tenacity==9.0.0 72 | texttable==1.7.0 73 | tokenizers==0.19.1 74 | torch==2.4.0 75 | torchaudio==2.4.0 76 | torchvision==0.19.0 77 | tornado==6.4.1 78 | tqdm==4.66.5 79 | traitlets==5.14.3 80 | transformers==4.44.2 81 | typing_extensions==4.12.2 82 | tzdata==2024.1 83 | urllib3==2.2.2 84 | wcwidth==0.2.13 85 | Werkzeug==3.0.4 86 | zipp==3.20.1 87 | -------------------------------------------------------------------------------- /testing-directories/analyzer.py: -------------------------------------------------------------------------------- 1 | from .utils import log_message 2 | from .data_processor import preprocess_data 3 | 4 | 5 | def analyze_results(data): 6 | log_message("Analyzing results") 7 | preprocessed = preprocess_data(data) 8 | total = sum(preprocessed) 9 | average = total / len(preprocessed) 10 | return {"total": total, "average": average} 11 | 12 | 13 | def generate_report(results): 14 | log_message("Generating report") 15 | return f"Report: Total = {results['total']}, Average = {results['average']}" 16 | -------------------------------------------------------------------------------- /testing-directories/data_processor.py: -------------------------------------------------------------------------------- 1 | from .utils import log_message, validate_data 2 | 3 | def process_data(data): 4 | log_message("Processing data") 5 | validate_data(data) 6 | return [x * 2 for x in data] 7 | 8 | def preprocess_data(data): 9 | log_message("Preprocessing data") 10 | return [x + 1 for x in data] -------------------------------------------------------------------------------- /testing-directories/main.py: -------------------------------------------------------------------------------- 1 | from .data_processor import process_data 2 | from .analyzer import analyze_results 3 | from .utils import log_message 4 | from .analyzer import generate_report 5 | 6 | 7 | def main(): 8 | log_message("Starting main process") 9 | data = [1, 2, 3, 4, 5] 10 | processed_data = process_data(data) 11 | results = analyze_results(processed_data) 12 | log_message(f"Analysis results: {results}") 13 | report = generate_report(results) 14 | log_message(f"Report: {report}") 15 | 16 | 17 | if __name__ == "__main__": 18 | main() 19 | -------------------------------------------------------------------------------- /testing-directories/utils.py: -------------------------------------------------------------------------------- 1 | def log_message(message): 2 | print(f"[LOG] {message}") 3 | 4 | 5 | def validate_data(data): 6 | if not isinstance(data, list): 7 | raise ValueError("Data must be a list") 8 | if not all(isinstance(x, (int, float)) for x in data): 9 | raise ValueError("All elements must be numbers") 10 | -------------------------------------------------------------------------------- /testing-files/basic_python.py: -------------------------------------------------------------------------------- 1 | def main(): 2 | print("Starting the program...") 3 | data = get_data() 4 | processed_data = process_data(data) 5 | result = analyze_results(processed_data) 6 | display_results(result) 7 | print("Program completed.") 8 | 9 | 10 | def get_data(): 11 | print("Fetching data...") 12 | data = [1, 2, 3, 4, 5] 13 | validate_data(data) 14 | return data 15 | 16 | 17 | def validate_data(data): 18 | print("Validating data...") 19 | if not data: 20 | raise ValueError("Data is empty") 21 | return True 22 | 23 | 24 | def process_data(data): 25 | print("Processing data...") 26 | return [x * 2 for x in data] 27 | 28 | 29 | def analyze_results(data): 30 | print("Analyzing results...") 31 | total = calculate_total(data) 32 | average = calculate_average(data) 33 | return {"total": total, "average": average} 34 | 35 | 36 | def calculate_total(data): 37 | return sum(data) 38 | 39 | 40 | def calculate_average(data): 41 | total = calculate_total(data) 42 | return total / len(data) 43 | 44 | 45 | def display_results(results): 46 | print("Displaying results...") 47 | print(f"Total: {results['total']}") 48 | print(f"Average: {results['average']}") 49 | 50 | 51 | if __name__ == "__main__": 52 | main() 53 | --------------------------------------------------------------------------------