2009-01-28 8 views
69

Gibt es eine einfache Möglichkeit, die Namen aller Module in einem Paket aufzulisten, ohne __all__ zu verwenden?Gibt es eine Standardmethode, Namen von Python-Modulen in einem Paket aufzulisten?

Zum Beispiel gegeben dieses Paket:

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

Ich frage mich, ob es eine Standard- oder integrierte Möglichkeit ist, so etwas zu tun:

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

Der manuelle Ansatz wäre, Durchlaufen der Modulsuchpfade, um das Verzeichnis des Pakets zu finden. Man könnte dann alle Dateien in diesem Verzeichnis auflisten, die py/pyc/pyo-Dateien mit eindeutigen Namen herausfiltern, die Erweiterungen entfernen und diese Liste zurückgeben. Aber das scheint eine Menge Arbeit für etwas zu sein, was der Modulimport-Mechanismus bereits intern tut. Ist diese Funktionalität überall verfügbar?

Antwort

16

Vielleicht wird dies tun, was Sie suchen?

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

Ich würde "und __init__.py" zum finalen "if" hinzufügen, da __init__.py nicht wirklich Teil des Pakets ist. Und .pyo ist eine andere gültige Erweiterung. Abgesehen davon ist die Verwendung von imp.find_module eine wirklich gute Idee; Ich denke, das ist die richtige Antwort. – DNS

+3

Ich stimme nicht zu - Sie können __init__ direkt importieren, also warum Spezialfall es? Es ist sicher nicht speziell genug, um die Regeln zu brechen. ;-) – cdleary

+5

Sie sollten wahrscheinlich 'imp.get_suffixes()' anstelle Ihrer handschriftlichen Liste verwenden. – itsadok

-1

Druck dir (Modul)

+1

Das listet den Inhalt o f ein Modul, das bereits importiert wurde. Ich suche nach einer Möglichkeit, den Inhalt eines Pakets aufzulisten, das noch nicht importiert wurde, genau wie 'von x-Import *', wenn __all__ nicht angegeben ist. – DNS

+0

von x import * importiert zuerst das Modul und kopiert dann alles auf das aktuelle Modul. – Seb

+0

Ich erkannte, dass "von x-Import *" tatsächlich keine Untermodule eines Pakets importiert, weil die Groß- und Kleinschreibung unter Windows Probleme bereitet. Ich habe das nur als Beispiel für das, was ich tun wollte, aufgenommen; Ich habe es außer Frage gestellt, um Verwirrung zu vermeiden. – DNS

18
import module 
help(module) 
+1

Obwohl help die Paketinhalte am Ende des Hilfetextes auflistet, liegt die Frage eher in der folgenden Vorgehensweise: f (Paketname) => ["Modul1_Name", "Modul2_Name"]. Ich nehme an, ich könnte die von der Hilfe zurückgegebene Zeichenfolge analysieren, aber das scheint umständlicher zu sein als das Verzeichnis aufzulisten. – DNS

+0

@DNS: 'help()' druckt Zeug, es gibt keine Zeichenkette zurück. – 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

Das funktioniert nur für Module, nicht für Pakete. Probieren Sie es in Pythons Logging-Paket aus, um zu sehen, was ich meine. Logging enthält zwei Module: Handler und Config. Ihr Code wird eine Liste von 66 Elementen zurückgeben, die diese beiden Namen nicht enthält. – DNS

155

Mit python2.3 and above, können Sie auch das pkgutil Modul verwenden:

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

EDIT: Beachten Sie, dass der Parameter nicht eine Liste der Module, sondern eine Liste von Pfaden, so Vielleicht möchten Sie so etwas tun:

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

Das ist beunruhigend undokumentiert, scheint aber der korrekteste Weg zu sein. Ich hoffe, es macht Ihnen nichts aus, dass ich die Notiz hinzugefügt habe. – itsadok

+10

'pkgutil' ist dort in [python2.3 und höher tatsächlich] (http://docs.python.org/library/pkgutil.html). Während 'pkgutil.iter_modules()' nicht rekursiv arbeitet, gibt es auch 'pkgutil.walk_packages()', die _will_ rekursiv sind. Danke für den Zeiger auf dieses Paket. –

+0

Warum 'iter_modules' funktioniert nicht für absoluten Import wie' a.b.testpkg'? Es gibt mir '[]' – Hussain

6

Ich weiß nicht, ob ich etwas übersehen habe oder ob die Antworten gerade veraltet sind, aber;

Wie von user815423426 angegeben, funktioniert dies nur für Live-Objekte und die aufgelisteten Module sind nur Module, die zuvor importiert wurden.

Module in einem Paket Listing scheint wirklich inspect einfach mit:

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

Ich habe importiert = __import __ ('myproj.mymod.mysubmod') m = inspect.getmembers (i, inspect.ismodule) aber der importierte Pfad ist ~/myproj/__ init__.py und m ist eine Liste mit (mymod, '~/myproj/mymod/__ init__.py') – hithwen

+1

@hithwen Stellen Sie keine Fragen in den Kommentaren, vor allem, wenn sie nicht sind direkt verwandt Sein ein barmherziger Samariter: Benutze 'imported = importimportlib; importlib.import_modul ('myproj.mymod.mysubmod')'. '__import__' importiert das oberste Modul, [siehe Dokumentation] (http: // docs .python.org/2/library/functions.html #__ import__). – siebz0r

+0

Hmm, das ist vielversprechend, aber es funktioniert nicht für mich de Ich importiere inspect, mypackage und dann inspect.getmembers (my_package, inspect.ismodule) 'Ich bekomme eine leere Liste, obwohl ich sicherlich verschiedene Module darin habe. –

0

Basierend auf cdleary dem Beispiel, hier ist eine rekursive Version Listing Pfad für alle Submodule:

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

Dies ist eine rekursive Version, arbeitet mit Python 3.6 und höher:

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