A static type analyzer for Python code
Home
User guide
Developer guide
Error classes
FAQ
Typing FAQ
Supported features
Mailing list
File a bug
View the Project on GitHub google/pytype
Hosted on GitHub Pages — Theme by orderedlist
Here’s a simple example of plain Python code:
def unannotated(x, y): return " + ".join(x, y)
This code has a bug: str.join
should be passed an iterable of strings, not the individual strings. pytype will discover and report this bug:
File "t.py", line 2, in unannotated: Function str.join expects 2 arg(s), got 3 [wrong-arg-count] Expected: (self, iterable) Actually passed: (self, iterable, _)
Here’s an example of type annotations:
def annotated(x: int, y: float = 0.0) -> int: return x + y
The above code uses the syntax from PEP 3107 and PEP 484 to declare the parameter and return types of the function annotated
.
Note that the return type of annotated
is declared to be an integer, but the function actually returns a float. pytype will also find this bug:
File "t.py", line 2, in annotated: bad option in return type [bad-return-type] Expected: int Actually returned: float
The above code only used built-in types (int
and float
). To formulate more complex types, you typically need to import some advanced typing constructs. For example, this is how you might declare a function that extracts the keys out of a mapping:
from collections.abc import Mapping, Sequence from typing import Any def keys(mapping: Mapping[str, Any]) -> Sequence[str]: return tuple(mapping)
or describe callable objects or higher order functions:
from collections.abc import Callable def instantiate(factory: Callable[[], int]) -> int: return factory()
A particularly useful construct is Union
, expressed with a |
. It can be used to specify that a function might return None
:
from collections.abc import Sequence def find_index_of_name(sequence: Sequence[Any], name: str) -> int | None: try: return sequence.index(name) except ValueError: return None
or allows a None
value:
def greet(name: str | None) -> str: return 'Hi John Doe' if name is None else 'Hi ' + name
Note: The |
syntax is new in Python 3.10. If you need to support older versions, use typing.Union[X, Y]
rather than X | Y
and typing.Optional[X]
as a shorthand for typing.Union[X, None]
.
The above-mentioned constructs are standardized and understood by all Python type checkers. Pytype additionally supports some experimental features. See the experimental features documentation for details.
Sometimes pytype generates “false positives”, i.e. errors that, from the perspective of the type-checker, are correct, but from a user standpoint aren’t.
For example, this is a class that uses late initialization.
import socket class Server: def __init__(self, port): self.port = port def listen(self): self.socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) self.socket.bind((socket.gethostname(), self.port)) self.socket.listen(backlog=5) def accept(self): return self.socket.accept()
pytype will complain:
File "t.py", line 13, in accept: No attribute 'socket' on Server [attribute-error]
(The reasoning is that if you call accept()
before listen()
, Python will crash with an AttributeError
.)
Note that the error message will contain the class of the error: attribute-error
To silence this warning, change line 13 to this:
return self.socket.accept() # pytype: disable=attribute-error
Alternatively, if there are a lot of these occurrences, put the following at the start of the file. A disable
on a line by itself will disable all these warnings for the rest of the file. (This is true even if the disable
is within an indented block.)
# pytype: disable=attribute-error
Use enable
to re-enable a disabled warning:
# pytype: disable=attribute-error return self.socket.accept() # pytype: enable=attribute-error
There is also a “catch-all”. Above, we could have written:
return self.socket.accept() # type: ignore
It’s preferred to use the precise form (pytype: disable=some-error
) instead of type: ignore
, and leave the latter for rare and special circumstances.
Above, we only silenced the error pytype gave us. A better fix is to make pytype aware of the attributes Server
has (or is going to have). For this, we add a PEP 526-style variable annotation:
class Server: socket: socket.socket
Adding type annotations to your code sometimes means that you have to add extra dependencies. For example, say we have the following function:
def start_ftp_server(server): return server.start()
While this function works in isolation and doesn’t need any imports, it potentially operates on types from another module. Adding the type annotation reveals that fact:
import ftp def start_ftp_server(server: ftp.Server): return server.start()
While we encourage to write the code like above, and hence make it clear that our code does depend, indirectly, on the types declared in ftp
, the additional imports can lead to concerns about load-time performance.
PEP 484 allows to declare imports in a block that’s only evaluated during type-checking. See Google’s Python Style Guide.
In some cases, it’s not possible to add annotations to a module by editing its source: C extension modules, external python source files, etc . For those cases, PEP 484 allows you to declare a module’s types in a separate “stub” file with a .pyi
extension. Pyi files follow a subset of the python syntax and are analogous to header files in C (examples).
If you already have a .pyi
and would like to merge it back into a .py
file, we provide a tool to automate this.
For builtins, standard library, and third party modules, pytype uses static pyi files rather than ones generated by running over the Python code. The files are located in pytype builtins, pytype stdlib, and typeshed. If you find a mistake in one of these files, please file a bug.