Use cases for __init__.py in python 3.3+
module, python, python-3.x
Solution
There's a very good discussion of this in this answer, and you should probably be familiar with PEP 420 to clarify the difference between and regular packages (use `__init__.py`) and namespace packages (don't).
What I offer by way of answer is a combination of reading, references, and opinion. No claims to being "canonical" or "pythonic" here.
Are [initialization] use cases still relevant with python 3.3+?
Yes. Take your example as a use case, where the package author wants to bring several things into the root package namespace so the user doesn't have to concern themselves with its internal structure.
Another case is creating a hierarchy of modules. That reference (O'Reilly) actually says:
The purpose of the `__init__.py` files is to include optional initialization code that runs as different levels of a package are encountered.
They do consider namespace packages in that discussion, but continue:
All things being equal, include the `__init__.py` files if you’re just starting out with the creation of a new package.
So, for your second question,
is it best practice to avoid `__init__.py` entirely if possible?
No, unless your intent is to create a namespace package rather than a regular package, in which case you must not use `__init__.py`.
Why might you want that? The O'Reilly reference has the clearest example I've seen about why namespace packages are cool, which is being able to collapse namespaces from separate, independently-maintained packages:
foo-package/
spam/
blah.py
bar-package/
spam/
grok.py
Which allows
>>> import sys
>>> sys.path.extend(['foo-package', 'bar-package'])
>>> import spam.blah
>>> import spam.grok
>>>
So anyone can extend the namespace with their own code. Cool.
Problem
Now that `__init__.py` is no longer required to make a directory recognized as a package, is it best practice to avoid them entirely if possible? Or are there still well-accepted use cases for `__init__.py` in python 3.3+? From what I understand, `__init__.py` were very commonly used to run code at module import time (for example to encapsulate internal file structure of the package or to perform some initialization steps). Are these use cases still relevant with python 3.3+?