2009-01-28 7 views
69

¿Existe una manera directa de listar los nombres de todos los módulos en un paquete, sin usar __all__?¿Hay una forma estándar de enumerar los nombres de los módulos de Python en un paquete?

Por ejemplo, dado este paquete:

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

Me pregunto si hay una manera estándar o incorporada a hacer algo como esto:

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

El enfoque manual sería iterar a través de las rutas de búsqueda del módulo para encontrar el directorio del paquete. Uno podría enumerar todos los archivos en ese directorio, filtrar los archivos de nombre único py/pyc/pyo, quitar las extensiones y devolver esa lista. Pero esto parece una buena cantidad de trabajo para algo que el mecanismo de importación de módulos ya está haciendo internamente. ¿Esa funcionalidad está expuesta en cualquier lugar?

Respuesta

16

Tal vez esto va a hacer lo que estás buscando?

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)]) 
+1

Agregaría 'y module! = "__init__.py"' ​​a la final 'if', ya que __init__.py no es realmente parte del paquete. Y .pyo es otra extensión válida. Aparte de eso, usar imp.find_module es una muy buena idea; Creo que esta es la respuesta correcta. – DNS

+3

No estoy de acuerdo, puede importar __init__ directamente, entonces ¿por qué es tan especial? Seguro que no es lo suficientemente especial como para romper las reglas. ;-) – cdleary

+5

Probablemente deberías usar 'imp.get_suffixes()' en lugar de tu lista escrita a mano. – itsadok

-1

de impresión dir (módulo)

+1

Eso enumera los contenidos o f un módulo que ya ha sido importado. Estoy buscando una forma de listar el contenido de un paquete que aún no se ha importado, al igual que 'de x import *' cuando __all__ no se especifica. – DNS

+0

desde x import * primero importa el módulo y luego copia todo en el módulo actual. – Seb

+0

Me di cuenta de que 'from x import *' no importa los submódulos de un paquete, debido a problemas de mayúsculas y minúsculas en Windows. Solo lo incluí como un ejemplo de lo que quería hacer; Lo he editado fuera de la cuestión para evitar confusiones. – DNS

18
import module 
help(module) 
+1

Aunque la ayuda enumera los contenidos del paquete en la parte inferior del texto de ayuda, la pregunta está más relacionada con la forma de hacerlo: f (package_name) => ["module1_name", "module2_name"]. Supongo que podría analizar la cadena devuelta por la ayuda, pero parece más indirecta que enumerar el directorio. – DNS

+0

@DNS: 'help()' imprime cosas, no devuelve una cadena. – Junuxx

-2
def package_contents(package_name): 
    package = __import__(package_name) 
    return [module_name for module_name in dir(package) if not module_name.startswith("__")] 
+0

Eso solo funciona para módulos, no paquetes. Pruébalo en el paquete 'logging' de Python para ver a qué me refiero. El registro contiene dos módulos: manejadores y config. Su código devolverá una lista de 66 elementos, que no incluye esos dos nombres. – DNS

155

Usando python2.3 and above, también se puede utilizar el módulo pkgutil:

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

EDIT: Tenga en cuenta que el parámetro no es una lista de los módulos, pero una lista de rutas, por lo Es posible que desee hacer algo como esto:

>>> import os.path, pkgutil 
>>> import testpkg 
>>> pkgpath = os.path.dirname(testpkg.__file__) 
>>> print [name for _, name, _ in pkgutil.iter_modules([pkgpath])] 
+14

Esto es inquietantemente indocumentado, pero parece ser la forma más correcta de hacerlo. Espero que no te importe, agregué la nota. – itsadok

+10

'pkgutil' está allí en [python2.3 y en realidad] (http://docs.python.org/library/pkgutil.html). Además, aunque 'pkgutil.iter_modules()' no funcionará recursivamente, también hay 'pkgutil.walk_packages()', que _will_ recurse. Gracias por el puntero a este paquete sin embargo. –

+0

¿Por qué 'iter_modules' no funciona para la importación absoluta como' a.b.testpkg'? Me está dando '[]' – Hussain

6

No sé si estoy pasando por alto algo, o si las respuestas están desactualizadas pero;

Según lo declarado por el usuario815423426, esto solo funciona para objetos en vivo y los módulos enumerados solo son módulos que se importaron anteriormente.

módulos listado en un paquete parece muy fácil de usar inspect:

>>> import inspect, testpkg 
>>> inspect.getmembers(testpkg, inspect.ismodule) 
['modulea', 'moduleb'] 
+0

He puesto import = __import __ ('myproj.mymod.mysubmod') m = inspect.getmembers (i, inspect.ismodule) pero la ruta de importación es ~/myproj/__ init__.py ym es una lista con (mymod, '~/myproj/mymod/__ init__.py') – hithwen

+1

@hithwen No haga preguntas en los comentarios, especialmente si no son directamente relacionado. Siendo un buen samaritano: Use 'imported = import importlib; importlib.import_module ('myproj.mymod.mysubmod')'. '__import__' importa el módulo de nivel superior, [consulte la documentación] (http: // docs .python.org/2/library/functions.html #__ import__). – siebz0r

+0

Hmm, esto es prometedor pero no funciona para mí. Wh es 'Importar inspección, paquete' y luego 'inspeccionar.obtener miembros (mi_paquete, inspeccionar.módulo) 'obtengo una lista vacía, aunque ciertamente tengo varios módulos en ella. –

0

Basado en el ejemplo de cdleary, he aquí una lista de ruta recursiva versión para todos los submódulos:

import imp, os 

def iter_submodules(package): 
    file, pathname, description = imp.find_module('isc_datasources') 
    for dirpath, _, filenames in os.walk(pathname): 
     for filename in filenames: 
      if os.path.splitext(filename)[1] == ".py": 
       yield os.path.join(dirpath, filename) 
1

Ésta es una versión recursiva que funciona con Python 3.6 y superior:

import importlib.util 
from pathlib import Path 
import os 
MODULE_EXTENSIONS = '.py' 

def package_contents(package_name): 
    spec = importlib.util.find_spec(package_name) 
    if spec is None: 
     return set() 

    pathname = Path(spec.origin).parent 
    ret = set() 
    with os.scandir(pathname) as entries: 
     for entry in entries: 
      if entry.name.startswith('__'): 
       continue 
      current = '.'.join((package_name, entry.name.partition('.')[0])) 
      if entry.is_file(): 
       if entry.name.endswith(MODULE_EXTENSIONS): 
        ret.add(current) 
      elif entry.is_dir(): 
       ret.add(current) 
       ret |= package_contents(current) 


    return ret 
Cuestiones relacionadas