LINUX.ORG.RU

Python 3 и locale

 , , , ,


0

2

Задача - парсить письма приходящие на определенный электронный адрес.

Скрипт написал, вручную пашет - парсит файл письма без проблем.

В постфиксе external-pipe настроил.

В админке iRedMail к определнному адресу прикрутил - все работает.

Но тут вылазит проблема: Невозможно сохранить вложения с кириллическими именами

UnicodeEncodeError: 'ascii' codec can't encode character '\u0424' in position 7: ordinal not in range(128)

А вручную-то пашет!

Нашел косяк, что при запуске скрипта от пользователя vmail локаль не задана. Глобально задать для него никак не получилось. Написал wrapper.sh, вида:

LANG=ru_RU.UTF-8 /opt/python3.5/bin/python3.5 mail_robot.py <&0
Файлы с кириллическими именами теперь создаются, но теперь перестал stdin для сообщений в кодировке, отличной от utf-8, парситься:
UnicodeDecodeError: 'utf-8' codec can't decode byte 0xe9 in position 2024: invalid continuation byte
А без задания локали пашет!

Как победить такую лажу?



Последнее исправление: cetjs2 (всего исправлений: 1)

Ответ на: комментарий от proud_anon

В первом случае:

with open(filename, 'wb') as file:
Во втором случае:
msg = email.message_from_file(sys.stdin)
Причем, если указать правильную кодировку для stdin, типа
io.TextIOWrapper(sys.stdin.buffer, encoding='utf-8')
то все пашет. У меня пока только грязные хаки на ум приходят: в первом варианте - транслитерировать имена файлов во втором варианте - пытаться определить кодировку текста перед загрузкой

javum
() автор топика
Ответ на: комментарий от javum

У меня пока только грязные хаки на ум приходят: в первом варианте - транслитерировать имена файлов

Ну ведь имя файла [EDIT: имя, не содержимое, хотя и содержимое тоже] может быть произвольной последовательностью байт. Если os.fsencode не работает (что при неправильно выставленной локали может быть), то предварительно сделай filename = bytes(filename, 'UTF-8') (или наоборот, filename = str(filename, 'UTF-8'), или как тебе надо. Если уже есть готовые имена файлов в виде bytes, то их и используй.

во втором варианте - пытаться определить кодировку текста перед загрузкой

Если у тебя данные в чёрт знает какой кодировке, то зачем ты с ними работаешь, как с текстом? Работай с потоками байт. Или невозможно?

Вообще говоря, что именно ты делаешь с этими текстами?

proud_anon ★★★★★
()
Последнее исправление: proud_anon (всего исправлений: 4)
Ответ на: комментарий от proud_anon

Ну ведь имя файла [EDIT: имя, не содержимое, хотя и содержимое тоже] может быть произвольной последовательностью байт.

Чисто да, но питон хочет str. И в моем случае filename - это str в юникоде. Если ее конвертить в байты, то питон ругается

TypeError: Can't convert 'bytes' object to str implicitly
при открытии файла.

Отследил кодировку stdin - ANSI_X3.4-1968. Думаю ее можно попробовать указать для io.TextIOWrapper. Но непонятно, насколько это корректно.

А смысл всей затеи в том, что на определенный ящик приходят письма, и в зависимости от наличия определенных слов в теме нужно текст письма загрузить в базу, вложения положить в определенное место.

Скрипт на основе примера: https://docs.python.org/3/library/email-examples.html (Here’s an example of how to unpack a MIME message like the one above, into a directory of files:)

Текст простого письма в KOI8-R c вложением: http://www.fast-files.com/getfile.aspx?file=101178

javum
() автор топика
Ответ на: комментарий от javum

Чисто да, но питон хочет str.

Странно. У меня open прекрасно работает, когда имя файла имеет тип bytes.

И в моем случае filename - это str в юникоде.

Ну тогда всё и так должно работать, странно.

Текст простого письма в KOI8-R c вложением: http://www.fast-files.com/getfile.aspx?file=101178

Попробуй скормить его как есть пакету email при помощи email.message_from_binary_file(sys.stdin.buffer) Ведь в письме указана его кодировка, пакет email должен это сам обработать.

proud_anon ★★★★★
()
Ответ на: комментарий от proud_anon

Хотя стоп, у меня почему-то даже email.message_from_binary_file какие-то ошибки на твоём примере выдаёт.

proud_anon ★★★★★
()
Ответ на: комментарий от proud_anon

Странно. У меня open прекрасно работает, когда имя файла имеет тип bytes.

Разобрался, это я дурак. Пытался имя в bytes объединить с путем в str. На самом деле все работает!

Попробуй скормить его как есть пакету email при помощи email.message_from_binary_file(sys.stdin.buffer)

И так тоже все работает!

Спасибо тебе, добрый человек! Всегда приятно поговорить с умными людьми!

javum
() автор топика
Ответ на: комментарий от javum

Да, действительно, ошибки у меня происходили, потому что я ступил. У меня получилось написать работающий скрипт, не знаю, нужно тебе, или уже нет:

#!/usr/bin/env python3
"""
Прочитать e-mail со стандартного ввода и вывести его тему.

ПРИМЕЧАНИЕ: если тело письма вообще не нужно, можно
использовать email.Parser.BytesParser.parse(headersonly=True)
"""
import email
import email.header
import sys

msg = email.message_from_binary_file(sys.stdin.buffer)
subject = msg['Subject']
if subject is None:
    print('У письма нет темы', file=sys.stderr)
    sys.exit(1)
subject_parts = email.header.decode_header(subject)
subject = []
for decoded_string, charset in subject_parts:
    if charset is None:
        charset = 'ascii'
    subject.append(str(decoded_string, charset, 'replace'))
print(''.join(subject))

proud_anon ★★★★★
()
Ответ на: комментарий от proud_anon

Только если не уметь его готовить.

Отличная отговорка PHPшников.

A1
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.