← Back to python
Print Python Module Structures
13 Jun 26 (10d ago)
Normally, when you are trying to figure out a python library, you just look at the documentation. But sometimes, the documentation is not that fleshed out. So, usually, I just print the whole library and skim through the whole data.
Heck, even in AI now, you can just feed it to RAG, and then ask it what are you trying to look for. Nevertheless, you still need to get the actual modules.
import inspect
import pkgutil
import sys
import mgear.anim_picker as anim_picker
def print_module_structure(root_module):
"""
Crawls through a package, maps its modules, classes, functions, and parent classes.
"""
indent_step = " "
print("=" * 80)
print(f"INTROSPECTING PACKAGE: {root_module.__name__}")
print(f"File Path: {getattr(root_module, '__file__', 'Built-in/Namespace')}")
print("=" * 80 + "\n")
# Dictionary to keep track of walked modules
modules_to_scan = {root_module.__name__: root_module}
# Discover all submodules within the package package
if hasattr(root_module, "__path__"):
for _, module_name, _ in pkgutil.walk_packages(root_module.__path__, root_module.__name__ + "."):
try:
# Dynamically import submodules to read them
__import__(module_name)
modules_to_scan[module_name] = sys.modules[module_name]
except Exception as e:
print(f"[Error importing {module_name}]: {e}")
# Process each discovered module
for mod_name, mod_obj in sorted(modules_to_scan.items()):
print(f"\n📦 MODULE: {mod_name}")
print("-" * 60)
# 1. Fetch module-level functions
functions = inspect.getmembers(mod_obj, inspect.isfunction)
# Filter to ensure functions belong to this module, not imported into it
local_functions = [f for f in functions if f[1].__module__ == mod_name]
if local_functions:
print(f"{indent_step}Functions:")
for func_name, func_obj in local_functions:
try:
sig = inspect.signature(func_obj)
except (ValueError, TypeError):
sig = "(...)"
print(f"{indent_step * 2}• {func_name}{sig}")
# 2. Fetch classes inside the module
classes = inspect.getmembers(mod_obj, inspect.isclass)
local_classes = [c for c in classes if c[1].__module__ == mod_name]
if local_classes:
print(f"{indent_step}Classes:")
for cls_name, cls_obj in local_classes:
# Show inheritance lineage (Grandparents / Parents of this class)
mro = inspect.getmro(cls_obj)
parents_lineage = " -> ".join([base.__name__ for base in mro[1:] if base.__name__ != 'object'])
parent_info = f" (Inherits from: {parents_lineage})" if parents_lineage else ""
print(f"{indent_step * 2}🔹 class {cls_name}{parent_info}:")
# Fetch methods inside this class
methods = inspect.getmembers(cls_obj, inspect.isroutine)
for meth_name, meth_obj in methods:
# Skip inherited boilerplate methods from built-in 'object' or Qt base bindings to keep output clean
if meth_name.startswith('__') and meth_name.endswith('__'):
continue
# Filter out inherited methods from deep Qt/UI ancestors to focus on mGear source overrides
defining_class = None
for base in mro:
if meth_name in base.__dict__:
defining_class = base.__name__
break
if defining_class and defining_class != cls_name and ("PySide" in defining_class or "QtWidgets" in defining_class):
continue # Skip raw un-overridden Qt ecosystem methods
try:
sig = inspect.signature(meth_obj)
except (ValueError, TypeError):
sig = "(...)"
origin_info = f" [from {defining_class}]" if defining_class and defining_class != cls_name else ""
print(f"{indent_step * 3}▪ {meth_name}{sig}{origin_info}")
# Run the introspection
print_module_structure(anim_picker)