Python PDF Umbenennung klappt nicht mehr nach Konvertierung zu einer .exe

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
xanox
User
Beiträge: 5
Registriert: Dienstag 13. Mai 2008, 00:48

Hallo!
Ich arbeite an einer Python-Anwendung, die PDF-Dateien basierend auf bestimmten Kriterien umbenennt. Ich verwende PySide6 für die GUI, und die Anwendung soll PDF-Dateien aus einem Verzeichnis lesen und umbenennen. Nun möchte ich das Tool gerne kostenlos Personen bereitstellen, die jedoch kein Python usw. installiert haben, daher idealerweise als eigenständige .exe.

Hier ist der Codeausschnitt, der die PDF-Dateien aus einem Verzeichnis liest:

Code: Alles auswählen

directory = os.path.dirname(os.path.realpath(__file__))
Option 1

Code: Alles auswählen

files = [entry.name for entry in os.scandir(self.directory) if entry.is_file() and entry.name.endswith(".pdf")]
Option 2

Code: Alles auswählen

files = [f for f in os.listdir(self.directory) if os.path.isfile(os.path.join(self.directory, f)) and f.endswith(".pdf")]
Beide Optionen funktionieren einwandfrei, wenn ich mein Skript über Python ausführe. Der Umbenennungsteil wird durch den folgenden Code ausgeführt:

Code: Alles auswählen

if date_str:
        new_file = os.path.join(self.directory, f"{date_str} - {file}")
        os.rename(file_path, new_file)
Das Problem, mit dem ich konfrontiert bin, tritt auf, wenn ich mein Skript mithilfe von auto-py-to-exe oder Nuitka in eine ausführbare Datei umwandle. Die ausführbare Datei startet einwandfrei mit beiden Tools, und die PySide6-GUI öffnet sich, kann jedoch keine PDF-Dateien finden und benennt nichts um. Es werden keine Fehler angezeigt, es führt einfach nicht die erwarteten Aktionen aus.

Code: Alles auswählen

python -m nuitka --onefile --follow-imports --windows-disable-console --enable-plugin=pyside6 PDFRenamer.pyw
Zur Einordnung: Ich führe die .exe-Datei im selben Verzeichnis wie meine .pyw-Datei aus.

Was könnte das Problem verursachen? Wie kann ich dies debuggen oder beheben? Jede Hilfe oder Vorschläge sind sehr willkommen.

Python-Version 3.11.4

Vielen Dank im Voraus!
Zuletzt geändert von xanox am Donnerstag 6. Juli 2023, 11:20, insgesamt 1-mal geändert.
Benutzeravatar
Dennis89
User
Beiträge: 1555
Registriert: Freitag 11. Dezember 2020, 15:13

Hallo,

die *.exe - Datei entpackt sich ein einem Temporären-Ordner, der nicht im gleichen Pfad ist, in dem du die Datei ausführst. Du musst einen absoluten Pfad zu dem gewünschten Ordner angeben oder den Benutzer selbst eingeben lassen, in welchem Ordner die *.pdf - Dateien liegen, die umbenannt werden sollen.


Grüße
Dennis
"When I got the music, I got a place to go" [Rancid, 1993]
Benutzeravatar
__blackjack__
User
Beiträge: 14053
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

Randbemerkung: Der `os.path`-Kram ist umständlicher als die `pathlib.Path`-API.

Edit: Um das mal zu zeigen das es ohne `os.path` lesbarer wird:

Code: Alles auswählen

directory = os.path.dirname(os.path.realpath(__file__))
#
# ->
#
SELF_PATH = Path(__file__).parent.absolute()

Code: Alles auswählen

        files = [
            f
            for f in os.listdir(self.directory)
            if (
                os.path.isfile(os.path.join(self.directory, f))
                and f.endswith(".pdf")
            )
        ]
        #
        # ->
        #
        file_paths = [
            path for path in self.directory_path.glob("*.pdf") if path.isfile()
        ]

Code: Alles auswählen

        if date_str:
            new_file = os.path.join(self.directory, f"{date_str} - {file}")
            os.rename(file_path, new_file)
        #
        # ->
        #
        if date_text:
            file_path.rename(
                file_path.with_name(f"{date_text} - {file_path.name}")
            )
“Vir, intelligence has nothing to do with politics!” — Londo Mollari
Sirius3
User
Beiträge: 18274
Registriert: Sonntag 21. Oktober 2012, 17:20

@Dennis89: nuitka arbeitet da anders, ohne zip-Dateien, aber was dort das __file__-Attribut enthält weiß ich aber nicht.

@xanox: ich würde als Nutzer erwarten, dass die PDF-Dateien relativ zum Arbeitsverzeichnis gesucht werden, und nicht relativ zu irgendeinem python-Modul. Das __file__ halte ich für falsch.

