Given a package, how can I automatically find all its sub-packages?
2 Answers
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))
4 Comments
Ram Rachum
Not working. That glob.glob thing returns a list: Is that a mistake?
James Emerton
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.)
Ram Rachum
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?James Emerton
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. =)
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