Modul zum Nachinstallieren fehlender Pythonpakete

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.
Nobuddy
User
Beiträge: 997
Registriert: Montag 30. Januar 2012, 16:38

Hallo zusammen,

gibt es vielleicht schon ein Modul, das fehlende Pythonpakete in einem Programm anzeigt und nachinstalliert?

Hier mal ein kleines Beispiel:
Traceback (most recent call last):
File "gui_start.py", line 5, in <module>
from Tkinter import *
File "/usr/lib/python2.7/lib-tk/Tkinter.py", line 42, in <module>
raise ImportError, str(msg) + ', please install the python-tk package'
ImportError: No module named _tkinter, please install the python-tk package
Natürlich kann man so etwas auch manuell nachinstallieren, aber für mich wäre interessant wenn es kein so ein Modul gibt, das dies bewerkstelligen könnte, wie man so eine Fehlermeldung zum Weiterverarbeiten auslesen könnte?

Grüße Nobuddy
BlackJack

@Nobuddy: Das ist eine ganz normale Ausnahme. Das Problem ist eher wie Du etwas nachinstallieren willst. In diesem Fall zum Beispiel müsste man wissen welche Linux-Distribution verwendet wird, oder zumindest welche Paketverwaltung zum Einsatz kommt.
webspider
User
Beiträge: 485
Registriert: Sonntag 19. Juni 2011, 13:41

Mal als Frage in den Raum geworfen: Tut pip nicht schon etwas in der Art?
BlackJack

@webspider: In diesem speziellen Fall ist das nichts für PIP weil hier ein Teil der Standardbibliothek von einer Linuxdistribution in ein eigenes Paket ausgelagert wurde. Und auch generell würde ich von so etwas abraten, weil es ja durchaus passieren kann, dass eine Abhängigkeit eine neuere Version eines bereits installierten Moduls nach sich zieht, damit aber einem anderen Programm ein Modul in der Version weg nimmt, die es braucht um zu funktionieren.
Nobuddy
User
Beiträge: 997
Registriert: Montag 30. Januar 2012, 16:38

Das war einfach eine Idee meinerseits. :wink:

Ich habe gedacht, wenn so eine Info, wie das am Ende der Fehlermeldung ausgegeben wird
...please install the python-tk package
ein eindeutiger Hinweis wäre.
Ist es nicht so, daß bei einer Installation von z.B. Python, gewisse Betriebssystemdaten zu diesem 'Programm' vorhanden sind, die dann auch Python seinerseits auswertet und so die Info ausgibt 'das Paket python-tk wird benötigt, ist aber nicht installiert'?

Vielleicht gibt es eine Möglichkeit, die Import-Anweisungen irgenwie zu überprüfen, ob diese bereit stehen oder ob noch ein Paket nachinstalliert werden muß?
Leonidas
Python-Forum Veteran
Beiträge: 16025
Registriert: Freitag 20. Juni 2003, 16:30
Kontaktdaten:

Nobuddy hat geschrieben:Ist es nicht so, daß bei einer Installation von z.B. Python, gewisse Betriebssystemdaten zu diesem 'Programm' vorhanden sind, die dann auch Python seinerseits auswertet und so die Info ausgibt 'das Paket python-tk wird benötigt, ist aber nicht installiert'?
Nein, das wird wohl ein Patch in Debian oder Ubuntu sein, die die Fehlermeldung bei Tkinter angepasst haben, damit Leute wissen was für ein Paket sie nachinstallieren müssen. Und die wissen natürlich, wie das Paket in ihrer Distribution heißt.
Nobuddy hat geschrieben:Vielleicht gibt es eine Möglichkeit, die Import-Anweisungen irgenwie zu überprüfen, ob diese bereit stehen oder ob noch ein Paket nachinstalliert werden muß?
Du meinst ohne das Programm auszuführen? Eher nein. Mit ausführen, klar, aber das löst ja keines der von BlackJack beschriebenen Probleme. PLT Scheme/Racket haben ja so ein System um direkt Sachen bei Ausführung zu installieren und in Go ist das ebenfalls vorgesehen, allerdings bei kompilation.
My god, it's full of CARs! | Leonidasvoice vs (former) Modvoice
Nobuddy
User
Beiträge: 997
Registriert: Montag 30. Januar 2012, 16:38

