UnicodeEncodeError: 'latin-1' codec can't encode character

python | error

При запуску python-скрипта через консоль, який мав взяти дані БД з колонки де були кириличні літери виникала помилка:

UnicodeEncodeError: 'latin-1' codec can't encode character '\u041b' in position 26: ordinal not in range(256)

$ ./_plates_to_json.py
--- Logging error ---
...
Traceback (most recent call last):
  File "/home/.../cgi-bin/./_plates_to_json.py", line 83, in <module>
    f_out_sec.write(comma + '{' + f'"id":"{row.carid}",...' + '}')
UnicodeEncodeError: 'latin-1' codec can't encode character '\u041b'
in position 26: ordinal not in range(256)

⁉️Причому, цей самий скрипт працював без проблем, коли запускався як subprocess з іншого скрипта...

plates_json_maker = '/cgi-bin/_plates_to_json.py'
try:
	subpr_res = subprocess.check_output([plates_json_maker], text=True)  # text=True - повертає строку, а не байти
except subprocess.CalledProcessError as e:
	subpr_err = f'Помилка при оновленняі JSON: {e.returncode}'
	log.error(subpr_err)
else:
	log.debug('Все Ok')

Причина

В системі VDS, що використовувався, стояла локаль за замовчуванням ISO-8859-1 що і є latin-1 (про яку писалося в помилці), яка оперує лише 256 символами:

cat /etc/default/locale
#  File generated by update-locale
LANG="en_US"
LANGUAGE="en_US:"

А якщо запустити такий скрипт:

test_locale.py
#!/usr/bin/python3

import sys
import locale

print(sys.getfilesystemencoding())
print(locale.getpreferredencoding())

Він напише:

iso8859-1
ISO-8859-1

Рішення

Нема необхідності додавати і встановлювати по замовчуванню кириличну локалізацію як то uk_UA.UTF-8 (це змінить мову системи).

Щоб позбутися помилки достатньо увімкнути ту ж, en_US, але з підтримкою utf.

Перемикаємося на root: sudo -i і подивимося, які локалі в нас є:

# locale -a
C
C.UTF-8
en_AG
en_AG.utf8
en_AU.utf8
en_BW.utf8
en_CA.utf8
en_DK.utf8
en_GB.utf8
en_HK.utf8
en_IE.utf8
en_IL
en_IL.utf8
en_IN
en_IN.utf8
en_NG
en_NG.utf8
en_NZ.utf8
en_PH.utf8
en_SG.utf8
en_US
en_US.iso88591
en_US.utf8
en_ZA.utf8
en_ZM
en_ZM.utf8
en_ZW.utf8
POSIX

Так, там є en_US.utf8.

Редагуємо /etc/default/locale (додаемо .UTF-8 до LANG):

nano /etc/default/locale

щоб стало так:

#  File generated by update-locale
LANG="en_US.UTF-8"
LANGUAGE="en_US:"

Після цього треба перелогинитись. Я вийшов з сеансу SSH і зайшов знову.

Запускаю тестовий скрипт test_locale.py і бачимо, що локаль змінилася:

# було
k...@vm4781730:~$ ./test_locale.py
iso8859-1
ISO-8859-1

# стало
k...@vm4781730:~$ ./test_locale.py
utf-8
UTF-8

Після цього і основний скрипт відпрацював без проблем!

Десктопна ubuntu 22.04 (linux lite, тобто)

Задля цікавості перевірив:

$ python3 test_locale.py 
utf-8
UTF-8

$ cat /etc/default/locale
#  File generated by update-locale
LANG="uk_UA.UTF-8"

$ locale -a
C
C.utf8
en_AG
en_AG.utf8
en_AU.utf8
en_BW.utf8
en_CA.utf8
en_DK.utf8
en_GB.utf8
en_HK.utf8
en_IE.utf8
en_IL
en_IL.utf8
en_IN
en_IN.utf8
en_NG
en_NG.utf8
en_NZ.utf8
en_PH.utf8
en_SG.utf8
en_US.utf8
en_ZA.utf8
en_ZM
en_ZM.utf8
en_ZW.utf8
POSIX
uk_UA.utf8

Last updated