Klassennamen, Funktioinsnamen etc. ausgeben lassen

Wenn du dir nicht sicher bist, in welchem der anderen Foren du die Frage stellen sollst, dann bist du hier im Forum für allgemeine Fragen sicher richtig.
Antworten
albertus
User
Beiträge: 52
Registriert: Mittwoch 7. Juli 2010, 14:48

Guten Morgen,

ich bin auf der Suche nach einem Python Modul das aus einem übergebenen Python Modul die Klassennamen, Funktionnsnamen Methodennamen, Dokstrings und Attribute extrahiert und mir diese zusammen mit den Quelltext-Metriken zu Weiterverarbeitung übergibt.

Kennt jemand so ein Modul?
Mit freundlichen Grüßen

Albertus
deets

pylint braucht sowas, und erledigt das mit einem extra Paket - logilab.astng.
albertus
User
Beiträge: 52
Registriert: Mittwoch 7. Juli 2010, 14:48

Hallo deets,

dein Hinweis auf pylint und dem extra Paket logilab.astng brachte mich auf die richtige Spur. In der Standard-Bibliothek von Python gibt es ein Modul das einen Abstract Syntax Tree generieren kann. Das Modul heißt 'ast'. Das Modul stellt unter anderem die Klasse NodeVisitor bereit. Das Beispiel auf: http://codemonkeytips.blogspot.com/2010 ... ample.html erklärt seine Verwendung. Damit sollte es möglich sein, ein groß teil der Informationen zu extrahieren.
Mit freundlichen Grüßen

Albertus
derdon
User
Beiträge: 1316
Registriert: Freitag 24. Oktober 2008, 14:32

Warum benutzt du dann nicht inspect aus der stdlib? Ist mehr high-level, da kannst du ganz einfach z.B. nach Klassen filtern.

Hier ein Beispiel:

Code: Alles auswählen

>>> class Foo(object):
...     def __init__(bar, baz):
...         self.bar = bar
...         self.baz = baz
...     def spam(self):
...         return self.bar + self.baz
... 
>>> # tu so, als ob Foo und func in mod.py definiert worden waeren
>>> import types
>>> mod = types.ModuleType('mod')
>>> mod.Foo = Foo
>>> inspect.getmembers(mod)
[('Foo', <class '__main__.Foo'>), ('__doc__', None), ('__name__', 'mod')]
>>> mod.func = lambda x: x+x
>>> inspect.getmembers(mod)
[('Foo', <class '__main__.Foo'>), ('__doc__', None), ('__name__', 'mod'), ('func', <function <lambda> at 0x140b8f0>)]
>>> inspect.getmembers(mod, inspect.isclass)
[('Foo', <class '__main__.Foo'>)]
>>> inspect.getmembers(mod, inspect.isfunction)
[('func', <function <lambda> at 0x140b8f0>)]
albertus
User
Beiträge: 52
Registriert: Mittwoch 7. Juli 2010, 14:48

Guten Morgen derdon,

ich habe mir deinen Vorschlag heute Morgen kurz angeschaut und auf der schnelle fallen mir zwei Gründe ein warum inspect ausscheidet:

1. Das Programm das ich zu schreiben gedenke soll nicht nur einzelne Module inspizieren, sondern auch ganze Paket Hierarchien. Wenn ich inspect nehme dann müsste ich jedes Paket importieren, das ließe sich noch mit __import__() bewerkstelligen. Aber die Gefahr der Namens Kollisionen dagegen, nicht so einfach.

2. Das inspect.getmembers(Foo) mir alle Methoden, auch die ererbten, einer Klasse zurück gibt, ist auch so ein Punkt. Wenn ich inspect nehmen würde, müsste ich alle ererbten Methoden einer Klasse aus filtern und ob das so leicht zu bewerkstelligen ist, wage ich mal zu bezweifeln.

Allerdings lasse ich mich gerne eines besseren belehren, wenn Du oder jemand anderes eine Idee hat wie man die obigen Punkt "abhaken" kann, dann her damit :-)
Mit freundlichen Grüßen

