py2exe und requests beißen sich?

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
Sophus
User
Beiträge: 1109
Registriert: Freitag 25. April 2014, 12:46
Wohnort: Osnabrück

Hallo Leute,

ich benutze in meinem Programm die requests-Bibliothek (requests (2.6.0)), und es funktioniert auch alles tadelos, aber nur solange ich das Programm über Python laufen lasse, also als SKript. Gehe ich nun dazu über mit py2exe eine EXE-Datei zu erstellen, dann funktioniert diese Bibliothek nicht mehr. Da ich zur Sicherheit und zum Test mein Programm in Console als Exe-Datei umwandeln lasse, kann ich die Fehlermeldung sehen. Er sagt, dass der globale Name 'get' nicht definiert sei.

Hier mein Code:

Code: Alles auswählen

#!/usr/bin/env python
#-*- coding:utf-8 -*-
try:
    import requests

    print "STATUS [OK]  (", FILE_NAME, "): All required libraries are imported"
except ImportError as ImpErr:
    print "STATUS [FAILED]  (", FILE_NAME, "): ", ImpErr
except:
    print "STATUS [FAILED]  (", FILE_NAME, "): No required libraries was imported"




VERSION_INFO = "0.1"

def check_update():
    try:
        data = get("www.blahblah.de")
        if str(data.text) > str(VERSION_INFO):
            print 'Software Update', 'Update Available!'
            return True
        else:
            print 'Software Update','No Updates are Available.'
            return False
    except Exception as ex:
        print ex
        print 'Software Update','Unable to Check for Update '
        return ex
Wie gesagt, im normalen Python funktioniert das, also im Skript, aber sobald ich das Programm in eine Exe-Datei umwandle, klappt es nicht mehr. Dann springt hier die except ein.
BlackJack

@Sophus: Das da läuft weder mit noch ohne py2exe und führt zu der von Dir genannten Ausnahme: Es wird `get()` verwendet was aber nirgends in dem Quelltext definiert wird.
Sirius3
User
Beiträge: 18335
Registriert: Sonntag 21. Oktober 2012, 17:20

@Sophus: es wird auch ein undefiniertes FILE_NAME verwendet. Und check_update liefert eine Exception als Rückgabewert - es ist also gut so, dass es nicht funktioniert.
Benutzeravatar
Sophus
User
Beiträge: 1109
Registriert: Freitag 25. April 2014, 12:46
Wohnort: Osnabrück

Ups, ich habe hier den Quelltext stark modifiziert. Es handelt sich also keineswegs um den gleichen Quelltext, den ich tatsächlich benutze.

Hier mein originaler Quelltext:

Code: Alles auswählen

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

FILE_NAME = "check_update.py"

try:
    import requests

    from xarphus.info import info_app
    from xarphus.config import Configuration

    print "STATUS [OK]  (", FILE_NAME, "): All required libraries are imported"
except ImportError as ImpErr:
    print "STATUS [FAILED]  (", FILE_NAME, "): ", ImpErr
except:
    print "STATUS [FAILED]  (", FILE_NAME, "): No required libraries was imported"
    raise

set_get_settings = Configuration()

VERSION_INFO = info_app.dict_info["product_version"]

def check_update():
    try:
        data = requests.get(set_get_settings.dict_set_settings["URLServerVersion"])
        if str(data.text) > str(VERSION_INFO):
            print 'Software Update', 'Update Available!'
            print "VERSION:", set_get_settings.dict_set_settings["URLServerVersion"]
            return "Yes"
        else:
            print 'Software Update','No Updates are Available.'
            return "No"
    except Exception as ex:
        print ex
        print 'Software Update','Unable to Check for Update '
        return "Error"

Witzig ist allerdings, dass die erstellte Exe-Datei über die Console behauptet, dass der Name 'requests' nicht definiert sei. Wird nicht genau 'requests' importiert? Und die Exe-Datei von cx_freeze hat mit dem gleichen Quelltext gar keine Probleme. Das heißt, die Abfrage klappt, denn es wird tatsächlich überprüft, ob die Version des Programms auf dem Server mit der Version des Programms auf dem Rechner übereinstimmen oder nicht.

