Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 10 additions & 18 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
# Function Finder
-----


- This is now on pypi!!! [Link](https://pypi.org/project/functionvis/)

- Every found a library with so many files you lose track of which function/class comes from where?
- What if you could see it visually :o

Expand All @@ -12,25 +14,15 @@
- .ipynb (If you have it for python that is)

## How to run


- Do a pip install functionvis
- Get requirements (Pathlib, graphviz, jupytext)
- Install graphviz (If you have linux you can get it from apt/aur ; For windows install it from their site)
```
pip install -r requirements.txt
```
- Just invoke the main.py file with the argument as the directory which you want to visualize
- eg:
```py
python main.py -d "Your directory path"
```
- Arguments (Default is True. Change only if you want False)
- -f Generate for Functions
- -c Generate for Classes
- -fo Format to save graph. **Default png. If you have a "huge" library. Use pdf or svg**
- ef:
```py
python main.py -fc -d "../datafly/"
```
- python
- ```py
import functionvis
functionvis.mainrunner()
```
> This also takes Jupyter notebooks into account

## Outputs
Expand Down
13 changes: 13 additions & 0 deletions build/lib/functionfinder/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import fire
from .main import *

if __name__=='__main__':
fire.Fire(mainrunner)








54 changes: 30 additions & 24 deletions main.py → build/lib/functionfinder/main.py
Original file line number Diff line number Diff line change
@@ -1,21 +1,23 @@
#!/usr/bin/env python3

from pathlib import Path
import argparse
# import argparse
import ast
from graphviz import Digraph
import jupytext
# import jupytext
import os
# import fire

opt = argparse.ArgumentParser("Function grapher")
opt.add_argument("-d", "--dir", help="Enter directory")
opt.add_argument(
"-f", type=bool, help="Generate for functions. True by default", default=True)
opt.add_argument(
"-c", type=bool, help="Generate for classes. True by default", default=True)
opt.add_argument(
"-fo", help="Format to save. Default pdf. Choose between svg/pdf/png etc.", default="png")
args = opt.parse_args()
# opt = argparse.ArgumentParser("Function grapher")
# opt.add_argument("-d", "--dir", help="Enter directory")
# opt.add_argument(
# "-f", type=bool, help="Generate for functions. True by default", default=True)
# opt.add_argument(
# "-c", type=bool, help="Generate for classes. True by default", default=True)
# opt.add_argument(
# "-fo", help="Format to save. Default pdf. Choose between svg/pdf/png etc.", default="png")
# args = opt.parse_args()
#


def top_level_functions(body):
Expand All @@ -38,32 +40,32 @@ def get_files(file_path):
print("[INFO] Creating list of files")
all_files = Path(file_path)
try:
ipynb_files = [ path for path in all_files.rglob("*.ipynb") ]
ipynb_files = [path for path in all_files.rglob("*.ipynb")]
tmp = [os.system(f"jupytext --to py {fil}") for fil in ipynb_files]
except:
ipynb_files = []

py_files = {path: [] for path in all_files.rglob("*.py")}
fils = list(py_files.keys())

for fil in fils:
try:
py_files[fil] = get_all_functions(fil)
except SyntaxError:
print(f"Could not read file: {fil}")

print("[INFO] Done creating list of files")

for fil in ipynb_files:
if "checkpoint" not in str(fil):
os.remove(str(fil).replace(".ipynb", ".py"))
return py_files


def graph_creator(dictionary, retType="functions"):
def graph_creator(dictionary, dir, fo, retType="functions"):
# Create the graph
dot = Digraph("Project")
dot.format = args.fo
dot.format = fo
# Create node names
for file in dictionary:
dot.node(file.name)
Expand All @@ -76,12 +78,16 @@ def graph_creator(dictionary, retType="functions"):

print(dot.source)
# Save graphs in required location
dot.render(Path.joinpath(Path(args.dir), retType))
Path.unlink(Path.joinpath(Path(args.dir), retType))
dot.render(Path.joinpath(Path(dir), retType))
Path.unlink(Path.joinpath(Path(dir), retType))
print("[INFO] Saved Graph")


processed = get_files(args.dir)
graph_creator(processed, "functions")
graph_creator(processed, "classes")
print("[INFO] Please check the project directory for the graphs")
def mainrunner(dir=".", fo="pdf"):
processed = get_files(dir)
graph_creator(processed, dir, fo, "functions")
graph_creator(processed, dir, fo, "classes")
print("[INFO] Please check the project directory for the graphs")

# if __name__ == '__main__':
# fire.Fire(mainrunner)
3 changes: 3 additions & 0 deletions build/lib/functionfinder/runner.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
from .main import *

fire.Fire(mainrunner)
13 changes: 13 additions & 0 deletions build/lib/functionvis/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import fire
from .main import *

if __name__=='__main__':
fire.Fire(mainrunner)








93 changes: 93 additions & 0 deletions build/lib/functionvis/main.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
#!/usr/bin/env python3

from pathlib import Path
# import argparse
import ast
from graphviz import Digraph
# import jupytext
import os
# import fire

# opt = argparse.ArgumentParser("Function grapher")
# opt.add_argument("-d", "--dir", help="Enter directory")
# opt.add_argument(
# "-f", type=bool, help="Generate for functions. True by default", default=True)
# opt.add_argument(
# "-c", type=bool, help="Generate for classes. True by default", default=True)
# opt.add_argument(
# "-fo", help="Format to save. Default pdf. Choose between svg/pdf/png etc.", default="png")
# args = opt.parse_args()
#


def top_level_functions(body):
return (f for f in body if isinstance(f, ast.FunctionDef))


def top_level_classes(body):
return (f for f in body if isinstance(f, ast.ClassDef))


def get_all_functions(filename):
with open(str(filename), 'r') as f:
temp = ast.parse(f.read())
classes = [func.name for func in top_level_classes(temp.body)]
functions = [func.name for func in top_level_functions(temp.body)]
return {"classes": classes, "functions": functions}


def get_files(file_path):
print("[INFO] Creating list of files")
all_files = Path(file_path)
try:
ipynb_files = [path for path in all_files.rglob("*.ipynb")]
tmp = [os.system(f"jupytext --to py {fil}") for fil in ipynb_files]
except:
ipynb_files = []

py_files = {path: [] for path in all_files.rglob("*.py")}
fils = list(py_files.keys())

for fil in fils:
try:
py_files[fil] = get_all_functions(fil)
except SyntaxError:
print(f"Could not read file: {fil}")

print("[INFO] Done creating list of files")

for fil in ipynb_files:
if "checkpoint" not in str(fil):
os.remove(str(fil).replace(".ipynb", ".py"))
return py_files


def graph_creator(dictionary, dir, fo, retType="functions"):
# Create the graph
dot = Digraph("Project")
dot.format = fo
# Create node names
for file in dictionary:
dot.node(file.name)
# Add functions/classes
try:
for methods in dictionary[file][retType]:
dot.edge(file.name, methods)
except TypeError:
pass

print(dot.source)
# Save graphs in required location
dot.render(Path.joinpath(Path(dir), retType))
Path.unlink(Path.joinpath(Path(dir), retType))
print("[INFO] Saved Graph")


def mainrunner(dir=".", fo="pdf"):
processed = get_files(dir)
graph_creator(processed, dir, fo, "functions")
graph_creator(processed, dir, fo, "classes")
print("[INFO] Please check the project directory for the graphs")

# if __name__ == '__main__':
# fire.Fire(mainrunner)
3 changes: 3 additions & 0 deletions build/lib/functionvis/runner.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
from .main import *

fire.Fire(mainrunner)
Binary file added classes.pdf
Binary file not shown.
Binary file added dist/functionvis-0.1.3-py3-none-any.whl
Binary file not shown.
Binary file added dist/functionvis-0.1.3.tar.gz
Binary file not shown.
Binary file added functions.pdf
Binary file not shown.
54 changes: 54 additions & 0 deletions functionvis.egg-info/PKG-INFO
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
Metadata-Version: 2.1
Name: functionvis
Version: 0.1.3
Summary: Visualize all functions and classes in a directory
Home-page: https://github.com/SubhadityaMukherjee/functionFinder
Author: Subhaditya Mukherjee
Author-email: [email protected]
License: MIT
Description: # Function Finder

Have a complex library?
Want to either understand someone elses or make your library easier to understand?

What if you could generate a detailed graph of all the functions and classes in your package. Including links between common functions in multiple files. Now you can

# What files are taken into account?

- .py
- .ipynb (Jupyter notebook)

# Outputs

- functions.pdf
- classes.pdf
- Extension can be configured

# How to use?

- cd to any directory you want
- Default outputs are in pdf
```py
import functionfinder
functionfinder.runner()
```
- Change the format of the outputs
```py
import functionfinder
functionfinder.runner(".", "svg")
```

# Example

- Functions
![](./classes.png)

- Modules
![](./functions.png)

Platform: UNKNOWN
Classifier: Programming Language :: Python :: 3
Classifier: License :: OSI Approved :: MIT License
Classifier: Operating System :: OS Independent
Requires-Python: >=3.6
Description-Content-Type: text/markdown
10 changes: 10 additions & 0 deletions functionvis.egg-info/SOURCES.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
README.md
setup.py
functionvis/__init__.py
functionvis/main.py
functionvis/runner.py
functionvis.egg-info/PKG-INFO
functionvis.egg-info/SOURCES.txt
functionvis.egg-info/dependency_links.txt
functionvis.egg-info/not-zip-safe
functionvis.egg-info/top_level.txt
1 change: 1 addition & 0 deletions functionvis.egg-info/dependency_links.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@

1 change: 1 addition & 0 deletions functionvis.egg-info/not-zip-safe
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@

1 change: 1 addition & 0 deletions functionvis.egg-info/top_level.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
functionvis
13 changes: 13 additions & 0 deletions functionvis/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import fire
from .main import *

if __name__=='__main__':
fire.Fire(mainrunner)








Binary file added functionvis/__pycache__/__init__.cpython-38.pyc
Binary file not shown.
Binary file added functionvis/__pycache__/main.cpython-38.pyc
Binary file not shown.
Loading