From 876ab24bc682afdce0373844d1cbf4af6d4373c6 Mon Sep 17 00:00:00 2001 From: Aathish Sivasubrahmanian Date: Wed, 8 Jul 2020 10:43:11 +0530 Subject: [PATCH 01/32] Implemented logging of terminal output to file. --- manim/config.py | 21 +++++++++++++++++---- manim/logger.py | 4 +++- manim/scene/scene_file_writer.py | 7 ++++++- 3 files changed, 26 insertions(+), 6 deletions(-) diff --git a/manim/config.py b/manim/config.py index a866eb89dd..6c06d729fc 100644 --- a/manim/config.py +++ b/manim/config.py @@ -110,12 +110,12 @@ def _parse_file_writer_config(config_parser, args): # in batches, depending on their type: booleans and strings for boolean_opt in ['preview', 'show_file_in_finder', 'quiet', 'sound', 'leave_progress_bars', 'write_to_movie', 'save_last_frame', - 'save_pngs', 'save_as_gif', 'write_all']: + 'save_pngs', 'save_as_gif', 'write_all','log_to_file']: attr = getattr(args, boolean_opt) config[boolean_opt] = (default.getboolean(boolean_opt) if attr is None else attr) # for str_opt in ['media_dir', 'video_dir', 'tex_dir', 'text_dir']: - for str_opt in ['media_dir']: + for str_opt in ['media_dir',"log_dir"]: attr = getattr(args, str_opt) config[str_opt] = (default[str_opt] if attr is None else attr) dir_names = {'video_dir': 'videos', @@ -262,6 +262,11 @@ def _parse_cli(arg_list, input=True): const=True, help="Save the video as gif", ) + parser.add_argument( + "--log_to_file", + action="store_const", + const=True, + help="Log terminal output to file.") # The default value of the following is set in manim.cfg parser.add_argument( @@ -276,6 +281,10 @@ def _parse_cli(arg_list, input=True): "--media_dir", help="directory to write media", ) + parser.add_argument( + "--log_dir", + help="directory to write log files to", + ) # video_group = parser.add_mutually_exclusive_group() # video_group.add_argument( # "--video_dir", @@ -365,9 +374,13 @@ def _parse_cli(arg_list, input=True): def _init_dirs(config): # Make sure all folders exist for folder in [config["media_dir"], config["video_dir"], - config["tex_dir"], config["text_dir"]]: + config["tex_dir"], config["text_dir"],config["log_dir"]]: if not os.path.exists(folder): - os.makedirs(folder) + # If log_to_file is False, ignore log_dir + if folder is config["log_dir"] and (not config["log_to_file"]): + pass + else: + os.makedirs(folder) def _from_command_line(): diff --git a/manim/logger.py b/manim/logger.py index 416a06a801..4ef5c3d4f4 100644 --- a/manim/logger.py +++ b/manim/logger.py @@ -1,12 +1,14 @@ import logging from rich.logging import RichHandler +from rich.console import Console +console=Console(record=True) logging.basicConfig( level="NOTSET", format="%(message)s", datefmt="[%X]", - handlers=[RichHandler()] + handlers=[RichHandler(console=console)] ) logger = logging.getLogger("rich") diff --git a/manim/scene/scene_file_writer.py b/manim/scene/scene_file_writer.py index aaea1d2f1f..322bad40e4 100644 --- a/manim/scene/scene_file_writer.py +++ b/manim/scene/scene_file_writer.py @@ -10,7 +10,7 @@ from ..constants import FFMPEG_BIN, GIF_FILE_EXTENSION from ..config import file_writer_config -from ..logger import logger +from ..logger import logger,console from ..utils.config_ops import digest_config from ..utils.file_ops import guarantee_existence from ..utils.file_ops import add_extension_if_not_present @@ -529,3 +529,8 @@ def print_file_ready_message(self, file_path): Prints the "File Ready" message to STDOUT. """ logger.info("\nFile ready at {}\n".format(file_path)) + if file_writer_config["log_to_file"]: + log_file_path=os.path.join( + file_writer_config["log_dir"],f"{self.get_default_scene_name()}.log") + with open(log_file_path,"w") as logfile: + logfile.write(console.export_text()) From 5611a318dabc64c370e3816ef2db1ea4ad4b2162 Mon Sep 17 00:00:00 2001 From: Aathish Sivasubrahmanian Date: Wed, 8 Jul 2020 10:43:48 +0530 Subject: [PATCH 02/32] Set default config values for log_to_file, log_dir --- manim/default.cfg | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/manim/default.cfg b/manim/default.cfg index c2cf102950..e45588481a 100644 --- a/manim/default.cfg +++ b/manim/default.cfg @@ -48,6 +48,9 @@ output_file = # --leave_progress_bars leave_progress_bars = False +# --log_to_file +log_to_file = False + # -c, --color background_color = BLACK @@ -67,6 +70,9 @@ upto_animation_number = -1 # --media_dir media_dir = ./media +# --log_dir +log_dir = ./media/logs + # # --video_dir # video_dir = %(MEDIA_DIR)s/videos From e9d9b295ffb5520f307829daf5512c48d0ef4918 Mon Sep 17 00:00:00 2001 From: Aathish Sivasubrahmanian Date: Wed, 8 Jul 2020 11:18:24 +0530 Subject: [PATCH 03/32] Change default log_dir to ./logs [skip ci] --- manim/default.cfg | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/manim/default.cfg b/manim/default.cfg index e45588481a..1ad0ff4717 100644 --- a/manim/default.cfg +++ b/manim/default.cfg @@ -71,7 +71,7 @@ upto_animation_number = -1 media_dir = ./media # --log_dir -log_dir = ./media/logs +log_dir = ./logs # # --video_dir # video_dir = %(MEDIA_DIR)s/videos From a6c41510d1a3dbe8fbec3a4fb6ed53b4c1ddcfa3 Mon Sep 17 00:00:00 2001 From: Aathish Sivasubrahmanian Date: Fri, 10 Jul 2020 19:40:38 +0530 Subject: [PATCH 04/32] Bring log-file up to date with Master. --- CONTRIBUTING.md | 3 ++ README.md | 41 +++++++++++++++++++++----- manim/__main__.py | 4 ++- manim/animation/indication.py | 3 +- manim/animation/transform.py | 2 +- manim/config.py | 4 +-- manim/default.cfg | 2 +- manim/mobject/coordinate_systems.py | 45 ++++++++++++++++++++++++----- tests/test_cli/test_cli.py | 6 ++-- tests/testing_utils.py | 37 +++++++----------------- 10 files changed, 95 insertions(+), 52 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index d414b71275..a3579c90d2 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -4,6 +4,7 @@ 2. (For the maintainers of this repo) Label PRs appropriately. 3. Link relevant issues. 4. Ensure compatibility with the latest changes in the repo. +5. After you've made your changes, please test them out thoroughly. If the repository has any example code or code exclusively used for testing, please test your changes against those. If you have added new features, consider writing and contributing to a test for that feature to ensure it works properly. ## Contributing to the Manim Community Fork - General instructions @@ -33,6 +34,8 @@ As this is a Fork of [Manim by 3b1b](https://github.com/3b1b/manim), contributin 4. After that, you can make your changes to the repo's files (the code is in the `manim` directory). Then, you can commit said changes. +> As stated in the General Contribution Guidelines, make sure to test your changes against the example scenes in the `example_scenes` directory and test scenes in the `tests` directory, and add a new test in the `tests` directory for your new feature if you've made one. + > Note: if your Pull Request doesn't change any of the actual code, please add `[skip ci]` to your commit message for Travis to ignore it. 5. Finally, instead of typing in `git push`, enter the command below. diff --git a/README.md b/README.md index 162beb492e..9563a8fe08 100644 --- a/README.md +++ b/README.md @@ -19,18 +19,23 @@ Manim is an animation engine for explanatory math videos. It's used to create pr + [FFmpeg Installation](#ffmpeg-installation) + [SoX Installation](#sox-installation) + [LaTeX Installation](#latex-installation) - * [Linux](#linux) + * [Ubuntu/Debian](#ubuntudebian) + [Cairo Installation](#cairo-installation-1) + [FFmpeg Installation](#ffmpeg-installation-1) + [SoX Installation](#sox-installation-1) + [LaTeX Installation](#latex-installation-1) + [Additional Installation Steps](#additional-installation-steps) - * [Mac](#mac) - + [Homebrew Installation](#homebrew-installation) + * [Arch/Manjaro](#archmanjaro) + [Cairo Installation](#cairo-installation-2) + [FFmpeg Installation](#ffmpeg-installation-2) + [SoX Installation](#sox-installation-2) + [LaTeX Installation](#latex-installation-2) + * [Mac](#mac) + + [Homebrew Installation](#homebrew-installation) + + [Cairo Installation](#cairo-installation-3) + + [FFmpeg Installation](#ffmpeg-installation-3) + + [SoX Installation](#sox-installation-3) + + [LaTeX Installation](#latex-installation-3) * [Installing Manim-Community itself](#installing-manim-community-itself) - [Usage](#usage) - [Documentation](#documentation) @@ -96,7 +101,7 @@ Before installing `manim-community`, there are some additional dependencies that

