How to handle convenience imports with Mypy
Python convenience imports are where objects are imported into a package’s
__init__.py module so client code has a simple, easy-to-remember location to
import objects from.
But, by default, Mypy will complain about such imports when run in strict mode.
For example, consider a
foo.__init__ module with contents:
# foo/__init__.py from ._queries import my_function
main.py module that imports
my_function from the
# main.py from foo import my_function ...
mypy --strict on
main.py yields an error:
$ mypy --strict main.py main.py:1: error: Module "foo" does not explicitly export attribute "my_function" [attr-defined] Found 1 error in 1 file (checked 1 source file)
This error relates to the
setting which is enabled by Mypy’s strict mode. This instructs Mypy to not
consider objects are exported unless:
- the item is imported using
from ... import ... as;
- the item is included in the
foo.__init__ module does not meet either of these criteria. If we
want to conform to strict mode, the best approach is probably to add the objects
we want to reexport to a
# foo/__init__.py from ._queries import my_function __all__ = ["my_function"]
Patching module objects in tests
This is also a problem when using
mock.patch to patch module objects in a
For example, Mypy (in strict mode) will complain that this test attempts to
requests object with a stub:
# tests/test_vendors.py from unittest import mock from foo import vendors @mock.patch.object(vendors, "requests") def test_vendor_client(requests): # Stub some responses etc requests.post.return_value = ...
foo/vendors.py imports the
# foo/vendors.py import requests ...
This generates a
$ mypy tests/test_vendors.py tests/test_vendors.py:4: error: "Module foo.vendors" does not explicitly export attribute "requests" [attr-defined]
As before, importing
requests like this indicates to Mypy that it isn’t
available for re-export and so other modules, like our test module, cannot
Since this is a common pattern in tests, it’s better to ignore the error rather
than change the way packages are imported (e.g. to
import requests as requests).
This can be done in your Mypy config file:
# setup.cfg [mypy] strict = true [mypy-tests.*] disable_error_code = attr-defined
This requires Mypy v0.991 or above to work.