Heutzutage würde man pathlib benutzen:

Code: Alles auswählen

self.directory = "."
files = list(pathlib.Path(self.directory).glob("*.pdf"))

if date:
    filepath.rename(filepath.with_stem(f"{date} - {filepath.stem}))
xanox
User
Beiträge: 5
Registriert: Dienstag 13. Mai 2008, 00:48

Hallo Dennis,

danke dir für den Tipp. Ich hatte mich damit mal weiter auf die Suche begeben und wurde fündig:
Dennis89 hat geschrieben: Donnerstag 6. Juli 2023, 11:19 die *.exe - Datei entpackt sich ein einem Temporären-Ordner, der nicht im gleichen Pfad ist, in dem du die Datei ausführst. Du musst einen absoluten Pfad zu dem gewünschten Ordner angeben oder den Benutzer selbst eingeben lassen, in welchem Ordner die *.pdf - Dateien liegen, die umbenannt werden sollen.
Ich habe es nun abgeändert auf:

Code: Alles auswählen

directory = os.getcwd()
Das hat augenscheinlich funktioniert. Die erste Datei wurde umbenannt. Danach meldete sich der Windows Defender und behauptete man hätte einen schwerwiegenden Trojan:Win32/Wacatac.B!ml entdeckt und die Datei war futsch.

Liegt dies eventuell an der os.getcwd() oder ist dies bei allen Python2Exe Convertern das mitfließende Übel? So möchte ich die Datei ungerne Kollegen zukommen lassen, die wenig IT affin sind.
Sirius3 hat geschrieben: Donnerstag 6. Juli 2023, 13:33

Code: Alles auswählen

self.directory = "."
files = list(pathlib.Path(self.directory).glob("*.pdf"))

if date:
    filepath.rename(filepath.with_stem(f"{date} - {filepath.stem}))
Danke dir ebenso für den Hinweis auf Pathlib! :- )

Edit:

Mit PyInstaller

Code: Alles auswählen

pyinstaller --onefile PDFRenamer.pyw
Scheint es zu klappen, auch wenn die Datei ganze 70 MB groß wurde :)

Ps. Kann man die Codeansicht hier verändern? Ich finde es sowohl im Darkmode als auch normal sehr schwer zu lesen / wenig Kontrast.
Benutzeravatar
DeaD_EyE
User
Beiträge: 1240
Registriert: Sonntag 19. September 2010, 13:45
Wohnort: Hagen
Kontaktdaten:

Man muss die Dateien auch hinzufügen.
Hier erst mal der Code, mit dem ich das getestet habe:

Code: Alles auswählen

import time
import subprocess
from importlib import resources
from pathlib import Path

import pdf


directory = Path(__file__).parent.absolute()
print("CWD:", directory)
print()


for file in Path(resources.files(pdf)).glob("*.pdf"):
    print(file)
    if file.name == "x.pdf":
        subprocess.run(["xdg-open", file])
        # wenn zu schnell beendet wird, ist die Datei wieder gelöscht und kann nicht geöffnet werden
        time.sleep(5)
Verzeichnisstruktur:

Code: Alles auswählen

main.py
pdf/file_1.pdf
...
pdf/x.pdf
pdf/__init__.py # leer
Zuerst mit PyInstaller:

Code: Alles auswählen

pyinstaller -F main.py
Dann die Datei main.spec bearbeiten:

Code: Alles auswählen

# -*- mode: python ; coding: utf-8 -*-


block_cipher = None


a = Analysis(
    ['main.py'],
    pathex=[],
    binaries=[],
    datas=[("pdf/*.pdf", "pdf")],
    hiddenimports=[],
    hookspath=[],
    hooksconfig={},
    runtime_hooks=[],
    excludes=[],
    win_no_prefer_redirects=False,
    win_private_assemblies=False,
    cipher=block_cipher,
    noarchive=False,
)
pyz = PYZ(a.pure, a.zipped_data, cipher=block_cipher)

exe = EXE(
    pyz,
    a.scripts,
    a.binaries,
    a.zipfiles,
    a.datas,
    [],
    name='main',
    debug=False,
    bootloader_ignore_signals=False,
    strip=False,
    upx=True,
    upx_exclude=[],
    runtime_tmpdir=None,
    console=True,
    disable_windowed_traceback=False,
    argv_emulation=False,
    target_arch=None,
    codesign_identity=None,
    entitlements_file=None,
)
Und dann noch einmal mit der main.spec erstellen:

Code: Alles auswählen

pyinstaller main.spec
Ausführen:

Code: Alles auswählen

dist/main
Nuitka:

Code: Alles auswählen

nuitka3 --static-libpython=no  --standalone --onefile --include-data-files=pdf/*.pdf=pdf/ main.py
--static-libpython=no musste ich verwenden, da ich mittels pyenv eine shared_library erstellt habe.

Dann starten:

Code: Alles auswählen

dist.main/main
Und noch einmal mit der gepackten ausführbaren Datei:

Code: Alles auswählen

./main.bin
Beim Testen ist mir noch folgendes aufgefallen.
  • importlib.resources.open_binary usw sind veraltet, man soll importlibe.resources.files() verwenden: https://importlib-resources.readthedocs ... rom-legacy
  • PyInstaller liefert ein MultiplexedPath, sofern resources.files("pdf") verwendet wird. Verwendet man stattdessen das importierte Modul pdf, dann bekommt man ein Path Objekt.
  • Nuitka mach aus dem Pfad object auch wieder was anderes, deswegegen habe ich Path(resources.files(pdf)) verwendet.
  • Temporäre Dateien werden automatisch gelöscht, sobald das Programm verlassen wird. Gilt für PyInstaller und Nuitka3.
sourceserver.info - sourceserver.info/wiki/ - ausgestorbener Support für HL2-Server
Benutzeravatar
ThomasL
User
Beiträge: 1379
Registriert: Montag 14. Mai 2018, 14:44
Wohnort: Kreis Unna NRW

Danach meldete sich der Windows Defender und behauptete man hätte einen schwerwiegenden Trojan:Win32/Wacatac.B!ml entdeckt und die Datei war futsch.
Na dann hoffe ich für dich, dass du diese PDF Datei zuvor nicht geöffnet/gelesen hast, denn sie war mit diesem Trojaner infiziert. Wurde erkannt und die Datei ist jetzt im Quarantänecontainer des Defender.
Kann von dort wieder hergestellt werden. Ich würde sie endgültig löschen.

PDFs aus dubiosen Quellen gezogen/bekommen?
Ich bin Pazifist und greife niemanden an, auch nicht mit Worten.
Für alle meine Code Beispiele gilt: "There is always a better way."
https://projecteuler.net/profile/Brotherluii.png
Benutzeravatar
__blackjack__
User
Beiträge: 14053
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@ThomasL: Nicht die PDF-Datei sondern die generierte EXE wird als Trojaner erkannt. Das ist schon mehreren Leuten mit Pyinstaller und Innosetup passiert. Der Defender ist halt doof. Auch andere Anti-Virensoftware. Die schlagen gerne mal bei Software zu die nicht ”offiziell” ist. Habe ich schon bei mehreren OpenSource-Projekten erlebt, dass immer mal wieder gehäuft Leute auf den Mailinglisten und Issuetrackern aufschlagen die panisch sind, weil ihnen der Defender sagt die EXE sei gefährlich. Da scheint es zum Beispiel so Heuristiken zu geben, dass Sachen die mit einen Crosscompiler unter Linux gebaut sind, potentiell schon mal gefährlich sind.

Ich würde da eher mal Windows löschen, oder zumindest in Quarantäne verschieben. Bei mir kommt das nur noch in eine VM. 😜
“Vir, intelligence has nothing to do with politics!” — Londo Mollari
Benutzeravatar
DeaD_EyE
User
Beiträge: 1240
Registriert: Sonntag 19. September 2010, 13:45
Wohnort: Hagen
Kontaktdaten:

PyInstaller verwendet UPX, sofern vorhanden, zum Packen der Dateien in die ausführbare Datei.
UPX hat die Eigenart, dass es vielfach als Virus durch Windows erkannt wird.

Liegt unter anderem daran, dass UPX bei Hackern sehr beliebt ist und auch verwendet wird, um den Payload der Viren in eine ausführbare Datei zu packen.

Ich vermute, dass nuitka das auch macht, nachdem Python in C++ übersetzt worden ist und kompiliert wurde. Danach hat man ja ganz viele Dateien und um die in die ausführbare Datei zu bekommen, ist UPX eine einfache Lösung.
sourceserver.info - sourceserver.info/wiki/ - ausgestorbener Support für HL2-Server
Benutzeravatar
ThomasL
User
Beiträge: 1379
Registriert: Montag 14. Mai 2018, 14:44
Wohnort: Kreis Unna NRW

Ok, habe das dazu gefunden, vielleicht hilft es dem OP

https://plainenglish.io/blog/pyinstalle ... 3842bd3184
Ich bin Pazifist und greife niemanden an, auch nicht mit Worten.
Für alle meine Code Beispiele gilt: "There is always a better way."
https://projecteuler.net/profile/Brotherluii.png
Antworten