views:

294

answers:

2

Given a package, how can I automatically find all its sub-packages?

+4  A: 

You can't rely on introspection of loaded modules, because sub-packages may not have been loaded. You'll have to look at the filesystem, assuming the top level package in question is not an egg, zip file, extension module, or loaded from memory.

def get_subpackages(module):
    dir = os.path.dirname(module.__file__)
    def is_package(d):
        d = os.path.join(dir, d)
        return os.path.isdir(d) and glob.glob(os.path.join(d, '__init__.py*'))

    return filter(is_package, os.listdir(dir))
James Emerton
Not working. That glob.glob thing returns a list: Is that a mistake?
cool-RR
works just fine.
SilentGhost
I had forgotten that os.listdir does not return the full path. Fixed the bug. (An empty list in a boolean context evaluates to false.)
James Emerton
SilentGhost tells me that my implementation is terrible, though it works for me. I'm assuming yours is okay, so I guess I'll select that as the answer. However, you think it's okay to put the question mark after the `py`? Wouldn't it not work when there isn't a `pyc` or `pyo` file?
cool-RR
You're correct, it should be an asterisk so that it matches .py, .pyc, and .pyo files. As it stands, there are still a lot of gotchas with this sort of thing. I hope you aren't using it for anything important, like missile control. =)
James Emerton
+1 for the warning to missile control operators :-)
Jarret Hardie
A: 

Inspired by James Emerton's answer:

def find_subpackages(module):
    result=[]
    for thing in os.listdir(os.path.dirname(module.__file__)):
        full=os.path.join(os.path.dirname(module.__file__),thing)
        if os.path.isdir(full):
            if glob.glob(os.path.join(full, '__init__.py*'))!=[]:
                result.append(thing)
    return result
cool-RR
this is terrible.
SilentGhost