Daß die Ausgabe spezifisch für das Betriebssystem Debian und Ubuntu ist, kann gut möglich sein und spricht dann wohl auch für die Betriebssysteme. :wink:

Wäre es vielleicht über die Schiene 'subprocess' möglich, die Fehlermeldung auszulesen?

Hier mal ein Beispiel, was ich schon versucht habe:

Code: Alles auswählen

process = subprocess.Popen([sys.executable, '{}/{}'.format(PATH, GUISTART)], shell=True, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE)

if process:
    print '1'
    print process.returncode
    print '2'
    print process.communicate()
    print '3'
    print process.stdout.read()
    print '4'
    print process.stderr.read()
Die Printanweisungen unter 'if process' geben nicht die Fehlermeldung aus.
1
None
2
('', '')
3
Traceback (most recent call last):
File "b.py", line 30, in <module>
print process.stdout.read()
ValueError: I/O operation on closed file
Bestimmt sind dies evtl. auch die falschen Ausgaben.

Wie müßte das aussehen, um die Fehlermeldung abzufangen und in eine Ausgabe umzuleiten?
Leonidas
Python-Forum Veteran
Beiträge: 16025
Registriert: Freitag 20. Juni 2003, 16:30
Kontaktdaten:

Nobuddy hat geschrieben:Daß die Ausgabe spezifisch für das Betriebssystem Debian und Ubuntu ist, kann gut möglich sein und spricht dann wohl auch für die Betriebssysteme. :wink:
Ich denke nicht, dass sie diese Unterstützung für beliebige Module die sie in den Repos haben implementiert haben. Vermutlich nur für python-tk, python-dev und python-profiler da diese normalerweise zur Standarddistribution von Python.org gehören.

Und die Ausgabe kannst du einfach lesen indem du den ImportError abfängst, sehe das Problem nicht?!
My god, it's full of CARs! | Leonidasvoice vs (former) Modvoice
Nobuddy
User
Beiträge: 997
Registriert: Montag 30. Januar 2012, 16:38

Habe gerade festgestellt, wenn ich bei 'subprocess' 'shell=True, stdin=subprocess.PIPE' weglasse, dann erhalte ich auch die Fehlerausgabe! :)

Code: Alles auswählen

process = subprocess.Popen([sys.executable, '{}/{}'.format(PATH, GUISTART)], stdout=subprocess.PIPE, stderr=subprocess.PIPE)

for line in process.communicate():
    print line
Ausgabe:
Traceback (most recent call last):
File "/media/daten/Scripte/officeplanet/lieferantenbestellung/gui_start.py", line 5, in <module>
from Tkinter import *
File "/usr/lib/python2.7/lib-tk/Tkinter.py", line 42, in <module>
raise ImportError, str(msg) + ', please install the python-tk package'
ImportError: No module named _tkinter, please install the python-tk package
:)
Leonidas
Python-Forum Veteran
Beiträge: 16025
Registriert: Freitag 20. Juni 2003, 16:30
Kontaktdaten:

Und das bringt dir jetzt was genau?
My god, it's full of CARs! | Leonidasvoice vs (former) Modvoice
Nobuddy
User
Beiträge: 997
Registriert: Montag 30. Januar 2012, 16:38

Ich versuche mit kleinen Schritten in Python weiter zukommen.
Manchmal habe ich einfach aus einer Situation heraus eine Idee, die ich dann verfolge ... learning by doing. :wink:

Hier könnte ich jetzt, die Ausgabe intern verwerten oder in eine LOG-Datei schreiben.

Code: Alles auswählen

process = subprocess.Popen([sys.executable, '{}/{}'.format(PATH, GUISTART)], stdout=subprocess.PIPE, stderr=subprocess.PIPE)

