Problem mit ausführbarer Datei | Zugriff auf externe Datei

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
SvenHornbach
User
Beiträge: 10
Registriert: Montag 3. April 2023, 10:25

Hallo,

ich bin neu in der Welt der Python-Programmierung, nicht aber generell was Programmierung anbelangt. Ich kenne mich etwas in Java, C und VBA aus.

Ich habe nun in Python ein Programm geschrieben, welches Dateien auf ein GoogleDrive Laufwerk verschiebt, den entsprechenden Ordner freigibt und die Freigabelink kopiert und in eine PDF schreibt. Alles über die Google API. In der Entwicklung PyCharm funktioniert auch alles. Wenn ich aber eine ausführbare Exe erstelle (--onefile) dann greift er nicht mehr auf die json zu, wo die Zugangsdaten zur API enthalten sind. Der Link zu der JSON ist korrekt (ich habe ihn ein mal als festen Pfad und einmal als dynamischen getestet). Auch habe ich die main.exe (hat noch keinen kreativen Namen das Programm) als Administrator ausgeführt.

Ich verstehe einfach nicht, warum es in der PyCharm-Umgebung reibungslos läuft, aber als kompilierte Version nicht.

Ich habe vorher noch nie ausführbare Dateien erstellt, meistens habe ich mit Scripten gearbeitet. Vergesse ich irgendwas, muss ich irgendwas beachten?

Vielen Dank für eure Hilfe.
__deets__
User
Beiträge: 14545
Registriert: Mittwoch 14. Oktober 2015, 14:29

Aller Wahrscheinlichkeit hast du mit relativen Pfaden gearbeitet, die innerhalb von pycharm funktionieren, weil das fuer dein Projekt ein festes Arbeitsverzeichnis implizit setzt.

Der uebliche Trick besteht darin, den Pfad zu der eingebetteten Resource via der __file__-Variablen zu bauen. Ggf. gibt es da aber auch noch etwas bei deinem gewaehlten EXE-Buendler zu beachten.

Code: Alles auswählen

import pathlib
BASE = pathlib.Path(__file__).parent # Points to the directory relative to the current module/scrip file
def main():
    print(BASE / "test.json") # assumes the test.json is placed parallel to this module/script.
SvenHornbach
User
Beiträge: 10
Registriert: Montag 3. April 2023, 10:25

Vielen lieben Dank für die schnelle Antwort.

Eigentlich nutze ich einen relativen Pfad

Code: Alles auswählen

current_directory = os.path.dirname(os.path.abspath(sys.argv[0]))
credentials_file = os.path.join(current_directory, r"_nicht_loeschen\drive.json")
Ich benenne in dem Ordner Dateien auch um, das heißt, generell besteht der Zugriff. Ich nutze jedoch nicht die pathlib. Ich kann das gerne gleich mal probieren, ob e damit besser läuft.
__deets__
User
Beiträge: 14545
Registriert: Mittwoch 14. Oktober 2015, 14:29

An sich kein schlechtes Vorgehen, aber da es nicht zu funktionieren scheint, ist es wohl nicht so geeignet. Ein unmittelbares Problem: du hast *eine* Exe, aber das ist ja "gelogen". Stattdessen ist das eine Art selbstentpackendes Archiv. Und da kann es sein, das sys.argv[0] zwar korrekt den Pfad der EXE benennt, aber Code & Daten liegen waehrend der Ausfuehrung in einem temporaeren Verzeichnis. Da hilft aber dann __file__.
SvenHornbach
User
Beiträge: 10
Registriert: Montag 3. April 2023, 10:25

Es hat leider nicht geklappt. Ich habe auch noch mal den festen Pfad eingepflegt, funktioniert trotzdem nicht. Ich bekomme auch keine Fehlermeldung, das die Datei nicht zu finden sei.

Code: Alles auswählen

def create_drive_service(credentials_file: str):
    try:
        with open(credentials_file, "r") as f:
            credentials_info = json.load(f)

        credentials = service_account.Credentials.from_service_account_info(credentials_info)
        service = discovery.build("drive", "v3", credentials=credentials)
        return service
    except HttpError as error:
        print(f"An error occurred: {error}")
        return None
Folgenden Fehler erhalte ich: An error occurred: name: drive version: v3

