Repository: ManimCommunity/manim
Pull request: Improved Error in :mod:`.utils.tex_file_writing`
## Overview: What does this pull request change?
This removes the infamous `max() arg is an empty sequence` error
Added an insight for when a package is not installed
Fixes #2360

## Further Information and Comments
<!-- If applicable, put further comments for the reviewers here. -->
I am not sure if
> Install it using your LaTeX package manager

is a helpful enough insight. Suggestions Welcome!

<!-- Thank you again for contributing! Do not modify the lines below, they are for reviewers. -->
## Reviewer Checklist
- [ ] The PR title is descriptive enough for the changelog, and the PR is labeled correctly
- [ ] If applicable: newly added non-private functions and classes have a docstring including a short summary and a PARAMETERS section
- [ ] If applicable: newly added functions and classes are tested


Problem statement:
Tex value error when handling compilation issue
## Description of bug / unexpected behavior
<!-- Add a clear and concise description of the problem you encountered. -->

I had some missing tex dependencies that are resolved now, but when manim was gathering the details to report the error it failed as `env_markers_indices` in `tex_file_writing` > `print_tex_error` method was empty. It instead through the value error below in the logs that creates some noise hiding the real issue. 

## Expected behavior
<!-- Add a clear and concise description of what you expected to happen. -->

Expected behaviour is that it should be able to handle empty `env_markers_indices` if there are cases where it will be empty and report the reason for the latex compilation error and not throw a ValueError

## How to reproduce the issue
<!-- Provide a piece of code illustrating the undesired behavior. -->

In my case I was missing latex dependencies that triggered this issue in ubuntu and ran the test

```
pytest manim/tests/test_graphical_units/test_axes.py::test_axes
```

<details><summary>Code for reproducing the problem</summary>

```py
Paste your code here.
```

</details>


## Additional media files
<!-- Paste in the files manim produced on rendering the code above. -->

<details><summary>Images/GIFs</summary>

<!-- PASTE MEDIA HERE -->

</details>


## Logs
<details><summary>Terminal output</summary>
<!-- Add "-v DEBUG" when calling manim to generate more detailed logs -->

```
[gw0] [100%] FAILED test_axes.py::test_axes (1, 480, 854, 4)
[12/04/21 08:58:14] INFO     Writing "x" to /tmp/pytest-o tex_file_writing.py:85
                             f-ryan/pytest-32/popen-gw0/t                       
                             est_axes0/049324c46070fc62.t                       
                             ex                                                 
                    ERROR    LaTeX compilation error:    tex_file_writing.py:265
                             LaTeX Error: File                                  
                             `standalone.cls' not found.                        
                                                                                

tests/test_graphical_units/test_axes.py:6 (test_axes)
scene = <manim.utils.testing._test_class_makers._make_test_scene_class.<locals>._TestedScene object at 0x7f4b94ce2f40>

    @frames_comparison
    def test_axes(scene):
        graph = Axes(
            x_range=[-10, 10, 1],
            y_range=[-10, 10, 1],
            x_length=6,
            y_length=6,
            color=WHITE,
        )
>       labels = graph.get_axis_labels()

test_axes.py:16: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
../../manim/mobject/coordinate_systems.py:437: in get_axis_labels
    self.get_x_axis_label(x_label),
