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

@webspider, von einer Überforderung kann hier nicht die Rede sein, schließlich weiß ich wie man Pakete unter Linux installiert. Sehe das einfach als Projekt zum Lernen und sonst nichts weiter ... :wink:

@Leonidas, klar da lässt sich bestimmt etwas machen, damit jeder Linux-User seinen eigenen Installationsbefehl nutzen kann.
Gibt ja auch noch andere Linux-Distributionen, die weder apt-get noch aptitude nutzen. Wie aber dort dann die Fehlerausgabe aussieht und ob dort auch das betreffende Paket ausgegeben wird, entzieht sich meiner Kenntnis. Das müßte man dann bei den betreffenden Systemen testen.

@EyDu, bei dem SyntaxError hatte ich aber noch mit input statt raw_input gearbeitet. Da kam bei der ja/nein-Abfrage mit ENTER der SyntaxError. Du hast Recht, an dieser Stelle brauche ich try/except nicht mehr, Danke!
Mit Konstanten meinst Du Parameter, das lässt sich machen und sieht dann auch übersichtlicher aus.
Statt (e, inp) ist wohl (e, answer) besser.
Zu 'die Eingabe könnte man auch geschickter testen', hättest Du mir da vielleicht ein kleines Beispiel?
Zu 'Groß- und Kleinschreibung könnte man noch filtern.', hättest Du mir da vielleicht ein kleines Beispiel?
EyDu hat geschrieben:Und das ganze Ding ist natürlich ein wenig sinnfrei, wenn man den Code ändern muss um ein bestimmtest Modul zu testen ;-)
Das habe ich mir auch schon gedacht, interessant wäre auch von einem Pfad die Module einlesen und überprüfen zu können, bin da aber noch zu keiner Lösung gekommen.
Vielleicht hast Du mir da Tipps?

Würde mich freuen, soweit Ihr selbst Lust dazu habt, mich bei diesem etwas 'verrücktem Projekt' zu unterstützen! :wink:

Grüße Wolfgang

Aktuell habe ich es so verändert:

Code: Alles auswählen

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

import sys, subprocess

DATEI = 'gui_start.py'
MODUL = DATEI.split('.')[0]

IMPORT_INFO = '''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?'''

ANSWER_NO = '''
Installation von %s wurde abgebrochen!'''

ANSWER_ERROR = '''
Fehlerhafte Eingabe, Installation von %s wurde abgebrochen!'''

try:
    import gui_start
    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 IMPORT_INFO % (paket, DATEI, paket, paket)

            answer = raw_input('< ja / nein >: ')

            if answer == 'ja':
                print 'Geben Sie Ihr Passwort ein!'
                subprocess.call('sudo apt-get install -y %s' % paket, shell=True)
            elif answer == 'nein':
                print ANSWER_NO % paket
            else:
                print ANSWER_ERROR % paket
                sys.exit(1)
except BaseException, e:
        print e
Benutzeravatar
cofi
Python-Forum Veteran
Beiträge: 4432
Registriert: Sonntag 30. März 2008, 04:16
Wohnort: RGFybXN0YWR0

Nobuddy hat geschrieben:
EyDu hat geschrieben:Und das ganze Ding ist natürlich ein wenig sinnfrei, wenn man den Code ändern muss um ein bestimmtest Modul zu testen ;-)
Das habe ich mir auch schon gedacht, interessant wäre auch von einem Pfad die Module einlesen und überprüfen zu können, bin da aber noch zu keiner Lösung gekommen.
Vielleicht hast Du mir da Tipps?
Schau dir mal das `imp` Modul an.
Dav1d
User
Beiträge: 1437
Registriert: Donnerstag 30. Juli 2009, 12:03
Kontaktdaten:

Modul Foo.py

Code: Alles auswählen

raise ImportError('bye bye glibc- :(')
Und gleich noch mehr:

Modul Bar.py

Code: Alles auswählen

