Obsluhovat to waitressem, ne vývojovým serverem Flasku

Flask pod pěkně naformátovanou úvodní hlášku vysypal červené varování, že
tohle není produkční server. Waitress je čistě pythonní, takže ho PyInstaller
zabalí bez řečí, a osm vláken pobere telefony u stolu i tabuli, která se
doptává každé dvě vteřiny.

Cestou se ukázalo, že hláška se nikdy nedostala do přesměrovaného výstupu:
mimo konzoli drží Python výstup v bloku a pouští ho, až program skončí —
jenže server neskončí. Zkouška binárky u Gitey si logovala do souboru a pak
ho vypisovala, takže celou dobu tiskla prázdno. Řeší to řádkové bufferování
v console.utf8(), kam to patří vedle té kódové stránky.
This commit is contained in:
gitea-actions committed 2026-08-22 18:29:00 +02:00
1 parent e3d56f4c22
commit 33e8582079
4 files changed
+39 -5

No files matched your search

+4
View File
@@ -106,6 +106,10 @@ schématu ještě nepsala, se poznají podle nuly a orazítkují se. Databázi
založenou novější verzí počitadla appka odmítne otevřít, místo aby ji tiše
rozbila.
Obsluhuje to `waitress` — čistě pythonní WSGI server, který se zabalí do
binárky stejně snadno jako zbytek. Vestavěný server Flasku by pod úvodní
hlášku vysypal varování, že takhle se to nemá, a měl by pravdu.
Počítá se s domácí sítí — appka nemá přihlašování a kdokoli na stejné WiFi
může zapisovat.
+10 -3
View File
@@ -1,10 +1,10 @@
"""Aby čeština prošla i tam, kde konzole čeká něco jiného."""
"""Aby výstup prošel i tam, kde ho nikdo nečeká na konzoli."""
import sys
def utf8() -> None:
"""Přepne standardní výstup na UTF-8.
"""Přepne standardní výstup na UTF-8 a na řádkové bufferování.
Windows sahá po historické kódové stránce (cp1252, cp852) všude, kde
výstup není konzole — přesměrovaný do souboru, puštěný ze skriptu, ve
@@ -14,7 +14,14 @@ def utf8() -> None:
Na konzoli i na ostatních systémech je UTF-8 dávno výchozí a tohle je
tam prázdná operace. `errors="replace"` je pojistka pro zbylé případy:
rozsypaný háček je pořád lepší než spadlý server.
Řádkové bufferování řeší druhou půlku téhož problému. Mimo konzoli
Python drží výstup v bloku a pouští ho, až se naplní nebo až program
skončí — jenže server neskončí, ten běží. Kdo si ho pustí na pozadí
s výpisem do souboru, našel by tam prázdno; přesně to dělá zkouška
binárky v CI. Pár řádek hlášky za to bufferovat nemá cenu.
"""
for stream in (sys.stdout, sys.stderr):
if hasattr(stream, "reconfigure"):
stream.reconfigure(encoding="utf-8", errors="replace")
stream.reconfigure(encoding="utf-8", errors="replace",
line_buffering=True)
+18 -1
View File
@@ -9,10 +9,12 @@ class Proud:
def __init__(self):
self.encoding = "cp1252"
self.errors = "strict"
self.line_buffering = False
def reconfigure(self, encoding, errors):
def reconfigure(self, encoding, errors, line_buffering=False):
self.encoding = encoding
self.errors = errors
self.line_buffering = line_buffering
def test_vystup_se_prepne_na_utf8(monkeypatch):
@@ -24,6 +26,7 @@ def test_vystup_se_prepne_na_utf8(monkeypatch):
assert (out.encoding, out.errors) == ("utf-8", "replace")
assert (err.encoding, err.errors) == ("utf-8", "replace")
assert out.line_buffering and err.line_buffering
def test_cestina_projde_i_pres_cp1252(monkeypatch):
@@ -46,3 +49,17 @@ def test_proud_bez_reconfigure_se_prezije(monkeypatch):
monkeypatch.setattr(console.sys, "stderr", object())
console.utf8()
def test_hlaska_je_v_souboru_hned(tmp_path, monkeypatch):
"""Server běží dál, takže na vyprázdnění bufferu při konci se čekat nedá."""
cesta = tmp_path / "server.log"
with cesta.open("w", encoding="utf-8") as soubor:
monkeypatch.setattr(console.sys, "stdout", soubor)
monkeypatch.setattr(console.sys, "stderr", soubor)
console.utf8()
print("Počitadlo je dostupné na:", file=console.sys.stdout)
# Bez zavření souboru — přesně jako u běžícího serveru.
assert "Počitadlo je dostupné" in cesta.read_text(encoding="utf-8")
+7 -1
View File
@@ -17,6 +17,7 @@ import qrcode
from flask import (Flask, abort, g, redirect, render_template, request,
url_for)
from markupsafe import Markup, escape
from waitress import serve
import console
import net
@@ -353,4 +354,9 @@ if __name__ == "__main__":
sys.exit(0)
announce()
app.run(host="0.0.0.0", port=PORT, threaded=True)
# Ne vestavěný server Flasku: ten pod úvodní hlášku vysype červené
# varování, že takhle se to nemá, a má pravdu. Waitress je čistě
# pythonní, takže se zabalí do binárky bez řečí, a osm vláken pobere
# telefony u stolu i tabuli, která se doptává každé dvě vteřiny.
serve(app, host="0.0.0.0", port=PORT, threads=8)