Python Namespace Package Imports and Pylint

I recently had an interesting (and frustrating) experience while linting a Python 3.6 project with pylint 1.6.5.

The Problem

Despite a functioning Python project, pylint would consistently fail to import namespace packages with the error E401: Unable to import [package]

TL;DR: solution

Described below is a simple project that demonstrates the issue.

Project structure:

myproject/
└── mypackage
    ├── badger.py
    ├── llama.py
    ├── .pylintrc
    ├── mysubpackage
    │   └── snake.py

Python file contents:

$ cat llama.py
#!/usr/bin/env python3

import sys
from sys import path
from os.path import dirname, abspath, join

path.append(abspath(join(dirname(__file__), '..')))

from mypackage import badger
from mypackage.mysubpackage import snake

if __name__ == '__main__':
    badger.whoami()
    snake.whoami()

$ cat badger.py
def whoami():
    print("I am badger!")

$ cat mysubpackage/snake.py
def whoami():
    print("I am snake!")

This all runs as expected:

$ ./llama.py
['/Users/rick/code/myproject/mypackage', '/usr/local/Cellar/python3/3.6.0/Frameworks/Python.framework/Versions/3.6/lib/python36.zip', '/usr/local/Cellar/python3/3.6.0/Frameworks/Python.framework/Versions/3.6/lib/python3.6', '/usr/local/Cellar/python3/3.6.0/Frameworks/Python.framework/Versions/3.6/lib/python3.6/lib-dynload', '/Users/rick/Library/Python/3.6/lib/python/site-packages', '/usr/local/lib/python3.6/site-packages', '/Users/rick/code/myproject']
I am badger!
I am snake!

However, pylint would still complain (Unable to import ${package}):

$ pylint --reports=no llama.py
************* Module llama
E: 10, 0: Unable to import 'mypackage' (import-error)
E: 11, 0: Unable to import 'mypackage.mysubpackage' (import-error)

I thought maybe it was a path issue in pylint’s environment, so I appended to the path in my .pylintrc too:

$ cat .pylintrc
[MASTER]
init-hook='import sys; import os; print(">>>INIT HOOK<<<"); sys.path.append(os.path.abspath(os.path.join(os.getcwd(), '..'))); print(sys.path); from mypackage import badger;badger.whoami()'

But that didn’t change the behavior:

$ pylint --reports=no llama.py
>>>INIT HOOK<<<
['/usr/local/bin', '/usr/local/Cellar/python3/3.6.0/Frameworks/Python.framework/Versions/3.6/lib/python36.zip', '/usr/local/Cellar/python3/3.6.0/Frameworks/Python.framework/Versions/3.6/lib/python3.6', '/usr/local/Cellar/python3/3.6.0/Frameworks/Python.framework/Versions/3.6/lib/python3.6/lib-dynload', '/Users/rick/Library/Python/3.6/lib/python/site-packages', '/usr/local/lib/python3.6/site-packages', '/usr/local/lib/python3.6/site-packages/astroid/brain', '/Users/rick/code/myproject']
I am badger!
************* Module llama
E: 10, 0: Unable to import 'mypackage' (import-error)
E: 11, 0: Unable to import 'mypackage.mysubpackage' (import-error)

The Solution

After some discussion with colleagues, it turned out that while Python >= 3.3 no longer requires __init__.py files (PEP-420), pylint still does (discussion here).

The fix was to add __init__.py files to each package directory.