Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion manim/mobject/graphing/probability.py
Original file line number Diff line number Diff line change
Expand Up @@ -292,7 +292,8 @@ def __init__(
):
if isinstance(bar_colors, str):
logger.warning(
"Passing a string to `bar_colors` has been deprecated since v0.15.2 and will be removed after v0.17.0, the parameter must be a list. "
"Passing a string to `bar_colors` has been deprecated since v0.15.2 and will be removed after v0.17.0, the parameter must be a list. ",
stacklevel=2,
)
bar_colors = list(bar_colors)

Expand Down
1 change: 1 addition & 0 deletions manim/mobject/mobject.py
Original file line number Diff line number Diff line change
Expand Up @@ -504,6 +504,7 @@ def _insert_submobjects(self, index: int, mobjects: Sequence[Mobject]) -> Self:
logger.warning(
"Attempted adding some Mobject as a child more than once, "
"this is not possible. Repetitions are ignored.",
stacklevel=3,
)

if not self.submobjects:
Expand Down
1 change: 1 addition & 0 deletions manim/mobject/opengl/opengl_mobject.py
Original file line number Diff line number Diff line change
Expand Up @@ -863,6 +863,7 @@ def _insert_submobjects(
logger.warning(
"Attempted adding some Mobject as a child more than once, "
"this is not possible. Repetitions are ignored.",
stacklevel=3,
)

if not self._submobjects:
Expand Down
4 changes: 2 additions & 2 deletions manim/mobject/text/text_mobject.py
Original file line number Diff line number Diff line change
Expand Up @@ -487,7 +487,7 @@ def __init__(
elif font.title() in fonts_list:
font = font.title()
else:
logger.warning(f"Font {font} not in {fonts_list}.")
logger.warning(f"Font {font} not in {fonts_list}.", stacklevel=2)
self.font = font
self._font_size = float(font_size)
# needs to be a float or else size is inflated when font_size = 24
Expand Down Expand Up @@ -1214,7 +1214,7 @@ def __init__(
elif font.title() in fonts_list:
font = font.title()
else:
logger.warning(f"Font {font} not in {fonts_list}.")
logger.warning(f"Font {font} not in {fonts_list}.", stacklevel=2)
self.font = font
self._font_size = float(font_size)
self.slant = slant
Expand Down
3 changes: 2 additions & 1 deletion manim/mobject/three_d/three_dimensions.py
Original file line number Diff line number Diff line change
Expand Up @@ -295,7 +295,8 @@ def param_surface(u, v):
if colorscale is None:
logger.warning(
"The value passed to the colorscale keyword argument was None, "
"the surface fill color has not been changed"
"the surface fill color has not been changed",
stacklevel=2,
)
return self
colorscale_list = list(colorscale)
Expand Down
10 changes: 7 additions & 3 deletions manim/scene/scene.py
Original file line number Diff line number Diff line change
Expand Up @@ -1160,7 +1160,8 @@ def validate_run_time(
f"The original {parameter_name} of {method_name}, "
f"{run_time:g} seconds, is too short for the current frame "
f"rate of {fps:g} FPS. Rendering with the shortest possible "
f"{parameter_name} of {seconds_per_frame:g} seconds instead."
f"{parameter_name} of {seconds_per_frame:g} seconds instead.",
stacklevel=3,
)
run_time = seconds_per_frame

Expand Down Expand Up @@ -1602,10 +1603,13 @@ def interact(self, shell: Any, keyboard_thread: threading.Thread) -> None:
def embed(self) -> None:
assert isinstance(self.renderer, OpenGLRenderer)
if not self.session_spec.presentation.live_preview:
logger.warning("Called embed() while no live preview window is available.")
logger.warning(
"Called embed() while no live preview window is available.",
stacklevel=2,
)
return
if self.renderer.file_writer.output_spec.enabled:
logger.warning("embed() is skipped while writing to a file.")
logger.warning("embed() is skipped while writing to a file.", stacklevel=2)
return

self.renderer.animation_start_time = 0
Expand Down
4 changes: 2 additions & 2 deletions manim/utils/deprecation.py
Original file line number Diff line number Diff line change
Expand Up @@ -245,7 +245,7 @@ def deprecate(func: Callable[..., T], *args: Any, **kwargs: Any) -> T:
The return value of the given callable when being passed the given
arguments.
"""
logger.warning(warning_msg())
logger.warning(warning_msg(), stacklevel=3)
return func(*args, **kwargs)

if type(func).__name__ != "function":
Expand Down Expand Up @@ -529,7 +529,7 @@ def deprecate_params(func: Callable[..., T], *args: Any, **kwargs: Any) -> T:
used = [param for param in params if param in kwargs]

if len(used) > 0:
logger.warning(warning_msg(func, used))
logger.warning(warning_msg(func, used), stacklevel=3)
redirect_params(kwargs, used)
return func(*args, **kwargs)

Expand Down
15 changes: 6 additions & 9 deletions tests/module/mobject/text/test_text_mobject.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,5 @@
from __future__ import annotations

from contextlib import redirect_stdout
from io import StringIO

import pytest

from manim.mobject.text.text_mobject import MarkupText, Text
Expand All @@ -19,13 +16,13 @@ def test_font_size():
assert round(markuptext_string.font_size, 5) == 14.4


def test_font_warnings():
def test_font_warnings(manim_caplog):
def warning_printed(font: str, **kwargs) -> bool:
io = StringIO()
with redirect_stdout(io):
Text("hi!", font=font, **kwargs)
txt = io.getvalue()
return "Font" in txt and "not in" in txt
# Inspect the log records rather than rendered console output: the
# latter is wrapped to the terminal width, which can split the message.
manim_caplog.clear()
Text("hi!", font=font, **kwargs)
return any("not in" in record.getMessage() for record in manim_caplog.records)

# check for normal fonts (no warning)
assert not warning_printed("System-ui", warn_missing_font=True)
Expand Down
69 changes: 69 additions & 0 deletions tests/module/utils/test_warning_stacklevel.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
"""Warnings caused by user code should be attributed to the caller.

Each ``logger.warning`` that a user's own call can trigger passes ``stacklevel``,
so the emitted record points at the line the user wrote rather than at the line
inside Manim where the warning happens to live.
"""

from __future__ import annotations

import inspect
import linecache
from logging import LogRecord

from manim import Mobject
from manim.utils.deprecation import deprecated, deprecated_params


@deprecated(since="v0.1.0", message="Use something else.")
def _deprecated_function() -> int:
return 1


@deprecated_params(params="old", since="v0.1.0", message="Use new instead.")
def _function_with_deprecated_param(**kwargs: int) -> int:
return 1


def _assert_blames_caller(record: LogRecord, source_fragment: str) -> None:
"""Assert the record points at the caller's own statement.

Checked by source text rather than a line number so the test survives
reformatting.
"""
caller = inspect.currentframe().f_back.f_code.co_name
assert record.pathname == __file__, (
f"warning was attributed to {record.pathname}, expected the calling module"
)
assert record.funcName == caller, (
f"warning was attributed to {record.funcName}(), expected {caller}()"
)
blamed_line = linecache.getline(record.pathname, record.lineno)
assert source_fragment in blamed_line, (
f"warning pointed at {blamed_line.strip()!r}, "
f"expected the line containing {source_fragment!r}"
)


def test_deprecated_function_warning_points_at_caller(manim_caplog):
_deprecated_function()
_assert_blames_caller(manim_caplog.records[0], "_deprecated_function()")


def test_deprecated_param_warning_points_at_caller(manim_caplog):
_function_with_deprecated_param(old=2)
_assert_blames_caller(
manim_caplog.records[0], "_function_with_deprecated_param(old=2)"
)


def test_duplicate_add_warning_points_at_caller(manim_caplog):
parent, child = Mobject(), Mobject()
parent.add(child, child)
_assert_blames_caller(manim_caplog.records[0], "parent.add(child, child)")


def test_duplicate_add_to_back_warning_points_at_caller(manim_caplog):
parent, child = Mobject(), Mobject()
parent.add_to_back(child, child)
_assert_blames_caller(manim_caplog.records[0], "parent.add_to_back(child, child)")