Es geht dabei um die Funktion, den Google Service zu starten. Aus der Umgebung heraus kein Problem.
__deets__
User
Beiträge: 14545
Registriert: Mittwoch 14. Oktober 2015, 14:29

Ich wuerde den Fehler komplett anzeigen, statt nur eine so kastrierte Version. Denn einen sinnvollen Weg, danach weiter zu machen, hast du doch nicht. So kann man naemlich nicht wirklich viel dazu finden. Wenn die Datei nicht gefunden wuerde, sollte der allerdings (denke ich) anders aussehen.
Sirius3
User
Beiträge: 18274
Registriert: Sonntag 21. Oktober 2012, 17:20

Der Fehler hat ja gar nichts damit zu tun, dass eine Datei nicht gefunden wird, sondern der Fehler tritt später auf.
Du fängst nur den HttpError, keinen IOError.
So eine Fehlerbehandlung ist im allgemeinen keine gute Idee. Denn im Fehlerfall wird die Information, wo der Fehler auftritt verheimlicht und die aufrufende Stelle bekommt statt einer schönen Exception, die sie behandeln kann nur einen Rückgabewert None.
Ein Grundsatz ist immer, dort das Exception Handling zu machen, wo man sinnvoll auf den Fehler reagieren kann, und ansonsten den Fehler einfach durchfallen lassen.
SvenHornbach
User
Beiträge: 10
Registriert: Montag 3. April 2023, 10:25

Sirius3 hat geschrieben: Montag 3. April 2023, 14:49 Der Fehler hat ja gar nichts damit zu tun, dass eine Datei nicht gefunden wird, sondern der Fehler tritt später auf.
Du fängst nur den HttpError, keinen IOError.
So eine Fehlerbehandlung ist im allgemeinen keine gute Idee. Denn im Fehlerfall wird die Information, wo der Fehler auftritt verheimlicht und die aufrufende Stelle bekommt statt einer schönen Exception, die sie behandeln kann nur einen Rückgabewert None.
Ein Grundsatz ist immer, dort das Exception Handling zu machen, wo man sinnvoll auf den Fehler reagieren kann, und ansonsten den Fehler einfach durchfallen lassen.
Für eine vernünftige Fehlerbehandlung dann lieber:

Code: Alles auswählen

def create_drive_service(credentials_file: str):
    try:
        with open(credentials_file, "r") as f:
            credentials_info = json.load(f)

        credentials = service_account.Credentials.from_service_account_info(credentials_info)
        service = discovery.build("drive", "v3", credentials=credentials)
        return service
    except IOError as error:
        print(f"An error occurred: {error}")
        return None
oder

Code: Alles auswählen

def create_drive_service(credentials_file: str):
    try:
        with open(credentials_file, "r") as f:
            credentials_info = json.load(f)

        credentials = service_account.Credentials.from_service_account_info(credentials_info)
        service = discovery.build("drive", "v3", credentials=credentials)
        return service
    except Exception as error:
        print(f"An error occurred: {error}")
        return None
__deets__
User
Beiträge: 14545
Registriert: Mittwoch 14. Oktober 2015, 14:29

Gar kein try/except. Das nimmt dir doch wertvolle Information.
SvenHornbach
User
Beiträge: 10
Registriert: Montag 3. April 2023, 10:25

__deets__ hat geschrieben: Montag 3. April 2023, 15:21 Gar kein try/except. Das nimmt dir doch wertvolle Information.
Ich weiß leider nicht wie man das macht. ChatGPT sagt mir, dass es besser sein mit dieser handhabe

ich habe also nun folgenden Code, um Fehler abzufangen:

Code: Alles auswählen

def create_drive_service(credentials_file: str):
    try:
        with open(credentials_file, "r") as f:
            credentials_info = json.load(f)

        credentials = service_account.Credentials.from_service_account_info(credentials_info)
        service = build("drive", "v3", credentials=credentials)
        return service

    except FileNotFoundError:
        print(f"Datei {credentials_file} nicht gefunden.")
        return None
    except json.JSONDecodeError:
        print(f"Ungültiges JSON in {credentials_file}.")
        return None
    except HttpError as error:
        print(f"HttpError Ein unerwarteter Fehler ist aufgetreten: {error.resp.status} {error.resp.reason}")
        return None
    except Exception as e:
        print(f"ExcetionError Ein unerwarteter Fehler ist aufgetreten: {e}")
        return None