-### Linux +### Ubuntu/Debian Before installing `manim-community`, there are some additional dependencies that you must have installed: - Cairo @@ -123,12 +128,32 @@ sudo apt install texlive texlive-latex-extra texlive-fonts-extra texlive-latex-r > Note: this installation may take up a lot of space. The developers are working on providing a simpler, lighter LaTeX package for you to install 2. You can check you did it right by running `latex` -#### Additional Installation Steps -- You must install additional codecs to play MP4 files: +### Arch/Manjaro + +Before installing `manim-community`, there are some additional dependencies that you must have installed: + - Cairo + - FFmpeg + - Sox (optional, for sound) + - LaTeX (optional, for LaTeX) + +#### Cairo Installation +1. Install the `cairo` package with your package manager: `sudo pacman -S cairo` + +#### FFmpeg Installation +1. Install the `ffmpeg` package with your package manager: `sudo pacman -S ffmpeg` +2. You can check you did it right by running `ffmpeg -version` + +#### SoX Installation +1. Install the `sox` package with your package manager: `sudo pacman -S sox` +2. You can check you did it right by running `sox` + +#### LaTeX Installation +1. Install `texlive` with your package manager by running the following commands: ``` -sudo apt install libdvdnav4 libdvd-pkg gstreamer1.0-plugins-bad gstreamer1.0-plugins-ugly libdvd-pkg -sudo apt install ubuntu-restricted-extras +sudo pacman -S texlive-most ``` +> Note: this installation may take up a lot of space. The developers are working on providing a simpler, lighter LaTeX package for you to install +2. You can check you did it right by running `latex` ### Mac diff --git a/manim/__main__.py b/manim/__main__.py index d1536e26fc..df5e9dd307 100644 --- a/manim/__main__.py +++ b/manim/__main__.py @@ -135,7 +135,9 @@ def get_module(file_name): sys.exit(2) else: if os.path.exists(file_name): - module_name = re.sub(r"\..+$", "", file_name.replace(os.sep, ".")) + if file_name[-3:] != ".py": + raise Exception(f"{file_name} is not a valid Manim python script.") + module_name = file_name[:-3].replace(os.sep, '.').split('.')[-1] spec = importlib.util.spec_from_file_location(module_name, file_name) module = importlib.util.module_from_spec(spec) spec.loader.exec_module(module) diff --git a/manim/animation/indication.py b/manim/animation/indication.py index 0daef10a57..43bc6d0103 100644 --- a/manim/animation/indication.py +++ b/manim/animation/indication.py @@ -1,6 +1,7 @@ import numpy as np from ..constants import * +from ..config import config from ..animation.animation import Animation from ..animation.movement import Homotopy from ..animation.composition import AnimationGroup @@ -45,7 +46,7 @@ def create_target(self): def create_starting_mobject(self): return Dot( - radius=FRAME_X_RADIUS + FRAME_Y_RADIUS, + radius=config['frame_x_radius'] + config['frame_y_radius'], stroke_width=0, fill_color=self.color, fill_opacity=0, diff --git a/manim/animation/transform.py b/manim/animation/transform.py index 13f58a91f6..37aa5bcf8c 100644 --- a/manim/animation/transform.py +++ b/manim/animation/transform.py @@ -152,7 +152,7 @@ def __init__(self, method, *args, **kwargs): method is a method of Mobject, *args are arguments for that method. Key word arguments should be passed in as the last arg, as a dict, since **kwargs is for - configuration of the transform itslef + configuration of the transform itself Relies on the fact that mobject methods return the mobject """ diff --git a/manim/config.py b/manim/config.py index 6c06d729fc..15b10b898e 100644 --- a/manim/config.py +++ b/manim/config.py @@ -177,7 +177,7 @@ def _parse_file_writer_config(config_parser, args): def _parse_cli(arg_list, input=True): parser = argparse.ArgumentParser( description='Animation engine for explanatory math videos', - epilog='Made with ❤ by the manim community devs' + epilog='Made with <3 by the manim community devs' ) if input: parser.add_argument( @@ -192,7 +192,7 @@ def _parse_cli(arg_list, input=True): ) parser.add_argument( "-o", "--output_file", - help="Specify the name of the output file, if" + help="Specify the name of the output file, if " "it should be different from the scene class name", default='', ) diff --git a/manim/default.cfg b/manim/default.cfg index 1ad0ff4717..ad84f644ae 100644 --- a/manim/default.cfg +++ b/manim/default.cfg @@ -118,7 +118,7 @@ pixel_height = 720 pixel_width = 1280 frame_rate = 30 -# These override the previous by usnig -l, --low_quality +# These override the previous by using -l, --low_quality [low_quality] pixel_height = 480 pixel_width = 854 diff --git a/manim/mobject/coordinate_systems.py b/manim/mobject/coordinate_systems.py index a509984059..7ea66468e9 100644 --- a/manim/mobject/coordinate_systems.py +++ b/manim/mobject/coordinate_systems.py @@ -289,6 +289,7 @@ def __init__(self, **kwargs): self.init_background_lines() def init_background_lines(self): + """Will init all the lines of NumberPlanes (faded or not)""" if self.faded_line_style is None: style = dict(self.background_line_style) # For anything numerical, like stroke_width @@ -311,6 +312,13 @@ def init_background_lines(self): ) def get_lines(self): + """Generate all the lines, faded and not faded. Two sets of lines are generated: one parallel to the X-axis, and parallel to the Y-axis. + + Returns + ------- + Tuple[:class:`~.VGroup`, :class:`~.VGroup`] + The first (i.e the non faded lines) and second (i.e the faded lines) sets of lines, respectively. + """ x_axis = self.get_x_axis() y_axis = self.get_y_axis() x_freq = self.x_line_frequency @@ -328,22 +336,43 @@ def get_lines(self): lines2 = VGroup(*x_lines2, *y_lines2) return lines1, lines2 - def get_lines_parallel_to_axis(self, axis1, axis2, freq, ratio): - line = Line(axis1.get_start(), axis1.get_end()) - dense_freq = (1 + ratio) + def get_lines_parallel_to_axis(self, axis_parallel_to, axis_perpendicular_to, freq, ratio_faded_lines): + """Generate a set of lines parallel to an axis. + + Parameters + ---------- + axis_parallel_to : :class:`~.Line` + The axis with which the lines will be parallel. + + axis_perpendicular_to : :class:`~.Line` + The axis with which the lines will be perpendicular. + + ratio_faded_lines : :class:`float` + The number of faded lines between each non-faded line. + + freq : :class:`float` + Frequency of non-faded lines (number of non-faded lines per graph unit). + + Returns + ------- + Tuple[:class:`~.VGroup`, :class:`~.VGroup`] + The first (i.e the non-faded lines parallel to `axis_parallel_to`) and second (i.e the faded lines parallel to `axis_parallel_to`) sets of lines, respectively. + """ + line = Line(axis_parallel_to.get_start(), axis_parallel_to.get_end()) + dense_freq = ratio_faded_lines step = (1 / dense_freq) * freq - lines1 = VGroup() lines2 = VGroup() + unit_vector_axis_perp_to = axis_perpendicular_to.get_unit_vector() ranges = ( - np.arange(0, axis2.x_max, step), - np.arange(0, axis2.x_min, -step), + np.arange(0, axis_perpendicular_to.x_max, step), + np.arange(0, axis_perpendicular_to.x_min, -step), ) for inputs in ranges: for k, x in enumerate(inputs): new_line = line.copy() - new_line.move_to(axis2.number_to_point(x)) - if k % (1 + ratio) == 0: + new_line.shift(unit_vector_axis_perp_to * x) + if k % ratio_faded_lines == 0: lines1.add(new_line) else: lines2.add(new_line) diff --git a/tests/test_cli/test_cli.py b/tests/test_cli/test_cli.py index da850a1479..ebe4462b86 100644 --- a/tests/test_cli/test_cli.py +++ b/tests/test_cli/test_cli.py @@ -16,13 +16,13 @@ def capture(command): def test_help(python_version): command = [python_version, "-m", "manim", "--help"] out, err, exitcode = capture(command) - assert exitcode == 0, f"Manim has been installed incorrectly. Please refer to the troubleshooting section on the wiki. Error:\n{err}" + assert exitcode == 0, f"Manim has been installed incorrectly. Please refer to the troubleshooting section on the wiki. Error:\n{err.decode()}" @pytest.mark.skip_end_to_end def test_basicScene(python_version): """ Simulate SquareToCircle. The cache will be saved in tests_caches/media_temp (temporary directory). This is mainly intended to test the partial-movies process. """ - path_basic_scene = os.path.join("tests_data", "basic_scenes.py") + path_basic_scene = os.path.join("tests", "tests_data", "basic_scenes.py") path_output = os.path.join("tests_cache", "media_temp") command = [python_version, "-m", "manim", path_basic_scene, "SquareToCircle", "-l", "--media_dir", path_output] @@ -35,7 +35,7 @@ def test_basicScene(python_version): @pytest.mark.skip_end_to_end def test_WriteStuff(python_version): """This is mainly intended to test the caching process of the tex objects""" - path_basic_scene = os.path.join("tests_data", "basic_scenes.py") + path_basic_scene = os.path.join("tests", "tests_data", "basic_scenes.py") path_output = os.path.join("tests_cache", "media_temp") command = [python_version, "-m", "manim", path_basic_scene, "WriteStuff", "-l", "--media_dir", path_output] diff --git a/tests/testing_utils.py b/tests/testing_utils.py index affd66e206..f8651de969 100644 --- a/tests/testing_utils.py +++ b/tests/testing_utils.py @@ -6,7 +6,7 @@ import pytest from manim import logger -from manim import config +from manim import config, file_writer_config class SceneTester: @@ -34,15 +34,16 @@ class SceneTester: def __init__(self, scene_object, module_tested, caching_needed=False): # Disable the the logs, (--quiet is broken) TODO logging.disable(logging.CRITICAL) - self.path_tests_medias_cache = os.path.join('tests_cache', module_tested) - self.path_tests_data = os.path.join('tests_data', module_tested) + self.path_tests_medias_cache = os.path.join('tests', 'tests_cache', module_tested) + self.path_tests_data = os.path.join('tests', 'tests_data', module_tested) if caching_needed: config['text_dir'] = os.path.join( self.path_tests_medias_cache, scene_object.__name__, 'Text') - config['tex_dir'] = os.path.join( + file_writer_config['tex_dir'] = os.path.join( self.path_tests_medias_cache, scene_object.__name__, 'Tex') + file_writer_config['skip_animations'] = True config['pixel_height'] = 480 config['pixel_width'] = 854 config['frame_rate'] = 15 @@ -124,30 +125,12 @@ def set_test_scene(scene_object, module_name): Normal usage:: set_test_scene(DotTest, "geometry") """ + file_writer_config['skip_animations'] = True + config['pixel_height'] = 480 + config['pixel_width'] = 854 + config['frame_rate'] = 15 - CONFIG_TEST = { - 'camera_config': { - 'frame_rate': 15, - 'pixel_height': 480, - 'pixel_width': 854 - }, - 'end_at_animation_number': None, - 'file_writer_config': { - 'file_name': None, - 'input_file_path': 'test.py', - 'movie_file_extension': '.mp4', - 'png_mode': 'RGB', - 'save_as_gif': False, - 'save_last_frame': False, - 'save_pngs': False, - 'write_to_movie': False - }, - 'leave_progress_bars': False, - 'skip_animations': True, - 'start_at_animation_number': None - } - - scene = scene_object(**CONFIG_TEST) + scene = scene_object() data = scene.get_frame() path = os.path.join("manim", "tests", "tests_data", "{}".format(module_name)) From 8c771ecaba61484d1e1da28f71657128967a0708 Mon Sep 17 00:00:00 2001 From: Aathish Sivasubrahmanian Date: Tue, 21 Jul 2020 00:14:14 +0530 Subject: [PATCH 05/32] Re-implement logging of Terminal output to log file. --- manim/logger.py | 4 ++-- manim/utils/config_utils.py | 23 ++++++++++++++++++++--- 2 files changed, 22 insertions(+), 5 deletions(-) diff --git a/manim/logger.py b/manim/logger.py index 4ac8b8d8a3..7e21e868d1 100644 --- a/manim/logger.py +++ b/manim/logger.py @@ -40,9 +40,9 @@ def parse_theme(fp): config_parser, successfully_read_files = config_items[1], config_items[-1] try: customTheme = parse_theme(successfully_read_files) - console = Console(theme=customTheme) + console = Console(theme=customTheme,record=True) except KeyError: - console = Console() + console = Console(record=True) printf( "[logging.level.warning]No cfg file found, creating one in " + successfully_read_files[0] diff --git a/manim/utils/config_utils.py b/manim/utils/config_utils.py index a6c7fb1875..359acc10de 100644 --- a/manim/utils/config_utils.py +++ b/manim/utils/config_utils.py @@ -48,13 +48,14 @@ def _parse_file_writer_config(config_parser, args): "save_pngs", "save_as_gif", "write_all", + 'log_to_file' ]: attr = getattr(args, boolean_opt) fw_config[boolean_opt] = ( default.getboolean(boolean_opt) if attr is None else attr ) # for str_opt in ['media_dir', 'video_dir', 'tex_dir', 'text_dir']: - for str_opt in ["media_dir"]: + for str_opt in ["media_dir","log_dir"]: attr = getattr(args, str_opt) fw_config[str_opt] = default[str_opt] if attr is None else attr dir_names = {"video_dir": "videos", "tex_dir": "Tex", "text_dir": "texts"} @@ -218,6 +219,12 @@ def _parse_cli(arg_list, input=True): help="Save the video as gif", ) + parser.add_argument( + "--log_to_file", + action="store_const", + const=True, + help="Log terminal output to file.") + # The default value of the following is set in manim.cfg parser.add_argument( "-c", "--color", help="Background color", @@ -228,6 +235,12 @@ def _parse_cli(arg_list, input=True): parser.add_argument( "--media_dir", help="directory to write media", ) + + parser.add_argument( + "--log_dir", + help="directory to write log files to", + ) + # video_group = parser.add_mutually_exclusive_group() # video_group.add_argument( # "--video_dir", @@ -324,10 +337,14 @@ def _init_dirs(config): config["video_dir"], config["tex_dir"], config["text_dir"], + config["log_dir"], ]: if not os.path.exists(folder): - os.makedirs(folder) - + # If log_to_file is False, ignore log_dir + if folder is config["log_dir"] and (not config["log_to_file"]): + pass + else: + os.makedirs(folder) def _from_command_line(): """Determine if manim was called from the command line.""" From 33efe31b0836c243ee27dce9f4a4821fa9e4e803 Mon Sep 17 00:00:00 2001 From: Aathish Sivasubrahmanian Date: Tue, 21 Jul 2020 00:14:48 +0530 Subject: [PATCH 06/32] Move log _writing_ statements to separate function. --- manim/scene/scene_file_writer.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/manim/scene/scene_file_writer.py b/manim/scene/scene_file_writer.py index 322bad40e4..c031637097 100644 --- a/manim/scene/scene_file_writer.py +++ b/manim/scene/scene_file_writer.py @@ -529,8 +529,13 @@ def print_file_ready_message(self, file_path): Prints the "File Ready" message to STDOUT. """ logger.info("\nFile ready at {}\n".format(file_path)) + if file_writer_config["log_to_file"]: + self.write_log() + + def write_log(self): log_file_path=os.path.join( file_writer_config["log_dir"],f"{self.get_default_scene_name()}.log") with open(log_file_path,"w") as logfile: logfile.write(console.export_text()) + logger.info("Log written to {}\n".format(log_file_path)) From 5549f3abfa5beec27d108601425238f0007969c9 Mon Sep 17 00:00:00 2001 From: Aathish Sivasubrahmanian Date: Tue, 21 Jul 2020 00:22:58 +0530 Subject: [PATCH 07/32] Add a test for logging. --- tests/test_cli/test_cli.py | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/tests/test_cli/test_cli.py b/tests/test_cli/test_cli.py index 0da7fa9bc4..07a4abb824 100644 --- a/tests/test_cli/test_cli.py +++ b/tests/test_cli/test_cli.py @@ -55,3 +55,15 @@ def test_dash_as_name(python_version): assert os.path.exists(os.path.join( path_output, "videos", "-", "480p15", "DashAsNameTest.mp4")), err rmtree(path_output) + +def test_logging_to_file(python_version): + """Test logging Terminal output to a log file.""" + path_basic_scene = os.path.join("tests", "tests_data", "basic_scenes.py") + path_output = os.path.join("tests_cache", "media_temp") + command = [python_version, "-m", "manim", path_basic_scene, + "SquareToCircle", "-l", "--log_to_file", "--log_dir",os.path.join(path_output,"logs"), "--media_dir", path_output] + out, err, exitcode = capture(command) + assert exitcode == 0, err + assert os.path.exists(os.path.join( + path_output, "logs", "SquareToCircle.log")), err + rmtree(path_output) From 175b6c77f2e7a2157ce07167f86005d9cfd6f92d Mon Sep 17 00:00:00 2001 From: Aathish Sivasubrahmanian Date: Tue, 21 Jul 2020 11:38:18 +0530 Subject: [PATCH 08/32] Added test to check if logs generated are same. --- tests/test_cli/test_cli.py | 12 ---------- tests/test_logging/test_logging.py | 35 ++++++++++++++++++++++++++++++ 2 files changed, 35 insertions(+), 12 deletions(-) create mode 100644 tests/test_logging/test_logging.py diff --git a/tests/test_cli/test_cli.py b/tests/test_cli/test_cli.py index 07a4abb824..0da7fa9bc4 100644 --- a/tests/test_cli/test_cli.py +++ b/tests/test_cli/test_cli.py @@ -55,15 +55,3 @@ def test_dash_as_name(python_version): assert os.path.exists(os.path.join( path_output, "videos", "-", "480p15", "DashAsNameTest.mp4")), err rmtree(path_output) - -def test_logging_to_file(python_version): - """Test logging Terminal output to a log file.""" - path_basic_scene = os.path.join("tests", "tests_data", "basic_scenes.py") - path_output = os.path.join("tests_cache", "media_temp") - command = [python_version, "-m", "manim", path_basic_scene, - "SquareToCircle", "-l", "--log_to_file", "--log_dir",os.path.join(path_output,"logs"), "--media_dir", path_output] - out, err, exitcode = capture(command) - assert exitcode == 0, err - assert os.path.exists(os.path.join( - path_output, "logs", "SquareToCircle.log")), err - rmtree(path_output) diff --git a/tests/test_logging/test_logging.py b/tests/test_logging/test_logging.py new file mode 100644 index 0000000000..cf54ba3d6b --- /dev/null +++ b/tests/test_logging/test_logging.py @@ -0,0 +1,35 @@ +import subprocess +import os +from shutil import rmtree +import pytest +import re + +def capture(command,instream=None): + proc = subprocess.Popen(command, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + stdin=instream + ) + out, err = proc.communicate() + return out, err, proc.returncode + + +def test_logging_to_file(python_version): + """Test logging Terminal output to a log file.""" + path_basic_scene = os.path.join("tests", "tests_data", "basic_scenes.py") + path_output = os.path.join("tests_cache", "media_temp") + command = [python_version, "-m", "manim", path_basic_scene, + "SquareToCircle", "-l", "--log_to_file", "--log_dir",os.path.join(path_output,"logs"), "--media_dir", path_output] + out, err, exitcode = capture(command) + log_file_path=os.path.join(path_output, "logs", "SquareToCircle.log") + assert exitcode == 0, err + assert os.path.exists(log_file_path), err + with open (log_file_path) as logfile: + if os.sep =="\\": + logs=re.sub(r"(\d{2}:\d{2}:\d{2})|(\.?\\.+) +","",logfile.read()) + else: + logs=re.sub(r"(\d{2}:\d{2}:\d{2})|(\.?/.+) +","",logfile.read()) + with open(os.path.join(os.path.dirname(__file__), "expected.log")) as ideal: + expected=ideal.read() + assert logs==expected, err + rmtree(path_output) From aa0415cd44f620a173696ba3e339273c3ac60e49 Mon Sep 17 00:00:00 2001 From: Aathish Sivasubrahmanian Date: Tue, 21 Jul 2020 11:38:31 +0530 Subject: [PATCH 09/32] Added model log file. --- .gitignore | 2 +- tests/test_logging/expected.log | 5 +++++ 2 files changed, 6 insertions(+), 1 deletion(-) create mode 100644 tests/test_logging/expected.log diff --git a/.gitignore b/.gitignore index 1325f8a6d0..3f677e487e 100644 --- a/.gitignore +++ b/.gitignore @@ -51,7 +51,7 @@ coverage.xml .hypothesis/ .pytest_cache/ -*.log +# *.log # Sphinx documentation docs/_build/ diff --git a/tests/test_logging/expected.log b/tests/test_logging/expected.log new file mode 100644 index 0000000000..3b4e6e7ca4 --- /dev/null +++ b/tests/test_logging/expected.log @@ -0,0 +1,5 @@ +[] INFO Read configuration files: config.py:92 + +[] INFO scene_file_writer.py:531 + File ready at + From f35032b250c9720bb832c03abca713070339c3e0 Mon Sep 17 00:00:00 2001 From: Aathish Sivasubrahmanian Date: Tue, 21 Jul 2020 11:38:42 +0530 Subject: [PATCH 10/32] Test on this branch --- .github/workflows/ci.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 8331a393b0..d6902cf63e 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -3,6 +3,7 @@ on: push: branches: - master + - test-log pull_request: branches: - master From cdce0657669aa549371131c87d7dcd82b12a19c6 Mon Sep 17 00:00:00 2001 From: Aathish Sivasubrahmanian Date: Tue, 21 Jul 2020 12:07:28 +0530 Subject: [PATCH 11/32] Change Regex expression for windows. --- tests/test_logging/test_logging.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_logging/test_logging.py b/tests/test_logging/test_logging.py index cf54ba3d6b..e8d9493cae 100644 --- a/tests/test_logging/test_logging.py +++ b/tests/test_logging/test_logging.py @@ -26,7 +26,7 @@ def test_logging_to_file(python_version): assert os.path.exists(log_file_path), err with open (log_file_path) as logfile: if os.sep =="\\": - logs=re.sub(r"(\d{2}:\d{2}:\d{2})|(\.?\\.+) +","",logfile.read()) + logs=re.sub(r"(\d{2}:\d{2}:\d{2})|([A-Z]:\\.*) +","",logfile.read()) else: logs=re.sub(r"(\d{2}:\d{2}:\d{2})|(\.?/.+) +","",logfile.read()) with open(os.path.join(os.path.dirname(__file__), "expected.log")) as ideal: From 219600c3bfc04fca84bc63bdd6c4f86efa7d90f4 Mon Sep 17 00:00:00 2001 From: Aathish Sivasubrahmanian Date: Tue, 21 Jul 2020 12:19:29 +0530 Subject: [PATCH 12/32] Print log on failure --- tests/test_logging/test_logging.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_logging/test_logging.py b/tests/test_logging/test_logging.py index e8d9493cae..07e1335c7a 100644 --- a/tests/test_logging/test_logging.py +++ b/tests/test_logging/test_logging.py @@ -31,5 +31,5 @@ def test_logging_to_file(python_version): logs=re.sub(r"(\d{2}:\d{2}:\d{2})|(\.?/.+) +","",logfile.read()) with open(os.path.join(os.path.dirname(__file__), "expected.log")) as ideal: expected=ideal.read() - assert logs==expected, err + assert logs==expected, logs rmtree(path_output) From bb914ef603e5a527fe69edc936ba0c3e7b08a802 Mon Sep 17 00:00:00 2001 From: Aathish Sivasubrahmanian Date: Tue, 21 Jul 2020 12:48:46 +0530 Subject: [PATCH 13/32] Print log windows. --- .github/workflows/ci.yml | 4 ++++ tests/test_logging/test_logging.py | 3 +-- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index d6902cf63e..76e1cda5d0 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -67,3 +67,7 @@ jobs: - name: Run tests run: pytest + - name : If errored, print log + if : ${{ failure() }} + run : | + Get-Content -Path .\tests_cache\media_temp\logs\SquareToCircle.log \ No newline at end of file diff --git a/tests/test_logging/test_logging.py b/tests/test_logging/test_logging.py index 07e1335c7a..3b4bf9a270 100644 --- a/tests/test_logging/test_logging.py +++ b/tests/test_logging/test_logging.py @@ -31,5 +31,4 @@ def test_logging_to_file(python_version): logs=re.sub(r"(\d{2}:\d{2}:\d{2})|(\.?/.+) +","",logfile.read()) with open(os.path.join(os.path.dirname(__file__), "expected.log")) as ideal: expected=ideal.read() - assert logs==expected, logs - rmtree(path_output) + assert logs==expected, err From 7749bf4dd9c7c1136c86272d3818d4bb468b27a6 Mon Sep 17 00:00:00 2001 From: Aathish Sivasubrahmanian Date: Tue, 21 Jul 2020 13:00:33 +0530 Subject: [PATCH 14/32] Edit regex expression for both unix and windows. --- tests/test_logging/test_logging.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/test_logging/test_logging.py b/tests/test_logging/test_logging.py index 3b4bf9a270..dd1cfc3a96 100644 --- a/tests/test_logging/test_logging.py +++ b/tests/test_logging/test_logging.py @@ -26,9 +26,9 @@ def test_logging_to_file(python_version): assert os.path.exists(log_file_path), err with open (log_file_path) as logfile: if os.sep =="\\": - logs=re.sub(r"(\d{2}:\d{2}:\d{2})|([A-Z]:\\.*) +","",logfile.read()) + logs=re.sub(r"(\d{2}:\d{2}:\d{2})|([A-Z]:\\.*) *","",logfile.read()) else: - logs=re.sub(r"(\d{2}:\d{2}:\d{2})|(\.?/.+) +","",logfile.read()) + logs=re.sub(r"(\d{2}:\d{2}:\d{2})|(\.?/.+) *","",logfile.read()) with open(os.path.join(os.path.dirname(__file__), "expected.log")) as ideal: expected=ideal.read() assert logs==expected, err From 7604d53131be92986f859b1d32e1b7fd5cb80359 Mon Sep 17 00:00:00 2001 From: Aathish Sivasubrahmanian Date: Tue, 21 Jul 2020 13:11:58 +0530 Subject: [PATCH 15/32] Edit regex expression. --- tests/test_logging/expected.log | 4 ++-- tests/test_logging/test_logging.py | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/test_logging/expected.log b/tests/test_logging/expected.log index 3b4e6e7ca4..e3c0bbbcaf 100644 --- a/tests/test_logging/expected.log +++ b/tests/test_logging/expected.log @@ -1,5 +1,5 @@ -[] INFO Read configuration files: config.py:92 +INFO Read configuration files: config.py:92 -[] INFO scene_file_writer.py:531 +INFO scene_file_writer.py:531 File ready at diff --git a/tests/test_logging/test_logging.py b/tests/test_logging/test_logging.py index dd1cfc3a96..66dccfd89b 100644 --- a/tests/test_logging/test_logging.py +++ b/tests/test_logging/test_logging.py @@ -26,9 +26,9 @@ def test_logging_to_file(python_version): assert os.path.exists(log_file_path), err with open (log_file_path) as logfile: if os.sep =="\\": - logs=re.sub(r"(\d{2}:\d{2}:\d{2})|([A-Z]:\\.*) *","",logfile.read()) + logs=re.sub(r"(\[\d{2}:\d{2}:\d{2}\] *)|([A-Z]:\\.*) *","",logfile.read()) else: - logs=re.sub(r"(\d{2}:\d{2}:\d{2})|(\.?/.+) *","",logfile.read()) + logs=re.sub(r"(\[\d{2}:\d{2}:\d{2}\])|(\.?/.+) *","",logfile.read()) with open(os.path.join(os.path.dirname(__file__), "expected.log")) as ideal: expected=ideal.read() assert logs==expected, err From 366d202cecc6f7847c872caea2c548aebd60b804 Mon Sep 17 00:00:00 2001 From: Aathish Sivasubrahmanian Date: Tue, 21 Jul 2020 13:18:51 +0530 Subject: [PATCH 16/32] Edit expected log. --- tests/test_logging/expected.log | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/test_logging/expected.log b/tests/test_logging/expected.log index e3c0bbbcaf..8add7a6d31 100644 --- a/tests/test_logging/expected.log +++ b/tests/test_logging/expected.log @@ -1,5 +1,5 @@ -INFO Read configuration files: config.py:92 + INFO Read configuration files: config.py:92 -INFO scene_file_writer.py:531 + INFO scene_file_writer.py:531 File ready at From b2daaeb6cc716894f4dd93987100306d57135ebf Mon Sep 17 00:00:00 2001 From: Aathish Sivasubrahmanian Date: Tue, 21 Jul 2020 13:40:25 +0530 Subject: [PATCH 17/32] Try stripping whitespace. --- tests/test_logging/expected.log | 4 ++-- tests/test_logging/test_logging.py | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/tests/test_logging/expected.log b/tests/test_logging/expected.log index 8add7a6d31..e3c0bbbcaf 100644 --- a/tests/test_logging/expected.log +++ b/tests/test_logging/expected.log @@ -1,5 +1,5 @@ - INFO Read configuration files: config.py:92 +INFO Read configuration files: config.py:92 - INFO scene_file_writer.py:531 +INFO scene_file_writer.py:531 File ready at diff --git a/tests/test_logging/test_logging.py b/tests/test_logging/test_logging.py index 66dccfd89b..9f91cbb1ad 100644 --- a/tests/test_logging/test_logging.py +++ b/tests/test_logging/test_logging.py @@ -26,9 +26,9 @@ def test_logging_to_file(python_version): assert os.path.exists(log_file_path), err with open (log_file_path) as logfile: if os.sep =="\\": - logs=re.sub(r"(\[\d{2}:\d{2}:\d{2}\] *)|([A-Z]:\\.*) *","",logfile.read()) + logs=re.sub(r"(\[\d{2}:\d{2}:\d{2}\] *)|([A-Z]:\\.*) *","",logfile.read()).rstrip() else: - logs=re.sub(r"(\[\d{2}:\d{2}:\d{2}\])|(\.?/.+) *","",logfile.read()) + logs=re.sub(r"(\[\d{2}:\d{2}:\d{2}\])|(\.?/.+) *","",logfile.read()).rstrip() with open(os.path.join(os.path.dirname(__file__), "expected.log")) as ideal: - expected=ideal.read() + expected=ideal.read().rstrip() assert logs==expected, err From 44ce406468ab4404290a7ee94ae72f8db36739da Mon Sep 17 00:00:00 2001 From: Aathish Sivasubrahmanian Date: Tue, 21 Jul 2020 17:33:45 +0530 Subject: [PATCH 18/32] Move test_logging to a separate file. --- tests/test_cli/test_cli.py | 11 ----------- tests/test_logging.py | 26 ++++++++++++++++++++++++++ 2 files changed, 26 insertions(+), 11 deletions(-) create mode 100644 tests/test_logging.py diff --git a/tests/test_cli/test_cli.py b/tests/test_cli/test_cli.py index 07a4abb824..156773246e 100644 --- a/tests/test_cli/test_cli.py +++ b/tests/test_cli/test_cli.py @@ -56,14 +56,3 @@ def test_dash_as_name(python_version): path_output, "videos", "-", "480p15", "DashAsNameTest.mp4")), err rmtree(path_output) -def test_logging_to_file(python_version): - """Test logging Terminal output to a log file.""" - path_basic_scene = os.path.join("tests", "tests_data", "basic_scenes.py") - path_output = os.path.join("tests_cache", "media_temp") - command = [python_version, "-m", "manim", path_basic_scene, - "SquareToCircle", "-l", "--log_to_file", "--log_dir",os.path.join(path_output,"logs"), "--media_dir", path_output] - out, err, exitcode = capture(command) - assert exitcode == 0, err - assert os.path.exists(os.path.join( - path_output, "logs", "SquareToCircle.log")), err - rmtree(path_output) diff --git a/tests/test_logging.py b/tests/test_logging.py new file mode 100644 index 0000000000..d56304f1b0 --- /dev/null +++ b/tests/test_logging.py @@ -0,0 +1,26 @@ +import subprocess +import os +from shutil import rmtree +import pytest + +def capture(command,instream=None): + proc = subprocess.Popen(command, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + stdin=instream + ) + out, err = proc.communicate() + return out, err, proc.returncode + + +def test_logging_to_file(python_version): + """Test logging Terminal output to a log file.""" + path_basic_scene = os.path.join("tests", "tests_data", "basic_scenes.py") + path_output = os.path.join("tests_cache", "media_temp") + command = [python_version, "-m", "manim", path_basic_scene, + "SquareToCircle", "-l", "--log_to_file", "--log_dir",os.path.join(path_output,"logs"), "--media_dir", path_output] + out, err, exitcode = capture(command) + assert exitcode == 0, err + assert os.path.exists(os.path.join( + path_output, "logs", "SquareToCircle.log")), err + rmtree(path_output) From 40b2b7001dd3e8684c55e919f9ceb9ac083619e9 Mon Sep 17 00:00:00 2001 From: Aathish Sivasubrahmanian Date: Wed, 22 Jul 2020 18:23:48 +0530 Subject: [PATCH 19/32] Print log for all OS's --- .github/workflows/ci.yml | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 76e1cda5d0..e00d80ecc3 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -67,7 +67,11 @@ jobs: - name: Run tests run: pytest - - name : If errored, print log - if : ${{ failure() }} - run : | - Get-Content -Path .\tests_cache\media_temp\logs\SquareToCircle.log \ No newline at end of file + + - name: Print Windows log + if: runner.os == 'Windows' && ${{ failure() }} + run: Get-Content -Path .\tests_cache\media_temp\logs\SquareToCircle.log + + - name: Print Windows log + if: (runner.os == 'macOS' || runner.os == 'Linux') && ${{ failure() }} + run: echo ./tests_cache/media_temp/logs/SquareToCircle.log From 6df05c0418c6f730b308a4412ef42fa83ea74bf1 Mon Sep 17 00:00:00 2001 From: Aathish Sivasubrahmanian Date: Wed, 22 Jul 2020 19:41:39 +0530 Subject: [PATCH 20/32] Make rules for passing looser. --- .github/workflows/ci.yml | 8 -------- tests/test_logging/expected.log | 5 ----- tests/test_logging/test_logging.py | 17 ++++++++++------- 3 files changed, 10 insertions(+), 20 deletions(-) delete mode 100644 tests/test_logging/expected.log diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index e00d80ecc3..d6902cf63e 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -67,11 +67,3 @@ jobs: - name: Run tests run: pytest - - - name: Print Windows log - if: runner.os == 'Windows' && ${{ failure() }} - run: Get-Content -Path .\tests_cache\media_temp\logs\SquareToCircle.log - - - name: Print Windows log - if: (runner.os == 'macOS' || runner.os == 'Linux') && ${{ failure() }} - run: echo ./tests_cache/media_temp/logs/SquareToCircle.log diff --git a/tests/test_logging/expected.log b/tests/test_logging/expected.log deleted file mode 100644 index e3c0bbbcaf..0000000000 --- a/tests/test_logging/expected.log +++ /dev/null @@ -1,5 +0,0 @@ -INFO Read configuration files: config.py:92 - -INFO scene_file_writer.py:531 - File ready at - diff --git a/tests/test_logging/test_logging.py b/tests/test_logging/test_logging.py index 9f91cbb1ad..4e5a3077e8 100644 --- a/tests/test_logging/test_logging.py +++ b/tests/test_logging/test_logging.py @@ -1,5 +1,6 @@ import subprocess import os +import sys from shutil import rmtree import pytest import re @@ -17,6 +18,8 @@ def capture(command,instream=None): def test_logging_to_file(python_version): """Test logging Terminal output to a log file.""" path_basic_scene = os.path.join("tests", "tests_data", "basic_scenes.py") + expected=['INFO', 'Read', 'configuration', 'files:', 'config.py:92', 'INFO', + 'scene_file_writer.py:531', 'File', 'ready', 'at'] path_output = os.path.join("tests_cache", "media_temp") command = [python_version, "-m", "manim", path_basic_scene, "SquareToCircle", "-l", "--log_to_file", "--log_dir",os.path.join(path_output,"logs"), "--media_dir", path_output] @@ -24,11 +27,11 @@ def test_logging_to_file(python_version): log_file_path=os.path.join(path_output, "logs", "SquareToCircle.log") assert exitcode == 0, err assert os.path.exists(log_file_path), err - with open (log_file_path) as logfile: - if os.sep =="\\": - logs=re.sub(r"(\[\d{2}:\d{2}:\d{2}\] *)|([A-Z]:\\.*) *","",logfile.read()).rstrip() - else: - logs=re.sub(r"(\[\d{2}:\d{2}:\d{2}\])|(\.?/.+) *","",logfile.read()).rstrip() - with open(os.path.join(os.path.dirname(__file__), "expected.log")) as ideal: - expected=ideal.read().rstrip() + if sys.platform.startswith("win32") or sys.platform.startswith("win32"): + enc="Windows-1252" + else: + enc="utf-8" + with open(log_file_path,encoding=enc) as logfile: + logs=logfile.read() + logs=[e for e in log if not any(x in e for x in ["\\","/",".mp4","[","]"])] assert logs==expected, err From ad5ae6feb73543e674e2d70e29cb06060926f291 Mon Sep 17 00:00:00 2001 From: Aathish Sivasubrahmanian Date: Wed, 22 Jul 2020 19:45:30 +0530 Subject: [PATCH 21/32] Fix a typo --- tests/test_logging/test_logging.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_logging/test_logging.py b/tests/test_logging/test_logging.py index 4e5a3077e8..75576f44e3 100644 --- a/tests/test_logging/test_logging.py +++ b/tests/test_logging/test_logging.py @@ -33,5 +33,5 @@ def test_logging_to_file(python_version): enc="utf-8" with open(log_file_path,encoding=enc) as logfile: logs=logfile.read() - logs=[e for e in log if not any(x in e for x in ["\\","/",".mp4","[","]"])] + logs=[e for e in logs if not any(x in e for x in ["\\","/",".mp4","[","]"])] assert logs==expected, err From cabd95675d345a97233ab65f375c857b96da2271 Mon Sep 17 00:00:00 2001 From: Aathish Sivasubrahmanian Date: Wed, 22 Jul 2020 19:49:49 +0530 Subject: [PATCH 22/32] Split string before cleaning. --- tests/test_logging/test_logging.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/test_logging/test_logging.py b/tests/test_logging/test_logging.py index 75576f44e3..94b9c45afe 100644 --- a/tests/test_logging/test_logging.py +++ b/tests/test_logging/test_logging.py @@ -27,11 +27,11 @@ def test_logging_to_file(python_version): log_file_path=os.path.join(path_output, "logs", "SquareToCircle.log") assert exitcode == 0, err assert os.path.exists(log_file_path), err - if sys.platform.startswith("win32") or sys.platform.startswith("win32"): + if sys.platform.startswith("win32") or sys.platform.startswith("cygwin"): enc="Windows-1252" else: enc="utf-8" with open(log_file_path,encoding=enc) as logfile: - logs=logfile.read() + logs=logfile.read().split() logs=[e for e in logs if not any(x in e for x in ["\\","/",".mp4","[","]"])] assert logs==expected, err From bd485d82c7ed6d01d3ef2460963973c76f20209f Mon Sep 17 00:00:00 2001 From: Aathish Sivasubrahmanian Date: Wed, 22 Jul 2020 20:11:32 +0530 Subject: [PATCH 23/32] Remove duplicate test_logging file. --- tests/test_logging.py | 26 -------------------------- 1 file changed, 26 deletions(-) delete mode 100644 tests/test_logging.py diff --git a/tests/test_logging.py b/tests/test_logging.py deleted file mode 100644 index d56304f1b0..0000000000 --- a/tests/test_logging.py +++ /dev/null @@ -1,26 +0,0 @@ -import subprocess -import os -from shutil import rmtree -import pytest - -def capture(command,instream=None): - proc = subprocess.Popen(command, - stdout=subprocess.PIPE, - stderr=subprocess.PIPE, - stdin=instream - ) - out, err = proc.communicate() - return out, err, proc.returncode - - -def test_logging_to_file(python_version): - """Test logging Terminal output to a log file.""" - path_basic_scene = os.path.join("tests", "tests_data", "basic_scenes.py") - path_output = os.path.join("tests_cache", "media_temp") - command = [python_version, "-m", "manim", path_basic_scene, - "SquareToCircle", "-l", "--log_to_file", "--log_dir",os.path.join(path_output,"logs"), "--media_dir", path_output] - out, err, exitcode = capture(command) - assert exitcode == 0, err - assert os.path.exists(os.path.join( - path_output, "logs", "SquareToCircle.log")), err - rmtree(path_output) From 64e8d2a7e334c10e798bf3628fdf3c4185b4f104 Mon Sep 17 00:00:00 2001 From: Aathish Date: Wed, 29 Jul 2020 13:52:17 +0530 Subject: [PATCH 24/32] Move default log directory into media directory. --- manim/default.cfg | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/manim/default.cfg b/manim/default.cfg index eb28c8117d..75e380871b 100644 --- a/manim/default.cfg +++ b/manim/default.cfg @@ -71,7 +71,7 @@ upto_animation_number = -1 media_dir = ./media # --log_dir -log_dir = ./logs +log_dir = %(media_dir)s/logs # # --video_dir # video_dir = %(MEDIA_DIR)s/videos From 19a7c7c1f27362b79c96189ec7344a8f32c2ff67 Mon Sep 17 00:00:00 2001 From: Aathish Date: Wed, 29 Jul 2020 21:58:51 +0530 Subject: [PATCH 25/32] Ignored Line Numbers during test. Added some documentation for why word-for-word comparisons are not used. --- tests/test_logging/test_logging.py | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/tests/test_logging/test_logging.py b/tests/test_logging/test_logging.py index 94b9c45afe..a203440444 100644 --- a/tests/test_logging/test_logging.py +++ b/tests/test_logging/test_logging.py @@ -16,10 +16,16 @@ def capture(command,instream=None): def test_logging_to_file(python_version): - """Test logging Terminal output to a log file.""" + """Test logging Terminal output to a log file. + `rich` formats it's output based on the size of the terminal it is outputting to. + As such, since there is no way to obtain the terminal size of the testing device + before running the test, this test employs a workaround where instead of the exact + text of the log (which can differ in whitespace and truncation) only the constant + parts, such as keywords, are compared. + """ path_basic_scene = os.path.join("tests", "tests_data", "basic_scenes.py") - expected=['INFO', 'Read', 'configuration', 'files:', 'config.py:92', 'INFO', - 'scene_file_writer.py:531', 'File', 'ready', 'at'] + expected=['INFO', 'Read', 'configuration', 'files:', 'config.py:', 'INFO', + 'scene_file_writer.py:', 'File', 'ready', 'at'] path_output = os.path.join("tests_cache", "media_temp") command = [python_version, "-m", "manim", path_basic_scene, "SquareToCircle", "-l", "--log_to_file", "--log_dir",os.path.join(path_output,"logs"), "--media_dir", path_output] @@ -34,4 +40,5 @@ def test_logging_to_file(python_version): with open(log_file_path,encoding=enc) as logfile: logs=logfile.read().split() logs=[e for e in logs if not any(x in e for x in ["\\","/",".mp4","[","]"])] + logs=[re.sub('[0-9]', '', i) for i in logs] assert logs==expected, err From e63ce10968f1986cf93527961cd303ccdce4ffe4 Mon Sep 17 00:00:00 2001 From: Aathish Date: Mon, 3 Aug 2020 16:51:11 +0530 Subject: [PATCH 26/32] Allow changing of Max width and height of logs via config files. --- manim/default.cfg | 2 + manim/logger.py | 29 ++-- manim/scene/scene_file_writer.py | 222 ++++++++++++++++--------------- 3 files changed, 135 insertions(+), 118 deletions(-) diff --git a/manim/default.cfg b/manim/default.cfg index 75e380871b..c642acd38d 100644 --- a/manim/default.cfg +++ b/manim/default.cfg @@ -159,3 +159,5 @@ log_level = log_time = cyan dim log_message = log_path = dim +log_width = -1 +log_height = -1 \ No newline at end of file diff --git a/manim/logger.py b/manim/logger.py index 7e21e868d1..fc21c12c6e 100644 --- a/manim/logger.py +++ b/manim/logger.py @@ -21,26 +21,37 @@ def parse_theme(fp): config_parser.read(fp) theme = dict(config_parser["logger"]) # replaces `_` by `.` as rich understands it - for key in theme: - temp = theme[key] - del theme[key] - key = key.replace("_", ".") - theme[key] = temp + theme = dict( + zip([key.replace("_", ".") for key in theme.keys()], list(theme.values())) + ) + + theme["log.width"] = None if theme["log.width"] == "-1" else int(theme["log.width"]) + + theme["log.height"] = ( + None if theme["log.height"] == "-1" else int(theme["log.height"]) + ) try: - customTheme = Theme(theme) + customTheme = Theme( + {k: v for k, v in theme.items() if k not in ["log.width", "log.height"]} + ) except (color.ColorParseError, errors.StyleSyntaxError): customTheme = None printf( "[logging.level.error]It seems your colour configuration couldn't be parsed. Loading the default color configuration...[/logging.level.error]" ) - return customTheme + return customTheme, theme config_items = _run_config() config_parser, successfully_read_files = config_items[1], config_items[-1] try: - customTheme = parse_theme(successfully_read_files) - console = Console(theme=customTheme,record=True) + customTheme, themedict = parse_theme(successfully_read_files) + console = Console( + theme=customTheme, + record=True, + height=themedict["log.height"], + width=themedict["log.width"], + ) except KeyError: console = Console(record=True) printf( diff --git a/manim/scene/scene_file_writer.py b/manim/scene/scene_file_writer.py index c031637097..de6475a8b6 100644 --- a/manim/scene/scene_file_writer.py +++ b/manim/scene/scene_file_writer.py @@ -10,7 +10,7 @@ from ..constants import FFMPEG_BIN, GIF_FILE_EXTENSION from ..config import file_writer_config -from ..logger import logger,console +from ..logger import logger, console from ..utils.config_ops import digest_config from ..utils.file_ops import guarantee_existence from ..utils.file_ops import add_extension_if_not_present @@ -52,42 +52,38 @@ def init_output_directories(self): """ module_directory = self.get_default_module_directory() scene_name = self.get_default_scene_name() - if file_writer_config['save_last_frame'] or file_writer_config['save_pngs']: - if file_writer_config['media_dir'] != "": - image_dir = guarantee_existence(os.path.join( - file_writer_config['media_dir'], - "images", - module_directory, - )) + if file_writer_config["save_last_frame"] or file_writer_config["save_pngs"]: + if file_writer_config["media_dir"] != "": + image_dir = guarantee_existence( + os.path.join( + file_writer_config["media_dir"], "images", module_directory, + ) + ) self.image_file_path = os.path.join( - image_dir, - add_extension_if_not_present(scene_name, ".png") + image_dir, add_extension_if_not_present(scene_name, ".png") ) - if file_writer_config['write_to_movie']: - if file_writer_config['video_dir']: - movie_dir = guarantee_existence(os.path.join( - file_writer_config['video_dir'], - module_directory, - self.get_resolution_directory(), - )) + if file_writer_config["write_to_movie"]: + if file_writer_config["video_dir"]: + movie_dir = guarantee_existence( + os.path.join( + file_writer_config["video_dir"], + module_directory, + self.get_resolution_directory(), + ) + ) self.movie_file_path = os.path.join( movie_dir, add_extension_if_not_present( - scene_name, file_writer_config['movie_file_extension'] - ) + scene_name, file_writer_config["movie_file_extension"] + ), ) self.gif_file_path = os.path.join( - movie_dir, - add_extension_if_not_present( - scene_name, GIF_FILE_EXTENSION - ) + movie_dir, add_extension_if_not_present(scene_name, GIF_FILE_EXTENSION) + ) + self.partial_movie_directory = guarantee_existence( + os.path.join(movie_dir, "partial_movie_files", scene_name,) ) - self.partial_movie_directory = guarantee_existence(os.path.join( - movie_dir, - "partial_movie_files", - scene_name, - )) def get_default_module_directory(self): """ @@ -99,7 +95,7 @@ def get_default_module_directory(self): str The name of the directory. """ - filename = os.path.basename(file_writer_config['input_file']) + filename = os.path.basename(file_writer_config["input_file"]) root, _ = os.path.splitext(filename) return root @@ -115,8 +111,8 @@ def get_default_scene_name(self): str The default scene name. """ - fn = file_writer_config['output_file'] - return (fn if fn else self.scene.__class__.__name__) + fn = file_writer_config["output_file"] + return fn if fn else self.scene.__class__.__name__ def get_resolution_directory(self): """ @@ -142,9 +138,7 @@ def get_resolution_directory(self): """ pixel_height = self.scene.camera.pixel_height frame_rate = self.scene.camera.frame_rate - return "{}p{}".format( - pixel_height, frame_rate - ) + return "{}p{}".format(pixel_height, frame_rate) # Directory getters def get_image_file_path(self): @@ -177,9 +171,8 @@ def get_next_partial_movie_path(self): result = os.path.join( self.partial_movie_directory, "{:05}{}".format( - self.scene.num_plays, - file_writer_config['movie_file_extension'], - ) + self.scene.num_plays, file_writer_config["movie_file_extension"], + ), ) return result @@ -207,9 +200,7 @@ def create_audio_segment(self): """ self.audio_segment = AudioSegment.silent() - def add_audio_segment(self, new_segment, - time=None, - gain_to_background=None): + def add_audio_segment(self, new_segment, time=None, gain_to_background=None): """ This method adds an audio segment from an AudioSegment type object and suitable parameters. @@ -240,8 +231,7 @@ def add_audio_segment(self, new_segment, diff = new_end - curr_end if diff > 0: segment = segment.append( - AudioSegment.silent(int(np.ceil(diff * 1000))), - crossfade=0, + AudioSegment.silent(int(np.ceil(diff * 1000))), crossfade=0, ) self.audio_segment = segment.overlay( new_segment, @@ -286,7 +276,7 @@ def begin_animation(self, allow_write=False): allow_write : bool, optional Whether or not to write to a video file. """ - if file_writer_config['write_to_movie'] and allow_write: + if file_writer_config["write_to_movie"] and allow_write: self.open_movie_pipe() def end_animation(self, allow_write=False): @@ -299,7 +289,7 @@ def end_animation(self, allow_write=False): allow_write : bool, optional Whether or not to write to a video file. """ - if file_writer_config['write_to_movie'] and allow_write: + if file_writer_config["write_to_movie"] and allow_write: self.close_movie_pipe() def write_frame(self, frame): @@ -312,11 +302,11 @@ def write_frame(self, frame): frame : np.array Pixel array of the frame. """ - if file_writer_config['write_to_movie']: + if file_writer_config["write_to_movie"]: self.writing_process.stdin.write(frame.tostring()) - if file_writer_config['save_pngs']: + if file_writer_config["save_pngs"]: path, extension = os.path.splitext(self.image_file_path) - Image.fromarray(frame).save(f'{path}{self.frame_count}{extension}') + Image.fromarray(frame).save(f"{path}{self.frame_count}{extension}") self.frame_count += 1 def save_final_image(self, image): @@ -357,11 +347,11 @@ def finish(self): If save_last_frame is True, saves the last frame in the default image directory. """ - if file_writer_config['write_to_movie']: + if file_writer_config["write_to_movie"]: if hasattr(self, "writing_process"): self.writing_process.terminate() self.combine_movie_files() - if file_writer_config['save_last_frame']: + if file_writer_config["save_last_frame"]: self.scene.update_frame(ignore_skipping=True) self.save_final_image(self.scene.get_image()) @@ -372,9 +362,11 @@ def open_movie_pipe(self): buffer. """ file_path = self.get_next_partial_movie_path() - temp_file_path = (os.path.splitext(file_path)[0] - + '_temp' - + file_writer_config['movie_file_extension']) + temp_file_path = ( + os.path.splitext(file_path)[0] + + "_temp" + + file_writer_config["movie_file_extension"] + ) self.partial_movie_file_path = file_path self.temp_partial_movie_file_path = temp_file_path @@ -384,27 +376,36 @@ def open_movie_pipe(self): command = [ FFMPEG_BIN, - '-y', # overwrite output file if it exists - '-f', 'rawvideo', - '-s', '%dx%d' % (width, height), # size of one frame - '-pix_fmt', 'rgba', - '-r', str(fps), # frames per second - '-i', '-', # The imput comes from a pipe - '-an', # Tells FFMPEG not to expect any audio - '-loglevel', 'error', + "-y", # overwrite output file if it exists + "-f", + "rawvideo", + "-s", + "%dx%d" % (width, height), # size of one frame + "-pix_fmt", + "rgba", + "-r", + str(fps), # frames per second + "-i", + "-", # The imput comes from a pipe + "-an", # Tells FFMPEG not to expect any audio + "-loglevel", + "error", ] # TODO, the test for a transparent background should not be based on # the file extension. - if file_writer_config['movie_file_extension'] == ".mov": + if file_writer_config["movie_file_extension"] == ".mov": # This is if the background of the exported # video should be transparent. command += [ - '-vcodec', 'qtrle', + "-vcodec", + "qtrle", ] else: command += [ - '-vcodec', 'libx264', - '-pix_fmt', 'yuv420p', + "-vcodec", + "libx264", + "-pix_fmt", + "yuv420p", ] command += [temp_file_path] self.writing_process = subprocess.Popen(command, stdin=subprocess.PIPE) @@ -418,8 +419,7 @@ def close_movie_pipe(self): self.writing_process.stdin.close() self.writing_process.wait() shutil.move( - self.temp_partial_movie_file_path, - self.partial_movie_file_path, + self.temp_partial_movie_file_path, self.partial_movie_file_path, ) def combine_movie_files(self): @@ -437,17 +437,16 @@ def combine_movie_files(self): # single piece. kwargs = { "remove_non_integer_files": True, - "extension": file_writer_config['movie_file_extension'], + "extension": file_writer_config["movie_file_extension"], } - if file_writer_config['from_animation_number'] is not None: - kwargs["min_index"] = file_writer_config['from_animation_number'] - if file_writer_config['upto_animation_number'] is not None: - kwargs["max_index"] = file_writer_config['upto_animation_number'] + if file_writer_config["from_animation_number"] is not None: + kwargs["min_index"] = file_writer_config["from_animation_number"] + if file_writer_config["upto_animation_number"] is not None: + kwargs["max_index"] = file_writer_config["upto_animation_number"] else: kwargs["remove_indices_greater_than"] = self.scene.num_plays - 1 partial_movie_files = get_sorted_integer_files( - self.partial_movie_directory, - **kwargs + self.partial_movie_directory, **kwargs ) if len(partial_movie_files) == 0: logger.error("No animations in this scene") @@ -456,65 +455,70 @@ def combine_movie_files(self): # Write a file partial_file_list.txt containing all # partial movie files file_list = os.path.join( - self.partial_movie_directory, - "partial_movie_file_list.txt" + self.partial_movie_directory, "partial_movie_file_list.txt" ) - with open(file_list, 'w') as fp: + with open(file_list, "w") as fp: for pf_path in partial_movie_files: - if os.name == 'nt': - pf_path = pf_path.replace('\\', '/') - fp.write("file \'file:{}\'\n".format(pf_path)) + if os.name == "nt": + pf_path = pf_path.replace("\\", "/") + fp.write("file 'file:{}'\n".format(pf_path)) movie_file_path = self.get_movie_file_path() commands = [ FFMPEG_BIN, - '-y', # overwrite output file if it exists - '-f', 'concat', - '-safe', '0', - '-i', file_list, - '-loglevel', 'error', + "-y", # overwrite output file if it exists + "-f", + "concat", + "-safe", + "0", + "-i", + file_list, + "-loglevel", + "error", ] if self.write_to_movie: - commands += [ - '-c', 'copy', - movie_file_path - ] + commands += ["-c", "copy", movie_file_path] if self.save_as_gif: - commands += [ - self.gif_file_path - ] + commands += [self.gif_file_path] if not self.includes_sound: - commands.insert(-1, '-an') + commands.insert(-1, "-an") combine_process = subprocess.Popen(commands) combine_process.wait() if self.includes_sound: sound_file_path = movie_file_path.replace( - file_writer_config['movie_file_extension'], ".wav" + file_writer_config["movie_file_extension"], ".wav" ) # Makes sure sound file length will match video file self.add_audio_segment(AudioSegment.silent(0)) self.audio_segment.export( - sound_file_path, - bitrate='312k', + sound_file_path, bitrate="312k", ) temp_file_path = movie_file_path.replace(".", "_temp.") commands = [ FFMPEG_BIN, - "-i", movie_file_path, - "-i", sound_file_path, - '-y', # overwrite output file if it exists - "-c:v", "copy", - "-c:a", "aac", - "-b:a", "320k", + "-i", + movie_file_path, + "-i", + sound_file_path, + "-y", # overwrite output file if it exists + "-c:v", + "copy", + "-c:a", + "aac", + "-b:a", + "320k", # select video stream from first file - "-map", "0:v:0", + "-map", + "0:v:0", # select audio stream from second file - "-map", "1:a:0", - '-loglevel', 'error', + "-map", + "1:a:0", + "-loglevel", + "error", # "-shortest", temp_file_path, ] @@ -534,8 +538,8 @@ def print_file_ready_message(self, file_path): self.write_log() def write_log(self): - log_file_path=os.path.join( - file_writer_config["log_dir"],f"{self.get_default_scene_name()}.log") - with open(log_file_path,"w") as logfile: - logfile.write(console.export_text()) - logger.info("Log written to {}\n".format(log_file_path)) + log_file_path = os.path.join( + file_writer_config["log_dir"], f"{self.get_default_scene_name()}.log" + ) + console.save_text(log_file_path) + logger.info("Log written to {}\n".format(log_file_path)) From 53420f0d7db91beadc16e500a17c6ed10e88135b Mon Sep 17 00:00:00 2001 From: Aathish Date: Mon, 3 Aug 2020 16:52:02 +0530 Subject: [PATCH 27/32] Implemented word-for-word tests for log file. (Except for data that _will_ change between tests) --- scripts/pycairoinstall.py | 79 +++++++++++++----------------- tests/test_logging/expected.txt | 4 ++ tests/test_logging/test_logging.py | 19 +++---- tests/tests_data/manim.cfg | 3 ++ 4 files changed, 52 insertions(+), 53 deletions(-) create mode 100644 tests/test_logging/expected.txt diff --git a/scripts/pycairoinstall.py b/scripts/pycairoinstall.py index 5cd7c8aeae..41d9fa195c 100644 --- a/scripts/pycairoinstall.py +++ b/scripts/pycairoinstall.py @@ -3,49 +3,40 @@ import sys import urllib.request -if 'Windows' in platform.system(): - # In case the python version is 3.6 and the system is 32-bit, try pycairo‑1.19.1‑cp37‑cp37m‑win32.whl version of cairo - if sys.version[:3]=='3.6' and platform.machine()=='x86': - urllib.request.urlretrieve("https://download.lfd.uci.edu/pythonlibs/w3jqiv8s/pycairo-1.19.1-cp36-cp36m-win32.whl", "pycairo-1.19.1-cp36-cp36m-win32.whl") - os.system("pip install pycairo-1.19.1-cp36-cp36m-win32.whl") - os.remove("pycairo-1.19.1-cp37-cp37m-win32.whl") +if "Windows" in platform.system(): + ver_num = "1.19.2" + fork = "naveen521kk" + # In case the python version is 3.6 and the system is 32-bit, try pycairo‑{ver_num}‑cp37‑cp37m‑win32.whl version of cairo + if sys.version[:3] == "3.6" and platform.machine() == "x86": + url = f"https://github.com/{fork}/pycairo/releases/download/v{ver_num}/pycairo-{ver_num}-cp36-cp36m-win32.whl" - # In case the python version is 3.6 and the system is 64-bit, try pycairo‑1.19.1‑cp37‑cp37m‑win32.whl version of cairo - elif sys.version[:3]=='3.6' and platform.machine()=='AMD64': - urllib.request.urlretrieve("https://download.lfd.uci.edu/pythonlibs/w3jqiv8s/pycairo-1.19.1-cp36-cp36m-win_amd64.whl", "pycairo-1.19.1-cp36-cp36m-win_amd64.whl") - print("Sucessfully downloaded Cairo for your system") - print("Installing Cairo") - os.system("pip install pycairo-1.19.1-cp36-cp36m-win_amd64.whl") - os.remove("pycairo-1.19.1-cp36-cp36m-win_amd64.whl") - - # In case the python version is 3.7 and the system is 32-bit, try pycairo‑1.19.1‑cp37‑cp37m‑win32.whl version of cairo - elif sys.version[:3]=='3.7' and platform.machine()=='x86': - urllib.request.urlretrieve("https://download.lfd.uci.edu/pythonlibs/w3jqiv8s/pycairo-1.19.1-cp37-cp37m-win32.whl", "pycairo-1.19.1-cp37-cp37m-win32.whl") - print("Sucessfully downloaded Cairo for your system") - print("Installing Cairo") - os.system("pip install pycairo-1.19.1-cp37-cp37m-win32.whl") - os.remove("pycairo-1.19.1-cp37-cp37m-win32.whl") + # In case the python version is 3.6 and the system is 64-bit, try pycairo‑{ver_num}‑cp37‑cp37m‑win32.whl version of cairo + elif sys.version[:3] == "3.6" and platform.machine() == "AMD64": + url = f"https://github.com/{fork}/pycairo/releases/download/v{ver_num}/pycairo-{ver_num}-cp36-cp36m-win_amd64.whl" - # In case the python version is 3.7 and the system is AMD64, try pycairo-1.19.1-cp37-cp37m-win_amd64.whl version of cairo - elif sys.version[:3]=='3.7' and platform.machine()=='AMD64': - urllib.request.urlretrieve("https://download.lfd.uci.edu/pythonlibs/w3jqiv8s/pycairo-1.19.1-cp37-cp37m-win_amd64.whl", "pycairo-1.19.1-cp37-cp37m-win_amd64.whl") - print("Sucessfully downloaded Cairo for your system") - print("Installing Cairo") - os.system("pip install pycairo-1.19.1-cp37-cp37m-win_amd64.whl") - os.remove("pycairo-1.19.1-cp37-cp37m-win_amd64.whl") - - # In case the python version is 3.8 and the system is 32-bit, try pycairo-1.19.1-cp38-cp38-win32.whl version of cairo - elif sys.version[:3]=='3.8' and platform.machine()=='x86': - urllib.request.urlretrieve("https://download.lfd.uci.edu/pythonlibs/w3jqiv8s/pycairo-1.19.1-cp38-cp38-win32.whl", "pycairo-1.19.1-cp38-cp38-win32.whl") - print("Sucessfully downloaded Cairo for your system") - print("Installing Cairo") - os.system("pip install pycairo-1.19.1-cp38-cp38-win32.whl") - os.remove("pycairo-1.19.1-cp38-cp38-win32.whl") - - # In case the python version is 3.8 and the system is AMD64, try pycairo-1.19.1-cp38-cp38-win_amd64.whl version of cairo - elif sys.version[:3]=='3.8' and platform.machine()=='AMD64': - urllib.request.urlretrieve("https://download.lfd.uci.edu/pythonlibs/w3jqiv8s/pycairo-1.19.1-cp38-cp38-win_amd64.whl", "pycairo-1.19.1-cp38-cp38-win_amd64.whl") - print("Sucessfully downloaded Cairo for your system") - print("Installing Cairo") - os.system("pip install pycairo-1.19.1-cp38-cp38-win_amd64.whl") - os.remove("pycairo-1.19.1-cp38-cp38-win_amd64.whl") + # In case the python version is 3.7 and the system is 32-bit, try pycairo‑{ver_num}‑cp37‑cp37m‑win32.whl version of cairo + elif sys.version[:3] == "3.7" and platform.machine() == "x86": + url = f"https://github.com/{fork}/pycairo/releases/download/v{ver_num}/pycairo-{ver_num}-cp37-cp37m-win32.whl" + + # In case the python version is 3.7 and the system is AMD64, try pycairo-{ver_num}-cp37-cp37m-win_amd64.whl version of cairo + elif sys.version[:3] == "3.7" and platform.machine() == "AMD64": + url = f"https://github.com/{fork}/pycairo/releases/download/v{ver_num}/pycairo-{ver_num}-cp37-cp37m-win_amd64.whl" + + # In case the python version is 3.8 and the system is 32-bit, try pycairo-{ver_num}-cp38-cp38-win32.whl version of cairo + elif sys.version[:3] == "3.8" and platform.machine() == "x86": + url = f"https://github.com/{fork}/pycairo/releases/download/v{ver_num}/pycairo-{ver_num}-cp38-cp38-win32.whl" + + # In case the python version is 3.8 and the system is AMD64, try pycairo-{ver_num}-cp38-cp38-win_amd64.whl version of cairo + elif sys.version[:3] == "3.8" and platform.machine() == "AMD64": + url = f"https://github.com/{fork}/pycairo/releases/download/v{ver_num}/pycairo-{ver_num}-cp38-cp38-win_amd64.whl" + else: + raise Exception("Could not find a PyCairo version for your system!") + + filename = url.split("/")[-1] + urllib.request.urlretrieve(url, filename) + os.system(f"pip{sys.version[:3]} install {filename}") + print("Installed PyCairo.\nCleaning up...") + os.remove(filename) + print("Done.") +else: + raise Exception("This script only works if your operating system is Windows.") diff --git a/tests/test_logging/expected.txt b/tests/test_logging/expected.txt new file mode 100644 index 0000000000..a5e2a9d0c5 --- /dev/null +++ b/tests/test_logging/expected.txt @@ -0,0 +1,4 @@ + INFO Read configuration files config.py + INFO scene_file_writer.py + File ready at + diff --git a/tests/test_logging/test_logging.py b/tests/test_logging/test_logging.py index a203440444..3d5dd7ed43 100644 --- a/tests/test_logging/test_logging.py +++ b/tests/test_logging/test_logging.py @@ -17,11 +17,9 @@ def capture(command,instream=None): def test_logging_to_file(python_version): """Test logging Terminal output to a log file. - `rich` formats it's output based on the size of the terminal it is outputting to. - As such, since there is no way to obtain the terminal size of the testing device - before running the test, this test employs a workaround where instead of the exact - text of the log (which can differ in whitespace and truncation) only the constant - parts, such as keywords, are compared. + As some data will differ with each log (the timestamps, file paths, line nums etc) + a regex substitution has been employed to replace the strings that may change with + whitespace. """ path_basic_scene = os.path.join("tests", "tests_data", "basic_scenes.py") expected=['INFO', 'Read', 'configuration', 'files:', 'config.py:', 'INFO', @@ -38,7 +36,10 @@ def test_logging_to_file(python_version): else: enc="utf-8" with open(log_file_path,encoding=enc) as logfile: - logs=logfile.read().split() - logs=[e for e in logs if not any(x in e for x in ["\\","/",".mp4","[","]"])] - logs=[re.sub('[0-9]', '', i) for i in logs] - assert logs==expected, err + logs=logfile.read() + pattern = r"\[(.*)]|:([1-9]*)|([A-Z]?:?[\/\\].*cfg)|([A-Z]?:?[\/\\].*mp4)" + + logs = re.sub(pattern,lambda m: " "*len((m.group(0))),logs) + with open(os.path.join(os.path.dirname(__file__),"expected.txt"),"r") as expectedfile: + expected = re.sub(pattern,lambda m: " "*len((m.group(0))),expectedfile.read()) + assert logs == expected,logs diff --git a/tests/tests_data/manim.cfg b/tests/tests_data/manim.cfg index 573960fff3..6a3178abfb 100644 --- a/tests/tests_data/manim.cfg +++ b/tests/tests_data/manim.cfg @@ -4,3 +4,6 @@ write_to_movie = True # write_all = False save_last_frame = False # save_pngs = False + +[logger] +log_width = 256 \ No newline at end of file From 232b7c9f11c1053b467b076f76d5451b7d29f0d7 Mon Sep 17 00:00:00 2001 From: Aathish Date: Mon, 3 Aug 2020 16:57:28 +0530 Subject: [PATCH 28/32] Formatted as per black's wishes. --- manim/utils/config_utils.py | 11 +++--- tests/test_logging/test_logging.py | 62 +++++++++++++++++++++--------- 2 files changed, 49 insertions(+), 24 deletions(-) diff --git a/manim/utils/config_utils.py b/manim/utils/config_utils.py index 359acc10de..e590966253 100644 --- a/manim/utils/config_utils.py +++ b/manim/utils/config_utils.py @@ -48,14 +48,14 @@ def _parse_file_writer_config(config_parser, args): "save_pngs", "save_as_gif", "write_all", - 'log_to_file' + "log_to_file", ]: attr = getattr(args, boolean_opt) fw_config[boolean_opt] = ( default.getboolean(boolean_opt) if attr is None else attr ) # for str_opt in ['media_dir', 'video_dir', 'tex_dir', 'text_dir']: - for str_opt in ["media_dir","log_dir"]: + for str_opt in ["media_dir", "log_dir"]: attr = getattr(args, str_opt) fw_config[str_opt] = default[str_opt] if attr is None else attr dir_names = {"video_dir": "videos", "tex_dir": "Tex", "text_dir": "texts"} @@ -223,7 +223,8 @@ def _parse_cli(arg_list, input=True): "--log_to_file", action="store_const", const=True, - help="Log terminal output to file.") + help="Log terminal output to file.", + ) # The default value of the following is set in manim.cfg parser.add_argument( @@ -237,8 +238,7 @@ def _parse_cli(arg_list, input=True): ) parser.add_argument( - "--log_dir", - help="directory to write log files to", + "--log_dir", help="directory to write log files to", ) # video_group = parser.add_mutually_exclusive_group() @@ -346,6 +346,7 @@ def _init_dirs(config): else: os.makedirs(folder) + def _from_command_line(): """Determine if manim was called from the command line.""" # Manim can be called from the command line in three different diff --git a/tests/test_logging/test_logging.py b/tests/test_logging/test_logging.py index 3d5dd7ed43..3ce7f6f47d 100644 --- a/tests/test_logging/test_logging.py +++ b/tests/test_logging/test_logging.py @@ -5,12 +5,11 @@ import pytest import re -def capture(command,instream=None): - proc = subprocess.Popen(command, - stdout=subprocess.PIPE, - stderr=subprocess.PIPE, - stdin=instream - ) + +def capture(command, instream=None): + proc = subprocess.Popen( + command, stdout=subprocess.PIPE, stderr=subprocess.PIPE, stdin=instream + ) out, err = proc.communicate() return out, err, proc.returncode @@ -22,24 +21,49 @@ def test_logging_to_file(python_version): whitespace. """ path_basic_scene = os.path.join("tests", "tests_data", "basic_scenes.py") - expected=['INFO', 'Read', 'configuration', 'files:', 'config.py:', 'INFO', - 'scene_file_writer.py:', 'File', 'ready', 'at'] + expected = [ + "INFO", + "Read", + "configuration", + "files:", + "config.py:", + "INFO", + "scene_file_writer.py:", + "File", + "ready", + "at", + ] path_output = os.path.join("tests_cache", "media_temp") - command = [python_version, "-m", "manim", path_basic_scene, - "SquareToCircle", "-l", "--log_to_file", "--log_dir",os.path.join(path_output,"logs"), "--media_dir", path_output] + command = [ + python_version, + "-m", + "manim", + path_basic_scene, + "SquareToCircle", + "-l", + "--log_to_file", + "--log_dir", + os.path.join(path_output, "logs"), + "--media_dir", + path_output, + ] out, err, exitcode = capture(command) - log_file_path=os.path.join(path_output, "logs", "SquareToCircle.log") + log_file_path = os.path.join(path_output, "logs", "SquareToCircle.log") assert exitcode == 0, err assert os.path.exists(log_file_path), err if sys.platform.startswith("win32") or sys.platform.startswith("cygwin"): - enc="Windows-1252" + enc = "Windows-1252" else: - enc="utf-8" - with open(log_file_path,encoding=enc) as logfile: - logs=logfile.read() + enc = "utf-8" + with open(log_file_path, encoding=enc) as logfile: + logs = logfile.read() pattern = r"\[(.*)]|:([1-9]*)|([A-Z]?:?[\/\\].*cfg)|([A-Z]?:?[\/\\].*mp4)" - logs = re.sub(pattern,lambda m: " "*len((m.group(0))),logs) - with open(os.path.join(os.path.dirname(__file__),"expected.txt"),"r") as expectedfile: - expected = re.sub(pattern,lambda m: " "*len((m.group(0))),expectedfile.read()) - assert logs == expected,logs + logs = re.sub(pattern, lambda m: " " * len((m.group(0))), logs) + with open( + os.path.join(os.path.dirname(__file__), "expected.txt"), "r" + ) as expectedfile: + expected = re.sub( + pattern, lambda m: " " * len((m.group(0))), expectedfile.read() + ) + assert logs == expected, logs From fac22b395ed3b48f9b74b2bb032e90bcb0f31db3 Mon Sep 17 00:00:00 2001 From: Aathish Date: Mon, 3 Aug 2020 18:35:57 +0530 Subject: [PATCH 29/32] Add cfg subcmd case for log_width and height. Modified regex to only ignore numbers and paths. --- manim/utils/cfg_subcmds.py | 19 ++++++++++++++----- tests/test_cli/test_cfg_subcmd.py | 2 +- tests/test_cli/write_cfg_sbcmd_input.txt | 3 +++ tests/test_logging/expected.txt | 4 ++-- tests/test_logging/test_logging.py | 14 +------------- 5 files changed, 21 insertions(+), 21 deletions(-) diff --git a/manim/utils/cfg_subcmds.py b/manim/utils/cfg_subcmds.py index 2b73cbc94e..f48b4abcdc 100644 --- a/manim/utils/cfg_subcmds.py +++ b/manim/utils/cfg_subcmds.py @@ -93,15 +93,24 @@ def write(level=None, openfile=False): console.print(RICH_COLOUR_INSTRUCTIONS) default = replace_keys(default) for key in default: - console.print(f"Enter the style for {key}:", style=key, end="") + desc = ( + "style" if key not in ["log.width", "log.height"] else "value" + ) + style = key if key not in ["log.width", "log.height"] else None + cond = ( + is_valid_style + if key not in ["log.width", "log.height"] + else lambda m: m.isdigit() + ) + console.print(f"Enter the {desc} for {key}:", style=style, end="") temp = input() if temp: - while not is_valid_style(temp): + while not cond(temp): console.print( - "[red bold]Invalid style. Try again.[/red bold]" + f"[red bold]Invalid {desc}. Try again.[/red bold]" ) console.print( - f"Enter the style for {key}:", style=key, end="" + f"Enter the {desc} for {key}:", style=style, end="" ) temp = input() else: @@ -162,7 +171,7 @@ def show(): for category in current_config: console.print(f"{category}", style="bold green underline") for entry in current_config[category]: - if category == "logger": + if category == "logger" and entry not in ["log_width", "log_height"]: console.print(f"{entry} :", end="") console.print( f" {current_config[category][entry]}", diff --git a/tests/test_cli/test_cfg_subcmd.py b/tests/test_cli/test_cfg_subcmd.py index 9995b0f576..2643ca2532 100644 --- a/tests/test_cli/test_cfg_subcmd.py +++ b/tests/test_cli/test_cfg_subcmd.py @@ -20,7 +20,7 @@ def test_cfg_show(python_version): """Test if the `manim cfg show` command works as intended.""" command = f"cd {this_folder} && {python_version} -m manim cfg show" out, err, exitcode = capture(command, use_shell=True) - assert exitcode == 0 + assert exitcode == 0, err assert f"{os.path.sep}tests{os.path.sep}".encode("utf-8") in out, err diff --git a/tests/test_cli/write_cfg_sbcmd_input.txt b/tests/test_cli/write_cfg_sbcmd_input.txt index 110779f7a8..a9fd7d0d55 100644 --- a/tests/test_cli/write_cfg_sbcmd_input.txt +++ b/tests/test_cli/write_cfg_sbcmd_input.txt @@ -24,3 +24,6 @@ False + + + diff --git a/tests/test_logging/expected.txt b/tests/test_logging/expected.txt index a5e2a9d0c5..a3a48a5e41 100644 --- a/tests/test_logging/expected.txt +++ b/tests/test_logging/expected.txt @@ -1,4 +1,4 @@ - INFO Read configuration files config.py - INFO scene_file_writer.py +[ : : ] INFO Read configuration files: [' '] config.py: +[ : : ] INFO scene_file_writer.py: File ready at diff --git a/tests/test_logging/test_logging.py b/tests/test_logging/test_logging.py index 3ce7f6f47d..89e7773c2d 100644 --- a/tests/test_logging/test_logging.py +++ b/tests/test_logging/test_logging.py @@ -21,18 +21,6 @@ def test_logging_to_file(python_version): whitespace. """ path_basic_scene = os.path.join("tests", "tests_data", "basic_scenes.py") - expected = [ - "INFO", - "Read", - "configuration", - "files:", - "config.py:", - "INFO", - "scene_file_writer.py:", - "File", - "ready", - "at", - ] path_output = os.path.join("tests_cache", "media_temp") command = [ python_version, @@ -57,7 +45,7 @@ def test_logging_to_file(python_version): enc = "utf-8" with open(log_file_path, encoding=enc) as logfile: logs = logfile.read() - pattern = r"\[(.*)]|:([1-9]*)|([A-Z]?:?[\/\\].*cfg)|([A-Z]?:?[\/\\].*mp4)" + pattern = r"([0-9])|([A-Z]?:?[\/\\].*cfg)|([A-Z]?:?[\/\\].*mp4)" logs = re.sub(pattern, lambda m: " " * len((m.group(0))), logs) with open( From 1ca28b2be64e1ec32d93ea60b4fdca9ec9bd276c Mon Sep 17 00:00:00 2001 From: Aathish Date: Mon, 3 Aug 2020 19:49:02 +0530 Subject: [PATCH 30/32] Make regex remove list indicators as well. --- tests/test_logging/expected.txt | 2 +- tests/test_logging/test_logging.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/test_logging/expected.txt b/tests/test_logging/expected.txt index a3a48a5e41..b59ca239ac 100644 --- a/tests/test_logging/expected.txt +++ b/tests/test_logging/expected.txt @@ -1,4 +1,4 @@ -[ : : ] INFO Read configuration files: [' '] config.py: +[ : : ] INFO Read configuration files: config.py: [ : : ] INFO scene_file_writer.py: File ready at diff --git a/tests/test_logging/test_logging.py b/tests/test_logging/test_logging.py index 89e7773c2d..e2047b11f0 100644 --- a/tests/test_logging/test_logging.py +++ b/tests/test_logging/test_logging.py @@ -45,7 +45,7 @@ def test_logging_to_file(python_version): enc = "utf-8" with open(log_file_path, encoding=enc) as logfile: logs = logfile.read() - pattern = r"([0-9])|([A-Z]?:?[\/\\].*cfg)|([A-Z]?:?[\/\\].*mp4)" + pattern = r"([0-9])|(\['[A-Z]?:?[\/\\].*cfg'])|([A-Z]?:?[\/\\].*mp4)" logs = re.sub(pattern, lambda m: " " * len((m.group(0))), logs) with open( From cc574f339474e8f42235ca22d3e553c513772e9c Mon Sep 17 00:00:00 2001 From: Aathish Date: Mon, 3 Aug 2020 21:46:43 +0530 Subject: [PATCH 31/32] Make regex remove timestamps. --- tests/test_logging/expected.txt | 4 ++-- tests/test_logging/test_logging.py | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/test_logging/expected.txt b/tests/test_logging/expected.txt index b59ca239ac..ac9ee92030 100644 --- a/tests/test_logging/expected.txt +++ b/tests/test_logging/expected.txt @@ -1,4 +1,4 @@ -[ : : ] INFO Read configuration files: config.py: -[ : : ] INFO scene_file_writer.py: + INFO Read configuration files: config.py: + INFO scene_file_writer.py: File ready at diff --git a/tests/test_logging/test_logging.py b/tests/test_logging/test_logging.py index e2047b11f0..02999e39ba 100644 --- a/tests/test_logging/test_logging.py +++ b/tests/test_logging/test_logging.py @@ -45,7 +45,7 @@ def test_logging_to_file(python_version): enc = "utf-8" with open(log_file_path, encoding=enc) as logfile: logs = logfile.read() - pattern = r"([0-9])|(\['[A-Z]?:?[\/\\].*cfg'])|([A-Z]?:?[\/\\].*mp4)" + pattern = r"(\[?\d+:?]?)|(\['[A-Z]?:?[\/\\].*cfg'])|([A-Z]?:?[\/\\].*mp4)" logs = re.sub(pattern, lambda m: " " * len((m.group(0))), logs) with open( From 1f51a2d18310cf68230774417b9bc08aa93a1ce7 Mon Sep 17 00:00:00 2001 From: Aathish Date: Mon, 3 Aug 2020 21:58:36 +0530 Subject: [PATCH 32/32] Added a comment about the monstrous regex. --- tests/test_logging/test_logging.py | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/test_logging/test_logging.py b/tests/test_logging/test_logging.py index 02999e39ba..892889a6ef 100644 --- a/tests/test_logging/test_logging.py +++ b/tests/test_logging/test_logging.py @@ -45,6 +45,7 @@ def test_logging_to_file(python_version): enc = "utf-8" with open(log_file_path, encoding=enc) as logfile: logs = logfile.read() + # The following regex pattern selects timestamps, file paths and all numbers.. pattern = r"(\[?\d+:?]?)|(\['[A-Z]?:?[\/\\].*cfg'])|([A-Z]?:?[\/\\].*mp4)" logs = re.sub(pattern, lambda m: " " * len((m.group(0))), logs)