The project uses Python ast module and simple algorithm (but effective) to generate static call graph of Python code. Basically it uses source code information (source file name, line no and col offset) when deciding function definition, but on the calling side, it only consider function names and the number of arguments when adding graph edges. The graph stop at nodes when multiple function definitions can match the calling.
The code and dependencies can be easily adjusted to work with both Python 2.7 or Python 3, but make sure to pickup the Python version matching source code (e.g. do not use Python 2 ast to analyse Python 3, or vise verse.)
NOTE: The purpose is not to achieve absolutely accurate (actually it is impossible), but to provide auxiliary tool to give a more comprehensive and convenient picture of the Python code base.
The following example is the result of checking load_plugins function of the open source project: https://github.com/nedbat/coveragepy:
Note in the following steps, mainly use Linux Python virtualenv path ./venv/bin.
(for Windows, see the next chapter)
-
Clone the code:
git clone https://github.com/zw-normal/pycallgraph.git cd pycallgraph -
Make sure Python version is the same as the one used to write code being inspected:
python --version
-
Create virtualenv according to Python version:
virtualenv -p /home/username/opt/python-2.7.15/bin/python venv
Please run according to Python version needed.
-
Upgrade pip in virtual environment to the latest version:
./venv/bin/pip install --upgrade pip
-
Install the dependencies:
./venv/bin/pip install -r requirements.txt
-
Now the environment is ready, copy
build_func_deps_config.example.pytobuild_func_deps_config.pyand setup output folder, the source code folders (source_rootslist),exclude_foldersandenable_ambiguity_call_guessing. Theenable_ambiguity_call_guessingoption allows the tool guess which function is called when multiple function definition matched. Note all method names from common Python classes (e.g. dict, list, str, set, tuple) are excluded. -
Run the following command to generate & save the whole call graphs of the source code to the output folder:
./venv/bin/python build_func_deps.py
-
Run the following command to generate the png file for the function to be inspected:
./venv/bin/python build_func_deps_dot.py 'load_plugins' 3 1The first argument is the name of the function to check. The second argument is the
upstream_cutoffsand the third one isdownstream_cutoff. Please run the following command to get helps:./venv/bin/python build_func_deps_dot.py -h
The output png file(s) in the png file(s) is in the format of
funcname-source_lineno_coloffset, as the tool differentiate function definitions with those info (small chances of conflicts as we only use base name of the source file when generating output). The node name(s) in the graph is in the format offuncname (source lineno), and use different colors to represent different function types (wheatfor normal function,yellowfor class,orchidfor property - support is turned off for speeding up,bisquefor class method,lightskybluefor static method andlightgrayfor instance method).The source info on the node represents which source file on which line the function is defined, and the line number on the edge represents on which line the target function is called.
NOTE: All line numbers and col offsets are estimated, because source code may be changed quite often, and those numbers have already been decided when building the graph. To update them, need to redo the step 8 based on the latest source code.
It is recommended for complex Python code keeping the
upstream_cutoffsanddownstream_cutoffnot too high, or it will take too much time to generate the result png, or the result png will be empty. -
From now on repeat step 10 to inspect any other functions. If you want to re-build the whole call graphs, just run step 9 again. Note unnecessary links can be present in the graph as we cannot differentiate functions from the calling side if there are functions having the same name, and the number of arguments matched.
Note : prompt is indicated here as $ but can be different.
-
Clone the code:
$ git clone https://github.com/zw-normal/pycallgraph.git $ cd pycallgraph -
Make sure Python version is the same as the one used to write code being inspected:
$ python --version
-
Create virtualenv according to Python version:
$ python -m venv .venv
Please check according to Python version needed.
-
Activation of the environment
$ .venv\scripts\activate
-
Upgrade pip in virtual environment to the latest version:
(.venv) $ pip install --upgrade pip
-
Install the dependencies:
(.venv) $ pip install -r requirements.txt
-
Install graphviz on windows:
Download the latest binary build from https://www2.graphviz.org/Packages/stable/windows/10/cmake/Release/x64/ and install. After installation, please make sure thebinfolder (e.g.C:\Program Files\Graphviz\bin) is inPathenvironment variable. -
Now the environment is ready, copy
build_func_deps_config.example.pytobuild_func_deps_config.pyand setup output folder, the source code folders (source_rootslist),exclude_foldersandenable_ambiguity_call_guessing. Theenable_ambiguity_call_guessingoption allows the tool guess which function is called when multiple function definition matched. Note all method names from common Python classes (e.g. dict, list, str, set, tuple) are excluded. -
Run the following command to generate & save the whole call graphs of the source code to the output folder:
(.venv) $ python build_func_deps.py
-
Run the following command to generate the png file for the function to be inspected (here in the example load_plugins):
(.venv) $ python build_func_deps_dot.py "load_plugins" 3 1Note the use of "..." and not simple '...' (or it will not find the function in the graph).
The first argument is the name of the function to check. The second argument is the
upstream_cutoffsand the third one isdownstream_cutoff. Please run the following command to get helps:(.venv) $ python build_func_deps_dot.py -h
The output png file(s) in the png file(s) is in the format of
funcname-source_lineno_coloffset, as the tool differentiate function definitions with those info (small chances of conflicts as we only use base name of the source file when generating output). The node name(s) in the graph is in the format offuncname (source lineno), and use different colors to represent different function types (wheatfor normal function,yellowfor class,orchidfor property - support is turned off for speeding up,bisquefor class method,lightskybluefor static method andlightgrayfor instance method).The source info on the node represents which source file on which line the function is defined, and the line number on the edge represents on which line the target function is called.
NOTE: All line numbers and col offsets are estimated, because source code may be changed quite often, and those numbers have already been decided when building the graph. To update them, need to redo the step 8 based on the latest source code.
It is recommended for complex Python code keeping the
upstream_cutoffsanddownstream_cutoffnot too high, or it will take too much time to generate the result png, or the result png will be empty. -
From now on repeat step 10 to inspect any other functions. If you want to re-build the whole call graphs, just run step 9 again. Note unnecessary links can be present in the graph as we cannot differentiate functions from the calling side if there are functions having the same name, and the number of arguments matched.
-
Go out of the environment when you have finished
(.venv) $ deactivate