Ich meine, wenn beide EXE-Dateien die gleichen Fehler ausspucken würden, hätte ich es durchaus verstanden. Aber sowohl py2exe als auch cx_freeze machen aus dem gleichen Quelltext eine EXE-Datei und bei cx_freeze tauchen keinerlei Fehlermeldungen auf, und bei py2exe wird behauptet dass 'requests' fehle. Das verwirrt mich etwas.
Daikoku
User
Beiträge: 66
Registriert: Montag 20. April 2015, 21:14

@Sophus

mit Bezug auf Dein obiges Script :
wenn requests nicht importiert wird, erhalte ich folgende Fehlermeldung :

[OUT] STATUS [FAILED] ( check_update.py ): No module named requests

Das hast Du mit Deiner Exeption, except ImportError as ImpErr auch genau so definiert.
Nach der Print-Anweisung geht es mit set_get_settings = Configuration() weiter.

Die Funktion Configuration() ist hier unbekannt.

Es wäre hilfreich, wenn Du Deine original Fehlermeldung hier posten könntest.
Benutzeravatar
Sophus
User
Beiträge: 1109
Registriert: Freitag 25. April 2014, 12:46
Wohnort: Osnabrück

@Daikoku: Hier der Quelltext ohne irgendwelche Abhängigkeiten:

Code: Alles auswählen

#!/usr/bin/env python
#-*- coding:utf-8 -*-
 
FILE_NAME = "check_update.py"
 
try:
    import requests
 
    from xarphus.info import info_app
    from xarphus.config import Configuration
 
    print "STATUS [OK]  (", FILE_NAME, "): All required libraries are imported"
except ImportError as ImpErr:
    print "STATUS [FAILED]  (", FILE_NAME, "): ", ImpErr
except:
    print "STATUS [FAILED]  (", FILE_NAME, "): No required libraries was imported"
    raise
 
set_get_settings = Configuration()
 
VERSION_INFO = "0.0.1"
 
def check_update():
    try:
        data = requests.get("http://xarphus.de/xarphus_version.txt")
        if str(data.text) > str(VERSION_INFO):
            print 'Software Update', 'Update Available!'
            return "Yes"
        else:
            print 'Software Update','No Updates are Available.'
            return "No"
    except Exception as ex:
        print ex
        print 'Software Update','Unable to Check for Update '
        return "Error"
Und zur Fehlermeldung, es wird über die Console nur ausgespuckt, dass 'requests' nicht definiert sei. Aber wie gesagt, wenn ich mit cx_freeze eine EXE-Datei erstelle, erhalte ich keinerlei Fehlermeldungen und über den Python-Interpreter klappt alles wunderbar. Ich vermute einfach sehr stark, das py2exe irgendein Problem hat. Was mich wiederum sstark verwirrt. Denn hinsichtlich des Erstellen einer EXE-Datei verfahren doch die meisten Programm den gleichen Weg oder? Ich gehe mal davon aus, das PyInstaller, cx_freeze und py2exe im Grunde auf dem gleichen Weg eine EXE-Datei erstellen. Es wäre sehr schade, wenn ich am Ende doch noch auf cx_freeze umsteigen muss, denn ich hatte mir eine umfangreiche setup.py-Datei für py2exe zusammengebastelt.
Benutzeravatar
sparrow
User
Beiträge: 4600
Registriert: Freitag 17. April 2009, 10:28

Bitte die komplette Fehlermeldung per copy & paste.

Und zeig doch mal deine umfangreiche setup.py
Benutzeravatar
Sophus
User
Beiträge: 1109
Registriert: Freitag 25. April 2014, 12:46
Wohnort: Osnabrück

Hier zunächst die Setup-Dateien:

Hier ist die Setup-Datei für py2exe.

Hier ist die Setup-Datei für cx_freeze.

Zu der Fehlermeldung: Ich habe hier ein Bild gemacht:

Bild

