Steuerrechnungstool/pruef_temp_merkzettel.py
TheMockTv 96671027c8 Aufraeumen nach der Pruefkette: doppelter Code an eine Stelle
Die Runde vom 07.09.2026 hat neben den zwei echten Fehlern eine Reihe
Wiederholungen und stumme Stellen gemeldet. Abgearbeitet in Etappen, jede fuer
sich pruefbar - nach jeder liefen beide Pruefketten gruen.

* Menueleiste: der Wechsel von einem Titel zum anderen brauchte ZWEI Klicks.
  Der grab_set der offenen Liste schluckt den ersten, die <Button-1>-Bindung
  des Titels feuert gar nicht. Das native tk.Menu, das diese Leiste ersetzt
  hat, wechselte schon beim blossen Ueberfahren - zwei Klicks waren also eine
  Verschlechterung gegenueber dem, was vorher da war. Die Klappliste wird
  ausserdem jetzt wie jede andere Position ueber theme.auf_bildschirm()
  begrenzt; am unteren Fensterrand lief sie vorher hinaus.
  Neu: pruef_menueleiste.py misst beides ueber echte Klickereignisse.

* %LOCALAPPDATA%/ravokk wurde an drei Stellen einzeln ausgerechnet. Jetzt
  fragen alle gemeinsam.standard_ordner(). Laufen die je auseinander, fuehren
  die beiden Programme zwei Nummernbuecher - und das faellt erst auf, wenn
  eine Rechnungsnummer zum zweiten Mal vergeben ist (§ 14 Abs. 4 Nr. 4 UStG).

* pruefe() stand 35-mal im Quelltext, in sechs Fassungen -> pruefhelfer.py.
  Die zwei Ausreisser sind mitgezogen: pruef_storno_verrechnet.py zaehlte in
  einer Zahl und konnte am Ende nicht sagen, WAS fehlschlug, pruef_bilder.py
  im Rechnungstool schrieb ein eigenes Format.

* dlg_darstellung() und die drei Handgriffe des Umschaltens standen in beiden
  app.py fast wortgleich -> ThemeMixin in theme.py. Beide trugen inzwischen
  denselben langen Kommentar zu demselben Fehler; das war der Beweis, dass es
  eine Sache ist. Programmspezifisch bleiben der Hinweistext im Dialog (R31)
  und die Frage, ob die Einstellungen ausdruecklich gespeichert werden muessen.

* pruef_gemeinsam_automatisch.py sagte im Text "genau ein Buch" und prueft
  ">= 1" mit einem any() - eine Pruefung, die nicht rot werden kann. Jetzt
  == 1, und der Dateiname wird mitgeprueft.
  Dazu neu: die neun Dateien, die im Kopf zusagen, sie laegen in beiden
  Programmen gleich, werden byteweise verglichen. Bei hinweis.py stimmte die
  Zusage seit dem 06.09. nicht mehr - gleicher Inhalt, CRLF gegen LF.

* .gitattributes, damit die Zeilenenden nicht von der Maschine abhaengen.
  Ohne das meldet genau diese Pruefung nach einem frischen Checkout einen
  Unterschied, den es im Repository gar nicht gibt.

* Die stummen "except OSError" in api.py, updater.py und einzelinstanz.py
  sagen jetzt, warum sie schweigen duerfen. Die stummen tk.TclError bleiben:
  dort wird ein Widget angefasst, das gerade zerstoert wurde, und eine Meldung
  waere Rauschen.

* Tote Parameter und Variablen aus dem Umbau des Vortags: symbol_setzen(dunkel=),
  kachel(grund=, radius=), mit_trennlinie, sechs x/y-Berechnungen, die von
  theme.mittig() sofort ueberschrieben wurden, vier lokale "import ctypes".

39 Pruefstaende, 0 rot.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01V4t48uxDok5rJXhC9bX1Ax
2026-09-07 18:39:03 +02:00

101 lines
4.1 KiB
Python

