mypy tips
Making your module type checking compatible §
Follow the steps in the mypy documentation on “Making PEP 561 compatible packages”.
In short, this consists of:
- Add an empty
py.typed
file into your module’s root (adjacent to the__init__.py
file). - Add
package_data={"": ["py.typed"]}
to yoursetup.py
to include thepy.typed
at build time.
It should now be possible for other modules to import this module, and avoid type checking errors upon import. Of course, you’ll still need to add type hinting to your module for this to actually be useful.
If you get errors, mypy has good documentation on how to resolve these.
Note that there are two other ways of enabling type checking, using stub files, or using stub only packages. My preference is for the first method of inline type annotations.
Cyclical imports §
Adding type checking can introduce cylical imports.
This can be avoided through first using if TYPE_CHECKING:
to conditionally import the necessary modules. This will be True
when type checking is performed, and False
otherwise (e.g. at runtime).
Then, put the affected imports as strings, e.g. "MyClass"
. This allows the imports to not be present at runtime, but when type checking occurs, mypy will infer the type from the string.
from typing import TYPE_CHECKING
if TYPE_CHECKING:
from my_module import MyClass
def foo(bar: "MyClass") -> None:
pass
Optional imports §
Alternatively, type checking can require all optional dependencies to be present, which can be problematic!
For example, consider having module A, which at runtime depends upon having module B xor C present - it doesn’t require both. However, the type checker does require both. This contradiction can be resolved using the same method as for resolving cyclical imports:
from typing import Union, TYPE_CHECKING
if TYPE_CHECKING:
from module_b import B
from module_c import C
def foo(bar: Union["B", "C"]) -> None:
pass
Signal handling §
For signal handlers:
- The
frame
type isFrameType
. - The
signum
type isint
, or perhapsUnion[int, Signals]
?
from types import FrameNum
import signal
signal.signal(signal.SIGINT, self.exit_handler)
signal.signal(signal.SIGTERM, self.exit_handler)
def exit_handler(signum: int, frame: FrameType) -> None:
pass
Common Uncommon Types §
The common types such as int
and str
and quite straight-forward. But some of the less-common common types are not quite so straight-forward.
Queue §
In Python 3.6 or below, the queue.Queue
type requires specifying as a string, e.g.:
import queue
my_q: "queue.Queue[int]" = queue.Queue()
In Python 3.7 or above, you can put from __future__ import annotations
in your imports and use Queues as normal, e.g.:
from __future__ import annotations
import queue
my_q: queue.Queue[int] = queue.Queue()