Jak ustawić sys.kodowanie stdout w Pythonie 3?

Ustawienie domyślnego kodowania wyjściowego w Pythonie 2 jest dobrze znanym idiomem:

sys.stdout = codecs.getwriter("utf-8")(sys.stdout)

To zawija obiekt sys.stdout do kodera kodującego wyjście w UTF-8.

Jednak ta technika nie działa w Pythonie 3, ponieważ sys.stdout.write() oczekuje str, ale wynikiem kodowania jest bytes, A błąd występuje, gdy codecs próbuje zapisać zakodowane bajty do oryginalnego sys.stdout.

Jak to zrobić w Pythonie 3?

Author: Greg Hewgill, 2010-12-07

7 answers

Od Pythona 3.7 można zmienić kodowanie standardowych strumieni za pomocą reconfigure():

sys.stdout.reconfigure(encoding='utf-8')

Możesz również zmodyfikować sposób obsługi błędów kodowania, dodając parametr errors.

 36
Author: sth,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-09-17 16:47:35

Python 3.1 dodał io.TextIOBase.detach(), z notatką w dokumentacji dla sys.stdout:

Standardowe strumienie są domyślnie w trybie tekstowym. Aby zapisać lub odczytać do nich dane binarne, użyj bazowego bufora binarnego. Na przykład, aby zapisać bajty do stdout, Użyj sys.stdout.buffer.write(b'abc'). Za pomocą io.TextIOBase.detach() strumienie mogą być domyślnie binarne. Funkcja ta ustawia stdin i stdout na binarne:

def make_streams_binary():
    sys.stdin = sys.stdin.detach()
    sys.stdout = sys.stdout.detach()

Dlatego odpowiedni idiom dla Pythona 3.1 i nowszych to:

sys.stdout = codecs.getwriter("utf-8")(sys.stdout.detach())
 44
Author: Greg Hewgill,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2020-06-20 09:12:55

Znalazłem ten wątek szukając rozwiązania tego samego błędu,

Alternatywnym rozwiązaniem dla tych już sugerowanych jest ustawienie PYTHONIOENCODING zmiennej środowiskowej przed uruchomieniem Python, do mojego użytku - jest to mniej kłopotu niż Zamiana sys.stdout po inicjalizacji Pythona:

PYTHONIOENCODING=utf-8:surrogateescape python3 somescript.py

Z tą zaletą, że nie trzeba edytować kodu Pythona.

 38
Author: ideasman42,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-09-23 05:27:10

Inne odpowiedzi wydają się zalecać użycie codecs, ale open działa dla mnie:

import sys
sys.stdout = open(sys.stdout.fileno(), mode='w', encoding='utf8', buffering=1)
print("日本語")
# Also works with other methods of writing to stdout:
sys.stdout.write("日本語\n")
sys.stdout.buffer.write("日本語\n".encode())
To działa nawet wtedy, gdy uruchamiam go z PYTHONIOENCODING="ascii".
 31
Author: Jack O'Connor,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-08-04 15:34:58

Ustawienie domyślnego kodowania wyjściowego w Pythonie 2 jest dobrze znanym idiomem

Eek! Czy to dobrze znany idiom w Pythonie 2? Dla mnie to niebezpieczna pomyłka.

To z pewnością zepsuje każdy skrypt, który próbuje napisać binarny na stdout (co będzie potrzebne, jeśli jesteś skryptem CGI zwracającym obraz, na przykład). Bajty i znaki to zupełnie inne zwierzęta; nie jest dobrym pomysłem, aby łatać interfejs, który jest określony tak, aby akceptował bajty z takim, który tylko bierze chars.

CGI i HTTP w ogóle jawnie działają z bajtami. Powinieneś wysyłać bajty tylko do sys.stdout. W Pythonie 3 oznacza to użycie sys.stdout.buffer.write do wysyłania bajtów bezpośrednio. Kodowanie zawartości strony w celu dopasowania jej parametru charset powinno być obsługiwane na wyższym poziomie w Twojej aplikacji(w przypadkach, gdy zwracasz zawartość tekstową, a nie binarną). Oznacza to również, że print nie nadaje się już do CGI.

(Aby dodać do zamieszania, cgihandler wsgiref został złamany w py3k do niedawna, co uniemożliwia wdrożenie WSGI do CGI w ten sposób. Z PEP 3333 i Python 3.2 jest to w końcu wykonalne.)

 18
Author: bobince,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-12-07 11:23:47

Użycie detach() powoduje, że interpreter wypisuje ostrzeżenie, gdy próbuje zamknąć stdout tuż przed jego zakończeniem:

Exception ignored in: <_io.TextIOWrapper mode='w' encoding='UTF-8'>
ValueError: underlying buffer has been detached

Zamiast tego, to działało dobrze dla mnie:

default_out = io.TextIOWrapper(sys.stdout.buffer, encoding='utf-8')

(i oczywiście pisanie do default_out zamiast stdout.)

 12
Author: ptomato,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-06-05 18:43:55

Sys.stdout jest w trybie tekstowym w Pythonie 3. Dlatego piszesz do niego bezpośrednio unicode, a idiom dla Pythona 2 nie jest już potrzebny.

Gdzie to by się nie udało w Pythonie 2:

>>> import sys
>>> sys.stdout.write(u"ûnicöde")
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
UnicodeEncodeError: 'ascii' codec can't encode character u'\xfb' in position 0: ordinal not in range(128)

Jednak działa po prostu dandy w Pythonie 3:

>>> import sys
>>> sys.stdout.write("Ûnicöde")
Ûnicöde7

Teraz, jeśli twój Python nie wie, jakie jest twoje kodowanie stdouts, jest to inny problem, najprawdopodobniej w kompilacji Pythona.

 8
Author: Lennart Regebro,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-12-07 09:44:02