@Nobuddy: Du hast also meinen shell-Einzeiler in 70 Zeilen Pythoncode umgewandelt. ›search_import_names‹ bekommt ein ›import_names‹, ändert es und gibt es wieder zurück. Das sollte nicht sein. Die Funktion sollte überhaupt nicht eine Datenstruktur bekommen, die gefüllt wird, sondern nur etwas zurückgeben.
›pool‹ wird nicht benutzt. Wenn man über ein Dateiobjekt (das ist kein ›handler‹) iteriert bekommt man Zeilen, keine Objekte; Variablennamen sind wichtig zum Verständnis. Die Funktion übersieht alle Importe, die aus welchen Gründen auch immer, eingerückt sind.
Statt `continue` solltest Du ein `else` benutzen, und das `elif` prüft den letzten möglichen Fall, ist also immer wahr.
`split` tut nicht das, was Du denkst. Sobald Du mehrere Leerzeichen (oder Tabs) benutzt, geht das schief. Das `replace` mußt Du mir erklären, denn da werden mehrere durch Komma getrennte Module zu einem zusammengepackt.
Weil schon mehrfach im Thread angesprochen, das ganze umgesetzt mit dem ast-Modul:
Code: Alles auswählen
def search_import_names(module):
import_names = set()
with open(module) as content:
tree = ast.parse(content.read())
for node in ast.walk(tree):
if isinstance(node, ast.Import):
for alias in node.names:
import_names.add(alias.name)
elif isinstance(node, ast.ImportFrom):
import_names.add(node.module)
return import_names
›search_py_files‹ tut zu viel. Es durchsucht nicht nur die Dateien, sondern ruft auch noch ›build_python_importnames‹ auf, das dann ›find_python_package_path‹ aufruft, was dann die eigentliche Hauptaufgabe des Programms macht. So tiefe Verschachtelungen sind schwer zu durchschauen. Rufe die Einzelnen Teile aus dem Hauptprogramm auf und schreibe Funktionen mit nur einer einfachen Aufgabe. Dann wird auch der Doc-String nicht so lang.
›search_by_files‹ soll also nur Dateiendurchsuchen, also ›search_in_files‹. ›modul2privat‹ ist eigentlich ein Set, weil das module2bool immer nur auf True mappt. Deine Suche geht exakt zwei Verzeichnisebenen tief, und das noch durch kopierten Code. Eigentlich will man beliebig tief suchen. Dafür gibt es walk von pathlib. Verzeichnisse setzt man auch nicht mit .format zusammen, dafür gibt es auch was aus pathlib.
Damit wird die Funktion zu:
Code: Alles auswählen
def search_in_files():
import_names = set()
for filename in pathlib.Path('.').walk('**/*.py'):
import_names.update(search_import_names(filename))
return import_names
›build_python_importnames‹ ist ein einfaches Filter, wobei man zum Prüfen, ob etwas in einem Wörterbuch ist, `in´ benutzt, und nicht einen KeyError.
Das mit dem `import` war nur als schnelle händlische Lösung gemeint, weil es bei 5 Modulen eigentlich nicht lohnt, ein ganzes Programm dafür zu schreiben. Will man tatsächlich nach den Dateien für ein Modul suchen, importiert man sie nicht, sondern benutzt importlib.
Code: Alles auswählen
def find_python_package_path(modules):
python_paths = set()
for module in modules:
loader = importlib.find_loader(module)
try:
python_paths.add(loader.path)
except AttributeError:
# Module has no path, probably a builtin module
# ignore it
pass
return sorted(python_paths)
In ›build_python_package‹ verstehe ich nicht, was da alles magisches passiert. Ein UnboundLocalError ist wirklich ein Fehler, und sollte in einer normalen Funktion nicht abgefangen werden. Für solche Fälle hat man den Wert `None`. `extra_packet` ist mal ein Wahrheitswert mal ein String. Eine Variable sollte nur einen Typ haben.
Zum Schluß, ich hatte angedeutet, dass Du Debian direkt nach dem Packetnamen fragen kannst, Du mußt also nicht raten, in welchem Paket das jeweilige Modul drin ist:
Code: Alles auswählen
dpkg -S /usr/lib/python3/dist-packages/reportlab/__init__.py