Pytype’s high-level workflow to analyse a single file1 is:
Run the bytecode5
For each frame6
Loop over opcodes, updating state[^run-instruction]
state = run_instruction(op, state)
analyzeto infer type signatures for all classes, methods and functions.
run_instruction is the central dispatch point for opcode analysis. For every
OP, we have a corresponding
looks this method up, calls it with the current state and the opcode, and uses
the return value as the new state.
TIP: If you want to get a feel for how pytype works, an excellent
starting point is to look at some of the
byte_* methods and see how they
mirror the workings of the python interpreter at a type level, popping arguments
off the stack, manipulating
globals dictionaries, and creating
objects for classes, methods and functions.
Pytype performs two passes when analyzing a file, as mentioned in the workflow above.
The first pass starts with
run_program(), which executes the bytecode of the
Python program using pytype’s virtual machine. This first step compiles the
source code, executes the bytecode and builds the typegraph for the program.
Besides regular type errors, this step also checks for errors such as:
However, this step will only find errors in functions and classes that are part
of the control flow graph, starting with the main function of the file. If a
function or class is not reachable from
main(), this pass will miss errors in
that member. If the file doesn’t have a
main() – i.e. it is a library – then
no class or function bodies will be type checked.
Because of that, pytype uses the typegraph to run a second analysis pass by
analyze(). This pass recursively type checks all members of the
program, starting at the top level definitions. These are mostly classes, though
some libraries define top-level functions.
Both passes will be performed, no matter if pytype is run in “inference” (
or “check” (
-C) mode. The second pass can be disabled using the
-m) debug option, in which case only the code that is reachable from
will be analyzed.
class CallTracer ↩
A frame is a segment of code, typically one method or function. See