diff --git a/manimlib/__init__.py b/manimlib/__init__.py index b7262f2f..d7457f7c 100644 --- a/manimlib/__init__.py +++ b/manimlib/__init__.py @@ -1,3 +1,7 @@ +import pkg_resources + +__version__ = pkg_resources.get_distribution("manimgl").version + from manimlib.constants import * from manimlib.animation.animation import * diff --git a/manimlib/__main__.py b/manimlib/__main__.py index 37264645..82e9b38f 100644 --- a/manimlib/__main__.py +++ b/manimlib/__main__.py @@ -2,10 +2,15 @@ import manimlib.config import manimlib.extract_scene import manimlib.utils.init_config +from manimlib import __version__ def main(): args = manimlib.config.parse_cli() + + print(f"ManimGL \033[32mv{__version__}\033[0m") + if args.version and args.file == None: + return if args.config: manimlib.utils.init_config.init_customization() diff --git a/manimlib/config.py b/manimlib/config.py index e307ac6e..636adb71 100644 --- a/manimlib/config.py +++ b/manimlib/config.py @@ -9,6 +9,7 @@ from screeninfo import get_monitors from manimlib.utils.config_ops import merge_dicts_recursively from manimlib.utils.init_config import init_customization +from manimlib.logger import log def parse_cli(): @@ -136,10 +137,15 @@ def parse_cli(): "--config_file", help="Path to the custom configuration file", ) + parser.add_argument( + "-v", "--version", + action="store_true", + help="Display the version of manimgl" + ) args = parser.parse_args() return args except argparse.ArgumentError as err: - print(str(err)) + log.error(str(err)) sys.exit(2) @@ -185,36 +191,45 @@ def get_custom_config(): return config +def check_temporary_storage(config): + if config["directories"]["temporary_storage"] == "" and sys.platform == "win32": + log.warning("You may be using Windows platform and have not specified the `temporary\ +_storage` path, which may cause OSError. So it is recommended that you specify the `temporary\ +_storage` in the config file (.yml)") + + def get_configuration(args): global __config_file__ # ensure __config_file__ always exists if args.config_file is not None: if not os.path.exists(args.config_file): - print(f"Can't find {args.config_file}.") + log.error(f"Can't find {args.config_file}.") if sys.platform == 'win32': - print(f"Copying default configuration file to {args.config_file}...") + log.info(f"Copying default configuration file to {args.config_file}...") os.system(f"copy default_config.yml {args.config_file}") elif sys.platform in ["linux2", "darwin"]: - print(f"Copying default configuration file to {args.config_file}...") + log.info(f"Copying default configuration file to {args.config_file}...") os.system(f"cp default_config.yml {args.config_file}") else: - print("Please create the configuration file manually.") - print("Read configuration from default_config.yml.") + log.info("Please create the configuration file manually.") + log.info("Read configuration from default_config.yml.") else: __config_file__ = args.config_file global_defaults_file = os.path.join(get_manim_dir(), "manimlib", "default_config.yml") if not (os.path.exists(global_defaults_file) or os.path.exists(__config_file__)): - print("There is no configuration file detected. Initial configuration:\n") + log.info("There is no configuration file detected. Initial configuration:\n") init_customization() elif not os.path.exists(__config_file__): - print(f"Warning: Using the default configuration file, which you can modify in {global_defaults_file}") - print(f"If you want to create a local configuration file, you can create a file named {__config_file__}, or run manimgl --config") + log.info(f"Using the default configuration file, which you can modify in `{global_defaults_file}`") + log.info(f"If you want to create a local configuration file, you can create a file named \ +`{__config_file__}`, or run `manimgl --config`") custom_config = get_custom_config() + check_temporary_storage(custom_config) write_file = any([args.write_file, args.open, args.finder]) if args.transparent: @@ -319,9 +334,9 @@ def get_camera_configuration(args, custom_config): try: bg_color = args.color or custom_config["style"]["background_color"] camera_config["background_color"] = colour.Color(bg_color) - except AttributeError as err: - print("Please use a valid color") - print(err) + except ValueError as err: + log.error("Please use a valid color") + log.error(err) sys.exit(2) # If rendering a transparent image/move, make sure the diff --git a/manimlib/extract_scene.py b/manimlib/extract_scene.py index 6036765f..e08f9f50 100644 --- a/manimlib/extract_scene.py +++ b/manimlib/extract_scene.py @@ -1,9 +1,9 @@ import inspect import sys -import logging from manimlib.scene.scene import Scene from manimlib.config import get_custom_config +from manimlib.logger import log class BlankScene(Scene): @@ -41,8 +41,11 @@ def prompt_user_for_choice(scene_classes): name_to_class[split_str] if not split_str.isnumeric() else scene_classes[int(split_str)-1] for split_str in user_input.replace(" ", "").split(",") ] + except IndexError: + log.error("Invalid scene number") + sys.exit(2) except KeyError: - logging.log(logging.ERROR, "Invalid scene") + log.error("Invalid scene name") sys.exit(2) except EOFError: sys.exit(1) @@ -78,10 +81,7 @@ def get_scenes_to_render(scene_classes, scene_config, config): found = True break if not found and (scene_name != ""): - logging.log( - logging.ERROR, - f"No scene named {scene_name} found", - ) + log.error(f"No scene named {scene_name} found") if result: return result if len(scene_classes) == 1: diff --git a/manimlib/logger.py b/manimlib/logger.py new file mode 100644 index 00000000..b8f9fa36 --- /dev/null +++ b/manimlib/logger.py @@ -0,0 +1,9 @@ +import logging +from rich.logging import RichHandler + +FORMAT = "%(message)s" +logging.basicConfig( + level="NOTSET", format=FORMAT, datefmt="[%X]", handlers=[RichHandler()] +) + +log = logging.getLogger("rich") \ No newline at end of file diff --git a/manimlib/scene/scene.py b/manimlib/scene/scene.py index 69061de7..21f05241 100644 --- a/manimlib/scene/scene.py +++ b/manimlib/scene/scene.py @@ -2,7 +2,6 @@ import inspect import random import platform import itertools as it -import logging from functools import wraps from tqdm import tqdm as ProgressDisplay @@ -21,6 +20,7 @@ from manimlib.utils.family_ops import extract_mobject_family_members from manimlib.utils.family_ops import restructure_list_to_exclude_certain_family_members from manimlib.event_handler.event_type import EventType from manimlib.event_handler import EVENT_DISPATCHER +from manimlib.logger import log class Scene(object): @@ -101,6 +101,8 @@ class Scene(object): # If there is a window, enter a loop # which updates the frame while under # the hood calling the pyglet event loop + log.info("Tips: You are now in the interactive mode. Now you can use the keyboard\ +and the mouse to interact with the scene. Just press `q` if you want to quit.") self.quit_interaction = False self.lock_static_mobject_data() while not (self.window.is_closing or self.quit_interaction): @@ -132,6 +134,8 @@ class Scene(object): local_ns["touch"] = self.interact for term in ("play", "wait", "add", "remove", "clear", "save_state", "restore"): local_ns[term] = getattr(self, term) + log.info("Tips: Now the embed iPython terminal is open. But you can't interact with \ +the window directly. To do so, you need to type `touch()` or `self.interact()`") shell(local_ns=local_ns, stack_depth=2) # End scene when exiting an embed. raise EndSceneEarlyException() @@ -459,8 +463,7 @@ class Scene(object): @handle_play_like_call def play(self, *args, **kwargs): if len(args) == 0: - logging.log( - logging.WARNING, + log.warning( "Called Scene.play with no animations" ) return @@ -602,7 +605,7 @@ class Scene(object): try: char = chr(symbol) except OverflowError: - print(" Warning: The value of the pressed key is too large.") + log.warning("The value of the pressed key is too large.") return event_data = {"symbol": symbol, "modifiers": modifiers} diff --git a/manimlib/scene/scene_file_writer.py b/manimlib/scene/scene_file_writer.py index fefdaa1d..8d74765e 100644 --- a/manimlib/scene/scene_file_writer.py +++ b/manimlib/scene/scene_file_writer.py @@ -12,6 +12,7 @@ from manimlib.utils.file_ops import guarantee_existence from manimlib.utils.file_ops import add_extension_if_not_present from manimlib.utils.file_ops import get_sorted_integer_files from manimlib.utils.sounds import get_full_sound_file_path +from manimlib.logger import log class SceneFileWriter(object): @@ -231,7 +232,7 @@ class SceneFileWriter(object): **kwargs ) if len(partial_movie_files) == 0: - print("No animations in this scene") + log.warning("No animations in this scene") return # Write a file partial_file_list.txt containing all @@ -300,7 +301,7 @@ class SceneFileWriter(object): self.print_file_ready_message(file_path) def print_file_ready_message(self, file_path): - print(f"\nFile ready at {file_path}\n") + log.info(f"\nFile ready at {file_path}\n") def should_open_file(self): return any([ diff --git a/manimlib/utils/bezier.py b/manimlib/utils/bezier.py index 39f09861..f5317584 100644 --- a/manimlib/utils/bezier.py +++ b/manimlib/utils/bezier.py @@ -5,6 +5,7 @@ from manimlib.utils.simple_functions import choose from manimlib.utils.space_ops import find_intersection from manimlib.utils.space_ops import cross2d from manimlib.utils.space_ops import midpoint +from manimlib.logger import log CLOSED_THRESHOLD = 0.001 @@ -68,9 +69,9 @@ def interpolate(start, end, alpha): try: return (1 - alpha) * start + alpha * end except TypeError: - print(type(start), start.dtype) - print(type(end), start.dtype) - print(alpha) + log.debug(type(start), start.dtype) + log.debug(type(end), start.dtype) + log.debug(alpha) import sys sys.exit(2) diff --git a/manimlib/utils/debug.py b/manimlib/utils/debug.py index 87ef886c..6d495f89 100644 --- a/manimlib/utils/debug.py +++ b/manimlib/utils/debug.py @@ -3,11 +3,12 @@ import time from manimlib.constants import BLACK from manimlib.mobject.numbers import Integer from manimlib.mobject.types.vectorized_mobject import VGroup +from manimlib.logger import log def print_family(mobject, n_tabs=0): """For debugging purposes""" - print("\t" * n_tabs, mobject, id(mobject)) + log.debug("\t" * n_tabs + str(mobject) + " " + str(id(mobject))) for submob in mobject.submobjects: print_family(submob, n_tabs + 1) diff --git a/manimlib/utils/tex_file_writing.py b/manimlib/utils/tex_file_writing.py index 4d76d300..0b0e0ba9 100644 --- a/manimlib/utils/tex_file_writing.py +++ b/manimlib/utils/tex_file_writing.py @@ -1,4 +1,3 @@ -import logging import sys import os import hashlib @@ -7,6 +6,7 @@ from contextlib import contextmanager from manimlib.utils.directories import get_tex_dir from manimlib.config import get_manim_dir from manimlib.config import get_custom_config +from manimlib.logger import log SAVED_TEX_CONFIG = {} @@ -87,15 +87,11 @@ def tex_to_dvi(tex_file): exit_code = os.system(" ".join(commands)) if exit_code != 0: log_file = tex_file.replace(".tex", ".log") - logging.log( - logging.ERROR, - "\n\n LaTeX Error! Not a worry, it happens to the best of us.\n" - ) + log.error("LaTeX Error! Not a worry, it happens to the best of us.") with open(log_file, "r") as file: for line in file.readlines(): if line.startswith("!"): - print(line[1:]) - logging.log(logging.INFO, line) + log.debug(f"The error could be: `{line[2:-1]}`") sys.exit(2) return result