Skip to content

Python3 - Popen und seine Geheimnisse ;)

Python3
  • Eine der letzten Baustellen in meinem Tool Restic UI war die Funktion zum Mounten eines Restic Backups.

    Das grĂ¶ĂŸte Problem dabei war, das dieser Prozess nicht beendet wird sonder offen bleibt. Das kennt ihr, wenn ihr das schon mal ĂŒber die Konsole gemacht habt, dann erscheint das folgende

    reading repository password from stdin
    repository 6859cd64 opened successfully, password is correct
    Now serving the repository at /home/frank/m5
    When finished, quit with Ctrl-c or umount the mountpoint.
    

    Jetzt kann man in meinem Tool das Passwort mittlerweile abspeichern oder wahlweise auch jedes mal eingeben. Aber, was mache ich wenn das Passwort falsch eingegeben wurde? Ich habe tagelang versucht, die Fehler abzufangen, das geht aber meiner Meinung nach nicht, da der Prozess am Laufen ist. Was machen?

    Ich habe so ziemlich jede Seite im Netz zum Thema Popen durch 😉 Nichts davon brachten den gewĂŒnschten Erfolg.

    Ok, fangen wir von vorne an, was sagt die Dokumentation von Restic?

    EXIT STATUS
    ===========
    
    Exit status is 0 if the command was successful, and non-zero if there was any error.
    

    Gut, wenn ich den richtigen Fehlercode nicht abfangen kann, dann kann ich evt. ermitteln ob der Prozess lĂ€uft oder einen Fehler ausgibt. Das wĂŒrde mir zu mindestens helfen, den User zu informieren.

    Bei den hunderten von Webseiten, hatte ich irgendwo was ĂŒber p.poll() aufgeschnappt. Das steht in der Python Dokumentation.

    Popen.poll()

    Check if child process has terminated. Set and return returncode attribute. Otherwise, returns None.
    

    Das sollte fĂŒr meinen Zweck ausreichend sein. Erster Versuch

    # no passwd in backup_data? Ok, we ask for one
    if len(backup_data[row].password) == 0:
        pwd = get_password(self) ## pwd ('1234', True)
        if pwd[1] is False:
            return False
    
    try:
        cmd = ['restic',
                                        '-r',
                                        backup_data[row].repository,
                                        'mount',
                                        mount_path]
        
       
        # We create the object, here with stdin and open the asynchronous subprocess
        p1 = subprocess.Popen(cmd,stdin=subprocess.PIPE,stderr=subprocess.PIPE) 
        p1.stdin.write(pwd[0].encode()))
        p1.stdin.close()
    
    except Exception:
        pass
    
    finally:
        try:
            if p1.poll() == 1:                          
                 raise Exception("The Restic mount command was not successful. Probable cause: wrong password or no key found")
            else:
                msg_box(self, "Successful mount of the Repository!")    
        except Exception as e:
            msg_box_error(self, repr(e))
    

    Ich hoffe man versteht was ich hier mache.

    • Wir holen uns das Passort ĂŒber eine Usereingabe
    • Wir bauen den Restic Befehl cmd
    • Wir starten den subprocess.Popen
    • Wir senden das Passwort auf stdin
    • Wir schließen stdin
    • In finally machen wir den p1.poll() und werten diesen aus.

    Bei den vielen, vielen, super vielen Test war mir aufgefallen, das das klappte.

    p1.poll() gab einmal None aus und einmal eine 1

    None war ein laufender Prozess, also erfolgreich gemountet.
    Eine 1 war ein Fehler, hier falsche Passwort Eingabe.

    Nach einigen weiteren Tests, ging es auf einmal nicht mehr. Ich wusste aber ganz genau, das es ging, ich konnte das in der Konsole noch lesen. Also meine Kontrollausgaben. Was war passiert. Eine halbe Stunde GrĂŒbeln und ich kam zu der Überzeugung, das muss ein Zeitproblem sein. Ok, also eine Bremse 🙂 einbauen. Im finally vor dem try kommt das rein.

    print("POLL", p1.poll()) 
    import time
    time.sleep(1.0)
    

    Kleine Kontrollausgabe, wir importieren das Modul time (ja, gehört nicht an diese Stelle, ich weiß...) und dann lassen wir den Prozess 1 Sekunde pausieren. Wir geben hier jetzt dem subprocess die Gelegenheit, das falsche Passwort zu erkennen und den richtigen Exit Status zu setzen. Und Bingo es ging so wie ich es mir vorgestellt hatte. đŸ€“

    Wieder extrem viel gelernt.

    Hinweis

    Der Code oben ist nur ein Ausschnitt, ich hoffe das er nachvollziehbar ist. Wenn nicht einfach hier drunter kommentieren, es gibt das dann in lang 😉

    FĂŒr Profis, die evt. hier mal drĂŒber stolpern. Ich nehme jede Verbesserung gerne auf und probiere es auch aus.

    WĂŒnsche mir eigentlich immer noch, das ich die Ausgabe

    Fatal: wrong password or no key found
    

    vernĂŒnftig abfangen und weiterverarbeiten kann. So wie mit

    output = process.stdout
    

    bei einem subprocess.run.

  • Rest-Server v0.13.0 released

    Restic
    2
    0 Stimmen
    2 BeitrÀge
    311 Aufrufe
    FrankMF
    Download Rest-Server und installieren Im Github Repository den aktuellen Release suchen. Hier am Beispiel der aktuellen Version 0.13.0 (27.07.2024) Datei herunterladen wget https://github.com/restic/rest-server/releases/download/v0.13.0/rest-server_0.13.0_linux_amd64.tar.gz Die Datei entpacken tar -xf rest-server_0.13.0_linux_amd64.tar.gz Ins Verzeichnis wechseln cd rest-server_0.13.0_linux_amd64 Wenn der Rest-Server lÀuft, dann muss man diesen erst mal stoppen. systemctl stop rest-server Danach kopiert man das File nach bin. Wer mag sichert vorher das alte File. cp rest-server /usr/local/bin Danach kann man den Rest-Server wieder starten. systemctl start rest-server Versionskontrolle root@rest-server:~# rest-server -v rest-server version rest-server 0.13.0 compiled with go1.22.5 on linux/amd64 Die Hilfe vom Rest-Server root@rest-server:~# rest-server -h Run a REST server for use with restic Usage: rest-server [flags] Flags: --append-only enable append only mode --cpu-profile string write CPU profile to file --debug output debug messages -h, --help help for rest-server --htpasswd-file string location of .htpasswd file (default: "<data directory>/.htpasswd)" --listen string listen address (default ":8000") --log filename write HTTP requests in the combined log format to the specified filename (use "-" for logging to stdout) --max-size int the maximum size of the repository in bytes --no-auth disable .htpasswd authentication --no-verify-upload do not verify the integrity of uploaded data. DO NOT enable unless the rest-server runs on a very low-power device --path string data directory (default "/tmp/restic") --private-repos users can only access their private repo --prometheus enable Prometheus metrics --prometheus-no-auth disable auth for Prometheus /metrics endpoint --tls turn on TLS support --tls-cert string TLS certificate path --tls-key string TLS key path -v, --version version for rest-server Systemd Wer noch ein passendes systemd File benötigt. [Unit] Description=Rest Server After=syslog.target After=network.target [Service] Type=simple User=rest-server Group=rest-server ExecStart=/usr/local/bin/rest-server --private-repos --tls --tls-cert /mnt/rest-server/<DOMAIN>/fullchain.pem --tls-key /mnt/rest-server/<DOMAIN>/key.pem --path /mnt/rest-server Restart=always RestartSec=5 # Optional security enhancements NoNewPrivileges=yes PrivateTmp=yes ProtectSystem=strict ProtectHome=yes ReadWritePaths=/mnt/rest-server [Install] WantedBy=multi-user.target
  • Restic UI - Migrate

    Restic UI
    5
    +2
    0 Stimmen
    5 BeitrÀge
    131 Aufrufe
    FrankMF
    Kurz noch was vom Restic Team https://twitter.com/resticbackup/status/1563820147995017216
  • Rest-Server v0.11.0

    Restic
    1
    0 Stimmen
    1 BeitrÀge
    120 Aufrufe
    Niemand hat geantwortet
  • PyQt5 - QThread

    Python3
    3
    0 Stimmen
    3 BeitrÀge
    219 Aufrufe
    FrankMF
    Und hier mal ein komplettes Beispiel. class Worker Wir legen den Worker an, das ist der Prozess der die Arbeit macht und etwas lĂ€nger braucht. class Worker(QObject): """ Worker Class for Rest function stats""" stats_finished = pyqtSignal(str) stats_error = pyqtSignal(str) def __init__(self): super().__init__() def run(self): # Restic function try: # long running task except Exception: # Process don't successful, send signal self.stats_error.emit(result.stderr) else: # Process successful, send signal self.stats_finished.emit(result.stdout) finally: pass in class MainWindow class MainWindow(QMainWindow): def __init__(self): super().__init__() "First Thread with documentation" # Worker for restic_stats # Create a QThread object self.thread = QThread() # Create a worker object self.worker1 = Worker() # Move worker to the thread self.worker1.moveToThread(self.thread) # Connect signals and slots self.thread.started.connect(self.worker1.run) self.worker1.stats_finished[str].connect(self.restic_stats_finished) self.worker1.stats_error[str].connect(self.restic_stats_error) "First thread end" ############################################### # Process for restic_stats is finished ############################################### @pyqtSlot(str) def restic_stats_finished(self, i): # Signal from worker thread without an error Spinner.stop(self) self.thread.quit() ############################################### # Process for restic_stats when get an error ############################################### @pyqtSlot(str) def restic_stats_error(self, i): # Signal from worker thread with an error! Spinner.stop(self) Funktion restic_stats def restic_stats(self): # we start the worker thread self.thread.start() # we start waitingspinnerwidget Spinner.start(self) Ich wollte gerade schreiben, das folgendes sehr wichtig ist self.thread.quit() da fĂ€llt mir ein Fehler auf. Kurz ausprobiert und bingo, wenn der Prozess einen Error triggert, muss natĂŒrlich auch der Prozess beendet werden. Wenn man das nicht macht, macht das Programm nicht das was es soll. Der Grund ist, das der Prozess einfach immer weiter lĂ€uft. Er MUSS beendet werden. Ich gehe dann mal in meinem Programm alles Ă€ndern Ok, jetzt geht die Funktion auch zweimal hintereinander und gibt auch ordentlich den Fehler aus. Fazit Ich habe wieder sehr viel gelernt und hoffe das ich es auch richtig verstanden habe Hoffe das es dem ein oder anderen AnfĂ€nger hilft. Und falls hier ein Profi mitliest und hier Blödsinn steht bitte ich um einen Kommentar, damit ich das Ă€ndern kann. Es steht schon genug Blödsinn im Netz
  • Python3 - HTML Text ausgeben

    Python3
    1
    0 Stimmen
    1 BeitrÀge
    144 Aufrufe
    Niemand hat geantwortet
  • Python3 - QT_QPA_PLATFORM

    Python3
    2
    +1
    0 Stimmen
    2 BeitrÀge
    353 Aufrufe
    FrankMF
    Nachdem ich ja mittlerweile schon lange weder auf Cinnamon bin, hatte ich nach einem Update von VSCodium wieder ein anderes Design. Ok, schauen wir rein. (venv) frank@frank-MS-7C37:~/restic-ui-public$ env | grep "QT" QT_ACCESSIBILITY=1 QT_QPA_PLATFORMTHEME=qt5ct Wenn ich das auf QT_QPA_PLATFORMTHEME=gnome3 Àndere, sieht es wie auf meinem Linux Mint Cinnamon Desktop aus.
  • Python3 - Globale Variablen

    Python3
    1
    0 Stimmen
    1 BeitrÀge
    138 Aufrufe
    Niemand hat geantwortet
  • Restic - Backblaze B2 Cloud Storage

    Restic
    1
    +1
    0 Stimmen
    1 BeitrÀge
    389 Aufrufe
    Niemand hat geantwortet