for line in tuple(process.stderr.readlines()):
    if 'ImportError:' in line and 'please install the' in line and 'package' in line:
        package = re.sub(' package','', line.strip().partition('install the ')[-1])
        print package
    elif 'Error:' in line and not 'ImportError:' in line:
        print 'Error: ', line
    elif line == '':
        print 'Es ist alles ok!'
Ich habe jetzt statt 'process.communicate()' 'process.stderr.readlines()' verwendet, das scheint mir hier geeigneter zu sein.
Worin besteht eigentlich der genaue Unterschied zwischen 'process.communicate() und 'process.stdout.readlines()' bzw. 'process.stderr.readlines()'?
BlackJack

@Nobuddy: Das ist schon wieder alles ziemlich wackelig und kompliziert. Pfade sollte man mit `os.path.join()` zusammensetzen, Wenn man sowohl `stderr` als auch `stdout` „piped”, sollte man sicherstellen das beides gelesen wird ohne dass es eine Verklemmung geben kann — das stellt `communicate()` sicher. Die Zeilenliste in ein `tuple()` umzuwandeln ist sinnfreie Rechenzeit und Speicherverschwendung. Schon wieder `partition()` und `re.sub()` wo es einfachere Zeichenketten-Methoden gegeben hätte.

Und letztendlich der falsche Weg, weil es viel robuster wäre das Modul zu importieren und `ImportError` zu behandeln, statt die Ausgabe zu parsen.
Nobuddy
User
Beiträge: 997
Registriert: Montag 30. Januar 2012, 16:38

@BlackJack, ja wirklich wackelig ... :wink:

Habe Deine Anmerkungen versucht umzusetzen. Bei `process.communicate()` kommt allerdings nichts, hingegen bei 'process.stderr.readlines()' schon. Vielleicht noch ein Fehler meinerseits irgendwo. Das mit der tuple braucht man wirklich nicht, war noch ein Überbleibsel beim Probieren. Das mit partition und re.sub habe ich durch split ersetzt. Bin mir da aber nicht sicher, ob der Paketnamen immer an der gleichen Stelle ist. Hast Du vielleicht noch etwas anderes gemeint außer split?

Hier mal dass Aktuelle:

Code: Alles auswählen

process = subprocess.Popen([sys.executable, os.path.join(PATH, GUISTART)], stdout=subprocess.PIPE, stderr=subprocess.PIPE)

for line in process.stderr.readlines():
    if 'ImportError:' in line and 'please install the' in line and 'package' in line:
        package = line.strip().split(' ')[8]
        print package
    elif 'Error:' in line and not 'ImportError:' in line:
        print 'Error: ', line
    elif line == '':
        print 'Es ist alles ok!'
Wie meinst Du das mit 'Modul zu importieren und `ImportError` zu behandeln'?

Code: Alles auswählen

try:
    import gui_start
except ImportError:
    ....
Nachtrag: Habe da gerade was gefunden:

Code: Alles auswählen

try:
    import gui_start
except Exception, e:
    print "Error:", e
Nobuddy
User
Beiträge: 997
Registriert: Montag 30. Januar 2012, 16:38

@BlackJack, mit ein bisschen Unterstützung, gehts halt leichter ... Danke! :wink:

Ich denke mal, Du hast so etwas in der Art gemeint:

Code: Alles auswählen

try:
    import gui_start
    if True:
        print 'Alles ok!'
        sys.exit(1)
except Exception, e:
    if e:
        p = len(' package')
        x = len(e[0])
        paket = e[0][45:int(x - p)]
        print paket
Liege ich da richtig?
Dav1d
User
Beiträge: 1437
Registriert: Donnerstag 30. Juli 2009, 12:03
Kontaktdaten:

`if True` ist unnötig, denn wann ist True nicht True?

Code: Alles auswählen

try:
    import foo
except ImportError, e:
    print e.message.rsplit(None, 1)
the more they change the more they stay the same
Benutzeravatar
cofi
Python-Forum Veteran
Beiträge: 4432
Registriert: Sonntag 30. März 2008, 04:16
Wohnort: RGFybXN0YWR0