Ich bekomme nun folgende Fehlermeldung:
Ein unerwarteter Fehler ist aufgetreten: name: drive version: v3
An error occurred: 'NoneType' object has no attribute 'files'

Lässt sich daraus ableiten, was es sein könnte?
SvenHornbach
User
Beiträge: 10
Registriert: Montag 3. April 2023, 10:25

Ich habe es jetzt abgeändert in:

Code: Alles auswählen

def create_drive_service(credentials_file: str):
    with open(credentials_file, "r") as f:
        credentials_info = json.load(f)

    credentials = service_account.Credentials.from_service_account_info(credentials_info)
    service = discovery.build("drive", "v3", credentials=credentials)
    return service
Bekomme aber immernoch keine andere Aussage außer: Ein unerwarteter Fehler ist aufgetreten: name: drive version: v3
__deets__
User
Beiträge: 14545
Registriert: Mittwoch 14. Oktober 2015, 14:29

Ohne Traceback?
Benutzeravatar
sparrow
User
Beiträge: 4538
Registriert: Freitag 17. April 2009, 10:28

Das passt nicht zu deinem Code.

Was machst du denn hinterher, wenn None zurückgegeben wird? Wird das sinnvoll behandelt?
Denn im Moment fängst du zwar Fehler, aber du gibst dann nur deren Info aus. Wichtige Informationen, wie zum Beispiel die Stelle, an der der Fehler aufgetreten ist, wird auf diese Weise verschluckt. Das brauchst du aber zum Debuggen.

Deshalb gilt: Kann man die Stelle des Fehlers nicht genau voraussagen oder würde das Fangen des Fehlers das Programm nicht in einem kontrollierten Zustand zurück lassen: Den Fehler nicht fangen.
Dann wird der Fehler nach oben durchgereicht, das Programm crasht und gibt einen ausführliche Traceback aus.

Zu deinem Problem mit der Datei:

1) Warum muss es eine exe-Datei sein? Das ist bei Python eher ungwöhnlich.
2) Wie wird die exe-Datei erstellt? Mit pyinstaller? Dann solltest du das hier lesen. Denn das Bünden von Dateien in pyinstaller ist nicht ohne.
SvenHornbach
User
Beiträge: 10
Registriert: Montag 3. April 2023, 10:25

sparrow hat geschrieben: Montag 3. April 2023, 16:56 Das passt nicht zu deinem Code.

Was machst du denn hinterher, wenn None zurückgegeben wird? Wird das sinnvoll behandelt?
Denn im Moment fängst du zwar Fehler, aber du gibst dann nur deren Info aus. Wichtige Informationen, wie zum Beispiel die Stelle, an der der Fehler aufgetreten ist, wird auf diese Weise verschluckt. Das brauchst du aber zum Debuggen.

Deshalb gilt: Kann man die Stelle des Fehlers nicht genau voraussagen oder würde das Fangen des Fehlers das Programm nicht in einem kontrollierten Zustand zurück lassen: Den Fehler nicht fangen.
Dann wird der Fehler nach oben durchgereicht, das Programm crasht und gibt einen ausführliche Traceback aus.

Zu deinem Problem mit der Datei:

1) Warum muss es eine exe-Datei sein? Das ist bei Python eher ungwöhnlich.
2) Wie wird die exe-Datei erstellt? Mit pyinstaller? Dann solltest du das hier lesen. Denn das Bünden von Dateien in pyinstaller ist nicht ohne.
Vielen Dank für deine Antwort. Ich habe Tracback eingebunden und bekomme:
Mit Traceback bekomme ich folgenden fehler:

An error occurred: name: drive version: v3
Traceback (most recent call last):
File "main.py", line 63, in create_drive_service
service = discovery.build("drive", "v3", credentials=credentials)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "googleapiclient\_helpers.py", line 130, in positional_wrapper
File "googleapiclient\discovery.py", line 287, in build
File "googleapiclient\discovery.py", line 404, in _retrieve_discovery_doc
googleapiclient.errors.UnknownApiNameOrVersion: name: drive version: v3
An error occurred: 'NoneType' object has no attribute 'files'
Press Enter to close the program.

