Čeština na výpisu nesmí shodit server
binárka / linux (pull_request) Successful in 10s
testy / pytest (3.11) (pull_request) Successful in 1s
testy / pytest (3.12) (pull_request) Successful in 0s
testy / pytest (3.13) (pull_request) Successful in 0s
testy / pytest (3.14) (pull_request) Successful in 1s
binárka / linux (pull_request) Successful in 10s
testy / pytest (3.11) (pull_request) Successful in 1s
testy / pytest (3.12) (pull_request) Successful in 0s
testy / pytest (3.13) (pull_request) Successful in 0s
testy / pytest (3.14) (pull_request) Successful in 1s
Windowsová binárka spadla dřív, než stihla vypsat adresu:
print("Počitadlo je dostupné na:")
UnicodeEncodeError: 'charmap' codec can't encode character 'č'
Windows sahá po historické kódové stránce všude, kde výstup není konzole —
přesměrovaný do souboru, puštěný ze skriptu, ve frontě CI. První háček nad
takovým proudem shodí celý program.
Oba vstupní body si teď výstup přepnou na UTF-8. Na konzoli i na ostatních
systémech je to prázdná operace, protože tam je UTF-8 dávno výchozí.
Pojistka errors="replace" je pro zbytek případů: rozsypaný háček je pořád
lepší než spadlý server.
Chytila to zkouška po stavbě, ne uživatel. Test to reprodukuje přes
TextIOWrapper s cp1252, takže se to bez opravy neprojde ani na Linuxu.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
1 parent
0fd8d072ec
commit
5821385dc8
5 files changed
+75
-1
No files matched your search
@@ -131,6 +131,7 @@ při remíze na prvním místě se hraje dál.
|
|||||||
| `stats.py` | síň slávy a kariérní statistiky |
|
| `stats.py` | síň slávy a kariérní statistiky |
|
||||||
| `web.py` | Flask aplikace |
|
| `web.py` | Flask aplikace |
|
||||||
| `dice.py` | totéž v terminálu, bez ukládání |
|
| `dice.py` | totéž v terminálu, bez ukládání |
|
||||||
|
| `console.py` | aby čeština prošla i windowsovou konzolí |
|
||||||
| `build.sh`, `kostky.spec` | stavba binárky |
|
| `build.sh`, `kostky.spec` | stavba binárky |
|
||||||
| `.gitea/workflows/` | testy a linuxová binárka doma |
|
| `.gitea/workflows/` | testy a linuxová binárka doma |
|
||||||
| `.github/workflows/` | binárky pro Windows a macOS |
|
| `.github/workflows/` | binárky pro Windows a macOS |
|
||||||
@@ -216,5 +217,5 @@ uv run --with-requirements requirements.txt --no-project pytest
|
|||||||
Nebo z připraveného prostředí prostě `pytest`.
|
Nebo z připraveného prostředí prostě `pytest`.
|
||||||
|
|
||||||
Nad každým pull requestem a nad `main` běží `.gitea/workflows/testy.yml` —
|
Nad každým pull requestem a nad `main` běží `.gitea/workflows/testy.yml` —
|
||||||
99 testů na Pythonu 3.11, 3.12, 3.13 i 3.14, a k tomu skriptovaná partie
|
102 testů na Pythonu 3.11, 3.12, 3.13 i 3.14, a k tomu skriptovaná partie
|
||||||
v terminálové verzi, na kterou pytest nesahá.
|
v terminálové verzi, na kterou pytest nesahá.
|
||||||
+20
@@ -0,0 +1,20 @@
|
|||||||
|
"""Aby čeština prošla i tam, kde konzole čeká něco jiného."""
|
||||||
|
|
||||||
|
import sys
|
||||||
|
|
||||||
|
|
||||||
|
def utf8() -> None:
|
||||||
|
"""Přepne standardní výstup na UTF-8.
|
||||||
|
|
||||||
|
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
|
||||||
|
frontě CI. První háček nad takovým proudem shodí celý program, takže
|
||||||
|
binárka umřela dřív, než stihla vypsat adresu, na které poslouchá.
|
||||||
|
|
||||||
|
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.
|
||||||
|
"""
|
||||||
|
for stream in (sys.stdout, sys.stderr):
|
||||||
|
if hasattr(stream, "reconfigure"):
|
||||||
|
stream.reconfigure(encoding="utf-8", errors="replace")
|
||||||
@@ -12,6 +12,7 @@ from os import name, system
|
|||||||
|
|
||||||
from tabulate import tabulate
|
from tabulate import tabulate
|
||||||
|
|
||||||
|
import console
|
||||||
from core import FINAL_SCORE, Game
|
from core import FINAL_SCORE, Game
|
||||||
|
|
||||||
table_header = ["Hráč", "Skóre"]
|
table_header = ["Hráč", "Skóre"]
|
||||||
@@ -128,4 +129,5 @@ def main() -> None:
|
|||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
|
console.utf8()
|
||||||
main()
|
main()
|
||||||
@@ -0,0 +1,48 @@
|
|||||||
|
import io
|
||||||
|
|
||||||
|
import console
|
||||||
|
|
||||||
|
|
||||||
|
class Proud:
|
||||||
|
"""Náhrada za výstup, která si pamatuje, na co ji přepnuli."""
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
self.encoding = "cp1252"
|
||||||
|
self.errors = "strict"
|
||||||
|
|
||||||
|
def reconfigure(self, encoding, errors):
|
||||||
|
self.encoding = encoding
|
||||||
|
self.errors = errors
|
||||||
|
|
||||||
|
|
||||||
|
def test_vystup_se_prepne_na_utf8(monkeypatch):
|
||||||
|
out, err = Proud(), Proud()
|
||||||
|
monkeypatch.setattr(console.sys, "stdout", out)
|
||||||
|
monkeypatch.setattr(console.sys, "stderr", err)
|
||||||
|
|
||||||
|
console.utf8()
|
||||||
|
|
||||||
|
assert (out.encoding, out.errors) == ("utf-8", "replace")
|
||||||
|
assert (err.encoding, err.errors) == ("utf-8", "replace")
|
||||||
|
|
||||||
|
|
||||||
|
def test_cestina_projde_i_pres_cp1252(monkeypatch):
|
||||||
|
"""Přesně to, na čem umřela první windowsová binárka."""
|
||||||
|
surovy = io.BytesIO()
|
||||||
|
proud = io.TextIOWrapper(surovy, encoding="cp1252")
|
||||||
|
monkeypatch.setattr(console.sys, "stdout", proud)
|
||||||
|
monkeypatch.setattr(console.sys, "stderr", proud)
|
||||||
|
|
||||||
|
console.utf8()
|
||||||
|
print("Počitadlo je dostupné na:", file=console.sys.stdout)
|
||||||
|
console.sys.stdout.flush()
|
||||||
|
|
||||||
|
assert "Počitadlo je dostupné" in surovy.getvalue().decode("utf-8")
|
||||||
|
|
||||||
|
|
||||||
|
def test_proud_bez_reconfigure_se_prezije(monkeypatch):
|
||||||
|
"""Zabalený výstup reconfigure mít nemusí; padat kvůli tomu nesmíme."""
|
||||||
|
monkeypatch.setattr(console.sys, "stdout", object())
|
||||||
|
monkeypatch.setattr(console.sys, "stderr", object())
|
||||||
|
|
||||||
|
console.utf8()
|
||||||
@@ -18,6 +18,7 @@ from flask import (Flask, abort, g, redirect, render_template, request,
|
|||||||
url_for)
|
url_for)
|
||||||
from markupsafe import Markup, escape
|
from markupsafe import Markup, escape
|
||||||
|
|
||||||
|
import console
|
||||||
import net
|
import net
|
||||||
import stats
|
import stats
|
||||||
import storage
|
import storage
|
||||||
@@ -320,6 +321,8 @@ def hall():
|
|||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
|
console.utf8()
|
||||||
|
|
||||||
found = net.addresses()
|
found = net.addresses()
|
||||||
|
|
||||||
if found:
|
if found:
|
||||||
|
|||||||
Reference in new issue
Block a user