Dav1d hat geschrieben:`if True` ist unnötig, denn wann ist True nicht True?
Ich will jetzt ja nich fies sein, aber ...

Code: Alles auswählen

In [1]: True, False = False, True

In [2]: True is True
Out[2]: True

In [3]: if True:
   ...:     print "yeah"
   ...:  
Zugegeben, True ist dann noch immer True .. aber halt nicht mehr True :twisted:
Nobuddy
User
Beiträge: 997
Registriert: Montag 30. Januar 2012, 16:38

@Dav1d, Danke für die Info, habe dies übernommen.
'e.message.rsplit(None, 1)' ist wesentlich kürzer als mein Code dazu. Ich habe dies so 'e.message.rsplit(None, 2)[0]' abgeändert und erhalte so gleich, das zu installierende Paket ausgegeben.

Habe mal etwas herum gespielt:

Code: Alles auswählen

#!/usr/bin/python
# -*- coding: utf-8 -*-

import sys, subprocess

DATEI = 'meinModul.py'

try:
    import meinModul
    print 'Alles ok!'
    sys.exit(1)
except ImportError, e:
    if 'please install the' in str(e):
        paket = e.message.rsplit(None, 2)[1]
        if paket:
            print '''Das Paket %s wird für das Starten
des Python-Modul %s benötigt.
Das Paket %s ist nicht installiert ist!

Soll das Paket %s jetzt installiert werden?''' % (paket, DATEI, paket, paket)

            try:
                inp = raw_input('< ja / nein >: ')

                if inp == 'ja':
                    print 'Geben Sie Ihr Passwort ein!'
                    subprocess.call('sudo apt-get install -y %s' % paket, shell=True)
                elif inp == 'nein':
                    print '''
Installation von %s wurde abgebrochen!''' % paket
                else:
                    print '''
Fehlerhafte Eingabe, Installation von %s wurde abgebrochen!''' % paket
                    sys.exit(1)
            except SyntaxError:
                    print '''
Fehlerhafte Eingabe, Installation von %s wurde abgebrochen!''' % paket
                    sys.exit(1)
except Exception, e:
        print e
Funktioniert bei mir einwandfrei. :D

Hoffe daß keine groben Schnitzer mehr drin sind, wenn doch freue ich mich auf Eure Info!
EyDu
User
Beiträge: 4881
Registriert: Donnerstag 20. Juli 2006, 23:06
Wohnort: Berlin

Nobuddy hat geschrieben:Hoffe daß keine groben Schnitzer mehr drin sind, wenn doch freue ich mich auf Eure Info!
Wie zum Beispiel das Abfangen des SyntaxErrors? Hattest du dort vorher vielleicht irgendwo ein ``input`` oder ein ``eval`` stehen?

Die ganzen Meldungen würde ich noch in Konstanten auslagern, dann kann man deinen Code wieder vernünftig lesen. Ansonsten sind die Namen natürlich teilweise nichtssagend (e, inp) und die Eingabe könnte man auch geschickter testen. Groß- und Kleinschreibung könnte man noch filtern.

Und das ganze Ding ist natürlich ein wenig sinnfrei, wenn man den Code ändern muss um ein bestimmtest Modul zu testen ;-)

Sebastian
Das Leben ist wie ein Tennisball.
Leonidas
Python-Forum Veteran
Beiträge: 16025
Registriert: Freitag 20. Juni 2003, 16:30
Kontaktdaten:

Und auf meinem System gibt es doch gar kein apt-get? Und selbst auf systemen mit apt-get bevorzuge ich aptitude, warum wird das im Code nicht berücksichtigt?
My god, it's full of CARs! | Leonidasvoice vs (former) Modvoice
webspider
User
Beiträge: 485
Registriert: Sonntag 19. Juni 2011, 13:41

Und ich dachte Linux-Nutzer wären alles andere als überfordert mit dem Befolgen so eindeutiger Anweisungen wie "please install the python-tk package". Selbst wenn man diese nicht versteht, reicht eine kurze Google-Suche ja auch aus. Aber gut, anscheinend ist dem nicht der Fall.
Antworten