views:

1251

answers:

5

Is there a straightforward way to list the names of all modules in a package, without using __all__?

For example, given this package:

/testpkg
/testpkg/__init__.py
/testpkg/modulea.py
/testpkg/moduleb.py

I'm wondering if there is a standard or built-in way to do something like this:

>>> package_contents("testpkg")
['modulea', 'moduleb']

The manual approach would be to iterate through the module search paths in order to find the package's directory. One could then list all the files in that directory, filter out the uniquely-named py/pyc/pyo files, strip the extensions, and return that list. But this seems like a fair amount of work for something the module import mechanism is already doing internally. Is that functionality exposed anywhere?

A: 

print dir(module)

JustSmith
That lists the contents of a module that has already been imported. I'm looking for a way to list the contents of a package that has not yet been imported, just like 'from x import *' does when __all__ is not specified.
DNS
from x import * first imports the module and then copies everything to the current module.
Sebastjan Trepča
I realized that 'from x import *' does not in fact import sub-modules of a package, because of case-sensitivity issues on Windows. I only included that as an example of what I wanted to do; I've edited it out of the question to avoid confusion.
DNS
That lists all attributes of an already-imported object, not a list of sub-modules only. So it doesn't answer the question.
bignose
A: 
import module
help(module)
Triptych
Although help does list package contents at the bottom of the help text, the question is more along the lines of how to do this: f(package_name) => ["module1_name", "module2_name"]. I suppose I could parse the string returned by help, but that seems more roundabout than listing the directory.
DNS
+3  A: 

Maybe this will do what you're looking for?

import imp
import os
MODULE_EXTENSIONS = ('.py', '.pyc', '.pyo')

def package_contents(package_name):
    file, pathname, description = imp.find_module(package_name)
    if file:
        raise ImportError('Not a package: %r', package_name)
    # Use a set because some may be both source and compiled.
    return set([os.path.splitext(module)[0]
        for module in os.listdir(pathname)
        if module.endswith(MODULE_EXTENSIONS)])
cdleary
I would add 'and module != "__init__.py"' to the final 'if', since __init__.py isn't really part of the package. And .pyo is another valid extension. Aside from that, using imp.find_module is a really good idea; I think this is the right answer.
DNS
I disagree -- you can import __init__ directly, so why special case it? It sure isn't special enough to break the rules. ;-)
cdleary
You should probably use `imp.get_suffixes()` instead of your hand-written list.
itsadok
Also, note that this doesn't work on subpackages like `xml.sax`
itsadok
A: 
def package_contents(package_name):
  package = __import__(package_name)
  return [module_name for module_name in dir(package) if not module_name.startswith("__")]
That only works for modules, not packages. Try it on Python's `logging` package to see what I mean. Logging contains two modules: handlers and config. Your code will return a list of 66 items, which doesn't include those two names.
DNS
+7  A: 

Using python2.5 (and up, I suppose), you could also use the pkgutil module:

>>> import pkgutil
>>> [name for _, name, _ in pkgutil.iter_modules(['testpkg'])]
['modulea', 'moduleb']

EDIT: Note that the parameter is not a list of modules, but a list of paths, so you might want to do something like this:

>>> import os.path, pkgutil
>>> import testpkg
>>> pkgpath = os.path.dirname(testpkg.__file__)
>>> [name for _, name, _ in pkgutil.iter_modules([pkgpath])
jp
This is disturbingly undocumented, but seems like the most correct way to do this. Hope you don't mind I added the note.
itsadok