Zu deinen Fragen:
1) Ich möchte gerne das das Programm auch von anderen schnell ausgeführt werden kann, ohne erst die PyCharm Umgebung zu öffnen. Es muss keine exe sein. Ich will nur, dass jemand anderes mit einem anderen PC, dieses Script einfach und schnell ausführen kann. Entsprechend sollte dann Python auch auf diesem Rechner installiert sein und ich kann durch eine Batch Datei, die ein cmd Befehl zum ausführen des Scripts binhaltet erstellen, als Alternative Lösung? Edit: Dazu dann das ganze Py-Projekt in einen Drive-Folder, damit von überall erreichbar.
2)Ja, mit pyinstaller. Ich werde mir den Beitrag mal anschauen. Gibt es eine einfacherer und besser Alternative zur Erstellung der exe?
Sirius3
User
Beiträge: 18274
Registriert: Sonntag 21. Oktober 2012, 17:20

Du hast ja immer noch die Fehlerhafte Fehlerbehandlung drin. Die Variante von 17:41Uhr war doch die richtige.
Was steht denn in `credentials_info`? Ist das für beide Umgebungen das selbe?
SvenHornbach
User
Beiträge: 10
Registriert: Montag 3. April 2023, 10:25

Sirius3 hat geschrieben: Montag 3. April 2023, 18:25 Du hast ja immer noch die Fehlerhafte Fehlerbehandlung drin. Die Variante von 17:41Uhr war doch die richtige.
Was steht denn in `credentials_info`? Ist das für beide Umgebungen das selbe?
Vielen Dank für eure Antworten bis dato.

Das liegt daran, dass ich nicht richtig weiß, wie ich dann traceback einbinden kann um eine aussagekräftige Antwort zu erhalten. Mit der Lösung, habe ich ein wenig mehr an Infos erhalten.

In der credentials_info werden json Daten geladen:
{
"type": "service_account",
"project_id": "xxx",
"private_key_id": "xxx",
"private_key": "-----BEGIN PRIVATE KEY-----\nxxx\n-----END PRIVATE KEY-----\n",
"client_email": "xxx",
"client_id": "xxx",
"auth_uri": "https://accounts.google.com/o/oauth2/auth",
"token_uri": "https://oauth2.googleapis.com/token",
"auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs",
"client_x509_cert_url": "xxx"
}
Die sind in beiden Fällen gleich, da auf die selbe Datei zugegriffen wird.
__deets__
User
Beiträge: 14545
Registriert: Mittwoch 14. Oktober 2015, 14:29

Ich kenne diese Bibilothek nicht, aber wenn man sich den Code mal anschaut - https://github.com/googleapis/google-ap ... ry.py#L399 - dann beziehen die sich da auf irgendwelche Daten, die nachgeladen werden, bzw. irgendwo als Cache rumliegen sollen. Das aber nicht tun. Das kann also sehr gut ein Problem deiner Buendelung als EXE sein. Das muss geloest werden. Dazu zB mal den Debugger an der Stelle positionieren, und sich anschauen, was da genau in diesem cache passiert.
SvenHornbach
User
Beiträge: 10
Registriert: Montag 3. April 2023, 10:25

__deets__ hat geschrieben: Dienstag 4. April 2023, 09:02 Ich kenne diese Bibilothek nicht, aber wenn man sich den Code mal anschaut - https://github.com/googleapis/google-ap ... ry.py#L399 - dann beziehen die sich da auf irgendwelche Daten, die nachgeladen werden, bzw. irgendwo als Cache rumliegen sollen. Das aber nicht tun. Das kann also sehr gut ein Problem deiner Buendelung als EXE sein. Das muss geloest werden. Dazu zB mal den Debugger an der Stelle positionieren, und sich anschauen, was da genau in diesem cache passiert.
Danke das du dir das angeschaut hast, ich versuche den Ansatz mit dem Cache nachzugehen ...
SvenHornbach
User
Beiträge: 10
Registriert: Montag 3. April 2023, 10:25

Ich habe jetzt die https://www.googleapis.com/discovery/v1 ... ve/v3/rest json heruntergeladen und lokal eingebunden. Dadurch funktioniert der Zugriff und die Verbindung wurde hergestellt. Der Hinweis mit dem Cache hat zur Lösung geführt, vielen Dank.
Antworten