Ich habe die roten Pfeile entsprechend gesetzt. Wir sehen also dass die frm_update.py geladen wird. Gleich daran wird dann die check_update()-Funktion aufgerufen, um zu überprüfen, ob eine neue Version vorhanden ist. Und genau da sagt mir die Console, dass 'requests' nicht definiert sei. Ich weiß es daher, dass es in der check_update()-Funktion, weil die print-Ausgabe mir sagt, dass angeblich unmöglich bzw. nicht möglich sei die Update-Version abzufragen. Sprich, hier springt die Exe-Datei vpn py2exe gleich in den except-Block. Dabei ist die Server-Adresse korrekt. Und der gleichen Quelltext wird von cx_freeze komiliert, und dort erscheint solcher Fehler nicht. Die Abfrage findet stat, mit gleicher Server-Adresse.
Daikoku
User
Beiträge: 66
Registriert: Montag 20. April 2015, 21:14

@Sophus Ich würde Dir gerne helfen, aber ich habe keine wirklich brauchbare Idee.

Ich habe noch nicht mit py2exe oder cx_freeze gearbeitet.

ich halte es für keine wirklich gute Idee, Fehler einfach zu ignorieren, ohne genau zu Wissen warum diese auftreten.
Ich weiss, Du hast hier gefragt und bisher keine befriedigende Antwort erhalten.

Meine Erfahrung mit Windows sagt mir jedoch, wenn Du cx_freeze jetzt einfach benutzt, nur weil es bei Dir jetzt gerade keinen Fehler erzeugt,
heißt das nicht unbedingt, welches das gleiche Programm bei mir auf meinem Windows-Rechner genauso fehlerfrei läuft.

ich arbeite auch mit Windows und Python und habe mit dieser Kombination schon die merkwürdigsten Dinge erlebt.
z.B. Fehlermeldungen, welche gar nicht erst angezeigt werden oder in einer separaten MessageBox, welche nur ganz kurz aufpoppt und dann noch hinter meiner eigenen Anwendung.

Ich persönlich fahre sehr gut damit, ein sauberes logging zu benutzen und auf print Anweisungen zu verzichten.

Code: Alles auswählen

import logging
import time
import os

workspace = os.path.realpath(os.path.join(os.path.dirname(__file__), ".."))
logdir = os.path.join(workspace, 'log')
 

# The 5 different log levels are :
#
# logging.debug("debug message")        # only File all.log
# logging.info("info message")          # only File all.log and Console
# logging.warning("warning message")    # only File all.log and Console
# logging.error("error message")        # File all- and error.log and Console
# logging.critical("critical message")  # File all- and error.log and Console
#
errorLogFile = os.path.join(logdir, '{}_test_error.log'.format(time.strftime('%Y%m%d')))
appLogFile = os.path.join(logdir, '{}_test_app.log'.format(time.strftime('%Y%m%d')))

# initialize the logger
logger = logging.getLogger()
logger.setLevel(logging.DEBUG)

# create console handler
handler = logging.StreamHandler()
handler.setLevel(logging.INFO)  # print console
formatter = logging.Formatter('\n=> %(message)s')
handler.setFormatter(formatter)
logger.addHandler(handler)

# create error file handler
handler = logging.FileHandler(errorLogFile, 'a')
handler.setLevel(logging.ERROR)
formatter = logging.Formatter('[%(asctime)s] %(name)-10s %(levelname)-8s %(module)-20s : %(lineno)-4d : %(message)s', datefmt='%d.%m.%Y-%H:%M:%S')
handler.setFormatter(formatter)
logger.addHandler(handler)

# create warning file handler
handler = logging.FileHandler(appLogFile, 'a')
handler.setLevel(logging.WARNING)
formatter = logging.Formatter('[%(asctime)s] %(name)-10s %(levelname)-8s %(module)-20s : %(lineno)-4d : %(message)s', datefmt='%d.%m.%Y-%H:%M:%S')
handler.setFormatter(formatter)
logger.addHandler(handler)

# create debug file handler
handler = logging.FileHandler(appLogFile, 'a')
handler.setLevel(logging.DEBUG)
formatter = logging.Formatter('[%(asctime)s] %(name)-10s %(levelname)-8s %(module)-20s : %(lineno)-4d : %(message)s', datefmt='%d.%m.%Y-%H:%M:%S')
handler.setFormatter(formatter)
logger.addHandler(handler)
Anstatt einer print Anweisung benutze ich zum Beispiel :

Code: Alles auswählen

