¿Existe una forma estándar de enumerar los nombres de los módulos de Python en un paquete?

StackOverflow https://stackoverflow.com/questions/487971

  •  20-08-2019
  •  | 
  •  

Pregunta

¿Hay una manera sencilla de enumerar los nombres de todos los módulos en un paquete, sin usar __all__?

Por ejemplo, dado este paquete:

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

Me pregunto si hay una forma estándar o incorporada de 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. Luego, se pueden enumerar todos los archivos en ese directorio, filtrar los archivos py / pyc / pyo con un nombre único, 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. ¿Está esa funcionalidad expuesta en alguna parte?

¿Fue útil?

Solución

¿Quizás esto hará 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)])

Otros consejos

Usando python2.3 y superior , también puede usar el módulo pkgutil :

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

EDITAR: Tenga en cuenta que el parámetro no es una lista de módulos, sino una lista de rutas, por lo que 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])]
import module
help(module)

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

Según lo indicado por user815423426, esto solo funciona para objetos vivos y los módulos enumerados son solo módulos que se importaron anteriormente.

Listado de módulos en un paquete parece realmente fácil usando inspeccionar :

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

Esta 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

Esto debería enumerar los módulos:

help("modules")

Si desea ver una información sobre su paquete fuera del código de Python (desde un símbolo del sistema), puede usar pydoc para ello.

# get a full list of packages that you have installed on you machine
$ python -m pydoc modules

# get information about a specific package
$ python -m pydoc <your package>

Tendrá el mismo resultado que pydoc pero dentro del intérprete con ayuda

>>> import <my package>
>>> help(<my package>)

Basado en el ejemplo de cdleary, aquí hay una ruta recursiva de listado de versiones para todos los submódulos:

import imp, os

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

directorio de impresión (módulo)

def package_contents(package_name):
  package = __import__(package_name)
  return [module_name for module_name in dir(package) if not module_name.startswith("__")]
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top