../../manim/mobject/coordinate_systems.py:291: in get_x_axis_label
    return self._get_axis_label(
../../manim/mobject/coordinate_systems.py:391: in _get_axis_label
    label = self._create_label_tex(label)
../../manim/mobject/coordinate_systems.py:359: in _create_label_tex
    label_tex = MathTex(label_tex)
../../manim/mobject/svg/tex_mobject.py:304: in __init__
    raise compilation_error
../../manim/mobject/svg/tex_mobject.py:283: in __init__
    super().__init__(
../../manim/mobject/svg/tex_mobject.py:90: in __init__
    file_name = tex_to_svg_file(
../../manim/utils/tex_file_writing.py:46: in tex_to_svg_file
    dvi_file = compile_tex(
../../manim/utils/tex_file_writing.py:181: in compile_tex
    print_all_tex_errors(log_file, tex_compiler, tex_file)
../../manim/utils/tex_file_writing.py:253: in print_all_tex_errors
    print_tex_error(tex_compilation_log, error_index, tex)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

tex_compilation_log = ['This is pdfTeX, Version 3.14159265-2.6-1.40.21 (TeX Live 2020/Debian) (preloaded format=latex 2021.11.25)  4 DEC 202.../test_axes0/049324c46070fc62.tex\n', '(/tmp/pytest-of-ryan/pytest-32/popen-gw0/test_axes0/049324c46070fc62.tex\n', ...]
error_start_index = 9
tex_source = ['\\documentclass[preview]{standalone}\n', '\n', '\\usepackage[english]{babel}\n', '\\usepackage{amsmath}\n', '\\usepackage{amssymb}\n', '\n', ...]

    def print_tex_error(tex_compilation_log, error_start_index, tex_source):
        logger.error(
            f"LaTeX compilation error: {tex_compilation_log[error_start_index][2:]}",
        )
    
        # TeX errors eventually contain a line beginning 'l.xxx` where xxx is the line number that caused the compilation
        # failure. This code finds the next such line after the error current error message
        line_of_tex_error = (
            int(
                [
                    log_line
                    for log_line in tex_compilation_log[error_start_index:]
                    if log_line.startswith("l.")
                ][0]
                .split(" ")[0]
                .split(".")[1],
            )
            - 1
        )
        # our tex error may be on a line outside our user input because of post-processing
        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])
        else:
>           marker_before_error = max(
                idx for idx in env_markers_indices if idx < line_of_tex_error
            )
E           ValueError: max() arg is an empty sequence

../../manim/utils/tex_file_writing.py:298: ValueError








Coverage.py warning: Module manim was previously imported, but not measured (module-not-measured)


============================== 1 failed in 2.80s ===============================
Manim Community v0.11.0


Process finished with exit code 1
```

<!-- Insert screenshots here (only when absolutely necessary, we prefer copy/pasted output!) -->

</details>


## System specifications

<details><summary>System Details</summary>

- OS (with version, e.g Windows 10 v2004 or macOS 10.15 (Catalina)): Ubuntu 21.10
- RAM: 16gb
- Python version (`python/py/python3 --version`): 3.9.7
- Installed modules (provide output from `pip list`):
```
PASTE HERE
```
</details>

<details><summary>LaTeX details</summary>

+ LaTeX distribution (e.g. TeX Live 2020): Version 3.14159265 (TeX Live 2020/Debian) (preloaded format=tex)
+ Installed LaTeX packages:
<!-- output of `tlmgr list --only-installed` for TeX Live or a screenshot of the Packages page for MikTeX -->
</details>

<details><summary>FFMPEG</summary>

Output of `ffmpeg -version`:

```
PASTE HERE
```
</details>

## Additional comments
<!-- Add further context that you think might be relevant for this issue here. -->

Hints from issue discussion:
This is one of the most-reported errors in our Discord, it would be great to improve the error message.

Commit message: Better Error and insight

Patch to review:
diff --git a/manim/utils/tex_file_writing.py b/manim/utils/tex_file_writing.py
index 6987996c8b..2da7276265 100644
--- a/manim/utils/tex_file_writing.py
+++ b/manim/utils/tex_file_writing.py
@@ -149,6 +149,11 @@ def insight_inputenc_error(match):
     yield "See the documentation for manim.mobject.svg.tex_mobject for details on using a custom TexTemplate"
 
 
+def insight_package_not_found_error(match):
+    yield f"You do not have package {match[1]} installed"
+    yield "Install it using your LaTeX package manager"
+
+
 def compile_tex(tex_file, tex_compiler, output_format):
     """Compiles a tex_file into a .dvi or a .xdv or a .pdf
 
@@ -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 `(.*?\.cls)' not found",
+        insight_package_not_found_error,
+    ),
 ]
 
 
@@ -286,35 +295,24 @@ 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]),
+            "".join(tex_compilation_log[error_start_index])[2:],
         )
         if match is not None:
             for insight in get_insight(match):


Code review comments:
diff_hunk: @@ -149,6 +149,11 @@ def insight_inputenc_error(match):
     yield "See the documentation for manim.mobject.svg.tex_mobject for details on using a custom TexTemplate"
 
 
+def insight_package_not_found_error(match):
+    yield f"You do not have package {match[1]} installed"
path: manim/utils/tex_file_writing.py
text: @user1:
```suggestion
    yield f"You do not have package {match[1]} installed."
```

diff_hunk: @@ -149,6 +149,11 @@ def insight_inputenc_error(match):
     yield "See the documentation for manim.mobject.svg.tex_mobject for details on using a custom TexTemplate"
 
 
+def insight_package_not_found_error(match):
+    yield f"You do not have package {match[1]} installed"
+    yield "Install it using your LaTeX package manager"
path: manim/utils/tex_file_writing.py
text: @user1:
```suggestion
    yield "Install it using your LaTeX package manager."
```