Tracing function calls¶
The core data type in MonkeyType is the CallTrace
. A CallTrace
instance represents a single traced call of a single function or method,
including the concrete type of each argument and the return or yield type.
A CallTrace
is recorded by monkeytype run or the
trace()
context manager (or direct use of a
CallTracer
), logged via a CallTraceLogger
, probably stored in
a CallTraceStore
, and later queried from that store
by monkeytype stub or monkeytype apply and combined with all other
traces of the same function in order to generate a stub or type annotation for
that function.
monkeytype run¶
The simplest way to trace some function calls with MonkeyType is to run a Python
script under MonkeyType tracing using monkeytype run
or
monkeytype run -m
at the command line:
$ monkeytype run myscript.py
$ monkeytype run -m mymodule
monkeytype run
accepts the same monkeytype -c
option as
monkeytype stub
and monkeytype apply
, to point MonkeyType to the config
it should use.
Because of the way Python treats scripts and imported modules differently,
MonkeyType will not record traces for the entry point itself (that is, the script
passed to monkeytype run
or the module passed to run -m
); traces are
recorded only for imported modules. If you want to annotate the entry point
script/module, write another short script that imports and calls its function(s),
and run that script with monkeytype run
.
trace context manager¶
You can also trace calls by wrapping a section of code inside the trace()
context manager:
import monkeytype
with monkeytype.trace():
# argument and yield/return types for all function calls will be traced
# and stored to `monkeytype.sqlite3`
You can pass a Config
object to trace()
to
customize its behavior:
import monkeytype
from my_mt_config import my_config
with monkeytype.trace(my_config):
# arg and yield/return types for function calls here will be traced and
# logged as specified by your config.
-
monkeytype.
trace
([config: Config]) → ContextManager¶ Trace all enclosed function calls and log them per the given
config
. If no config is given, use theDefaultConfig
.
CallTracer¶
-
class
monkeytype.tracing.
CallTracer
(logger: CallTraceLogger, code_filter: CodeFilter, sample_rate: int)¶
For more complex tracing cases where you can’t easily wrap the code to trace in
a context manager, you can also use a CallTracer
directly.
CallTracer
doesn’t accept a Config
object;
instead you pass it a logger, filter, and sample rate.
If you have a config, you can easily pull those from it:
from monkeytype.tracing import CallTracer
from my_mt_config import my_config
logger = my_config.trace_logger()
tracer = CallTracer(
logger=logger,
code_filter=my_config.code_filter(),
sample_rate=my_config.sample_rate(),
)
The CallTracer
has no public API apart from its constructor, but it is
suitable for passing to sys.setprofile
as a profiler:
sys.setprofile(tracer)
# run some code to be traced
sys.setprofile(None) # remove the tracer
If your CallTraceLogger
requires flushing, you should also do this
after completing tracing:
logger.flush()
Deciding which calls to trace¶
You probably don’t want to store traces for every single function called by your program; that will likely include a lot of calls to Python standard-library or third-party library functions that aren’t your target for type annotation.
To filter the calls that will be traced, you can return a predicate function
from the code_filter()
method of your
Config()
. This function should take a Python code
object and return a boolean: True
means the function will be traced, and
False
means it will not.
The DefaultConfig
includes a default code filter.
If the environment variable MONKEYTYPE_TRACE_MODULES
is set to a list of
package and/or module names, the default filter traces only code from within
those modules. Otherwise, the default filter simply excludes code from the
Python standard library and site-packages.
Logging traces¶
A call-trace logger is responsible for accepting CallTrace
instances
one by one as they are generated by the tracing code and doing something with
them. It could print them directly to stdout, in the simplest case, or (more
likely) hand them off to a CallTraceStore
for
storage and later retrieval.
-
class
monkeytype.tracing.
CallTraceLogger
¶ Defines the interface that call-trace loggers should implement.
-
log
(trace: CallTrace) → None¶ Accept a single
CallTrace
and do something with it. This method is called every time a newCallTrace
is generated.
-
flush
() → None¶ Flush logged call traces. This method is called once on exiting from the
trace()
context manager.This method doesn’t have to be implemented; by default it is a no-op. For very simple trace loggers (e.g. logging to stdout), each trace can be fully handled in
log()
directly as it is received, and no batching or flushing is needed.
-
CallTraceStoreLogger¶
-
class
monkeytype.db.base.
CallTraceStoreLogger
(store: CallTraceStore)¶
The typical function of a call-trace logger is just to batch collected traces
and then store them in a CallTraceStore
. This is implemented by
CallTraceStoreLogger
. Its
log()
method just appends the trace to
an in-memory list, and its flush()
method saves all collected traces to the given store
.
CallTrace¶
-
class
monkeytype.tracing.
CallTrace
(func: Callable, arg_types: Dict[str, type], return_type: Optional[type] = None, yield_type: Optional[type] = None)¶ Type information for one traced call of one function.
-
func: Callable
The function that was called.
-
funcname: str
Fully-qualified name of the function, including module name (e.g.
some.module.some_func
orsome.module.SomeClass.some_method
).
-
arg_types: Dict[str, type]
Dictionary mapping argument names to types, for this particular traced call.
-
return_type: Optional[type]
Type returned by this call, or
None
if this call did not return.
-
yield_type: Optional[type]
Type yielded by this call, or
None
if this call did not yield.
-