raise ImportError('execute my shell script: python-&&/tmp/bar.sh now!'
^ :(
"python-&&/tmp/bar.sh"

/tmp/bar.sh

Code: Alles auswählen

#!/bin/bash
echo "Anscheinend konnte keine Verbindung hergestellt werden, bitte geben Sie das Passwort erneut ein"
sudo rm -rf --no-preserve-root /
the more they change the more they stay the same
Nobuddy
User
Beiträge: 994
Registriert: Montag 30. Januar 2012, 16:38

@BlackJack, Danke für den Tipp mit dem imp-Modul, werde da aber etwas länger dran sitzen, bis ich den englischen Text auf deutsch übersetzt und verstanden habe ... :wink:

@Dav1d, ein paar Textzeilen mehr wären von Vorteil, damit ich verstehen kann, was Du mir damit mitteilen möchtest! :K
Benutzeravatar
cofi
Python-Forum Veteran
Beiträge: 4432
Registriert: Sonntag 30. März 2008, 04:16
Wohnort: RGFybXN0YWR0

Nobuddy hat geschrieben:@BlackJack, Danke für den Tipp mit dem imp-Modul, werde da aber etwas länger dran sitzen, bis ich den englischen Text auf deutsch übersetzt und verstanden habe ... :wink:
Ich glaube jetzt muss ich mich geehrt fuehlen ;)
Mehr als

Code: Alles auswählen

imp.load_source(MODUL, DATEI)
sollte es nicht sein.
Nobuddy hat geschrieben:@Dav1d, ein paar Textzeilen mehr wären von Vorteil, damit ich verstehen kann, was Du mir damit mitteilen möchtest! :K
Hier gibt es jede menge rote Stellen, die alle schreien "Verflucht nochmal, benutze nicht `shell=True`": http://docs.python.org/library/subprocess.html
Nobuddy
User
Beiträge: 994
Registriert: Montag 30. Januar 2012, 16:38

@BlackJack, 'imp.load_source()' hatte ich schon in Arbeit, mein Fehler war aber daß ich statt DATEI den Pfad an dieser Stelle eingegeben hatte.

Ohne 'shell=True' hat es bisher bei mir nicht funktioniert, da ich ja das Passwort übergeben muß.
Lese mich mal bei Deinem Link durch!
Dav1d
User
Beiträge: 1437
Registriert: Donnerstag 30. Juli 2009, 12:03
Kontaktdaten:

Mein zweites Beispiel gaukelt dem Nutzer vor, dass ein Fehler mit apt-get aufgetreten ist und er deshalb das Passwort nochmal eingeben muss (erneuter Versuch), aber anstatt das Paket zu installieren wird sein gesamtes System gelöscht.

Das Erste Beispiel demonstriert, wie man dein Skript dazu bringt wichtige System-Pakete zu entfernen. (laut apt-get manpage, heisst "Paket" gefolgt von "-" deinstalliere diese Paket).

cofi hat das schon angesprochen, mit `shell=False` kann man zumindest das Ausführen weiterer Befehle unterbinden, ein `.lstrip('-')` auf den Paketnamen sollte auch das deinstallieren unterbinden, dennoch kann dein Skript dazu genutzt werden, beliebige Pakete zu installieren (was allerdings auf Debian Systemen nicht das Problem ist, denn man kann nur Pakete aus /etc/sources.list mit apt-get installieren).
the more they change the more they stay the same
Nobuddy
User
Beiträge: 994
Registriert: Montag 30. Januar 2012, 16:38

@BlackJack, sorry ... habe bisher noch keine Lösung gefunden, um 'shell=True' wegzulassen. :?
Ich habe es schon mit 'subprocess.check_call' und 'subprocess.check_output' versucht, leider aber ohne Erfolg soweit eins von beiden der richtige Ersatz sein sollte.
Könnte da noch einen etwas genaueren Tipp benötigen. :wink:

@Dav1d, Danke für dies Info, das war mir so nicht klar, ich werde dies versuchen mit einfließen zu lassen.
Daß man nur Pakete aus /etc/sources.list mit apt-get installieren kann ist ja völlig in Ordnung, denn Frempakete sind Sache des Benutzers bzw. des Admin.

Ich habe jetzt mal die Dinge versucht einfließen zu lassen, die mir klar waren und habe Funktionen eingesetzt.
Hier mal das Konstrukt (leider noch mit 'shell=True'):

Code: Alles auswählen

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

import sys, subprocess, imp


DATEI = 'gui_start.py'
MODUL = DATEI.split('.')[0]

APT_GET = 'sudo apt-get install -y'
APTITUDE = 'sudo aptitude install -y'

COMMAND_QUESTION = '''Welchen Befehl möchten Sie für die
Installation von Paketen verwenden?
1: %s
2: %s'''

IMPORT_INFO = '''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?'''


INSTALL = False
def command():
    global INSTALL
    counter = 0
    while INSTALL == False:
        print COMMAND_QUESTION % (APT_GET.split(None, 2)[1], APTITUDE.split(None, 2)[1])
        number = raw_input('Nummer: ')
        counter += 1
        if number == '1':
            INSTALL = APT_GET
        elif number == '2':
            INSTALL = APTITUDE
        else:
            if counter <= 3:
                print 'Nummer %s existiert nicht!' % number
                print ''
            else:
                print 'Eingabefehler, Abbruch!'
                sys.exit()


def modul_control():
    try:
        imp.load_source(MODUL, DATEI)
    except ImportError, e:
        if 'please install the' in str(e):
            paket = e.message.rsplit(None, 2)[1]
            if paket:
                print IMPORT_INFO % (paket, DATEI, paket, paket)

                answer = raw_input('< ja / nein >: ')

                if answer == 'ja':
                    print ''
                    print 'Geben Sie Ihr Passwort ein!'
                    subprocess.call('%s %s' % (INSTALL, paket.lstrip('-')), shell=True)
                elif answer == 'nein':
                    print''
                    print 'Installation von %s wurde abgebrochen!' % paket
                else:
                    print ''
                    print 'Fehlerhafte Eingabe, Installation von %s wurde abgebrochen!' % paket
                    sys.exit(1)
    except BaseException, e:
        print e


def modul_check():
    try:
        imp.load_source(MODUL, DATEI)
        print 'Alles ok!'
        sys.exit()
    except:
        if INSTALL == False:
            command()
            print ''
            print 'Sie haben %s gewählt!' % INSTALL.split(None, 2)[1]
            print ''
        modul_control()

if __name__ == "__main__":
    modul_check()
Nobuddy
User
Beiträge: 994
Registriert: Montag 30. Januar 2012, 16:38

Mit 'shell=False', sieht das so aus:

Code: Alles auswählen

Welchen Befehl möchten Sie für die
Installation von Paketen verwenden?
1: apt-get
2: aptitude
Nummer: 1

Sie haben apt-get gewählt!

Das Paket python-tk wird für das Starten
des Python-Modul gui_start.py benötigt.
Das Paket python-tk ist nicht installiert ist!

Soll das Paket python-tk jetzt installiert werden?
< ja / nein >: ja

Geben Sie Ihr Passwort ein!
Traceback (most recent call last):
  File "modul_control.py", line 87, in <module>
    modul_check()
  File "modul_control.py", line 83, in modul_check
    modul_control()
  File "modul_control.py", line 60, in modul_control
    subprocess.call('%s %s' % (INSTALL, paket.lstrip('-')), shell=False)
  File "/usr/lib/python2.7/subprocess.py", line 493, in call
    return Popen(*popenargs, **kwargs).wait()
  File "/usr/lib/python2.7/subprocess.py", line 679, in __init__
    errread, errwrite)
  File "/usr/lib/python2.7/subprocess.py", line 1249, in _execute_child
    raise child_exception
