Lazy import html2text and weasyprint

This commit is contained in:
Cédric Krier
2025-12-11 17:57:01 +01:00
parent b034e23e91
commit 2989e66585
7 changed files with 37 additions and 60 deletions
@@ -11,12 +11,6 @@ from urllib.parse import (
parse_qsl, quote, urlencode, urljoin, urlsplit, urlunsplit)
from dateutil.relativedelta import relativedelta
try:
import html2text
except ImportError:
html2text = None
from genshi.core import END, START, Attrs, QName
from genshi.template import MarkupTemplate
from genshi.template import TemplateError as GenshiTemplateError
@@ -31,7 +25,7 @@ from trytond.model import (
fields)
from trytond.pool import Pool
from trytond.pyson import Eval, If, PYSONDecoder, TimeDelta
from trytond.report import Report
from trytond.report import Report, html_to_text
from trytond.sendmail import SMTPDataManager, send_message_transactional
from trytond.tools import grouped_slice, pairwise_longest, reduce_ids
from trytond.tools.chart import sparkline
@@ -750,9 +744,7 @@ class Activity(
set_from_header(msg, from_, sender or from_)
msg['To'] = to
msg['Subject'] = subject
if html2text:
converter = html2text.HTML2Text()
content_text = converter.handle(content)
if content_text := html_to_text(content):
msg.add_alternative(content_text, subtype='plain')
if msg.is_multipart():
msg.add_alternative(content, subtype='html')
+2 -9
View File
@@ -14,11 +14,6 @@ from genshi.template import TemplateError as GenshiTemplateError
from sql import Literal
from sql.aggregate import Count
try:
import html2text
except ImportError:
html2text = None
import trytond.config as config
from trytond.i18n import gettext
from trytond.ir.session import token_hex
@@ -26,7 +21,7 @@ from trytond.model import (
DeactivableMixin, Index, ModelSQL, ModelView, Unique, Workflow, fields)
from trytond.pool import Pool
from trytond.pyson import Eval
from trytond.report import Report, get_email
from trytond.report import Report, get_email, html_to_text
from trytond.sendmail import SMTPDataManager, send_message_transactional
from trytond.tools import grouped_slice, reduce_ids
from trytond.tools.email_ import (
@@ -506,9 +501,7 @@ class Message(Workflow, ModelSQL, ModelView):
msg['List-Unsubscribe'] = (
f'<{email.get_email_unsubscribe_url()}>')
msg['List-Unsubscribe-Post'] = 'List-Unsubscribe=One-Click'
if html2text:
converter = html2text.HTML2Text()
content_text = converter.handle(content)
if content_text := html_to_text(content):
msg.add_alternative(content_text, subtype='plain')
if msg.is_multipart():
msg.add_alternative(content, subtype='html')
+7
View File
@@ -147,3 +147,10 @@ Email reports must have ``HTML`` as output format.
.. warning::
It may return the content unconverted if some dependencies are missing.
.. function:: html_to_text(content):
Converts ``HTML`` content to plain text.
.. warning::
It may return ``None`` if some dependencies are missing.
+8 -14
View File
@@ -6,10 +6,6 @@ import re
from email.message import EmailMessage
from email.utils import getaddresses
try:
import html2text
except ImportError:
html2text = None
from genshi.template import TextTemplate
import trytond.config as config
@@ -18,7 +14,7 @@ from trytond.model import EvalEnvironment, ModelSQL, ModelView, fields
from trytond.model.exceptions import AccessError, ValidationError
from trytond.pool import Pool
from trytond.pyson import Bool, Eval, PYSONDecoder
from trytond.report import Report
from trytond.report import Report, html_to_text
from trytond.rpc import RPC
from trytond.sendmail import send_message_transactional
from trytond.tools import escape_wildcard, slugify
@@ -107,16 +103,14 @@ class Email(ResourceAccessMixin, ModelSQL, ModelView):
'body': body,
'signature': user.signature or '',
}
if html2text:
body_text = HTML_EMAIL % {
'subject': subject,
'body': body,
'signature': '',
}
converter = html2text.HTML2Text()
body_text = converter.handle(body_text)
body_text = HTML_EMAIL % {
'subject': subject,
'body': body,
'signature': '',
}
if body_text := html_to_text(body_text):
if user.signature:
body_text += '\n-- \n' + converter.handle(user.signature)
body_text += '\n-- \n' + html_to_text(user.signature)
msg.add_alternative(body_text, subtype='plain')
if msg.is_multipart():
msg.add_alternative(body_html, subtype='html')
+2 -2
View File
@@ -1,5 +1,5 @@
# This file is part of Tryton. The COPYRIGHT file at the top level of
# this repository contains the full copyright notices and license terms.
from .report import Report, get_email, mjml_to_html
from .report import Report, get_email, html_to_text, mjml_to_html
__all__ = [Report, get_email, mjml_to_html]
__all__ = [Report, get_email, html_to_text, mjml_to_html]
+13 -22
View File
@@ -21,17 +21,6 @@ from io import BytesIO
from itertools import groupby
import dateutil.tz
try:
import html2text
except ImportError:
html2text = None
try:
import weasyprint
except ImportError:
weasyprint = None
from genshi.filters import Translator
from genshi.template.text import TextTemplate
@@ -422,10 +411,10 @@ class Report(URLMixin, PoolBase):
input_format = report.template_extension
output_format = report.extension or report.template_extension
if (weasyprint
and input_format in {'html', 'xhtml'}
if (input_format in {'html', 'xhtml'}
and output_format == 'pdf'):
return output_format, weasyprint.HTML(string=data).write_pdf()
if weasyprint := _lazy_import('weasyprint'):
return output_format, weasyprint.HTML(string=data).write_pdf()
if (input_format == 'xml'
and output_format == 'html'
@@ -612,7 +601,6 @@ def get_email(report, record, languages):
else:
report_name = report
Report_ = pool.get(report_name, type='report')
converter = None
title = None
msg = EmailMessage()
header_factory = msg.policy.header_factory
@@ -631,13 +619,11 @@ def get_email(report, record, languages):
break
else:
maintype, subtype = 'application', ext
if maintype == 'text' and subtype == 'html' and html2text:
if not converter:
converter = html2text.HTML2Text()
content_text = converter.handle(content)
msg.add_alternative(content_text, subtype='plain', headers=[
header_factory('Content-Language', language.code),
])
if maintype == 'text' and subtype == 'html':
if content_text := html_to_text(content):
msg.add_alternative(content_text, subtype='plain', headers=[
header_factory('Content-Language', language.code),
])
types = {
'subtype': subtype,
}
@@ -663,3 +649,8 @@ def mjml_to_html(content):
if mrml := _lazy_import('mrml'):
content = mrml.to_html(content).content
return content
def html_to_text(content):
if html2text := _lazy_import('html2text'):
return html2text.HTML2Text().handle(content)
+3 -3
View File
@@ -136,9 +136,9 @@ class ReportTestCase(TestCase):
'<!doctype html><title>Test</title>')
@with_transaction()
@patch('trytond.report.report.html2text')
def test_get_email_html_without_html2text(self, html2text):
html2text.__bool__.side_effect = lambda: False
@patch('trytond.report.report.html_to_text')
def test_get_email_html_without_html2text(self, html_to_text):
html_to_text.side_effect = lambda content: None
class FakeReport(Report):
@classmethod