pymetacg are MetaCG's Python bindings that support parsing MetaCG files from Python projects.
Currently, manipulating and writing call graphs is not supported.
- nanobind
- pytest (for tests only)
- pytest-cmake (for tests only)
pymetacg can be built as an optional feature during the MetaCG build, producing a dynamic library (e.g., pymetacg.cpython-313-x86_64-linux-gnu.so) that can then be loaded from Python.
Pass -DMETACG_BUILD_PYMETACG=ON to CMake to enable pymetacg.
$> cmake -S . -B build -DCMAKE_INSTALL_PREFIX=/where/to/install -DMETACG_BUILD_PYMETACG=ON
$> cmake --build build --parallel
$> cmake --install buildBy default, nanobind is downloaded automatically during configruation.
To use an already installed nanobind, use -DMETACG_USE_EXTERNAL_NANOBIND=ON and specify the installation path using -Dnanobind_ROOT=<your nanobind installation>, if necessary.
The CMake setup of pymetacg uses CMake's FindPython to find the Python installation for which pymetacg will be built.
To specify which Python installation to use, use -DPython_ROOT_DIR=<python installation>.
By default, the resulting dynamic library will be installed in <MetaCG installation prefix>/lib/<python version>/site-packages.
To override the installation path, use -DCMAKE_INSTALL_PYTHON_LIBDIR.
Finally, make sure that the installation directory is found by Python, e.g., by using the PYTHONPATH environment variable.
Test whether pymetacg was built successfully:
$> python -c "import pymetacg; print(pymetacg.info)"import pymetacg
# print MetaCG version information
print(pymetacg.info)
# read MetaCG file (.mcg)
cg = pymetacg.Callgraph.from_file("/path/to/callgraph.mcg")
# check whether node exists in graph
main_exists = "main" in cg
# get number of nodes in graph
num_nodes = len(cg)
# access nodes in graph (assumes function name to be unique in graph)
main = cg.get_single_node("main")
foo = cg.get_single_node("foo")
# shorthand for `get_single_node`
main = cg["main"]
foo = cg["foo"]
# access node by function name (ignoring possible other entries with same function name)
foo = cg.get_first_node("foo")
# get iterator over all nodes of name
for node in cg.get_nodes("foo")
print(hash(node)) # get node ID
list_of_nodes = list(cg.get_nodes("foo"))
# iterate over node callees and collect callee function names into list
callees_names = [callee.function_name for callee in main.callees]
# iterate over node callers and collect caller function names into list
callers_names = [caller.function_name for caller in foo.callers]
# check whether function calls itself
foo_is_recursive = foo in foo.callees
# iterate over nodes in graph and print function names
for node in cg:
print(node.function_name)
# check if node has some meta data
main_has_statement_metadata = "numStatements" in main.meta_data
# access JSON representation of node's meta data using `.data` (independent of meta data type)
metadata = main.meta_data["numStatements"].data
# global meta data can be accessed in the same way
global_metadata = cg.meta_data["someGlobalMetaData"]pymetacg comes with a PyTest-based test suite, which can be enabled via the CMake option -DMETACG_BUILD_PYMETACG_TESTS=ON.
The test suite requires pytest and pytest-cmake to be installed from PyPI.
# create and activate Python virtual environment
$> python -m venv .venv
$> source .venv/bin/activate
# install test dependencies from PyPI
$> pip install -r pymetacg/tests/requirements.txt
# configure and build MetaCG with pymetacg and its test suite enabled
$> cmake -S . -B build -G Ninja -DMETACG_BUILD_PYMETACG=ON -DMETACG_BUILD_PYMETACG_TESTS=ON
$> cmake --build build --parallel
# execute the tests
$> cd build/pymetacg/tets
$> ctest . --verbose