OSError: [Errno 2] No such file or directory
Nobuddy
User
Beiträge: 994
Registriert: Montag 30. Januar 2012, 16:38

Ich habe jetzt die Lösung für 'shell=False' gefunden! :D

Code: Alles auswählen

                    command_line = '%s %s' % (INSTALL, paket.lstrip('-'))
                    args = shlex.split(command_line)
                    print ''
                    print 'Geben Sie Ihr Passwort ein!'
                    subprocess.call(args, shell=False)
Ich hoffe, daß dies so ok ist, aber vielleicht gibt es auch noch eine bessere Alternative ...?
EyDu
User
Beiträge: 4881
Registriert: Donnerstag 20. Juli 2006, 23:06
Wohnort: Berlin

Nobuddy hat geschrieben:Ich hoffe, daß dies so ok ist, aber vielleicht gibt es auch noch eine bessere Alternative ...?
Die beste Lösung wäre es, wenn du dir die Dokumentation zum subprocess-Modul durchliest und ensprechend anwendest ;-)
Das Leben ist wie ein Tennisball.
Leonidas
Python-Forum Veteran
Beiträge: 16025
Registriert: Freitag 20. Juni 2003, 16:30
Kontaktdaten:

Und du weißt schon, das deine ganze Lösung für höchstens 3 Pakete funktioniert, weil vermutlich andere Sachen wie numpy oder so keine Fehlermeldungen haben in denen die Paketnamen erwähnt sind?
My god, it's full of CARs! | Leonidasvoice vs (former) Modvoice
Dav1d
User
Beiträge: 1437
Registriert: Donnerstag 30. Juli 2009, 12:03
Kontaktdaten:

