diff --git a/manim/utils/tex_file_writing.py b/manim/utils/tex_file_writing.py index 6987996c8b..f10ee3b512 100644 --- a/manim/utils/tex_file_writing.py +++ b/manim/utils/tex_file_writing.py @@ -84,7 +84,7 @@ def generate_tex_file(expression, environment=None, tex_template=None): result = os.path.join(tex_dir, tex_hash(output)) + ".tex" if not os.path.exists(result): - logger.info('Writing "{}" to {}'.format("".join(expression), result)) + logger.info(f"Writing {expression} to %(path)s", {"path": f"{result}"}) with open(result, "w", encoding="utf-8") as outfile: outfile.write(output) return result @@ -142,11 +142,16 @@ def tex_compilation_command(tex_compiler, output_format, tex_file, tex_dir): return " ".join(commands) -def insight_inputenc_error(match): - code_point = chr(int(match[1], 16)) +def insight_inputenc_error(matching): + code_point = chr(int(matching[1], 16)) name = unicodedata.name(code_point) - yield f"TexTemplate does not support character '{name}' (U+{match[1]})" - yield "See the documentation for manim.mobject.svg.tex_mobject for details on using a custom TexTemplate" + yield f"TexTemplate does not support character '{name}' (U+{matching[1]})." + yield "See the documentation for manim.mobject.svg.tex_mobject for details on using a custom TexTemplate." + + +def insight_package_not_found_error(matching): + yield f"You do not have package {matching[1]} installed." + yield f"Install {matching[1]} it using your LaTeX package manager, or check for typos." def compile_tex(tex_file, tex_compiler, output_format): @@ -260,6 +265,10 @@ def print_all_tex_errors(log_file, tex_compiler, tex_file): r"inputenc Error: Unicode character (?:.*) \(U\+([0-9a-fA-F]+)\)", insight_inputenc_error, ), + ( + r"LaTeX Error: File `(.*?[clsty])' not found", + insight_package_not_found_error, + ), ] @@ -286,36 +295,26 @@ def print_tex_error(tex_compilation_log, error_start_index, tex_source): if line_of_tex_error >= len(tex_source): return None - # all lines numbers containing '\begin{' or '\end{' - except the Manim added center and document tags - env_markers_indices = [ - idx - for idx, log_line in enumerate(tex_source) - if any(marker in log_line for marker in [r"\begin{", r"\end{"]) - ][2:-2] - - context = "Context of error:\n\n" - if line_of_tex_error in env_markers_indices: - context += "".join(tex_source[line_of_tex_error - 1 : line_of_tex_error + 1]) + context = ["Context of error: \n"] + if line_of_tex_error < 3: + context += tex_source[: line_of_tex_error + 3] + context[-4] = "-> " + context[-4] + elif line_of_tex_error > len(tex_source) - 3: + context += tex_source[line_of_tex_error - 1 :] + context[1] = "-> " + context[1] else: - marker_before_error = max( - idx for idx in env_markers_indices if idx < line_of_tex_error - ) - marker_after_error = min( - idx for idx in env_markers_indices if idx > line_of_tex_error - ) - context += "".join(tex_source[marker_before_error:marker_after_error]) + context += tex_source[line_of_tex_error - 3 : line_of_tex_error + 3] + context[-4] = "-> " + context[-4] + + context = "".join(context) logger.error(context) - for prog, get_insight in LATEX_ERROR_INSIGHTS: - error_end_index = [ - idx - for idx, _ in enumerate(tex_compilation_log[error_start_index:]) - if _.startswith("l.") - ][0] - match = re.search( - prog, - "".join(tex_compilation_log[error_start_index:error_end_index]), + for insights in LATEX_ERROR_INSIGHTS: + prob, get_insight = insights + matching = re.search( + prob, + "".join(tex_compilation_log[error_start_index])[2:], ) - if match is not None: - for insight in get_insight(match): + if matching is not None: + for insight in get_insight(matching): logger.info(insight) diff --git a/tests/control_data/logs_data/bad_tex_scene_BadTex.txt b/tests/control_data/logs_data/bad_tex_scene_BadTex.txt new file mode 100644 index 0000000000..2445f20db1 --- /dev/null +++ b/tests/control_data/logs_data/bad_tex_scene_BadTex.txt @@ -0,0 +1,8 @@ +{"levelname": "INFO", "module": "logger_utils", "message": "Log file will be saved in <>"} +{"levelname": "INFO", "module": "tex_file_writing", "message": "Writing \\frac{1}{0} to <>"} +{"levelname": "ERROR", "module": "tex_file_writing", "message": "LaTeX compilation error: LaTeX Error: File `notapackage.sty' not found.\n"} +{"levelname": "ERROR", "module": "tex_file_writing", "message": "Context of error: \n\\documentclass[preview]{standalone}\n-> \\usepackage{notapackage}\n\\begin{document}\n\n\\begin{center}\n"} +{"levelname": "INFO", "module": "tex_file_writing", "message": "You do not have package notapackage.sty installed."} +{"levelname": "INFO", "module": "tex_file_writing", "message": "Install notapackage.sty it using your LaTeX package manager, or check for typos."} +{"levelname": "ERROR", "module": "tex_file_writing", "message": "LaTeX compilation error: Emergency stop.\n"} +{"levelname": "ERROR", "module": "tex_file_writing", "message": "Context of error: \n\\documentclass[preview]{standalone}\n-> \\usepackage{notapackage}\n\\begin{document}\n\n\\begin{center}\n"} diff --git a/tests/test_logging/bad_tex_scene.py b/tests/test_logging/bad_tex_scene.py new file mode 100644 index 0000000000..d8ec0a286a --- /dev/null +++ b/tests/test_logging/bad_tex_scene.py @@ -0,0 +1,9 @@ +from manim import Scene, Tex, TexTemplate + + +class BadTex(Scene): + def construct(self): + tex_template = TexTemplate(preamble=r"\usepackage{notapackage}") + some_tex = r"\frac{1}{0}" + my_tex = Tex(some_tex, tex_template=tex_template) + self.add(my_tex) diff --git a/tests/test_logging/test_logging.py b/tests/test_logging/test_logging.py index f92e97ad6e..e56c01a69e 100644 --- a/tests/test_logging/test_logging.py +++ b/tests/test_logging/test_logging.py @@ -76,3 +76,26 @@ def test_error_logging(tmp_path, python_version): _, err, exitcode = capture(command) assert exitcode != 0 and len(err) > 0 + + +@logs_comparison( + "bad_tex_scene_BadTex.txt", + Path("logs/bad_tex_scene_BadTex.log"), +) +def test_tex_error_logs(tmp_path, python_version): + bad_tex_scene = os.path.join("tests", "test_logging", "bad_tex_scene.py") + command = [ + python_version, + "-m", + "manim", + "-ql", + "--log_to_file", + "-v", + "INFO", + "--media_dir", + str(tmp_path), + bad_tex_scene, + "BadTex", + ] + _, err, exitcode = capture(command) + assert exitcode != 0 and len(err) > 0