From c4d67881a941999555c6a63004c3a83ccccce15f Mon Sep 17 00:00:00 2001 From: JasonGrace2282 Date: Wed, 14 Aug 2024 12:35:16 -0400 Subject: [PATCH 1/5] New method of specifying seed --- manim/_config/logger_utils.py | 5 +++ manim/scene/scene.py | 26 +++++++++++---- manim/utils/deprecation.py | 56 +++++++++++++++++++++++--------- mypy.ini | 3 ++ tests/module/scene/test_scene.py | 16 +++++++++ 5 files changed, 85 insertions(+), 21 deletions(-) diff --git a/manim/_config/logger_utils.py b/manim/_config/logger_utils.py index 9205635eef..5e7a4accf6 100644 --- a/manim/_config/logger_utils.py +++ b/manim/_config/logger_utils.py @@ -95,6 +95,11 @@ def make_logger( keywords=HIGHLIGHTED_KEYWORDS, ) + # redirect warnings.warn to logging.warn + logging.captureWarnings(True) + py_warning = logging.getLogger("py.warnings") + py_warning.addHandler(rich_handler) + # finally, the logger logger = logging.getLogger("manim") logger.addHandler(rich_handler) diff --git a/manim/scene/scene.py b/manim/scene/scene.py index 0e03293929..dfb4a3c156 100644 --- a/manim/scene/scene.py +++ b/manim/scene/scene.py @@ -14,6 +14,7 @@ import threading import time import types +import warnings from queue import Queue import srt @@ -98,17 +99,30 @@ def construct(self): """ + random_seed: int | None = None + def __init__( self, - renderer=None, - camera_class=Camera, - always_update_mobjects=False, - random_seed=None, - skip_animations=False, + renderer: OpenGLRenderer | CairoRenderer | None = None, + camera_class: type[Camera] = Camera, + always_update_mobjects: bool = False, + random_seed: int | None = None, + skip_animations: bool = False, ): self.camera_class = camera_class self.always_update_mobjects = always_update_mobjects - self.random_seed = random_seed + + if random_seed is not None: + warnings.warn( + "Setting the random seed in the Scene constructor is deprecated. " + "Please set the class attribute random_seed instead.", + category=FutureWarning, + # assuming no scene subclassing, this should refer + # to instantiation of the Scene + stacklevel=4, + ) + self.random_seed = random_seed + self.skip_animations = skip_animations self.animations = None diff --git a/manim/utils/deprecation.py b/manim/utils/deprecation.py index afa9cb41a2..4eeb4729c0 100644 --- a/manim/utils/deprecation.py +++ b/manim/utils/deprecation.py @@ -8,13 +8,18 @@ import inspect import logging import re -from collections.abc import Iterable -from typing import Any, Callable +import warnings +from collections.abc import Callable, Iterable +from typing import Any, TypeVar, cast, overload from decorator import decorate, decorator +from typing_extensions import ParamSpec logger = logging.getLogger("manim") +T = TypeVar("T") +P = ParamSpec("P") + def _get_callable_info(callable_: Callable, /) -> tuple[str, str]: """Returns type and name of a callable. @@ -70,12 +75,32 @@ def _deprecation_text_component( return f"deprecated {since}and {until}.{msg}" +@overload +def deprecated( + func: None = None, + since: str | None = None, + until: str | None = None, + replacement: str | None = None, + message: str = "", +) -> Callable[[Callable[P, T]], Callable[P, T]]: ... + + +@overload def deprecated( - func: Callable = None, + func: Callable[P, T], since: str | None = None, until: str | None = None, replacement: str | None = None, - message: str | None = "", + message: str = "", +) -> Callable[P, T]: ... + + +def deprecated( + func: Callable | None = None, + since: str | None = None, + until: str | None = None, + replacement: str | None = None, + message: str = "", ) -> Callable: """Decorator to mark a callable as deprecated. @@ -187,7 +212,7 @@ def warning_msg(for_docs: bool = False) -> str: deprecated = _deprecation_text_component(since, until, msg) return f"The {what} {name} has been {deprecated}" - def deprecate_docs(func: Callable): + def deprecate_docs(func: Callable[..., object]) -> None: """Adjust docstring to indicate the deprecation. Parameters @@ -199,7 +224,7 @@ def deprecate_docs(func: Callable): doc_string = func.__doc__ or "" func.__doc__ = f"{doc_string}\n\n.. attention:: Deprecated\n {warning}" - def deprecate(func: Callable, *args, **kwargs): + def deprecate(func: Callable[P, T], *args: P.args, **kwargs: P.kwargs) -> T: """The actual decorator used to extend the callables behavior. Logs a warning message. @@ -219,12 +244,12 @@ def deprecate(func: Callable, *args, **kwargs): The return value of the given callable when being passed the given arguments. """ - logger.warning(warning_msg()) + warnings.warn(warning_msg(), category=FutureWarning, stacklevel=1) return func(*args, **kwargs) if type(func).__name__ != "function": deprecate_docs(func) - func.__init__ = decorate(func.__init__, deprecate) + func.__init__ = decorate(func.__init__, deprecate) # type: ignore[misc] return func func = decorate(func, deprecate) @@ -236,10 +261,10 @@ def deprecated_params( params: str | Iterable[str] | None = None, since: str | None = None, until: str | None = None, - message: str | None = "", + message: str = "", redirections: None | (Iterable[tuple[str, str] | Callable[..., dict[str, Any]]]) = None, -) -> Callable: +) -> Callable[[Callable[P, T]], Callable[P, T]]: """Decorator to mark parameters of a callable as deprecated. It can also be used to automatically redirect deprecated parameter values to their @@ -426,7 +451,7 @@ def foo(**kwargs): redirections = list(redirections) - def warning_msg(func: Callable, used: list[str]): + def warning_msg(func: Callable, used: list[str]) -> str: """Generate the deprecation warning message. Parameters @@ -449,7 +474,7 @@ def warning_msg(func: Callable, used: list[str]): deprecated = _deprecation_text_component(since, until, message) return f"The parameter{parameter_s} {used_} of {what} {name} {has_have_been} {deprecated}" - def redirect_params(kwargs: dict, used: list[str]): + def redirect_params(kwargs: dict[str, Any], used: list[str]) -> None: """Adjust the keyword arguments as defined by the redirections. Parameters @@ -473,7 +498,7 @@ def redirect_params(kwargs: dict, used: list[str]): if len(redirector_args) > 0: kwargs.update(redirector(**redirector_args)) - def deprecate_params(func, *args, **kwargs): + def deprecate_params(func: Callable[P, T], *args: P.args, **kwargs: P.kwargs) -> T: """The actual decorator function used to extend the callables behavior. Logs a warning message when a deprecated parameter is used and redirects it if @@ -501,8 +526,9 @@ def deprecate_params(func, *args, **kwargs): used.append(param) if len(used) > 0: - logger.warning(warning_msg(func, used)) + warnings.warn(warning_msg(func, used), category=FutureWarning, stacklevel=1) redirect_params(kwargs, used) return func(*args, **kwargs) - return decorator(deprecate_params) + # unfortunately, decorator has really bad stubs that involve Any + return cast(Callable, decorator(deprecate_params)) diff --git a/mypy.ini b/mypy.ini index 956b44ae21..ff337ba353 100644 --- a/mypy.ini +++ b/mypy.ini @@ -82,6 +82,9 @@ ignore_errors = True [mypy-manim.utils.*] ignore_errors = True +[mypy-manim.utils.deprecation] +ignore_errors = False + [mypy-manim.utils.iterables] ignore_errors = False warn_return_any = False diff --git a/tests/module/scene/test_scene.py b/tests/module/scene/test_scene.py index 6c3240980b..cf08514b20 100644 --- a/tests/module/scene/test_scene.py +++ b/tests/module/scene/test_scene.py @@ -101,3 +101,19 @@ def assert_names(mobjs, names): scene.replace(second, beta) assert_names(scene.mobjects, ["alpha", "group", "fourth"]) assert_names(scene.mobjects[1], ["beta", "third"]) + + +def test_random_seed(): + class GoodScene(Scene): + random_seed = 3 + + class BadScene(Scene): + def __init__(self): + super().__init__(random_seed=3) + + good = GoodScene() + assert good.random_seed == 3 + + with pytest.warns(FutureWarning): + bad = BadScene() + assert bad.random_seed == 3 From 6897ea033f0b6dc3155e90511cdcb528ed038137 Mon Sep 17 00:00:00 2001 From: JasonGrace2282 Date: Wed, 14 Aug 2024 12:41:32 -0400 Subject: [PATCH 2/5] docs --- manim/scene/scene.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/manim/scene/scene.py b/manim/scene/scene.py index dfb4a3c156..10b40ac4a6 100644 --- a/manim/scene/scene.py +++ b/manim/scene/scene.py @@ -97,6 +97,15 @@ class MyScene(Scene): def construct(self): self.play(Write(Text("Hello World!"))) + You can specify the seed for random functions inside Manim via :attr:`Scene.random_seed`. + + .. code-block:: python + + class MyScene(Scene): + random_seed = 3 + + def construct(self): ... + """ random_seed: int | None = None From d83c8eac8d69734c98becb28c7fa56691a8b84ad Mon Sep 17 00:00:00 2001 From: JasonGrace2282 Date: Wed, 14 Aug 2024 12:46:40 -0400 Subject: [PATCH 3/5] Improve test to make sure random is correctly set --- tests/module/scene/test_scene.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/tests/module/scene/test_scene.py b/tests/module/scene/test_scene.py index cf08514b20..8a794c1351 100644 --- a/tests/module/scene/test_scene.py +++ b/tests/module/scene/test_scene.py @@ -1,7 +1,9 @@ from __future__ import annotations import datetime +import random +import numpy as np import pytest from manim import Circle, FadeIn, Group, Mobject, Scene, Square @@ -113,7 +115,13 @@ def __init__(self): good = GoodScene() assert good.random_seed == 3 + random_random = random.random() + np_random = np.random.random() with pytest.warns(FutureWarning): bad = BadScene() assert bad.random_seed == 3 + + # check that they both actually set the seed + assert random.random() == random_random + assert np.random.random() == np_random From 4e6587ed84d8fd1f01f0603701ecfb6b115125c8 Mon Sep 17 00:00:00 2001 From: JasonGrace2282 Date: Wed, 14 Aug 2024 12:54:34 -0400 Subject: [PATCH 4/5] More precise typing --- manim/utils/deprecation.py | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/manim/utils/deprecation.py b/manim/utils/deprecation.py index 4eeb4729c0..864c2ae67e 100644 --- a/manim/utils/deprecation.py +++ b/manim/utils/deprecation.py @@ -96,12 +96,12 @@ def deprecated( def deprecated( - func: Callable | None = None, + func: Callable[P, T] | None = None, since: str | None = None, until: str | None = None, replacement: str | None = None, message: str = "", -) -> Callable: +) -> Callable[P, T] | Callable[[Callable[P, T]], Callable[P, T]]: """Decorator to mark a callable as deprecated. The decorated callable will cause a warning when used. The docstring of the @@ -185,7 +185,11 @@ def foo(): """ # If used as factory: if func is None: - return lambda func: deprecated(func, since, until, replacement, message) + + def wrapper(f: Callable[P, T]) -> Callable[P, T]: + return deprecated(f, since, until, replacement, message) + + return wrapper what, name = _get_callable_info(func) From d7295c1106d53df4edf7bd1b34cc8ab354c1959b Mon Sep 17 00:00:00 2001 From: JasonGrace2282 Date: Wed, 14 Aug 2024 13:11:01 -0400 Subject: [PATCH 5/5] Rewrite tests to use pytest.warns instead of a caplog --- tests/module/utils/test_deprecation.py | 236 ++++++++----------------- 1 file changed, 78 insertions(+), 158 deletions(-) diff --git a/tests/module/utils/test_deprecation.py b/tests/module/utils/test_deprecation.py index c822a7cbdf..33bf15dadd 100644 --- a/tests/module/utils/test_deprecation.py +++ b/tests/module/utils/test_deprecation.py @@ -1,11 +1,8 @@ from __future__ import annotations -from manim.utils.deprecation import deprecated, deprecated_params - +import pytest -def _get_caplog_record_msg(manim_caplog): - logger_name, level, message = manim_caplog.record_tuples[0] - return message +from manim.utils.deprecation import deprecated, deprecated_params @deprecated @@ -67,88 +64,61 @@ def __init__(self): doc_admonition = "\n\n.. attention:: Deprecated\n " -def test_deprecate_class_no_args(manim_caplog): +def test_deprecate_class_no_args(): """Test the deprecation of a class (decorator with no arguments).""" - f = Foo() - 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." - ) + + msg = "The class Foo has been deprecated and may be removed in a later version." + with pytest.warns(FutureWarning, match=msg): + f = Foo() assert f.__doc__ == f"{doc_admonition}{msg}" -def test_deprecate_class_since(manim_caplog): +def test_deprecate_class_since(): """Test the deprecation of a class (decorator with since argument).""" - b = Bar() - 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." - ) + msg = "The class Bar has been deprecated since v0.6.0 and may be removed in a later version." + with pytest.warns(FutureWarning, match=msg): + b = Bar() assert b.__doc__ == f"The Bar class.{doc_admonition}{msg}" -def test_deprecate_class_until(manim_caplog): +def test_deprecate_class_until(): """Test the deprecation of a class (decorator with until argument).""" - bz = Baz() - 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." - ) + msg = "The class Baz has been deprecated and is expected to be removed after 06/01/2021." + with pytest.warns(FutureWarning, match=msg): + bz = Baz() assert bz.__doc__ == f"The Baz class.{doc_admonition}{msg}" -def test_deprecate_class_since_and_until(manim_caplog): +def test_deprecate_class_since_and_until(): """Test the deprecation of a class (decorator with since and until arguments).""" - qx = Qux() - 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." - ) + msg = "The class Qux has been deprecated since 0.7.0 and is expected to be removed after 0.9.0-rc2." + with pytest.warns(FutureWarning, match=msg): + qx = Qux() assert qx.__doc__ == f"{doc_admonition}{msg}" -def test_deprecate_class_msg(manim_caplog): +def test_deprecate_class_msg(): """Test the deprecation of a class (decorator with msg argument).""" - qu = Quux() - 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." - ) + msg = "The class Quux has been deprecated and may be removed in a later version. Use something else." + with pytest.warns(FutureWarning, match=msg): + qu = Quux() assert qu.__doc__ == f"{doc_admonition}{msg}" -def test_deprecate_class_replacement(manim_caplog): +def test_deprecate_class_replacement(): """Test the deprecation of a class (decorator with replacement argument).""" - qz = Quuz() - 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." - ) + msg = "The class Quuz has been deprecated and may be removed in a later version. Use ReplaceQuuz instead." + with pytest.warns(FutureWarning, match=msg): + qz = Quuz() doc_msg = "The class Quuz has been deprecated and may be removed in a later version. Use :class:`~.ReplaceQuuz` instead." assert qz.__doc__ == f"{doc_admonition}{doc_msg}" -def test_deprecate_class_all(manim_caplog): +def test_deprecate_class_all(): """Test the deprecation of a class (decorator with all arguments).""" - qza = QuuzAll() - 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." - ) + 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." + with pytest.warns(FutureWarning, match=msg): + qza = QuuzAll() doc_msg = "The class QuuzAll has been deprecated since 0.7.0 and is expected to be removed after 1.2.1. Use :class:`~.ReplaceQuuz` instead. Don't use this please." assert qza.__doc__ == f"{doc_admonition}{doc_msg}" @@ -233,156 +203,106 @@ def quuz(self, **kwargs): return kwargs -def test_deprecate_func_no_args(manim_caplog): +def test_deprecate_func_no_args(): """Test the deprecation of a method (decorator with no arguments).""" - useless() - 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." - ) + msg = "The function useless has been deprecated and may be removed in a later version." + with pytest.warns(FutureWarning, match=msg): + useless() assert useless.__doc__ == f"{doc_admonition}{msg}" -def test_deprecate_func_in_class_since_and_message(manim_caplog): +def test_deprecate_func_in_class_since_and_message(): """Test the deprecation of a method within a class (decorator with since and message arguments).""" t = Top() - t.mid_func() - 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." - ) + 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." + with pytest.warns(FutureWarning, match=msg): + t.mid_func() assert t.mid_func.__doc__ == f"Middle function in Top.{doc_admonition}{msg}" -def test_deprecate_nested_class_until_and_replacement(manim_caplog): +def test_deprecate_nested_class_until_and_replacement(): """Test the deprecation of a nested class (decorator with until and replacement arguments).""" - n = Top().Nested() - 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." - ) + msg = "The class Top.Nested has been deprecated and is expected to be removed after 1.4.0. Use Top.NewNested instead." + with pytest.warns(FutureWarning, match=msg): + n = Top().Nested() doc_msg = "The class Top.Nested has been deprecated and is expected to be removed after 1.4.0. Use :class:`~.Top.NewNested` instead." assert n.__doc__ == f"{doc_admonition}{doc_msg}" -def test_deprecate_nested_class_func_since_and_until(manim_caplog): +def test_deprecate_nested_class_func_since_and_until(): """Test the deprecation of a method within a nested class (decorator with since and until arguments).""" n = Top().NewNested() - n.nested_func() - 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." - ) + msg = "The method Top.NewNested.nested_func has been deprecated since 1.0.0 and is expected to be removed after 12/25/2025." + with pytest.warns(FutureWarning, match=msg): + n.nested_func() assert ( n.nested_func.__doc__ == f"Nested function in Top.NewNested.{doc_admonition}{msg}" ) -def test_deprecate_nested_func(manim_caplog): +def test_deprecate_nested_func(): """Test the deprecation of a nested method (decorator with no arguments).""" b = Top().Bottom() answer = b.normal_func() - answer(1) - 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." - ) + msg = "The method Top.Bottom.normal_func..nested_func has been deprecated and may be removed in a later version." + with pytest.warns(FutureWarning, match=msg): + answer(1) assert answer.__doc__ == f"{doc_admonition}{msg}" -def test_deprecate_func_params(manim_caplog): +def test_deprecate_func_params(): """Test the deprecation of method parameters (decorator with params argument).""" t = Top() - t.foo(a=2, b=3, z=4) - 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." - ) + msg = "The parameters a and b of method Top.foo have been deprecated and may be removed in a later version. Use something else." + with pytest.warns(FutureWarning, match=msg): + t.foo(a=2, b=3, z=4) -def test_deprecate_func_single_param_since_and_until(manim_caplog): +def test_deprecate_func_single_param_since_and_until(): """Test the deprecation of a single method parameter (decorator with since and until arguments).""" t = Top() - t.bar(a=1, b=2) - 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." - ) + msg = "The parameter a of method Top.bar has been deprecated since v0.2 and is expected to be removed after v0.4." + with pytest.warns(FutureWarning, match=msg): + t.bar(a=1, b=2) -def test_deprecate_func_param_redirect_tuple(manim_caplog): +def test_deprecate_func_param_redirect_tuple(): """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(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." - ) + msg = "The parameter old_param of method Top.baz has been deprecated and may be removed in a later version." + with pytest.warns(FutureWarning, match=msg): + obj = t.baz(x=1, old_param=2) assert obj == {"x": 1, "new_param": 2} -def test_deprecate_func_param_redirect_lambda(manim_caplog): +def test_deprecate_func_param_redirect_lambda(): """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(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." - ) + msg = "The parameter runtime_in_ms of method Top.qux has been deprecated and may be removed in a later version." + with pytest.warns(FutureWarning, match=msg): + obj = t.qux(runtime_in_ms=500) assert obj == {"run_time": 0.5} -def test_deprecate_func_param_redirect_many_to_one(manim_caplog): +def test_deprecate_func_param_redirect_many_to_one(): """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(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." - ) + msg = "The parameters point2D_x and point2D_y of method Top.quux have been deprecated and may be removed in a later version." + with pytest.warns(FutureWarning, match=msg): + obj = t.quux(point2D_x=3, point2D_y=5) assert obj == {"point2D": (3, 5)} -def test_deprecate_func_param_redirect_one_to_many(manim_caplog): +def test_deprecate_func_param_redirect_one_to_many(): """Test the deprecation of one method parameter and redirecting it to many.""" t = Top() - obj1 = t.quuz(point2D=0) - 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." - ) + msg = "The parameter point2D of method Top.quuz has been deprecated and may be removed in a later version." + with pytest.warns(FutureWarning, match=msg): + obj1 = t.quuz(point2D=0) assert obj1 == {"x": 0, "y": 0} - manim_caplog.clear() - - obj2 = t.quuz(point2D=(2, 3)) - 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." - ) + msg = "The parameter point2D of method Top.quuz has been deprecated and may be removed in a later version." + with pytest.warns(FutureWarning, match=msg): + obj2 = t.quuz(point2D=(2, 3)) assert obj2 == {"x": 2, "y": 3}