Call Map is a tool for navigating call graphs in Python, with plans to support
other languages. Below is a screen shot after running call_map -m toolz
, then
clicking on some functions.
While Call Map is intended to help in gaining a general understanding of a codebase, it is also a natural fit for tracing code paths, which constitutes an important security concern. Many security issues are revealed by finding a code path that connects user input to dangerous coding patterns.
See the blog post for more.
Call Map is distributed as a python package for Python 3.5
. To install the
gpl-licensed version with pip3
, run:
# From a directory where you want to install call_map git clone -b gpl-licensed https://github.com/ajylee/call_map pip3 install -e call_map
Note that some Python 3 distributions call the package manager pip
, and some
call it pip3
, so you may need substitute pip3
with pip
.
The above commands should install the executable call_map
to the same path
that the Python interpreter is in.
If you want to install the bsd-licensed version, you will need to install PySide2
yourself. In contrast, the gpl-licensed version depends on PyQt5
, which pip
should
be able download and install automatically.
For details and alternative methods see INSTALL.rst
.
Open files to start exploring:
call_map -f example.py # you can also open multiple files call_map -f *.py
You can also add to the module search path (sys.path
):
call_map -f *.py -p .
Call Map will try to resolve the files as modules whenever they can be found in
the module search path. For more documentation on command line
arguments, call_map -h
.
To configure Call Map, set the environment variable CALL_MAP_RC_DIRECTORY. The path to the Call Map configuration file will be:
$CALL_MAP_RC_DIRECTORY/call_map_rc.py
At this time the configuration options are:
open_in_editor(path: pathlib.Path, line: int)
: if you define this function it will be called whenever you open a file. For example, the following can be used to open files in an Emacs server if have you calledserver-mode
in a running Emacs session:def open_in_editor(path, line): import subprocess as sbp sbp.call(['emacsclient', '+{}'.format(line), str(path)])
The following can be used to open files in the GVim server if you started a GVim server with
gvim --servername my_vim_server
:def open_in_editor(path, line): import subprocess as sbp sbp.call(['gvim', '--servername', 'my_vim_server', '--remote', '+{}'.format(line), str(path)])
MULTITHREADING
: Whether to use a separate thread for the GUI and searching the call graph. Defaults toTrue
. Turning it off is for debug purposes.
There are a couple of quirks in the UI design, due to the fact that I haven't arrived at a better solution or the tradeoff of additional complexity is unfavorable.
- Usages can appear to show up more than once, but actually they are different usages in the same scope.
- You may notice some bulitins such as
help
,id
, andfilter
are ignored. Seecall_map/config.py
in the source code for the full list of ignored functions. - If the position to be highlighted is at the start of a file, it won't be highlighted. This is because typically only modules and scripts are positioned at the start of their respective files.
Some quirks are inherited from the jedi
Python analysis backend.
- The search scope for usages is the set of modules that have been loaded by the
jedi
backend. That means that the scope will change as you explore new modules. If you want to explicitly include a module in the search scope, add it to the initial list of modules to be inspected. jedi
always searches the interpreter'ssys.path
, even when it is not explicitly included. However, it will prioritize the user-definedsys_path
.
Any caveats that exist for static analysis backends also apply to Call Map. (At
this time only the jedi
backend is integrated into Call Map.)
The jedi
backend for Python analysis does not always find all usages. The
dynamic nature of Python makes it impossible to always determine the definition
of a function with static analysis. Sometimes jedi
resolves a call to multiple
possible functions. For example os.path.abspath
depends on the platform. In
this case call_map
lists both possibilities as abspath
and abspath (2)
.
jedi
also does not resolve all calls and usages. Jedi's own documentation also
has a list of caveats.
Sometimes when Jedi throws an error when analyzing one item, the error affects other items. For example, when finding usages, if Jedi raises an error on one usage, the other usages it has found may be unrecoverable (as of Jedi v0.10.0).