본문 바로가기
카테고리 없음

python 스크립트 내 함수 호출 관계 확인 코드

by mirso 2025. 9. 23.

 

 파이썬 스크립트 파악할 때, 함수간 호출관계 및 파라미터 파악이 귀찮아서 파이썬 소스 기반 확인하는 스크립트 작성함.

 파이썬 노트북에서 실행 가정하고 만듦.

 

소스코드

import ast
import networkx as nx
from pathlib import Path
import matplotlib.pyplot as plt
import pandas as pd

class CallGraphBuilder(ast.NodeVisitor):
    def __init__(self):
        self.graph = nx.DiGraph()
        self.current_func = None
        self.func_defs = {}  # 함수 이름 → 파라미터 리스트
        self.edges = []      # (caller, callee, Source_param, Target_param)

    def visit_FunctionDef(self, node):
        func_name = node.name
        self.func_defs[func_name] = [arg.arg for arg in node.args.args]
        prev = self.current_func
        self.current_func = func_name
        self.generic_visit(node)
        self.current_func = prev

    def visit_Call(self, node):
        if self.current_func:
            callee = self._get_func_name(node.func)
            if callee and callee in self.func_defs:  # 파일 내부 정의된 함수만 추적
                arg_pairs = self._map_args(callee, node)
                self.graph.add_edge(self.current_func, callee)
                for target_param, source_expr in arg_pairs.items():
                    self.edges.append(
                        (self.current_func, callee, source_expr, target_param)
                    )
        self.generic_visit(node)

    def _get_func_name(self, node):
        if isinstance(node, ast.Name):
            return node.id
        return None  # Attribute 등은 무시

    def _map_args(self, callee, node: ast.Call):
        arg_map = {}
        params = self.func_defs.get(callee, [])

        # 위치 인자
        for param, arg in zip(params, node.args):
            arg_map[param] = self._expr_to_str(arg)

        # 키워드 인자
        for kw in node.keywords:
            if kw.arg:
                arg_map[kw.arg] = self._expr_to_str(kw.value)

        return arg_map

    def _expr_to_str(self, node):
        try:
            return ast.unparse(node)  # Python 3.9+
        except Exception:
            return type(node).__name__


def build_call_graph(pyfile: str):
    src = Path(pyfile).read_text(encoding="utf-8")
    tree = ast.parse(src)
    builder = CallGraphBuilder()
    builder.visit(tree)
    return builder.graph, builder.edges


def visualize_graph(G):
    pos = nx.spring_layout(G, seed=42)

    plt.figure(figsize=(12, 8))
    nx.draw(
        G, pos,
        with_labels=True,
        node_color="lightblue",
        node_size=2000,
        font_size=12,
        font_weight="bold",
        arrows=True
    )
    plt.show()


def edges_to_dataframe(edges):
    return pd.DataFrame(edges, columns=["Source", "Target", "Source_param", "Target_param"])


# === 실행 예시 ===
graph, edges = build_call_graph("../langgraph/policy_api.py")  # 분석할 .py 파일
visualize_graph(graph)

df = edges_to_dataframe(edges)[["Source", "Source_param", "Target_param", "Target"]].sort_values(['Source','Source_param'])
df

 

실행 예시