# -*- coding: utf-8 -*-
"""Pruefstand: ein Merkzettel in den Temp-Ordner wird verworfen.
DER FALL, DER DAZU GEFUEHRT HAT (07.09.2026)
--------------------------------------------
Die installierte Fassung fuehrte ihr Journal in
`%TEMP%/abnahme_bst/Daten` - haengengeblieben aus einem Abnahmelauf vom 03.09.
Windows raeumt den Temp-Ordner von selbst auf (Datentraegerbereinigung,
Speicheroptimierung); das Journal waere ohne Vorwarnung verschwunden. Es geht
um aufbewahrungspflichtige Aufzeichnungen, zehn Jahre (§ 147 AO).
Ich habe daraufhin `_ist_temp()` geschrieben, ausfuehrlich begruendet - und
NICHT ANGESCHLOSSEN. Die Funktion stand da und wurde von niemandem gerufen;
der Schutz war ein Kommentar, keine Massnahme. Zwei Pruefagenten haben das
unabhaengig voneinander gefunden, einer davon mit einer Messung, die zeigte,
dass das Journal weiterhin im Temp landet.
⚠️ Das ist die schlimmste Sorte Fehler: ein Kommentar, der eine Schutzmassnahme
zusichert, die es nicht gibt. Wer die Datei liest, haelt die Falle fuer
geschlossen. Deshalb dieser Pruefstand - er prueft nicht, ob die FUNKTION
existiert, sondern ob sie WIRKT.
Angefasst wird nichts Echtes: LOCALAPPDATA zeigt waehrend des Laufs auf einen
eigenen Ordner, der danach wieder verschwindet.
⚠️ Dieser Ordner liegt AUSSERHALB des Temp-Ordners - im ersten Anlauf hatte
ich ihn mit tempfile.mkdtemp() angelegt, und der liegt selbst im Temp. Dann
erkennt _ist_temp auch die Vergleichspfade als Temp und der Pruefstand meldet
Fehler, die es nicht gibt. Wer das Verhalten IM Temp-Ordner prueft, darf seine
Spielwiese nicht dort aufbauen.
"""
import importlib
import os
import shutil
import sys
import tempfile
HIER = os.path.dirname(os.path.abspath(__file__))
sys.path.insert(0, HIER)
echtes_lad = os.environ.get("LOCALAPPDATA", "")
spielwiese = os.path.join(os.path.expanduser("~"), ".pruef_merkzettel")
shutil.rmtree(spielwiese, ignore_errors=True)
os.makedirs(spielwiese, exist_ok=True)
os.environ["LOCALAPPDATA"] = spielwiese
# Als EXE tun, sonst nimmt ablage.py den Quellordner und der Merkzettel
# spielt gar keine Rolle.
sys.frozen = True
sys.argv = [os.path.join(spielwiese, "Steuerrechnungstool.exe")]
import ablage # noqa: E402
from pruefhelfer import fehler, pruefe
importlib.reload(ablage)
# ---- 1. Die Funktion erkennt einen Temp-Pfad ---------------------------
temp = tempfile.gettempdir()
pruefe(ablage._ist_temp(os.path.join(temp, "abnahme_bst", "Daten")),
"ein Pfad im Temp-Ordner wird als solcher erkannt")
pruefe(not ablage._ist_temp(os.path.join(spielwiese, "ravokk")),
"ein normaler Pfad nicht")
# ---- 2. Und sie WIRKT - das ist der Punkt ------------------------------
# Genau so sah der Merkzettel bei ihm aus.
falscher = os.path.join(temp, "abnahme_bst_pruefstand", "Daten")
os.makedirs(falscher, exist_ok=True)
zeiger = ablage._zeiger_datei()
os.makedirs(os.path.dirname(zeiger), exist_ok=True)
with open(zeiger, "w", encoding="utf-8") as f:
f.write(falscher)
gewaehlt = ablage.daten_ordner()
print(f" (Merkzettel zeigte auf {falscher})")
print(f" (gewaehlt wurde {gewaehlt})")
pruefe(not ablage._ist_temp(gewaehlt),
f"der Datenordner liegt NICHT im Temp-Ordner ({gewaehlt})")
pruefe(os.path.normcase(gewaehlt) != os.path.normcase(falscher),
"dem Merkzettel wurde nicht gefolgt")
pruefe("ravokk" in gewaehlt.lower(),
f"stattdessen der Ordner der Firma ({gewaehlt})")
# ---- 3. Ein GUELTIGER Merkzettel wird weiter befolgt -------------------
# Der Schutz darf nicht dazu fuehren, dass jemand seinen bewusst gewaehlten
# Ordner verliert - etwa ein Netzlaufwerk.
gewollt = os.path.join(spielwiese, "woanders", "Daten")
os.makedirs(gewollt, exist_ok=True)
with open(zeiger, "w", encoding="utf-8") as f:
f.write(gewollt)
pruefe(os.path.normcase(ablage.daten_ordner()) == os.path.normcase(gewollt),
"ein gueltiger Merkzettel wird weiter befolgt")
os.environ["LOCALAPPDATA"] = echtes_lad
shutil.rmtree(spielwiese, ignore_errors=True)
shutil.rmtree(os.path.dirname(falscher), ignore_errors=True)
if fehler:
print(f"ROT | {len(fehler)} Punkt(e) nicht erfuellt")
sys.exit(1)
print("GRUEN | Der Temp-Schutz ist angeschlossen und wirkt")