logging.info('Starte Funktion getURL')
Auf meinem Bildschirm/Console erscheint :
=> Starte Funktion getURL

gleichzeitig wird in die Datei test_app.log
[28.06.2015-00:37:10] root INFO getDaten : 288 : Starte Funktion getURL
geschrieben.

Das hat mir schon sehr oft geholfen Fehler zu finden, welches mit einer einfachen print Anweisung nicht so ohne weiters möglich gewesen wäre.

Bitte übernimm jetzt nicht einfach obiges Beispiel, es berücksichtigt zum Beispiel kein multiprocessing oder threading.
Solltest Du Dich für ein sauberes Logging entscheiden, und hast hierzu weitergehende Fragen, wäre es vielleicht sinnvoll, einen separaten Thread zu erstellen, weil das ansonsten hier untergehen könnte.
Ich denke, es ist auch sinnvoll die erfahrenden Programmierer einmal über Deine Logging Konfiguration drüber schauen zu lassen, damit das wirklich alles ganz sauber funktioniert.

Ich denke nicht, dass dieses jetzt Dein aktuelles Problem behebt, aber zukünftige Fehler einfacher verifizieren lässt.

Darüber hinaus brauchst Du auch FILE_NAME nicht mehr hard coded im Modul speichern. => %(module)
VERSION_INFO = "0.0.1" würde ich ebenfalls hier nicht hard coded im Modul selber hinterlegen.
Ein zentrales Modul welches alle benötigten Informationen enthält ? Dann ginge so etwas wie check_update.version_info. Nur so eine Idee.

Die Anweisungen from xarphus.info import info_app, from xarphus.config import Configuration und set_get_settings = Configuration() werden in Deinem Modul gar nicht weiter benötigt.

Nach except ImportError as ImpErr einfach weiter zu arbeiten und nur eine Fehlermeldung auszugeben, macht für mich auch keinen wirklichen Sinn, weil eigentlich die Reise hier zu Ende ist.

Ich möchte Deinen Code hier jetzt nicht auseinander nehmen, aber einige Dinge erschliessen sich für mich nicht wirklich.

Ich würde jetzt wie folgt vorgehen :

Code: Alles auswählen

try:
    logging.info('importiere requests ... [Start]')
    import requests
    logging.info('importiere requests ... [Done]')
    ....

