Recursively populating __all__ in __init__.py

flask, python

Solution

You probably shouldn't be doing this: The default behaviour of `import` is quite flexible. If you don't want a module (or any other variable) to be automatically exported, give it a name that starts with `_` and python won't export it. That's the standard python way, and reinventing the wheel is considered unpythonic. Also, don't forget that other things besides modules may need exporting; once you set `__all__`, you'll need to find and export them as well.

Still, you ask how to best generate a list of your exportable modules. Since you can't export what's not present, I'd just check what modules of your own are known to your main module:

basedir = os.path.dirname(__file__)
for m in sys.modules:
    if m in locals() and not m.startswith('_'): # Only export regular names
        mod = locals()[m]
        if '__file__' in mod.__dict__  and mod.__file__.startswith(basedir):
            print m

`sys.modules` includes the names of every module that python has loaded, including many that have not been exported to your main module-- so we check if they're in `locals()`.

This is faster than scanning your filesystem, and more robust than assuming that every `.py` file in your directory tree will somehow end up as a top-level submodule. Naturally you should run this code near the end of your `__init__.py`, when everything has been loaded.

Problem

I'm using the following code to populate `__all__` in my module's `__init__.py` and I was wandering if there was a more efficient way. Any ideas? ``` import fnmatch import os __all__ = [] for root, dirnames, filenames in os.walk(os.path.dirname(__file__)): root = root[os.path.dirname(__file__).__len__():] for filename in fnmatch.filter(filenames, "*.py"): __all__.append(os.path.join(root, filename[:-3])) ```

Original source