fix(qr): reportlab renderPM-Backend fehlte auf CI-Runner
Erster echter Test für services/qr_png.py (bisher nie getestet, auch nicht in ASSET-010) deckte auf: reportlab.graphics.renderPM braucht einen nativen Rendering-Backend, der auf Python 3.13 nicht lauffähig installiert war. Hätte auch die ASSET-010-QR-Anzeige im Ausgabe- Dialog treffen können, war nur nie bemerkt worden. Auf qrcode[pil] (reiner Python-Encoder, kein natives Backend nötig) umgestellt. label.py (PDF-Vektor-Labels) ist unverändert, nicht betroffen. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CVgbozhYmuEhiEJHffRXCV
This commit is contained in:
1 parent
777bf5eaa5
commit
f93798e3f3
3 files changed
+28
-17
No files matched your search
@@ -102,6 +102,18 @@ Build-Host, kein Dauerbetrieb der App dort (App läuft auf Endgeräten).
|
||||
Hinweis in `AdminPage.tsx` verlinkt jetzt auf `/app` statt Version/URL
|
||||
zu duplizieren. Tests: `test_app_download.py` (404 ohne version.json,
|
||||
volle URL korrekt zusammengesetzt, PNG-Response, kein Auth-Zwang).
|
||||
- **CI-Fund 2026-09-10 (echter Bug, nicht nur Testartefakt):** der erste
|
||||
QR-PNG-Test deckte auf, dass `services/qr_png.py` (bisher NIE getestet,
|
||||
auch nicht in ASSET-010) über `reportlab.graphics.renderPM` einen
|
||||
nativen Rendering-Backend braucht, der auf dem CI-Runner (Python 3.13)
|
||||
nicht lauffähig installiert war ("cannot import desired renderPM backend
|
||||
rlPyCairo") — hätte auch in Produktion die ASSET-010-QR-Anzeige im
|
||||
Ausgabe-Dialog treffen können, war nur nie bemerkt worden, weil dafür
|
||||
bislang kein Test existierte. Behoben durch Wechsel auf `qrcode[pil]`
|
||||
(reiner Python-QR-Encoder + Pillow, kein natives Backend nötig) statt
|
||||
`reportlab.graphics.renderPM` — betrifft nur die PNG-Ausgabe, die
|
||||
PDF-Vektor-Labels (`label.py`, `generiere_qr_label_pdf`) sind unverändert
|
||||
und nicht betroffen (`renderPDF.draw`, keine `renderPM`-Abhängigkeit).
|
||||
- **Bugfix 2026-09-10:** Portal-Link war fest auf `v0.5.apk` verlinkt,
|
||||
dadurch nach dem v0.6.0-Build stale (zeigte auf eine ältere Version, ohne
|
||||
dass ein Frontend-Deploy das automatisch mitbekommen hätte).
|
||||
|
||||
@@ -1,27 +1,25 @@
|
||||
import io
|
||||
|
||||
from reportlab.graphics import renderPM
|
||||
from reportlab.graphics.barcode import qr
|
||||
from reportlab.graphics.shapes import Drawing
|
||||
import qrcode
|
||||
|
||||
# CI-Fund 2026-09-10: reportlab.graphics.renderPM braucht einen nativen
|
||||
# Backend (_rl_renderPM-Extension oder rlPyCairo) - auf dem CI-Runner (Python
|
||||
# 3.13) war keins davon lauffähig installiert ("cannot import desired
|
||||
# renderPM backend rlPyCairo"), reine PDF-Vektorausgabe (label.py) war davon
|
||||
# NICHT betroffen, nur die reine PNG-Ausgabe hier. Reiner Python-QR-Encoder
|
||||
# (qrcode + Pillow, ohnehin schon transitive Abhängigkeit über
|
||||
# python-barcode[images]/label.py) umgeht das nativ kompilierte Backend
|
||||
# komplett - robuster über Python-Versionen/Plattformen hinweg.
|
||||
_ZIELGROESSE = 220
|
||||
|
||||
|
||||
def generiere_qr_png(inhalt: str) -> bytes:
|
||||
"""ASSET-010: rendert einen beliebigen Text (hier: Signier-Seiten-URL) als
|
||||
PNG - reine Bild-Ausgabe statt PDF-Label (label.py), fürs direkte Anzeigen
|
||||
im Ausgabe-Dialog des Portals."""
|
||||
qr_code = qr.QrCodeWidget(inhalt)
|
||||
bounds = qr_code.getBounds()
|
||||
breite = bounds[2] - bounds[0]
|
||||
hoehe = bounds[3] - bounds[1]
|
||||
skalierung = _ZIELGROESSE / max(breite, hoehe)
|
||||
|
||||
zeichnung = Drawing(
|
||||
_ZIELGROESSE, _ZIELGROESSE, transform=[skalierung, 0, 0, skalierung, -bounds[0] * skalierung, -bounds[1] * skalierung]
|
||||
)
|
||||
zeichnung.add(qr_code)
|
||||
"""ASSET-010/FLUT-001: rendert einen beliebigen Text als PNG - reine
|
||||
Bild-Ausgabe (kein PDF-Label wie label.py), fürs direkte Anzeigen im
|
||||
Portal oder Einbetten in eine Download-Seite."""
|
||||
bild = qrcode.make(inhalt, border=2)
|
||||
bild = bild.resize((_ZIELGROESSE, _ZIELGROESSE))
|
||||
|
||||
puffer = io.BytesIO()
|
||||
renderPM.drawToFile(zeichnung, puffer, fmt="PNG")
|
||||
bild.save(puffer, format="PNG")
|
||||
return puffer.getvalue()
|
||||
@@ -20,6 +20,7 @@ dependencies = [
|
||||
"aiosmtplib>=3.0,<3.1",
|
||||
"python-barcode[images]>=0.15,<0.16",
|
||||
"reportlab>=4.2,<4.3",
|
||||
"qrcode[pil]>=7.4,<7.5",
|
||||
"slowapi>=0.1.9,<0.2",
|
||||
"sentry-sdk[fastapi]>=2.17,<2.18",
|
||||
]
|
||||
|
||||
Reference in new issue
Block a user