def check_update():
    try:
        logging.info('requests.get ... [Start]')
        data = requests.get("http://xarphus.de/xarphus_version.txt")
        logging.info('requests.get ... [Done]')
        logging.info('{} - {}'.format(data.text, VERSION_INFO)

        # Das nachstehede gefällt mir nicht wirklich
        # - VERSION_INFO ist bereits ein str
        # - data.text ist ebenfalls bereits ein str
        # - ob das if-statement wirklich immer funktioniert ???
        # - Ich würde so etwas wie sys.version_info implementieren,
        #   dann geht so etwas wie sys.version_info >= (3, 0), da hätte
        #   ich ein grösseres vertrauen zu.
        if str(data.text) > str(VERSION_INFO):
            ....
Wie gesagt das ist jetzt nur einmal so, was mir dazu eingefallen ist.
Benutzeravatar
Sophus
User
Beiträge: 1109
Registriert: Freitag 25. April 2014, 12:46
Wohnort: Osnabrück

@Daikoku: Kurz zu den Anmerkungen. Ich habe zum Beispiel die Versionen und die URL zu der Versions-Datei deshalb hart kodiert, weil ich dadurch eine Unabhängigkeit erschaffen wollte, eine Unabhängigkeit hier im Forum. In Wirklichkeit habe ich ein - wie du bereits angedeutet hast - ein Modul, in dem sämtliche Informationen zum Programm stehen, und handhabe das mit den Wörterbuch (directory). Da ich diesen Beitrag nicht unnötig mit zusätzlichen Modulen unübersichtlich gestalten wollte, griff ich auf übliche Methoden zurück. Und da vergaß ich die unnötigen Imports zu entfernen.

Zu deiner Anmerkung, man solle nicht die Fehler ignorieren. ich bin noch weit am Anfang, ein blutiger Anfänger, der jeden Tag ein Stückchen lernt. Demzufolge bin noch weit davon entfern meine Software zu verteilen. Ab und zu "kompiliere" ich mein kleines Python-Programm und sozusagen Stück für Stück zu überprüfen, ob jetzt schon Fehler auftauchen. Ich halte es für mich unangebracht, erst einmal zu Ende zu programmieren und am Ende in einem Rutsch zu versuchen das fertige Programm dann zu "kompilieren". Das man Fehler macht ist menschlich und gehört wohl zum Programmieren dazu. Demzufolge wäre ich dann erneut damit beschäftigt, dass fertige Programm wieder auseinander zu pflücken, nur um Fehler zu finden. Also gehe ich immer Stück für Stück vor. Nur befinde ich mich gerade in einer Sackgasse, weil ich den Widerspruch zwischen py2exe und cx_freeze nicht verstehe, dazu noch zum Python-Interpreter. Denn wenn dort ein gravierender Fehler vorläge, dann würde der Python-Interpreter entsprechend reagieren. Python verzeiht einem nicht so viele Fehler, richtig?

Deine Logg-Datei klingt interessant. Sowas ähnliches macht die EXE-Datei von py2exe auch, wenn ein Fehler auftaucht. Wenn man also seine EXE-Datei ohne Konsole "kompiliert", dann wird eine Textdatei angelegt, in dem die ganzen Fehler vorhanden sind. Aber zu Testzwecken benutze ich immer die Konsole Ansicht. Ist es dein Skript? Ich frage nur, wenn ich mal wieder etwas mehr Zeit habe, würde ich mir den Skript zu Testzwecken übernehmen.
Benutzeravatar
sparrow
User
Beiträge: 4600
Registriert: Freitag 17. April 2009, 10:28

Das was du hier mit den Tools tust in eine bisschen fischen im Dukeln, oder?
Hast du die entsprechenden Manuals gelesen und dich damit beschäftigt?
Ich habe py2exe noch nie verwendet, aber ein schnelles Googeln hat ergeben, dass Module, die nicht in der Standardbibliothek enthalten sind, in "includes" in der setup.py enthalten sein müssen. Requests sehe ich dort allerdings nicht.

Das mit dem fehlerhaften Import würdest du auch sofort merken, wenn du die Ausnahme (beim Import) nicht abfangen und durch eine Fehlermeldung ersetzen würdest, die du eh nicht liest.
Benutzeravatar
Sophus
User
Beiträge: 1109
Registriert: Freitag 25. April 2014, 12:46
Wohnort: Osnabrück

@sparrow: Ich bin ein wenig verwirrt. Denn sowohl py2exe als auch cx_freeze legen eine .*zip-Datei an, und dort ist requests in beiden Dateien enthalten. Und außerdem dachte ich, dass py2exe und cx_freeze die Abhängigkeiten erkennt und dementsprechend die Module automatisch sammelt. Sprich, wenn py2exe erkennt, dass requests in einem Modul importiert wird, dass dadurch eine Abhängigkeit erkannt wird, und die Bibliothek dann mit übernommen wird.
Benutzeravatar
Sophus
User
Beiträge: 1109
Registriert: Freitag 25. April 2014, 12:46
Wohnort: Osnabrück

Nachdem sparrow mich darauf hingewiesen hat, dass man requests in die include packen muss, hat es mir keine Ruhe gelassen, und ich habe die Konsolenausgabe noch einmal genauer studiert, indem ich hochgescrollt habe:

Bild
Man sieht die entsprechenden Pfeile, und dort steht, dass certs und urllib3 nicht importiert werden konnte. Also kann ich behaupten, dass cx_freeze diese Arbeit von selbst erledigt, während py2exe offenbar nicht in der Lage ist abhängige Module und Bibliotheken zu bündeln?
jerch
User
Beiträge: 1669
Registriert: Mittwoch 4. März 2009, 14:19

@Sophus:
Just a wild guess - manchmal werden Exceptions gezielt zur Flusskontrolle "missbraucht". Vllt. macht das py2exe ja so und sieht die Abhängigkeit nicht, weil Du die import-Exception abfängst?
Benutzeravatar
snafu
User
Beiträge: 6908
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

@Sophus: Gib doch einfach mal alle Abhängigkeiten, die außerhalb von Pythons Standardbibliothek liegen, mit an. Vielleicht ist die automatische Erkennung von Abhängigkeiten ja fehlerbehaftet. Wenn es funktioniert, dann lass es so stehen.
Benutzeravatar
Sophus
User
Beiträge: 1109
Registriert: Freitag 25. April 2014, 12:46
Wohnort: Osnabrück

jerch: Wenn ich dich richtig verstehe, könnte py2exe ein Problem damit haben, dass er die reuests Bibliothek nicht erkennt, weil sie im try-Block ist?
Benutzeravatar
Sophus
User
Beiträge: 1109
Registriert: Freitag 25. April 2014, 12:46
Wohnort: Osnabrück

@snafu: Mit angeben? Du meinst in den include packen? Aber dann wird requests ja nicht mehr in die *.zip-Datei geladen, sondern so lose in den Ordner kopiert. Merkwürdig finde ich ja auch, dass in der*.zip-Datei, die von py2exe mit angelegt wird, ja unter anderem requests mit dabei ist.
Benutzeravatar
snafu
User
Beiträge: 6908
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

Sophus hat geschrieben:jerch: Wenn ich dich richtig verstehe, könnte py2exe ein Problem damit haben, dass er die reuests Bibliothek nicht erkennt, weil sie im try-Block ist?
Um es präziser zu sagen: Der Gedanke war wohl, dass ein `ImportError`, auf dem nach jerchs Vermutung die Programmlogik von py2exe beruht (oder beruhen könnte), niemals bis zum py2exe-Code durchdringt, wenn er vorher von dir abgefangen wird. Immer vorausgesetzt, dass py2exe tatsächlich auf diesem Wege Module bzw fehlende Module erkennt.
Zuletzt geändert von snafu am Samstag 11. Juli 2015, 15:13, insgesamt 1-mal geändert.
Benutzeravatar
snafu
User
Beiträge: 6908
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

Sophus hat geschrieben:@snafu: Mit angeben? Du meinst in den include packen? Aber dann wird requests ja nicht mehr in die *.zip-Datei geladen, sondern so lose in den Ordner kopiert. Merkwürdig finde ich ja auch, dass in der*.zip-Datei, die von py2exe mit angelegt wird, ja unter anderem requests mit dabei ist.
Gibt es bei py2exe etwa keine Funktion, um eine einzige ausführbare Datei zu erstellen? Soweit ich mich erinnere, gibt es sowas. Das ist zwar dann in Wirklichkeit ein sich selbst entpackendes Archiv, aber aus (naiver) Anwendersicht sieht es aus wie eine einzelne EXE-Datei.
Daikoku
User
Beiträge: 66
Registriert: Montag 20. April 2015, 21:14

@Sophus Sorry, mir ist ein wenig die Zeit ausgegangen, um mich etwas intensivere mit Deinem Problem zu beschäftigen.

Auf Grund meiner Erfahrung mit Windows, möchte ich definitiv ausschließen, das Du ein Problem mit dem Modul requests selber haben wirst.

Das sieht nach Windows 7, Python 2.7 und einer virtualenv Umgebung aus.

Anders dargestellt, so als wenn Du zwei verschiedene Python Laufzeitumgebungen installiert hast.
Die eine ist sauber und in der anderen fehlen einige Module.

Könntest Du bitte einmal posten:
- welche Windows Version Du benutzt,
- welche Python Version
. Python 2.7.xx ... [MSC v.1500 32 bit (Intel)] on win32 ???
. Python 2.7.xx ... [MSC v.1500 64 bit (AMD64)] on win32 ????
- welchen C-Compiler ?
- und ob Du Visual Studio als Entwicklungsumgebung benutzt ?

Kopf hoch, das Problem bekommen wir schon in den Griff.

Bis jetzt ist alles völlig normal, auch wenn die Pinguine sich jetzt bestätigt fühlen, das Windows als Entwicklungsumgebung eher suboptimal ist.
Aber 90% Windows, 8% MAC OS X und 2% Linux als Desktop-Betriebssystem sprechen da eine deutliche Sprache.

PS.: certs gehört zu site-packages/requests/certs.py. Alles was nicht funktioniert gehört zu requests.
Antworten