Esiste un modo standard per elencare i nomi dei moduli Python in un pacchetto?


100

Esiste un modo semplice per elencare i nomi di tutti i moduli in un pacchetto, senza utilizzarli __all__?

Ad esempio, dato questo pacchetto:

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

Mi chiedo se esiste un modo standard o integrato per fare qualcosa del genere:

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

L'approccio manuale consiste nell'iterare i percorsi di ricerca del modulo per trovare la directory del pacchetto. Si potrebbe quindi elencare tutti i file in quella directory, filtrare i file py / pyc / pyo dal nome univoco, rimuovere le estensioni e restituire quell'elenco. Ma questo sembra una discreta quantità di lavoro per qualcosa che il meccanismo di importazione dei moduli sta già facendo internamente. Quella funzionalità è esposta da qualche parte?

Risposte:


23

Forse questo farà quello che stai cercando?

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
Vorrei aggiungere 'e module! = " Init .py"' ​​al finale 'if', poiché init .py non fa realmente parte del pacchetto. E .pyo è un'altra estensione valida. A parte questo, usare imp.find_module è davvero una buona idea; Penso che questa sia la risposta giusta.
— DNS

3
Non sono d'accordo - puoi importare init direttamente, quindi perché è un caso speciale? Di certo non è abbastanza speciale da infrangere le regole. ;-)
— cdleary

6
Probabilmente dovresti usare al imp.get_suffixes()posto del tuo elenco scritto a mano.
— itsadok

3
Inoltre, xml.sax
— tieni

1
Questo è davvero un brutto modo. Non puoi dire in modo affidabile cos'è un modulo dall'estensione del nome del file.
— wim

188

Usando python2.3 e versioni successive , potresti anche usare il pkgutilmodulo:

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

EDIT: nota che il parametro non è un elenco di moduli, ma un elenco di percorsi, quindi potresti voler fare qualcosa del genere:

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

15
Questo è incredibilmente non documentato, ma sembra il modo più corretto per farlo. Spero non ti dispiaccia se ho aggiunto la nota.
— itsadok

13
pkgutilè presente in python2.3 e versioni successive . Inoltre, mentre pkgutil.iter_modules()non funziona in modo ricorsivo, v'è una pkgutil.walk_packages()pure, che verrà recurse. Grazie per il puntatore a questo pacchetto però.
— Sandip Bhattacharya

Perché iter_modulesnon funziona per l'importazione assoluta come a.b.testpkg? Mi sta dando[]
— Hussain

Ho trascurato la tua MODIFICA :(. Scusa. Funziona dopo aver seguito il secondo frammento.
— Hussain

1
Non posso confermare che pkgutil.walk_packages()ricorre, mi dà lo stesso output di pkgutil.iter_modules(), quindi penso che la risposta sia incompleta.
— rwst

29
import module
help(module)

2
Anche se la guida elenca il contenuto del pacchetto in fondo al testo della guida, la domanda è più simile a come farlo: f (nome_pacchetto) => ["nome_modulo1", "nome_modulo2"]. Suppongo di poter analizzare la stringa restituita da help, ma sembra più indiretto che elencare la directory.
— DNS

1
@DNS: help()stampa cose, non restituisce una stringa.
— Junuxx

Sono d'accordo che questo sia un modo indiretto, ma mi ha mandato in una tana del coniglio per vedere come help()funziona. In ogni caso, il built-in pydocmodulo può aiutare a sputare fuori la stringa che help()impagina: import pydoc; pydoc.render_doc('mypackage').
— sraboy

8

Non so se sto trascurando qualcosa, o se le risposte sono solo obsolete ma;

Come affermato da user815423426, funziona solo per oggetti live ei moduli elencati sono solo moduli importati in precedenza.

Elencare i moduli in un pacchetto sembra davvero facile usando inspect :

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

Ho inserito import = import __ ('myproj.mymod.mysubmod') m = inspect.getmembers (i, inspect.ismodule) ma il percorso importd è ~ / myproj / __ init .py e m è un elenco con (mymod, '~ /myproj/mymod/__init__.py ')
— hithwen

1
@hithwen Non fare domande nei commenti, soprattutto se non sono direttamente correlati. Essere un buon samaritano: usa imported = import importlib; importlib.import_module('myproj.mymod.mysubmod'). __import__importa il modulo di primo livello, vedere la documentazione .
— siebz0r

Hmm, questo è promettente ma non funziona per me. Quando lo faccio import inspect, mypackagee poi inspect.getmembers(my_package, inspect.ismodule)ottengo una lista vuota, anche se certamente ho vari moduli in essa.
— Amelio Vazquez-Reina

1
È un dato di fatto, questo sembra funzionare solo se io import my_package.fooe non solo import mypackage, nel qual caso poi ritorna foo. Ma questo sconfigge lo scopo
— Amelio Vazquez-Reina

3
@ user815423426 Hai perfettamente ragione ;-) Sembra che stavo trascurando qualcosa.
— siebz0r

3

Questa è una versione ricorsiva che funziona con python 3.6 e versioni successive:

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

Qual è il vantaggio di utilizzare os.scandircome gestore di contesto invece di iterare direttamente sulle voci dei risultati?
— monkut

1
@monkut Vedi docs.python.org/3/library/os.html#os.scandir che suggerisce di usarlo come gestore di contesto per assicurarti che closevenga chiamato quando hai finito per assicurarti che tutte le risorse detenute vengano rilasciate.
— tacaswell

questo non funziona reperché elenca tutti i pacchetti ma li aggiunge re.a tutti
— Tushortz

1

Sulla base dell'esempio di cdleary, ecco un percorso di elenco delle versioni ricorsivo per tutti i sottomoduli:

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)

0

Questo dovrebbe elencare i moduli:

help("modules")

0

Se desideri visualizzare un'informazione sul tuo pacchetto al di fuori del codice python (da un prompt dei comandi) puoi usare pydoc per questo.

# 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>

Avrai lo stesso risultato di pydoc ma all'interno dell'interprete usando l'aiuto

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

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

Funziona solo per i moduli, non per i pacchetti. Provalo sul loggingpacchetto di Python per vedere cosa intendo. La registrazione contiene due moduli: gestori e config. Il codice restituirà un elenco di 66 elementi, che non include questi due nomi.
— DNS

-3

print dir (modulo)


1
Questo elenca i contenuti di un modulo che è già stato importato. Sto cercando un modo per elencare il contenuto di un pacchetto che non è stato ancora importato, proprio come fa 'from x import *' quando non è tutto specificato.
— DNS

from x import * importa prima il modulo e poi copia tutto nel modulo corrente.
— Seb

Mi sono reso conto che "from x import *" in realtà non importa i sottomoduli di un pacchetto, a causa di problemi di distinzione tra maiuscole e minuscole su Windows. L'ho incluso solo come esempio di ciò che volevo fare; L'ho modificato fuori questione per evitare confusione.
— DNS

Questo elenca tutti gli attributi di un oggetto già importato, non solo un elenco di sottomoduli. Quindi non risponde alla domanda.
— bignose
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.