-
Notifications
You must be signed in to change notification settings - Fork 2.5k
Added new manim checkhealth CLI subcommand
#3299
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
27 commits
Select commit
Hold shift + click to select a range
8721bf8
added new checkhealth subcommand
behackl 4769f58
basic checkhealth tests
behackl 3599055
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] e1a888c
check -> healthcheck
behackl 5cc66ef
more helpful test output on checkhealth fail
behackl 47d7047
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] 61fdf05
callable -> Callable
behackl 10cbf35
Merge branch 'cli-checkhealth' of github.com:behackl/manim into cli-c…
behackl 814f723
fix executable check for windows
behackl 57af9da
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] 1dd987f
fixed type
behackl 9b2a533
shutil already returns .exe in case it is there
behackl f06d6b4
debug commit ...
behackl 3746b25
Merge branch 'cli-checkhealth' of github.com:behackl/manim into cli-c…
behackl d3cad81
do proper debug commit for windows ...
behackl c5996a0
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] eeb1df3
fix failing test if executable is batch file
behackl 3f4c97b
added more helpful (?) recommendations on failed tests
behackl 4c3f5a4
allow rendering ManimBanner from prerendered SVG path
behackl 85ab0dd
improved test scene, actually test text / latex
behackl 2af2fb8
added debug info on python executable
behackl fc7b76e
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] eafc57c
remove debug test again
behackl 027a756
move SCALE_FACTOR_PER_FONT_POINT to constants
behackl 238746d
access constants via module in logo.py
behackl 08ed37c
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] 558dfd8
replaced other occurrence of 48 / 960
behackl File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Empty file.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,173 @@ | ||
| """Auxiliary module for the checkhealth subcommand, contains | ||
| the actual check implementations.""" | ||
|
|
||
| from __future__ import annotations | ||
|
|
||
| import os | ||
| import shutil | ||
| import subprocess | ||
| from typing import Callable | ||
|
|
||
| from ..._config import config | ||
|
|
||
| HEALTH_CHECKS = [] | ||
|
|
||
|
|
||
| def healthcheck( | ||
| description: str, | ||
| recommendation: str, | ||
| skip_on_failed: list[Callable | str] | None = None, | ||
| post_fail_fix_hook: Callable | None = None, | ||
| ): | ||
| """Decorator used for declaring health checks. | ||
|
|
||
| This decorator attaches some data to a function, | ||
| which is then added to a list containing all checks. | ||
|
|
||
| Parameters | ||
| ---------- | ||
| description | ||
| A brief description of this check, displayed when | ||
| the checkhealth subcommand is run. | ||
| recommendation | ||
| Help text which is displayed in case the check fails. | ||
| skip_on_failed | ||
| A list of check functions which, if they fail, cause | ||
| the current check to be skipped. | ||
| post_fail_fix_hook | ||
| A function that is supposed to (interactively) help | ||
| to fix the detected problem, if possible. This is | ||
| only called upon explicit confirmation of the user. | ||
|
|
||
| Returns | ||
| ------- | ||
| A check function, as required by the checkhealth subcommand. | ||
| """ | ||
| if skip_on_failed is None: | ||
| skip_on_failed = [] | ||
| skip_on_failed = [ | ||
| skip.__name__ if callable(skip) else skip for skip in skip_on_failed | ||
| ] | ||
|
|
||
| def decorator(func): | ||
| func.description = description | ||
| func.recommendation = recommendation | ||
| func.skip_on_failed = skip_on_failed | ||
| func.post_fail_fix_hook = post_fail_fix_hook | ||
| HEALTH_CHECKS.append(func) | ||
| return func | ||
|
|
||
| return decorator | ||
|
|
||
|
|
||
| @healthcheck( | ||
| description="Checking whether manim is on your PATH", | ||
| recommendation=( | ||
| "The command <manim> is currently not on your system's PATH.\n\n" | ||
| "You can work around this by calling the manim module directly " | ||
| "via <python -m manim> instead of just <manim>.\n\n" | ||
| "To fix the PATH issue properly: " | ||
| "Usually, the Python package installer pip issues a warning " | ||
| "during the installation which contains more information. " | ||
| "Consider reinstalling manim via <pip uninstall manim> " | ||
| "followed by <pip install manim> to see the warning again, " | ||
| "then consult the internet on how to modify your system's " | ||
| "PATH variable." | ||
| ), | ||
| ) | ||
| def is_manim_on_path(): | ||
| path_to_manim = shutil.which("manim") | ||
| return path_to_manim is not None | ||
|
|
||
|
|
||
| @healthcheck( | ||
| description="Checking whether the executable belongs to manim", | ||
| recommendation=( | ||
| "The command <manim> does not belong to your installed version " | ||
| "of this library, it likely belongs to manimgl / manimlib.\n\n" | ||
| "Run manim via <python -m manim> or via <manimce>, or uninstall " | ||
| "and reinstall manim via <pip install --upgrade " | ||
| "--force-reinstall manim> to fix this." | ||
| ), | ||
| skip_on_failed=[is_manim_on_path], | ||
| ) | ||
| def is_manim_executable_associated_to_this_library(): | ||
| path_to_manim = shutil.which("manim") | ||
| with open(path_to_manim, "rb") as f: | ||
| manim_exec = f.read() | ||
|
|
||
| # first condition below corresponds to the executable being | ||
| # some sort of python script. second condition happens when | ||
| # the executable is actually a Windows batch file. | ||
| return b"manim.__main__" in manim_exec or b'"%~dp0\\manim"' in manim_exec | ||
|
|
||
|
|
||
| @healthcheck( | ||
| description="Checking whether ffmpeg is available", | ||
| recommendation=( | ||
| "Manim does not work without ffmpeg. Please follow our " | ||
| "installation instructions " | ||
| "at https://docs.manim.community/en/stable/installation.html " | ||
| "to download ffmpeg. Then, either ...\n\n" | ||
| "(a) ... make the ffmpeg executable available to your system's PATH,\n" | ||
| "(b) or, alternatively, use <manim cfg write --open> to create a " | ||
| "custom configuration and set the ffmpeg_executable variable to the " | ||
| "full absolute path to the ffmpeg executable." | ||
| ), | ||
| ) | ||
| def is_ffmpeg_available(): | ||
MrDiver marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| path_to_ffmpeg = shutil.which(config.ffmpeg_executable) | ||
| return path_to_ffmpeg is not None and os.access(path_to_ffmpeg, os.X_OK) | ||
|
|
||
|
|
||
| @healthcheck( | ||
| description="Checking whether ffmpeg is working", | ||
| recommendation=( | ||
| "Your installed version of ffmpeg does not support x264 encoding, " | ||
| "which manim requires. Please follow our installation instructions " | ||
| "at https://docs.manim.community/en/stable/installation.html " | ||
| "to download and install a newer version of ffmpeg." | ||
| ), | ||
| skip_on_failed=[is_ffmpeg_available], | ||
| ) | ||
| def is_ffmpeg_working(): | ||
| ffmpeg_version = subprocess.run( | ||
MrDiver marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| [config.ffmpeg_executable, "-version"], | ||
| stdout=subprocess.PIPE, | ||
| ).stdout.decode() | ||
| return ( | ||
| ffmpeg_version.startswith("ffmpeg version") | ||
| and "--enable-libx264" in ffmpeg_version | ||
| ) | ||
|
|
||
|
|
||
| @healthcheck( | ||
| description="Checking whether latex is available", | ||
| recommendation=( | ||
| "Manim cannot find <latex> on your system's PATH. " | ||
| "You will not be able to use Tex and MathTex mobjects " | ||
| "in your scenes.\n\n" | ||
| "Consult our installation instructions " | ||
| "at https://docs.manim.community/en/stable/installation.html " | ||
| "or search the web for instructions on how to install a " | ||
| "LaTeX distribution on your operating system." | ||
| ), | ||
| ) | ||
| def is_latex_available(): | ||
| path_to_latex = shutil.which("latex") | ||
| return path_to_latex is not None and os.access(path_to_latex, os.X_OK) | ||
|
|
||
|
|
||
| @healthcheck( | ||
| description="Checking whether dvisvgm is available", | ||
| recommendation=( | ||
| "Manim could find <latex>, but not <dvisvgm> on your system's " | ||
| "PATH. Make sure your installed LaTeX distribution comes with " | ||
| "dvisvgm and consider installing a larger distribution if it " | ||
| "does not." | ||
| ), | ||
| skip_on_failed=[is_latex_available], | ||
| ) | ||
| def is_dvisvgm_available(): | ||
| path_to_dvisvgm = shutil.which("dvisvgm") | ||
| return path_to_dvisvgm is not None and os.access(path_to_dvisvgm, os.X_OK) | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,81 @@ | ||
| """A CLI utility helping to diagnose problems with | ||
| your Manim installation. | ||
|
|
||
| """ | ||
|
|
||
| from __future__ import annotations | ||
|
|
||
| import sys | ||
|
|
||
| import click | ||
| import cloup | ||
|
|
||
| from .checks import HEALTH_CHECKS | ||
|
|
||
|
|
||
| @cloup.command( | ||
| context_settings=None, | ||
| ) | ||
| def checkhealth(): | ||
| """This subcommand checks whether Manim is installed correctly | ||
| and has access to its required (and optional) system dependencies. | ||
| """ | ||
| click.echo(f"Python executable: {sys.executable}\n") | ||
| click.echo("Checking whether your installation of Manim Community is healthy...") | ||
| failed_checks = [] | ||
|
|
||
| for check in HEALTH_CHECKS: | ||
| click.echo(f"- {check.description} ... ", nl=False) | ||
| if any( | ||
| failed_check.__name__ in check.skip_on_failed | ||
| for failed_check in failed_checks | ||
| ): | ||
| click.secho("SKIPPED", fg="blue") | ||
| continue | ||
| check_result = check() | ||
| if check_result: | ||
| click.secho("PASSED", fg="green") | ||
| else: | ||
| click.secho("FAILED", fg="red") | ||
| failed_checks.append(check) | ||
|
|
||
| click.echo() | ||
|
|
||
| if failed_checks: | ||
| click.echo( | ||
| "There are problems with your installation, " | ||
| "here are some recommendations to fix them:" | ||
| ) | ||
| for ind, failed_check in enumerate(failed_checks): | ||
| click.echo(failed_check.recommendation) | ||
| if ind + 1 < len(failed_checks): | ||
| click.confirm("Continue with next recommendation?") | ||
|
|
||
| else: # no problems detected! | ||
| click.echo("No problems detected, your installation seems healthy!") | ||
| render_test_scene = click.confirm( | ||
| "Would you like to render and preview a test scene?" | ||
| ) | ||
| if render_test_scene: | ||
| import manim as mn | ||
|
|
||
| class CheckHealthDemo(mn.Scene): | ||
| def construct(self): | ||
| banner = mn.ManimBanner().shift(mn.UP * 0.5) | ||
| self.play(banner.create()) | ||
| self.wait(0.5) | ||
| self.play(banner.expand()) | ||
| self.wait(0.5) | ||
| text_left = mn.Text("All systems operational!") | ||
| formula_right = mn.MathTex(r"\oint_{\gamma} f(z)~dz = 0") | ||
| text_tex_group = mn.VGroup(text_left, formula_right) | ||
| text_tex_group.arrange(mn.RIGHT, buff=1).next_to(banner, mn.DOWN) | ||
| self.play(mn.Write(text_tex_group)) | ||
| self.wait(0.5) | ||
| self.play( | ||
| mn.FadeOut(banner, shift=mn.UP), | ||
| mn.FadeOut(text_tex_group, shift=mn.DOWN), | ||
| ) | ||
|
|
||
| with mn.tempconfig({"preview": True, "disable_caching": True}): | ||
| CheckHealthDemo().render() |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.