Skip to content

Commit

Permalink
Improved Error in :mod:.utils.tex_file_writing (#2574)
Browse files Browse the repository at this point in the history
* Better Error and insight

* Do not use keywords as identifiers

* add_tests

* Nasty comma

* Windows does its own thing

* Use os.path.join for windows

* Do not log path

* Include Insights

* Full stop.

Co-authored-by: Darylgolden <[email protected]>

* Full stop to test data.

Co-authored-by: Darylgolden <[email protected]>
  • Loading branch information
ad-chaos and Darylgolden authored Feb 26, 2022
1 parent d202d26 commit 5b11a0e
Show file tree
Hide file tree
Showing 4 changed files with 72 additions and 33 deletions.
65 changes: 32 additions & 33 deletions manim/utils/tex_file_writing.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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):
Expand Down Expand Up @@ -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,
),
]


Expand All @@ -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)
8 changes: 8 additions & 0 deletions tests/control_data/logs_data/bad_tex_scene_BadTex.txt
Original file line number Diff line number Diff line change
@@ -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"}
9 changes: 9 additions & 0 deletions tests/test_logging/bad_tex_scene.py
Original file line number Diff line number Diff line change
@@ -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)
23 changes: 23 additions & 0 deletions tests/test_logging/test_logging.py
Original file line number Diff line number Diff line change
Expand Up @@ -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

0 comments on commit 5b11a0e

Please sign in to comment.