diff --git a/manim/_config/logger_utils.py b/manim/_config/logger_utils.py index f3b43f8f78..9205635eef 100644 --- a/manim/_config/logger_utils.py +++ b/manim/_config/logger_utils.py @@ -99,6 +99,7 @@ def make_logger( logger = logging.getLogger("manim") logger.addHandler(rich_handler) logger.setLevel(verbosity) + logger.propagate = False if not (libav_logger := logging.getLogger()).hasHandlers(): libav_logger.addHandler(rich_handler) diff --git a/manim/_config/utils.py b/manim/_config/utils.py index 6c2492d95e..46613e8d74 100644 --- a/manim/_config/utils.py +++ b/manim/_config/utils.py @@ -40,6 +40,8 @@ __all__ = ["config_file_paths", "make_config_parser", "ManimConfig", "ManimFrame"] +logger = logging.getLogger("manim") + def config_file_paths() -> list[Path]: """The paths where ``.cfg`` files will be searched for. @@ -799,7 +801,7 @@ def digest_args(self, args: argparse.Namespace) -> Self: try: self.upto_animation_number = nflag[1] except Exception: - logging.getLogger("manim").info( + logger.info( f"No end scene number specified in -n option. Rendering from {nflag[0]} onwards...", ) @@ -1038,7 +1040,7 @@ def verbosity(self, val: str) -> None: val, ["DEBUG", "INFO", "WARNING", "ERROR", "CRITICAL"], ) - logging.getLogger("manim").setLevel(val) + logger.setLevel(val) @property def format(self) -> str: @@ -1054,7 +1056,7 @@ def format(self, val: str) -> None: ) self.resolve_movie_file_extension(self.transparent) if self.format == "webm": - logging.getLogger("manim").warning( + logger.warning( "Output format set as webm, this can be slower than other formats", ) @@ -1272,6 +1274,8 @@ def background_opacity(self) -> float: @background_opacity.setter def background_opacity(self, value: float) -> None: self._set_between("background_opacity", value, 0, 1) + if self.background_opacity < 1: + self.resolve_movie_file_extension(is_transparent=True) @property def frame_size(self) -> tuple[int, int]: @@ -1306,8 +1310,8 @@ def quality(self, value: str | None) -> None: @property def transparent(self) -> bool: - """Whether the background opacity is 0.0 (-t).""" - return self._d["background_opacity"] == 0.0 + """Whether the background opacity is less than 1.0 (-t).""" + return self._d["background_opacity"] < 1.0 @transparent.setter def transparent(self, value: bool) -> None: @@ -1425,6 +1429,7 @@ def window_size(self, value: str) -> None: self._d.__setitem__("window_size", value) def resolve_movie_file_extension(self, is_transparent: bool) -> None: + prev_file_extension = self.movie_file_extension if is_transparent: self.movie_file_extension = ".webm" if self.format == "webm" else ".mov" elif self.format == "webm": @@ -1433,6 +1438,11 @@ def resolve_movie_file_extension(self, is_transparent: bool) -> None: self.movie_file_extension = ".mov" else: self.movie_file_extension = ".mp4" + if self.movie_file_extension != prev_file_extension: + logger.warning( + f"Output format changed to '{self.movie_file_extension}' " + "to support transparency", + ) @property def enable_gui(self) -> bool: @@ -1778,7 +1788,7 @@ def tex_template_file(self) -> Path: def tex_template_file(self, val: str) -> None: if val: if not os.access(val, os.R_OK): - logging.getLogger("manim").warning( + logger.warning( f"Custom TeX template {val} not found or not readable.", ) else: diff --git a/manim/cli/render/global_options.py b/manim/cli/render/global_options.py index d7424dd1ee..5941d4fd68 100644 --- a/manim/cli/render/global_options.py +++ b/manim/cli/render/global_options.py @@ -1,13 +1,14 @@ from __future__ import annotations +import logging import re from cloup import Choice, option, option_group -from ... import logger - __all__ = ["global_options"] +logger = logging.getLogger("manim") + def validate_gui_location(ctx, param, value): if value: diff --git a/manim/cli/render/render_options.py b/manim/cli/render/render_options.py index 5a2d992db8..21a91ce393 100644 --- a/manim/cli/render/render_options.py +++ b/manim/cli/render/render_options.py @@ -1,15 +1,16 @@ from __future__ import annotations +import logging import re from cloup import Choice, option, option_group from manim.constants import QUALITIES, RendererType -from ... import logger - __all__ = ["render_options"] +logger = logging.getLogger("manim") + def validate_scene_range(ctx, param, value): try: diff --git a/manim/renderer/shader_wrapper.py b/manim/renderer/shader_wrapper.py index 00e111aad6..8a2b0d1fbe 100644 --- a/manim/renderer/shader_wrapper.py +++ b/manim/renderer/shader_wrapper.py @@ -1,14 +1,13 @@ from __future__ import annotations import copy +import logging import re from pathlib import Path import moderngl import numpy as np -from .. import logger - # Mobjects that should be rendered with # the same shader will be organized and # clumped together based on keeping track @@ -17,6 +16,8 @@ __all__ = ["ShaderWrapper"] +logger = logging.getLogger("manim") + def get_shader_dir(): return Path(__file__).parent / "shaders" diff --git a/manim/utils/deprecation.py b/manim/utils/deprecation.py index 2d443c5e72..4fd76880b4 100644 --- a/manim/utils/deprecation.py +++ b/manim/utils/deprecation.py @@ -6,13 +6,14 @@ import inspect +import logging import re from collections.abc import Iterable from typing import Any, Callable from decorator import decorate, decorator -from .. import logger +logger = logging.getLogger("manim") def _get_callable_info(callable: Callable) -> tuple[str, str]: diff --git a/manim/utils/testing/_frames_testers.py b/manim/utils/testing/_frames_testers.py index be0bc38447..a0ca9ddd99 100644 --- a/manim/utils/testing/_frames_testers.py +++ b/manim/utils/testing/_frames_testers.py @@ -1,18 +1,19 @@ from __future__ import annotations import contextlib +import logging import warnings from pathlib import Path import numpy as np -from manim import logger - from ._show_diff import show_diff_helper FRAME_ABSOLUTE_TOLERANCE = 1.01 FRAME_MISMATCH_RATIO_TOLERANCE = 1e-5 +logger = logging.getLogger("manim") + class _FramesTester: def __init__(self, file_path: Path, show_diff=False) -> None: diff --git a/tests/conftest.py b/tests/conftest.py index e69c1080d5..16962f4d2c 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -1,5 +1,6 @@ from __future__ import annotations +import logging import sys from pathlib import Path @@ -45,6 +46,15 @@ def pytest_collection_modifyitems(config, items): item.add_marker(slow_skip) +@pytest.fixture +def manim_caplog(caplog): + logger = logging.getLogger("manim") + logger.propagate = True + caplog.set_level(logging.INFO, logger="manim") + yield caplog + logger.propagate = False + + @pytest.fixture def config(): saved = manim.config.copy() diff --git a/tests/helpers/graphical_units.py b/tests/helpers/graphical_units.py index 01eae516a9..1395559e52 100644 --- a/tests/helpers/graphical_units.py +++ b/tests/helpers/graphical_units.py @@ -2,14 +2,16 @@ from __future__ import annotations +import logging import tempfile from pathlib import Path import numpy as np -from manim import logger from manim.scene.scene import Scene +logger = logging.getLogger("manim") + def set_test_scene(scene_object: type[Scene], module_name: str, config): """Function used to set up the test data for a new feature. This will basically set up a pre-rendered frame for a scene. This is meant to be used only diff --git a/tests/module/animation/test_animation.py b/tests/module/animation/test_animation.py index d57f57247e..448c0e76d6 100644 --- a/tests/module/animation/test_animation.py +++ b/tests/module/animation/test_animation.py @@ -15,20 +15,20 @@ def test_animation_forbidden_run_time(run_time): test_scene.play(FadeIn(None, run_time=run_time)) -def test_animation_run_time_shorter_than_frame_rate(caplog, config): +def test_animation_run_time_shorter_than_frame_rate(manim_caplog, config): test_scene = Scene() test_scene.play(FadeIn(None, run_time=1 / (config.frame_rate + 1))) assert ( "Original run time of FadeIn(Mobject) is shorter than current frame rate" - in caplog.text + in manim_caplog.text ) @pytest.mark.parametrize("frozen_frame", [False, True]) -def test_wait_run_time_shorter_than_frame_rate(caplog, frozen_frame): +def test_wait_run_time_shorter_than_frame_rate(manim_caplog, frozen_frame): test_scene = Scene() test_scene.wait(1e-9, frozen_frame=frozen_frame) assert ( "Original run time of Wait(Mobject) is shorter than current frame rate" - in caplog.text + in manim_caplog.text ) diff --git a/tests/module/mobject/geometry/test_unit_geometry.py b/tests/module/mobject/geometry/test_unit_geometry.py index 45f4da279f..e953ef2de3 100644 --- a/tests/module/mobject/geometry/test_unit_geometry.py +++ b/tests/module/mobject/geometry/test_unit_geometry.py @@ -15,8 +15,7 @@ def test_get_arc_center(): ) -def test_BackgroundRectangle(caplog): - caplog.set_level(logging.INFO) +def test_BackgroundRectangle(manim_caplog): c = Circle() bg = BackgroundRectangle(c) bg.set_style(fill_opacity=0.42) @@ -24,5 +23,5 @@ def test_BackgroundRectangle(caplog): bg.set_style(fill_opacity=1, hello="world") assert ( "Argument {'hello': 'world'} is ignored in BackgroundRectangle.set_style." - in caplog.text + in manim_caplog.text ) diff --git a/tests/module/utils/test_deprecation.py b/tests/module/utils/test_deprecation.py index e498b8b196..c822a7cbdf 100644 --- a/tests/module/utils/test_deprecation.py +++ b/tests/module/utils/test_deprecation.py @@ -1,23 +1,13 @@ from __future__ import annotations -import logging - -import pytest - from manim.utils.deprecation import deprecated, deprecated_params -def _get_caplog_record_msg(warn_caplog_manim): - logger_name, level, message = warn_caplog_manim.record_tuples[0] +def _get_caplog_record_msg(manim_caplog): + logger_name, level, message = manim_caplog.record_tuples[0] return message -@pytest.fixture() -def warn_caplog_manim(caplog): - caplog.set_level(logging.WARNING, logger="manim") - yield caplog - - @deprecated class Foo: def __init__(self): @@ -77,11 +67,11 @@ def __init__(self): doc_admonition = "\n\n.. attention:: Deprecated\n " -def test_deprecate_class_no_args(warn_caplog_manim): +def test_deprecate_class_no_args(manim_caplog): """Test the deprecation of a class (decorator with no arguments).""" f = Foo() - assert len(warn_caplog_manim.record_tuples) == 1 - msg = _get_caplog_record_msg(warn_caplog_manim) + assert len(manim_caplog.record_tuples) == 1 + msg = _get_caplog_record_msg(manim_caplog) assert ( msg == "The class Foo has been deprecated and may be removed in a later version." @@ -89,11 +79,11 @@ def test_deprecate_class_no_args(warn_caplog_manim): assert f.__doc__ == f"{doc_admonition}{msg}" -def test_deprecate_class_since(warn_caplog_manim): +def test_deprecate_class_since(manim_caplog): """Test the deprecation of a class (decorator with since argument).""" b = Bar() - assert len(warn_caplog_manim.record_tuples) == 1 - msg = _get_caplog_record_msg(warn_caplog_manim) + assert len(manim_caplog.record_tuples) == 1 + msg = _get_caplog_record_msg(manim_caplog) assert ( msg == "The class Bar has been deprecated since v0.6.0 and may be removed in a later version." @@ -101,11 +91,11 @@ def test_deprecate_class_since(warn_caplog_manim): assert b.__doc__ == f"The Bar class.{doc_admonition}{msg}" -def test_deprecate_class_until(warn_caplog_manim): +def test_deprecate_class_until(manim_caplog): """Test the deprecation of a class (decorator with until argument).""" bz = Baz() - assert len(warn_caplog_manim.record_tuples) == 1 - msg = _get_caplog_record_msg(warn_caplog_manim) + assert len(manim_caplog.record_tuples) == 1 + msg = _get_caplog_record_msg(manim_caplog) assert ( msg == "The class Baz has been deprecated and is expected to be removed after 06/01/2021." @@ -113,11 +103,11 @@ def test_deprecate_class_until(warn_caplog_manim): assert bz.__doc__ == f"The Baz class.{doc_admonition}{msg}" -def test_deprecate_class_since_and_until(warn_caplog_manim): +def test_deprecate_class_since_and_until(manim_caplog): """Test the deprecation of a class (decorator with since and until arguments).""" qx = Qux() - assert len(warn_caplog_manim.record_tuples) == 1 - msg = _get_caplog_record_msg(warn_caplog_manim) + assert len(manim_caplog.record_tuples) == 1 + msg = _get_caplog_record_msg(manim_caplog) assert ( msg == "The class Qux has been deprecated since 0.7.0 and is expected to be removed after 0.9.0-rc2." @@ -125,11 +115,11 @@ def test_deprecate_class_since_and_until(warn_caplog_manim): assert qx.__doc__ == f"{doc_admonition}{msg}" -def test_deprecate_class_msg(warn_caplog_manim): +def test_deprecate_class_msg(manim_caplog): """Test the deprecation of a class (decorator with msg argument).""" qu = Quux() - assert len(warn_caplog_manim.record_tuples) == 1 - msg = _get_caplog_record_msg(warn_caplog_manim) + assert len(manim_caplog.record_tuples) == 1 + msg = _get_caplog_record_msg(manim_caplog) assert ( msg == "The class Quux has been deprecated and may be removed in a later version. Use something else." @@ -137,11 +127,11 @@ def test_deprecate_class_msg(warn_caplog_manim): assert qu.__doc__ == f"{doc_admonition}{msg}" -def test_deprecate_class_replacement(warn_caplog_manim): +def test_deprecate_class_replacement(manim_caplog): """Test the deprecation of a class (decorator with replacement argument).""" qz = Quuz() - assert len(warn_caplog_manim.record_tuples) == 1 - msg = _get_caplog_record_msg(warn_caplog_manim) + assert len(manim_caplog.record_tuples) == 1 + msg = _get_caplog_record_msg(manim_caplog) assert ( msg == "The class Quuz has been deprecated and may be removed in a later version. Use ReplaceQuuz instead." @@ -150,11 +140,11 @@ def test_deprecate_class_replacement(warn_caplog_manim): assert qz.__doc__ == f"{doc_admonition}{doc_msg}" -def test_deprecate_class_all(warn_caplog_manim): +def test_deprecate_class_all(manim_caplog): """Test the deprecation of a class (decorator with all arguments).""" qza = QuuzAll() - assert len(warn_caplog_manim.record_tuples) == 1 - msg = _get_caplog_record_msg(warn_caplog_manim) + assert len(manim_caplog.record_tuples) == 1 + msg = _get_caplog_record_msg(manim_caplog) assert ( msg == "The class QuuzAll has been deprecated since 0.7.0 and is expected to be removed after 1.2.1. Use ReplaceQuuz instead. Don't use this please." @@ -243,11 +233,11 @@ def quuz(self, **kwargs): return kwargs -def test_deprecate_func_no_args(warn_caplog_manim): +def test_deprecate_func_no_args(manim_caplog): """Test the deprecation of a method (decorator with no arguments).""" useless() - assert len(warn_caplog_manim.record_tuples) == 1 - msg = _get_caplog_record_msg(warn_caplog_manim) + assert len(manim_caplog.record_tuples) == 1 + msg = _get_caplog_record_msg(manim_caplog) assert ( msg == "The function useless has been deprecated and may be removed in a later version." @@ -255,12 +245,12 @@ def test_deprecate_func_no_args(warn_caplog_manim): assert useless.__doc__ == f"{doc_admonition}{msg}" -def test_deprecate_func_in_class_since_and_message(warn_caplog_manim): +def test_deprecate_func_in_class_since_and_message(manim_caplog): """Test the deprecation of a method within a class (decorator with since and message arguments).""" t = Top() t.mid_func() - assert len(warn_caplog_manim.record_tuples) == 1 - msg = _get_caplog_record_msg(warn_caplog_manim) + assert len(manim_caplog.record_tuples) == 1 + msg = _get_caplog_record_msg(manim_caplog) assert ( msg == "The method Top.mid_func has been deprecated since 0.8.0 and may be removed in a later version. This method is useless." @@ -268,11 +258,11 @@ def test_deprecate_func_in_class_since_and_message(warn_caplog_manim): assert t.mid_func.__doc__ == f"Middle function in Top.{doc_admonition}{msg}" -def test_deprecate_nested_class_until_and_replacement(warn_caplog_manim): +def test_deprecate_nested_class_until_and_replacement(manim_caplog): """Test the deprecation of a nested class (decorator with until and replacement arguments).""" n = Top().Nested() - assert len(warn_caplog_manim.record_tuples) == 1 - msg = _get_caplog_record_msg(warn_caplog_manim) + assert len(manim_caplog.record_tuples) == 1 + msg = _get_caplog_record_msg(manim_caplog) assert ( msg == "The class Top.Nested has been deprecated and is expected to be removed after 1.4.0. Use Top.NewNested instead." @@ -281,12 +271,12 @@ def test_deprecate_nested_class_until_and_replacement(warn_caplog_manim): assert n.__doc__ == f"{doc_admonition}{doc_msg}" -def test_deprecate_nested_class_func_since_and_until(warn_caplog_manim): +def test_deprecate_nested_class_func_since_and_until(manim_caplog): """Test the deprecation of a method within a nested class (decorator with since and until arguments).""" n = Top().NewNested() n.nested_func() - assert len(warn_caplog_manim.record_tuples) == 1 - msg = _get_caplog_record_msg(warn_caplog_manim) + assert len(manim_caplog.record_tuples) == 1 + msg = _get_caplog_record_msg(manim_caplog) assert ( msg == "The method Top.NewNested.nested_func has been deprecated since 1.0.0 and is expected to be removed after 12/25/2025." @@ -297,13 +287,13 @@ def test_deprecate_nested_class_func_since_and_until(warn_caplog_manim): ) -def test_deprecate_nested_func(warn_caplog_manim): +def test_deprecate_nested_func(manim_caplog): """Test the deprecation of a nested method (decorator with no arguments).""" b = Top().Bottom() answer = b.normal_func() answer(1) - assert len(warn_caplog_manim.record_tuples) == 1 - msg = _get_caplog_record_msg(warn_caplog_manim) + assert len(manim_caplog.record_tuples) == 1 + msg = _get_caplog_record_msg(manim_caplog) assert ( msg == "The method Top.Bottom.normal_func..nested_func has been deprecated and may be removed in a later version." @@ -311,36 +301,36 @@ def test_deprecate_nested_func(warn_caplog_manim): assert answer.__doc__ == f"{doc_admonition}{msg}" -def test_deprecate_func_params(warn_caplog_manim): +def test_deprecate_func_params(manim_caplog): """Test the deprecation of method parameters (decorator with params argument).""" t = Top() t.foo(a=2, b=3, z=4) - assert len(warn_caplog_manim.record_tuples) == 1 - msg = _get_caplog_record_msg(warn_caplog_manim) + assert len(manim_caplog.record_tuples) == 1 + msg = _get_caplog_record_msg(manim_caplog) assert ( msg == "The parameters a and b of method Top.foo have been deprecated and may be removed in a later version. Use something else." ) -def test_deprecate_func_single_param_since_and_until(warn_caplog_manim): +def test_deprecate_func_single_param_since_and_until(manim_caplog): """Test the deprecation of a single method parameter (decorator with since and until arguments).""" t = Top() t.bar(a=1, b=2) - assert len(warn_caplog_manim.record_tuples) == 1 - msg = _get_caplog_record_msg(warn_caplog_manim) + assert len(manim_caplog.record_tuples) == 1 + msg = _get_caplog_record_msg(manim_caplog) assert ( msg == "The parameter a of method Top.bar has been deprecated since v0.2 and is expected to be removed after v0.4." ) -def test_deprecate_func_param_redirect_tuple(warn_caplog_manim): +def test_deprecate_func_param_redirect_tuple(manim_caplog): """Test the deprecation of a method parameter and redirecting it to a new one using tuple.""" t = Top() obj = t.baz(x=1, old_param=2) - assert len(warn_caplog_manim.record_tuples) == 1 - msg = _get_caplog_record_msg(warn_caplog_manim) + assert len(manim_caplog.record_tuples) == 1 + msg = _get_caplog_record_msg(manim_caplog) assert ( msg == "The parameter old_param of method Top.baz has been deprecated and may be removed in a later version." @@ -348,12 +338,12 @@ def test_deprecate_func_param_redirect_tuple(warn_caplog_manim): assert obj == {"x": 1, "new_param": 2} -def test_deprecate_func_param_redirect_lambda(warn_caplog_manim): +def test_deprecate_func_param_redirect_lambda(manim_caplog): """Test the deprecation of a method parameter and redirecting it to a new one using lambda function.""" t = Top() obj = t.qux(runtime_in_ms=500) - assert len(warn_caplog_manim.record_tuples) == 1 - msg = _get_caplog_record_msg(warn_caplog_manim) + assert len(manim_caplog.record_tuples) == 1 + msg = _get_caplog_record_msg(manim_caplog) assert ( msg == "The parameter runtime_in_ms of method Top.qux has been deprecated and may be removed in a later version." @@ -361,12 +351,12 @@ def test_deprecate_func_param_redirect_lambda(warn_caplog_manim): assert obj == {"run_time": 0.5} -def test_deprecate_func_param_redirect_many_to_one(warn_caplog_manim): +def test_deprecate_func_param_redirect_many_to_one(manim_caplog): """Test the deprecation of multiple method parameters and redirecting them to one.""" t = Top() obj = t.quux(point2D_x=3, point2D_y=5) - assert len(warn_caplog_manim.record_tuples) == 1 - msg = _get_caplog_record_msg(warn_caplog_manim) + assert len(manim_caplog.record_tuples) == 1 + msg = _get_caplog_record_msg(manim_caplog) assert ( msg == "The parameters point2D_x and point2D_y of method Top.quux have been deprecated and may be removed in a later version." @@ -374,23 +364,23 @@ def test_deprecate_func_param_redirect_many_to_one(warn_caplog_manim): assert obj == {"point2D": (3, 5)} -def test_deprecate_func_param_redirect_one_to_many(warn_caplog_manim): +def test_deprecate_func_param_redirect_one_to_many(manim_caplog): """Test the deprecation of one method parameter and redirecting it to many.""" t = Top() obj1 = t.quuz(point2D=0) - assert len(warn_caplog_manim.record_tuples) == 1 - msg = _get_caplog_record_msg(warn_caplog_manim) + assert len(manim_caplog.record_tuples) == 1 + msg = _get_caplog_record_msg(manim_caplog) assert ( msg == "The parameter point2D of method Top.quuz has been deprecated and may be removed in a later version." ) assert obj1 == {"x": 0, "y": 0} - warn_caplog_manim.clear() + manim_caplog.clear() obj2 = t.quuz(point2D=(2, 3)) - assert len(warn_caplog_manim.record_tuples) == 1 - msg = _get_caplog_record_msg(warn_caplog_manim) + assert len(manim_caplog.record_tuples) == 1 + msg = _get_caplog_record_msg(manim_caplog) assert ( msg == "The parameter point2D of method Top.quuz has been deprecated and may be removed in a later version." diff --git a/tests/test_config.py b/tests/test_config.py index 6429f1d5cf..649c94351d 100644 --- a/tests/test_config.py +++ b/tests/test_config.py @@ -75,6 +75,18 @@ def test_transparent(config): np.testing.assert_allclose(frame[0, 0], [0, 0, 0, 0]) +def test_transparent_by_background_opacity(config, dry_run): + config.background_opacity = 0.5 + assert config.transparent is True + + scene = MyScene() + scene.render() + frame = scene.renderer.get_frame() + np.testing.assert_allclose(frame[0, 0], [0, 0, 0, 127]) + assert config.movie_file_extension == ".mov" + assert config.transparent is True + + def test_background_color(config): """Test the 'background_color' config option."""