Looking for inactive functions – Part 4
If you’ve ever opened a big Python project and thought, “Where do I even start?” . As some of my projects grow, keeping track of how classes, functions, and modules connect quickly turn into a tangled mess – sometime. I often ask myself “Wouldn’t it be nice if you could actually see how everything fits together?”
That’s where this knowledge graph comes in. I am turning my codebase into a network. The nodes represent my files, classes and functions. Each edges show how they depend on each other. I could use arrows but the would only require an little edit to the code below – just change G = nx.Graph() to G = nx.DiGraph()
Suddenly, instead of guessing where that function gets called or which class inherits from what, I can literally map it out.
I use the Python libraries NetworkX and Matplotlib’s pyplot. You can build and visualize these graphs in just a few lines of code. NetworkX helps you create the relationships (the brain of the graph), while Matplotlib draws it out (the eyes). The end result? A clear, visual overview of the code structure — kind of like a mind map for a Python project.
I think this approach is useful for beginners because it helps:
- Understand how different parts of your project connect.
- Spot problems like tightly coupled functions or circular dependencies.
- Learn faster, since you can see the bigger picture instead of diving blindly into the code.
So, next time you’re exploring a new project — or your own code starts feeling too big to hold in your head — try mapping it out as a graph. It’s a fun, visual way to make sense of complexity and build real intuition about how Python projects fit together.
Added the functions to the Knowledge Graph. To my surprise I found a I found a few classes that I set up right in the beginning which I had forgotten about. The Game.py file is pretty long and I am now thinking about seperating some of the core Game logic and the UI functions.
In my next post, I will but adding links to find what functions are calling which. This is the aim of this litte side project because I am looking to old functions that are no longer active and can be delete.



import os
import re
import networkx as nx
import matplotlib.pyplot as plt
class Map(object):
def __init__(self):
self.use_directories = []
self.ignore_filenames = []
self.file_extensions_required = []
self.map_files = []
self.classes_found = []
self.functions_found = []
def mapper(self):
self.get_all_files()
def get_all_files(self):
# read all the required files in the directories into map_files array
counter = 0
for directory in self.use_directories:
files = [ f for f in os.listdir(directory) if os.path.isfile(os.path.join(directory, f)) ]
for f in files:
if self.is_file_required(f):
full_path = "/".join([directory, f])
graph_id = "file" + str(counter)
self.map_files.append( { 'id' : graph_id, 'path' : full_path, 'name' : f } )
counter = counter + 1
# if we have any files at all
if self.map_files:
self.find_functions_and_classes()
self.cleanup()
def cleanup(self):
# Clean up references to Parent Classes to their files OR Child classes to their reps. parent classes
for e, m in enumerate(mapper.classes_found):
if m['class_parent'] == 'object':
m['parentid'] = m['fileid']
else:
for f in mapper.classes_found:
if f['class'] == m['class_parent']:
m['parentid'] = f['id']
def is_file_required(self, filename):
# Ignoring : file has no extension
if "." not in filename:
return False
# Ignoring : file extension not required
file_parts = filename.split(".")
if file_parts[-1] not in self.file_extensions_required:
return False
# Ignoring : Filename is excluded
if filename in self.ignore_filenames:
return False
return True
def find_functions_and_classes(self):
counter = 0
for mf in self.map_files:
parent_file = mf['id']
with open(mf['path'], "r") as f:
current_class = ""
for l in f:
l = l.replace("\n", "").strip()
if re.search("^class", l):
current_class=l
class_definition = re.match(r"class (?P<classname>.+)\((?P<classparent>.*)\)", l)
class_name = ''
class_parent = ''
if class_definition:
cdetails = class_definition.groupdict()
class_name = cdetails['classname']
class_parent = cdetails['classparent']
graph_id = "class" + str(counter)
classid = graph_id
self.classes_found.append({ 'id' : graph_id, 'fileid' : mf['id'], 'parentid' : '', 'current_class' : current_class, 'class' : class_name, 'class_parent' : class_parent })
counter = counter + 1
if re.search("^def\s(.*):$", l):
function_definition = re.match(r"def (?P<functionname>.+)\((?P<aarrgs>.*)\)", l)
function_name = ''
if function_definition:
fdetails = function_definition.groupdict()
function_name = fdetails['functionname']
graph_id = "function" + str(counter)
self.functions_found.append({ 'id' : graph_id, 'fileid' : mf['id'], 'parentid' : classid, 'function' : function_name })
counter = counter + 1
# create class
mapper = Map()
# set requirements for mapper
mapper.use_directories.append("/home/phill/Desktop/projects/zombizied_gui")
mapper.use_directories.append("/home/phill/Desktop/projects/zombizied_gui/classes")
mapper.file_extensions_required.append("py")
mapper.ignore_filenames.append("zombizied_ai_preprocessor.py")
mapper.ignore_filenames.append("zombizied_mapbuilder.py")
# start mapping
mapper.mapper()
G = nx.Graph()
# Add Root node
G.add_node("Zombizied", entity="root", label='Zombizied GUI')
# Add Files to root
for e, m in enumerate(mapper.map_files):
G.add_node(m['id'], entity="file", label=m['name'])
G.add_edge("Zombizied", m['id'] )
# Add Class Nodes
for e, c in enumerate(mapper.classes_found):
try:
c['parentid'] != ''
except:
c['parentid'] = 'Zombizied'
finally:
G.add_node(c['id'], weight= 1, entity="class", label=c['class'])
G.add_edge(c['parentid'], c['id'])
# Add Function Nodes
for e, f in enumerate(mapper.functions_found):
try:
f['parentid'] != ''
except:
f['parentid'] = 'Zombizied'
finally:
G.add_node(f['id'], weight= 1, entity="function", label=f['function'])
G.add_edge(f['parentid'], f['id'])
labels = nx.get_node_attributes(G, 'label')
color_map = {
'root' : 'red',
'file' : 'skyblue',
'class' : 'lightgreen',
'function' : 'lightcoral'
}
node_colors = [
color_map.get(G.nodes[node].get('entity', ''), 'gray')
for node in G.nodes()
]
pos = nx.spring_layout(G, seed=2323424)
nx.draw(G,
pos,
labels=labels,
node_color=node_colors,
node_size=500,
font_size=8,
edge_color='lightgray'
)
plt.show()