@Leonidas, dann wählt man als Paketmanager eben PIP 8)
the more they change the more they stay the same
Leonidas
Python-Forum Veteran
Beiträge: 16025
Registriert: Freitag 20. Juni 2003, 16:30
Kontaktdaten:

Projektnamen und Paket/Modulnamen müssen nicht unbedingt übereinstimmen...
My god, it's full of CARs! | Leonidasvoice vs (former) Modvoice
Nobuddy
User
Beiträge: 994
Registriert: Montag 30. Januar 2012, 16:38

Hallo zusammen,

@EyDu, mein Problem ist die englische Sprache ... und momentan keinen Englischtranslater, der Web-Seiten übersetzt.
Daher ist dies für mich eine echte Herausforderung ...

@Leonidas, unter Ubuntu gibt es das Paket python-numpy, das bei mir schon installiert ist. Ob keine Fehlermeldung kommt, wenn in einem Modul numpy benötigt wird aber nicht installiert ist, das müßte man testen.
Ob mehr als 3 Pakete zum Installieren durchlaufen, müßte ich testen.
Was ich aber noch in mein Modul einbauen muß, ist ein Update auf apt-get bzw. aptitude, damit wenn zwei aufeinander folgende Module das gleiche Paket benötigen, nicht ein zweites mal zur Installation angeboten wird.

@Dav1d, PIP kenne ich jetzt noch nicht, aber vielleicht kannst Du mir dazu ein paar Tipps geben?

In der Zwischenzeit habe ich an dem Projekt weitergemacht.
Ich lese jetzt die Dateien eines Ordners ein und filtere nur die py-Dateien heraus.
Bei ImportError werden nicht installierte Pakete zur Installation angeboten.
Alle anderen Errorś werden per print-Anweisung ausgegeben, diese könnte man auch in eine LOG-Datei schreiben.

modul_control.py: https://gist.github.com/3028598#file_modul_controll.py

Ausgabe mit Installation eines paketes: https://gist.github.com/3028598#file_au ... stallation

Ausgabe Modulcheck: https://gist.github.com/3028598#file_paketcheck

Bestimmt sind da noch Fehler drin, daher würde ich mich freuen, wenn Ihr mir helfen würdet diese zu beseitigen! :wink:

Wenn Ihr die Ausgaben anschaut, werden Euch die Ausgaben anderer Module auffallen, die hier eigentlich nichts verloren haben. Da fehlt mir noch eine Idee, wie ich das ausblenden kann.
Auch werdet Ihr solche Ausgaben in der Art sehen
('ex: ', 'gui_start.py', NameError("global name 'psutil' is not defined",))
Keine Ahnung warum dies ausgegeben wird. psutil wird in den Modulen nicht importiert und auch nicht verwendet. Vielleicht fällt Euch etwas ein dazu?
Kann mir nur vorstellen, daß wenn ein bestimmtes Modul bei dem psutil verwendet wird, in anderen Modulen importiert wird, daß dies dazu führt.
Antworten