diff --git a/frappe/exceptions.py b/frappe/exceptions.py index 8dbd778a7d..f4bcb661f1 100644 --- a/frappe/exceptions.py +++ b/frappe/exceptions.py @@ -252,6 +252,10 @@ class SessionBootFailed(ValidationError): http_status_code = 500 +class PrintFormatError(ValidationError): + pass + + class TooManyWritesError(Exception): pass diff --git a/frappe/utils/error.py b/frappe/utils/error.py index 47902176ea..a3d39bbe7d 100644 --- a/frappe/utils/error.py +++ b/frappe/utils/error.py @@ -157,5 +157,5 @@ def guess_exception_source(exception: str) -> str | None: app_name = matches.group("app_name") apps[app_name] += app_priority.get(app_name, 0) - if probably_source := apps.most_common(1): + if (probably_source := apps.most_common(1)) and probably_source[0][0] != "frappe": return f"{probably_source[0][0]} (app)" diff --git a/frappe/utils/pdf.py b/frappe/utils/pdf.py index 721b061257..6751364edc 100644 --- a/frappe/utils/pdf.py +++ b/frappe/utils/pdf.py @@ -1,5 +1,6 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # License: MIT. See LICENSE +import contextlib import io import os import re @@ -43,7 +44,30 @@ def pdf_header_html(soup, head, content, styles, html_id, css): def pdf_body_html(template, args, **kwargs): - return template.render(args, filters={"len": len}) + try: + return template.render(args, filters={"len": len}) + except Exception as e: + # Guess line number ? + frappe.throw( + _("Error in print format on line {0}: {1}").format( + _guess_template_error_line_number(template), e + ), + exc=frappe.PrintFormatError, + title=_("Print Format Error"), + ) + + +def _guess_template_error_line_number(template) -> int | None: + """Guess line on which exception occured from current traceback.""" + with contextlib.suppress(Exception): + import sys + import traceback + + _, _, tb = sys.exc_info() + + for frame in reversed(traceback.extract_tb(tb)): + if template.filename in frame.filename: + return frame.lineno def pdf_footer_html(soup, head, content, styles, html_id, css):