Albertus
derdon
User
Beiträge: 1316
Registriert: Freitag 24. Oktober 2008, 14:32

Zum ersten Punkt: Python unterscheidet nicht wirklich zwischen Paketen und Modulen. Ein Paket ist einfach ein Modul, das den Namen des Verzeichnisses bekommt, in dem es sich befindet und in der Datei __init__.py definiert ist. Der Typ (also die Klasse, die dahinter steckt) ist in jedem Fall types.ModuleType. Zur Veranschaulichung:

Code: Alles auswählen

/tmp/temp-2345678345678% tree                
.
└── pkg
    ├── __init__.py
    ├── mod1.py
    └── mod2.py

1 directory, 3 files
/tmp/temp-2345678345678% cat pkg/__init__.py
import pkg.mod1
import pkg.mod2
/tmp/temp-2345678345678% python2.7           
Python 2.7 (r27:82508, Jul  3 2010, 20:17:05) 
[GCC 4.0.1 (Apple Inc. build 5493)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> import inspect
>>> import pkg
>>> from pprint import pprint
>>> pprint(inspect.getmembers(pkg)[1:])  # der erste eintrag ist 'builtins' -> zu viel output ;)
[('__doc__', None),
 ('__file__', 'pkg/__init__.py'),
 ('__name__', 'pkg'),
 ('__package__', 'pkg'),
 ('__path__', ['pkg']),
 ('mod1', <module 'pkg.mod1' from 'pkg/mod1.py'>),
 ('mod2', <module 'pkg.mod2' from 'pkg/mod2.py'>),
 ('pkg', <module 'pkg' from 'pkg/__init__.py'>)]
>>> pprint(inspect.getmembers(pkg, inspect.ismodule))
[('mod1', <module 'pkg.mod1' from 'pkg/mod1.py'>),
 ('mod2', <module 'pkg.mod2' from 'pkg/mod2.py'>),
 ('pkg', <module 'pkg' from 'pkg/__init__.py'>)]
Abschließend noch eine Frage zu deinem Einwand in deinem ersten Punkt: Inwiefern spricht das für das ast Modul und gegen das inspect Modul?

Zu deinem zweiten Punkt: Ich verstehe nicht, warum du dich daran störst, wenn die geerbten Attribute mitangezeigt werden. Sie gehören nun mal mit zu der Klasse und Python sieht sie nicht als "besondere" Attribute an, weil sie geerbt wurden, sondern als ein Attribut wie jedes andere auch. Außerdem: Warum sollten sie aus Sicht des Benutzers nicht angezeigt werden? Der würde dann doch denken, dass z. B. die Klasse FooBar keine Methode spam hätte, weil sie nicht in der Klasse definiert wurde, sondern von einer Elternklasse geerbt wurde. Ich kenne deinen Anwendungsfall ja nicht, aber ich kann mir vorstellen, dass du eine Codeanalyse, wie sie für IDEs oft verwendet wird, erstellen möchtest. Dort würde ich (genauso wie Python selbst) keinen Unterschied zwischen geerbten und nicht-geerbten Methoden machen.
lunar

@derdon: Im ersten Beitrag war von „Metriken“ die Rede. Je nach Art der Metrik ist es nicht sinnvoll, vererbte Attribute zu berücksichtigen, e.g. wenn man die Quelltextduplizierung in Veerbungshierarchien messen möchte.

Bei inspect ist es halt generell so, dass Module vorher geladen werden müssen. Im Falle von Paketen muss man daher ordentlich importieren, damit Imports im geladenen Modul funktionieren. Bei einfachen Parsen der Module reicht es dagegen, den Dateibaum einfach mit "os.walk()" abzulaufen. Außerdem ist es ja nicht immer erwünscht, denn Quelltext von Modulen auszuführen, e.g. bei Modulen mit Seiteneffekten oder Quelltext aus dritter Hand.
Antworten