Message-Id invalide selon RFC 2822 + encoding

Bonsoir,

Ce message pourrait faire l’objet d’une nouvelle issue mais je soupçonne fortement une régression…

En effet, il semblerait que je ne peux plus utiliser frappe.sendmail() avec un sujet qui comporte un accent.

Depuis la console, ceci fonctionne

frappe.sendmail(
    recipients=['blabla@gmail.com'],
    subject="Message sans accent",
    message="Lien",
    as_markdown=False,
    delayed=False,
)

alors que ceci génère une erreur :

frappe.sendmail(
    recipients=['blabla@gmail.com'],
    subject="Message accentué",
    message="Lien",
    as_markdown=False,
    delayed=False,
)

Voici la trace :

In [7]: frappe.sendmail(
   ...:     recipients=['blabla@gmail.com'],
   ...:     subject="Message accentué",
   ...:     message="Lien",
   ...:     as_markdown=False,
   ...:     delayed=False,
   ...: )
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
~/dokos/apps/frappe/frappe/commands/utils.py in <module>
      4     message="Lien",
      5     as_markdown=False,
----> 6     delayed=False,
      7 )

~/dokos/apps/frappe/frappe/__init__.py in sendmail(recipients, sender, subject, message, as_markdown, delayed, reference_doctype, reference_name, unsubscribe_method, unsubscribe_params, unsubscribe_message, add_unsubscribe_link, attachments, content, doctype, name, reply_to, queue_separately, cc, bcc, message_id, in_reply_to, send_after, expose_recipients, send_priority, communication, retry, now, read_receipt, is_notification, inline_images, template, args, header, print_letterhead, with_container)
    531                 send_after=send_after, expose_recipients=expose_recipients, send_priority=send_priority, queue_separately=queue_separately,
    532                 communication=communication, now=now, read_receipt=read_receipt, is_notification=is_notification,
--> 533                 inline_images=inline_images, header=header, print_letterhead=print_letterhead, with_container=with_container)
    534 
    535 whitelisted = []

~/dokos/apps/frappe/frappe/email/queue.py in send(recipients, sender, subject, message, text_content, reference_doctype, reference_name, unsubscribe_method, unsubscribe_params, unsubscribe_message, attachments, reply_to, cc, bcc, message_id, in_reply_to, send_after, expose_recipients, send_priority, communication, now, read_receipt, queue_separately, is_notification, add_unsubscribe_link, inline_images, header, print_letterhead, with_container)
    163                 header=header,
    164                 now=now,
--> 165                 print_letterhead=print_letterhead)
    166 
    167 

~/dokos/apps/frappe/frappe/email/queue.py in add(recipients, sender, subject, **kwargs)
    184                         frappe.db.commit()
    185         else:
--> 186                 email_queue = get_email_queue(recipients, sender, subject, **kwargs)
    187                 if kwargs.get('now'):
    188                         send_one(email_queue.name, now=True)

~/dokos/apps/frappe/frappe/email/queue.py in get_email_queue(recipients, sender, subject, **kwargs)
    228 
    229                 e.message_id = mail.msg_root["Message-Id"].strip(" <>")
--> 230                 e.message = cstr(mail.as_string())
    231                 e.sender = mail.sender
    232 

~/dokos/apps/frappe/frappe/email/email_body.py in as_string(self)
    246                 self.validate()
    247                 self.make()
--> 248                 return self.msg_root.as_string(policy=policy.SMTP)
    249 
    250 def get_formatted_html(subject, message, footer=None, print_html=None,

/usr/lib/python3.7/email/message.py in as_string(self, unixfrom, maxheaderlen, policy)
    156                       maxheaderlen=maxheaderlen,
    157                       policy=policy)
--> 158         g.flatten(self, unixfrom=unixfrom)
    159         return fp.getvalue()
    160 

/usr/lib/python3.7/email/generator.py in flatten(self, msg, unixfrom, linesep)
    114                     ufrom = 'From nobody ' + time.ctime(time.time())
    115                 self.write(ufrom + self._NL)
--> 116             self._write(msg)
    117         finally:
    118             self.policy = old_gen_policy

/usr/lib/python3.7/email/generator.py in _write(self, msg)
    193         meth = getattr(msg, '_write_headers', None)
    194         if meth is None:
--> 195             self._write_headers(msg)
    196         else:
    197             meth(self)

/usr/lib/python3.7/email/generator.py in _write_headers(self, msg)
    220     def _write_headers(self, msg):
    221         for h, v in msg.raw_items():
--> 222             self.write(self.policy.fold(h, v))
    223         # A blank line always separates headers from body
    224         self.write(self._NL)

/usr/lib/python3.7/email/policy.py in fold(self, name, value)
    181 
    182         """
--> 183         return self._fold(name, value, refold_binary=True)
    184 
    185     def fold_binary(self, name, value):

/usr/lib/python3.7/email/policy.py in _fold(self, name, value, refold_binary)
    203     def _fold(self, name, value, refold_binary=False):
    204         if hasattr(value, 'name'):
--> 205             return value.fold(policy=self)
    206         maxlen = self.max_line_length if self.max_line_length else float('inf')
    207         lines = value.splitlines()

/usr/lib/python3.7/email/headerregistry.py in fold(self, policy)
    256                 parser.CFWSList([parser.WhiteSpaceTerminal(' ', 'fws')]))
    257         header.append(self._parse_tree)
--> 258         return header.fold(policy=policy)
    259 
    260 

/usr/lib/python3.7/email/_header_value_parser.py in fold(self, policy)
    142 
    143     def fold(self, *, policy):
--> 144         return _refold_parse_tree(self, policy=policy)
    145 
    146     def pprint(self, indent=''):

/usr/lib/python3.7/email/_header_value_parser.py in _refold_parse_tree(parse_tree, policy)
   2650                 # combining it with previously encoded words if allowed.
   2651                 last_ew = _fold_as_ew(tstr, lines, maxlen, last_ew,
-> 2652                                       part.ew_combine_allowed, charset)
   2653             want_encoding = False
   2654             continue

/usr/lib/python3.7/email/_header_value_parser.py in _fold_as_ew(to_encode, lines, maxlen, last_ew, ew_combine_allowed, charset)
   2727             # XXX We'll get an infinite loop here if maxlen is <= 7
   2728             continue
-> 2729         first_part = to_encode[:text_space]
   2730         ew = _ew.encode(first_part, charset=encode_as)
   2731         excess = len(ew) - remaining_space

TypeError: slice indices must be integers or None or have an __index__ method

Dans le fichier apps/frappe/frappe/email/email_body.py, la ligne 248 est appelée.
Ligne modifiée dans ce commit : fix: Change encoding policy to correctly encode subject in header (67842c03) · Commits · atelierdusoleiletudvent / Dodock · GitLab

Et commit qui a été effectué suite aux échanges ci-dessus…

Pouvez-vous reproduire le bug ?