From 3a5a8025bcc100b42b5e8657828443a0d5e7a18e Mon Sep 17 00:00:00 2001 From: "F. Muenkel" <25496279+fmuenkel@users.noreply.github.com> Date: Wed, 26 Mar 2025 02:15:05 -0300 Subject: [PATCH 1/8] Stop ignoring manim._config erros in mypy.ini --- mypy.ini | 6 ------ 1 file changed, 6 deletions(-) diff --git a/mypy.ini b/mypy.ini index 4a96a6d7f8..692d22ee7f 100644 --- a/mypy.ini +++ b/mypy.ini @@ -52,12 +52,6 @@ warn_return_any = True # # disable_recursive_aliases = True -[mypy-manim._config.utils] -ignore_errors = True - -[mypy-manim.animation.animation] -ignore_errors = True - [mypy-manim.animation.creation] ignore_errors = True From e3ace0630c64eddf584ce4cf90e953c5447045b2 Mon Sep 17 00:00:00 2001 From: Henrik Skov Midtiby Date: Sat, 26 Apr 2025 12:00:36 +0200 Subject: [PATCH 2/8] Aspect ratio should be a float. --- manim/_config/utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/manim/_config/utils.py b/manim/_config/utils.py index 96e154d37d..2674399af0 100644 --- a/manim/_config/utils.py +++ b/manim/_config/utils.py @@ -1110,7 +1110,7 @@ def pixel_height(self, value: int) -> None: self._set_pos_number("pixel_height", value, False) @property - def aspect_ratio(self) -> int: + def aspect_ratio(self) -> float: """Aspect ratio (width / height) in pixels (--resolution, -r).""" return self._d["pixel_width"] / self._d["pixel_height"] From ff5b06fd5d7ebefad134a15f7ba420adbba8d3ad Mon Sep 17 00:00:00 2001 From: Henrik Skov Midtiby Date: Fri, 23 May 2025 09:00:45 +0200 Subject: [PATCH 3/8] Handled more mypy issues in _config/utils.py --- manim/_config/utils.py | 65 +++++++++++++++++++++++------------------- 1 file changed, 36 insertions(+), 29 deletions(-) diff --git a/manim/_config/utils.py b/manim/_config/utils.py index 2674399af0..0bc6649f3b 100644 --- a/manim/_config/utils.py +++ b/manim/_config/utils.py @@ -20,7 +20,7 @@ import os import re import sys -from collections.abc import Iterable, Iterator, Mapping, MutableMapping +from collections.abc import Iterator, Mapping, MutableMapping from pathlib import Path from typing import TYPE_CHECKING, Any, ClassVar, NoReturn @@ -135,7 +135,7 @@ def make_config_parser( return parser -def _determine_quality(qual: str) -> str: +def _determine_quality(qual: str | None) -> str: for quality, values in constants.QUALITIES.items(): if values["flag"] is not None and values["flag"] == qual: return quality @@ -338,6 +338,7 @@ def __len__(self) -> int: def __contains__(self, key: object) -> bool: try: + assert isinstance(key, str) self.__getitem__(key) return True except AttributeError: @@ -659,8 +660,8 @@ def digest_parser(self, parser: configparser.ConfigParser) -> Self: # plugins plugins = parser["CLI"].get("plugins", fallback="", raw=True) - plugins = [] if plugins == "" else plugins.split(",") - self.plugins = plugins + plugin_list = [] if plugins is None or plugins == "" else plugins.split(",") + self.plugins = plugin_list # the next two must be set AFTER digesting pixel_width and pixel_height self["frame_height"] = parser["CLI"].getfloat("frame_height", 8.0) width = parser["CLI"].getfloat("frame_width", None) @@ -670,31 +671,31 @@ def digest_parser(self, parser: configparser.ConfigParser) -> Self: self["frame_width"] = width # other logic - val = parser["CLI"].get("tex_template_file") - if val: - self.tex_template_file = val + tex_template_file = parser["CLI"].get("tex_template_file") + if tex_template_file: + self.tex_template_file = Path(tex_template_file) - val = parser["CLI"].get("progress_bar") - if val: - self.progress_bar = val + progress_bar = parser["CLI"].get("progress_bar") + if progress_bar: + self.progress_bar = progress_bar - val = parser["ffmpeg"].get("loglevel") - if val: - self.ffmpeg_loglevel = val + ffmpeg_loglevel = parser["ffmpeg"].get("loglevel") + if ffmpeg_loglevel: + self.ffmpeg_loglevel = ffmpeg_loglevel try: - val = parser["jupyter"].getboolean("media_embed") + media_embed = parser["jupyter"].getboolean("media_embed") except ValueError: - val = None - self.media_embed = val + media_embed = None + self.media_embed = media_embed - val = parser["jupyter"].get("media_width") - if val: - self.media_width = val + media_width = parser["jupyter"].get("media_width") + if media_width: + self.media_width = media_width - val = parser["CLI"].get("quality", fallback="", raw=True) - if val: - self.quality = _determine_quality(val) + quality = parser["CLI"].get("quality", fallback="", raw=True) + if quality: + self.quality = _determine_quality(quality) return self @@ -1042,7 +1043,7 @@ def verbosity(self, val: str) -> None: logger.setLevel(val) @property - def format(self) -> str: + def format(self) -> str | None: """File format; "png", "gif", "mp4", "webm" or "mov".""" return self._d["format"] @@ -1074,7 +1075,7 @@ def ffmpeg_loglevel(self, val: str) -> None: logging.getLogger("libav").setLevel(self.ffmpeg_loglevel) @property - def media_embed(self) -> bool: + def media_embed(self) -> bool | None: """Whether to embed videos in Jupyter notebook.""" return self._d["media_embed"] @@ -1112,6 +1113,8 @@ def pixel_height(self, value: int) -> None: @property def aspect_ratio(self) -> float: """Aspect ratio (width / height) in pixels (--resolution, -r).""" + assert isinstance(self._d["pixel_width"], int) + assert isinstance(self._d["pixel_height"], int) return self._d["pixel_width"] / self._d["pixel_height"] @property @@ -1135,6 +1138,7 @@ def frame_width(self, value: float) -> None: @property def frame_y_radius(self) -> float: """Half the frame height (no flag).""" + assert isinstance(self._d["frame_height"], float) return self._d["frame_height"] / 2 @frame_y_radius.setter @@ -1146,6 +1150,7 @@ def frame_y_radius(self, value: float) -> None: @property def frame_x_radius(self) -> float: """Half the frame width (no flag).""" + assert isinstance(self._d["frame_width"], float) return self._d["frame_width"] / 2 @frame_x_radius.setter @@ -1310,6 +1315,7 @@ def quality(self, value: str | None) -> None: @property def transparent(self) -> bool: """Whether the background opacity is less than 1.0 (-t).""" + assert isinstance(self._d["background_opacity"], float) return self._d["background_opacity"] < 1.0 @transparent.setter @@ -1637,6 +1643,7 @@ def get_dir(self, key: str, **kwargs: Any) -> Path: all_args["quality"] = f"{self.pixel_height}p{self.frame_rate:g}" path = self._d[key] + assert isinstance(path, str) while "{" in path: try: path = path.format(**all_args) @@ -1736,7 +1743,7 @@ def custom_folders(self, value: str | Path) -> None: self._set_dir("custom_folders", value) @property - def input_file(self) -> str: + def input_file(self) -> str | Path: """Input file name.""" return self._d["input_file"] @@ -1765,7 +1772,7 @@ def scene_names(self, value: list[str]) -> None: @property def tex_template(self) -> TexTemplate: """Template used when rendering Tex. See :class:`.TexTemplate`.""" - if not hasattr(self, "_tex_template") or not self._tex_template: + if not hasattr(self, "_tex_template") or not self._tex_template: # type: ignore[has-type] fn = self._d["tex_template_file"] if fn: self._tex_template = TexTemplate.from_file(fn) @@ -1801,7 +1808,7 @@ def plugins(self) -> list[str]: return self._d["plugins"] @plugins.setter - def plugins(self, value: list[str]): + def plugins(self, value: list[str]) -> None: self._d["plugins"] = value @@ -1848,7 +1855,7 @@ def __init__(self, c: ManimConfig) -> None: self.__dict__["_c"] = c # there are required by parent class Mapping to behave like a dict - def __getitem__(self, key: str | int) -> Any: + def __getitem__(self, key: str) -> Any: if key in self._OPTS: return self._c[key] elif key in self._CONSTANTS: @@ -1856,7 +1863,7 @@ def __getitem__(self, key: str | int) -> Any: else: raise KeyError(key) - def __iter__(self) -> Iterable[str]: + def __iter__(self) -> Iterator[Any]: return iter(list(self._OPTS) + list(self._CONSTANTS)) def __len__(self) -> int: From 4313933cd631c5b50d4b03f8ee5aa5b775b97782 Mon Sep 17 00:00:00 2001 From: Henrik Skov Midtiby Date: Fri, 23 May 2025 09:01:31 +0200 Subject: [PATCH 4/8] Handled more mypy issues in _config/utils.py --- manim/_config/utils.py | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/manim/_config/utils.py b/manim/_config/utils.py index 0bc6649f3b..3d411a6d56 100644 --- a/manim/_config/utils.py +++ b/manim/_config/utils.py @@ -429,7 +429,7 @@ def __deepcopy__(self, memo: dict[str, Any]) -> Self: # Deepcopying the underlying dict is enough because all properties # either read directly from it or compute their value on the fly from # values read directly from it. - c._d = copy.deepcopy(self._d, memo) + c._d = copy.deepcopy(self._d, memo) # type: ignore[arg-type] return c # helper type-checking methods @@ -655,8 +655,10 @@ def digest_parser(self, parser: configparser.ConfigParser) -> Self: "window_size" ] # if not "default", get a tuple of the position if window_size != "default": - window_size = tuple(map(int, re.split(r"[;,\-]", window_size))) - self.window_size = window_size + window_size_numbers = tuple(map(int, re.split(r"[;,\-]", window_size))) + self.window_size = window_size_numbers + else: + self.window_size = window_size # plugins plugins = parser["CLI"].get("plugins", fallback="", raw=True) @@ -1143,7 +1145,7 @@ def frame_y_radius(self) -> float: @frame_y_radius.setter def frame_y_radius(self, value: float) -> None: - self._d.__setitem__("frame_y_radius", value) or self._d.__setitem__( + self._d.__setitem__("frame_y_radius", value) or self._d.__setitem__( # type: ignore[func-returns-value] "frame_height", 2 * value ) @@ -1155,7 +1157,7 @@ def frame_x_radius(self) -> float: @frame_x_radius.setter def frame_x_radius(self, value: float) -> None: - self._d.__setitem__("frame_x_radius", value) or self._d.__setitem__( + self._d.__setitem__("frame_x_radius", value) or self._d.__setitem__( # type: ignore[func-returns-value] "frame_width", 2 * value ) @@ -1288,7 +1290,7 @@ def frame_size(self) -> tuple[int, int]: @frame_size.setter def frame_size(self, value: tuple[int, int]) -> None: - self._d.__setitem__("pixel_width", value[0]) or self._d.__setitem__( + self._d.__setitem__("pixel_width", value[0]) or self._d.__setitem__( # type: ignore[func-returns-value] "pixel_height", value[1] ) @@ -1298,7 +1300,7 @@ def quality(self) -> str | None: keys = ["pixel_width", "pixel_height", "frame_rate"] q = {k: self[k] for k in keys} for qual in constants.QUALITIES: - if all(q[k] == constants.QUALITIES[qual][k] for k in keys): + if all(q[k] == constants.QUALITIES[qual][k] for k in keys): # type: ignore[literal-required] return qual return None @@ -1425,12 +1427,12 @@ def window_position(self, value: str) -> None: self._d.__setitem__("window_position", value) @property - def window_size(self) -> str: - """The size of the opengl window as 'width,height' or 'default' to automatically scale the window based on the display monitor.""" + def window_size(self) -> str | tuple[int, ...]: + """The size of the opengl window. 'default' to automatically scale the window based on the display monitor.""" return self._d["window_size"] @window_size.setter - def window_size(self, value: str) -> None: + def window_size(self, value: str | tuple[int, ...]) -> None: self._d.__setitem__("window_size", value) def resolve_movie_file_extension(self, is_transparent: bool) -> None: @@ -1881,4 +1883,4 @@ def __delitem__(self, key: Any) -> NoReturn: for opt in list(ManimFrame._OPTS) + list(ManimFrame._CONSTANTS): - setattr(ManimFrame, opt, property(lambda self, o=opt: self[o])) + setattr(ManimFrame, opt, property(lambda self, o=opt: self[o])) # type: ignore[misc] From be389ca61b292688b3c2a87c344967db819eb0a5 Mon Sep 17 00:00:00 2001 From: Henrik Skov Midtiby Date: Sun, 27 Apr 2025 21:42:48 +0200 Subject: [PATCH 5/8] Removed two assert statements that triggered errors in the unittests. --- manim/_config/utils.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/manim/_config/utils.py b/manim/_config/utils.py index 3d411a6d56..eb80f5a754 100644 --- a/manim/_config/utils.py +++ b/manim/_config/utils.py @@ -1140,8 +1140,7 @@ def frame_width(self, value: float) -> None: @property def frame_y_radius(self) -> float: """Half the frame height (no flag).""" - assert isinstance(self._d["frame_height"], float) - return self._d["frame_height"] / 2 + return self._d["frame_height"] / 2 # type: ignore[operator] @frame_y_radius.setter def frame_y_radius(self, value: float) -> None: @@ -1152,8 +1151,7 @@ def frame_y_radius(self, value: float) -> None: @property def frame_x_radius(self) -> float: """Half the frame width (no flag).""" - assert isinstance(self._d["frame_width"], float) - return self._d["frame_width"] / 2 + return self._d["frame_width"] / 2 # type: ignore[operator] @frame_x_radius.setter def frame_x_radius(self, value: float) -> None: From b663eb4bde151648e6965a2e1b0de533c4339da0 Mon Sep 17 00:00:00 2001 From: Henrik Skov Midtiby Date: Fri, 23 May 2025 09:03:27 +0200 Subject: [PATCH 6/8] Fix last mypy issue in utils.py and activate mypy checking --- manim/_config/utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/manim/_config/utils.py b/manim/_config/utils.py index eb80f5a754..98886759d1 100644 --- a/manim/_config/utils.py +++ b/manim/_config/utils.py @@ -1459,7 +1459,7 @@ def enable_gui(self, value: bool) -> None: self._set_boolean("enable_gui", value) @property - def gui_location(self) -> tuple[Any]: + def gui_location(self) -> tuple[int, ...]: """Location parameters for the GUI window (e.g., screen coordinates or layout settings).""" return self._d["gui_location"] From 632666ae4b2362209dc3417c86f4f8c4287922ff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Francisco=20Manr=C3=ADquez=20Novoa?= <49853152+chopan050@users.noreply.github.com> Date: Mon, 11 Aug 2025 14:42:02 -0400 Subject: [PATCH 7/8] Fix type of window_size in opengl_renderer_window.py --- manim/renderer/opengl_renderer_window.py | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/manim/renderer/opengl_renderer_window.py b/manim/renderer/opengl_renderer_window.py index 4472ba6c2a..3cf2215d26 100644 --- a/manim/renderer/opengl_renderer_window.py +++ b/manim/renderer/opengl_renderer_window.py @@ -25,14 +25,23 @@ class Window(PygletWindow): def __init__( self, renderer: OpenGLRenderer, - window_size: str = config.window_size, + window_size: str | tuple[int, ...] = config.window_size, **kwargs: Any, ) -> None: monitors = get_monitors() mon_index = config.window_monitor monitor = monitors[min(mon_index, len(monitors) - 1)] - if window_size == "default": + invalid_window_size_error_message = ( + "window_size must be specified either as 'default', a string of the form " + "'width,height', or a tuple of 2 ints of the form (width, height)." + ) + + if isinstance(window_size, tuple): + if len(window_size) != 2: + raise ValueError(invalid_window_size_error_message) + size = window_size + elif window_size == "default": # make window_width half the width of the monitor # but make it full screen if --fullscreen window_width = monitor.width @@ -48,9 +57,7 @@ def __init__( (window_width, window_height) = tuple(map(int, window_size.split(","))) size = (window_width, window_height) else: - raise ValueError( - "Window_size must be specified as 'width,height' or 'default'.", - ) + raise ValueError(invalid_window_size_error_message) super().__init__(size=size) From 81f182c5addc4b4d2994887b30e0418b7c2ca113 Mon Sep 17 00:00:00 2001 From: Henrik Skov Midtiby Date: Wed, 27 Aug 2025 08:44:27 +0200 Subject: [PATCH 8/8] ... --- mypy.ini | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/mypy.ini b/mypy.ini index 692d22ee7f..4a96a6d7f8 100644 --- a/mypy.ini +++ b/mypy.ini @@ -52,6 +52,12 @@ warn_return_any = True # # disable_recursive_aliases = True +[mypy-manim._config.utils] +ignore_errors = True + +[mypy-manim.animation.animation] +ignore_errors = True + [mypy-manim.animation.creation] ignore_errors = True