Search code examples
pythonpylintmypy

Requirement for __init__.py just to satisfy pylint and mypy


I have a project with the following (partial) directory structure

.
├── mypy.ini
├── src
│   ├── preppy
│   │   ├── cli.py
│   │   ├── __main__.py
│   │   ├── model.py
│   │   └── tools.py
├── pyproject.toml
└── tests

In cli.py, I have the following code (lines 13 and 14 in the file):

from .model import Problem
from .tools import get_abs_path, transcode

I also have similarly styled relative imports in model.py and __main__.py All similar imports throw errors in both pylint (2.5.3) and mypy (0.761) when the tools are automatically run in my IDE (Code - OSS), e.g.:

Attempted relative import beyond top-level package pylint(relative-beyond-top-level) [13,1]
Cannot find implementation or library stub for module named '.model' mypy(error) [13,1]
Attempted relative import beyond top-level package pylint(relative-beyond-top-level) [14,1]
Cannot find implementation or library stub for module named '.tools' mypy(error) [14,1]
See https://mypy.readthedocs.io/en/latest/running_mypy.html#missing-imports mypy(note) [13,1]

When I add a blank __init__.py file to the folder, the errors disappear.

I don't need this __init__.py file for the package to work.

I thought that post-PEP 420, it shouldn't be required, especially if it's just there to satisfy linters.

Is there something else I'm doing wrong, or should I just add the __init__.py and get over it :) ?

Config for pylint is in pyproject.toml:

[tool.pylint.'MESSAGES CONTROL']
# Pylint and black disagree on hanging indentation.
disable = "C0330"

[tool.pylint.MISCELLANEOUS]
# Note: By default, "TODO" is flagged, this is disabled by omitting it
#       from the list below.
notes = "FIXME,XXX"

Config for mypy is in mypy.ini:

[mypy]
disallow_untyped_calls = True
disallow_untyped_defs = True
disallow_incomplete_defs = True
disallow_untyped_decorators = True
mypy_path = src
namespace_packages = True

[mypy-openpyxl]
ignore_missing_imports = True

[mypy-pulp]
ignore_missing_imports = True

[mypy-pytest]
ignore_missing_imports = True

I'm running python 3.8.0.


Solution

  • PEP 420 does not allow to "create a package by omitting __init__.py", it enforces to "create a namespace package by omitting __init__.py". This means:

    • If you want a package, add __init__.py.
    • If you want a namespace package, omit __init__.py.

    While using a namespace package like a regular package usually works, it may unexpectedly fail when package names clash. In most cases, a namespace package is not desirable.