manim 0.18.0.post0__py3-none-any.whl → 0.19.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of manim might be problematic. Click here for more details.

Files changed (146) hide show
  1. manim/__init__.py +3 -6
  2. manim/__main__.py +61 -20
  3. manim/_config/__init__.py +6 -3
  4. manim/_config/cli_colors.py +16 -8
  5. manim/_config/default.cfg +1 -3
  6. manim/_config/logger_utils.py +14 -8
  7. manim/_config/utils.py +651 -472
  8. manim/animation/animation.py +152 -5
  9. manim/animation/composition.py +80 -39
  10. manim/animation/creation.py +196 -14
  11. manim/animation/fading.py +5 -9
  12. manim/animation/indication.py +103 -47
  13. manim/animation/movement.py +22 -5
  14. manim/animation/rotation.py +3 -2
  15. manim/animation/specialized.py +4 -6
  16. manim/animation/speedmodifier.py +10 -5
  17. manim/animation/transform.py +4 -5
  18. manim/animation/transform_matching_parts.py +1 -1
  19. manim/animation/updaters/mobject_update_utils.py +17 -14
  20. manim/camera/camera.py +15 -6
  21. manim/cli/__init__.py +17 -0
  22. manim/cli/cfg/group.py +70 -44
  23. manim/cli/checkhealth/checks.py +93 -75
  24. manim/cli/checkhealth/commands.py +14 -5
  25. manim/cli/default_group.py +157 -25
  26. manim/cli/init/commands.py +32 -24
  27. manim/cli/plugins/commands.py +16 -3
  28. manim/cli/render/commands.py +72 -60
  29. manim/cli/render/ease_of_access_options.py +4 -3
  30. manim/cli/render/global_options.py +51 -15
  31. manim/cli/render/output_options.py +6 -5
  32. manim/cli/render/render_options.py +97 -32
  33. manim/constants.py +65 -19
  34. manim/gui/gui.py +2 -0
  35. manim/mobject/frame.py +0 -1
  36. manim/mobject/geometry/arc.py +112 -78
  37. manim/mobject/geometry/boolean_ops.py +32 -25
  38. manim/mobject/geometry/labeled.py +300 -77
  39. manim/mobject/geometry/line.py +132 -64
  40. manim/mobject/geometry/polygram.py +126 -30
  41. manim/mobject/geometry/shape_matchers.py +35 -15
  42. manim/mobject/geometry/tips.py +38 -29
  43. manim/mobject/graph.py +414 -133
  44. manim/mobject/graphing/coordinate_systems.py +126 -64
  45. manim/mobject/graphing/functions.py +25 -15
  46. manim/mobject/graphing/number_line.py +24 -10
  47. manim/mobject/graphing/probability.py +2 -10
  48. manim/mobject/graphing/scale.py +6 -5
  49. manim/mobject/matrix.py +17 -19
  50. manim/mobject/mobject.py +314 -165
  51. manim/mobject/opengl/opengl_compatibility.py +2 -0
  52. manim/mobject/opengl/opengl_geometry.py +30 -9
  53. manim/mobject/opengl/opengl_image_mobject.py +2 -0
  54. manim/mobject/opengl/opengl_mobject.py +509 -343
  55. manim/mobject/opengl/opengl_point_cloud_mobject.py +5 -7
  56. manim/mobject/opengl/opengl_surface.py +3 -2
  57. manim/mobject/opengl/opengl_three_dimensions.py +2 -0
  58. manim/mobject/opengl/opengl_vectorized_mobject.py +46 -79
  59. manim/mobject/svg/brace.py +63 -13
  60. manim/mobject/svg/svg_mobject.py +4 -3
  61. manim/mobject/table.py +11 -13
  62. manim/mobject/text/code_mobject.py +186 -548
  63. manim/mobject/text/numbers.py +9 -7
  64. manim/mobject/text/tex_mobject.py +23 -14
  65. manim/mobject/text/text_mobject.py +70 -24
  66. manim/mobject/three_d/polyhedra.py +98 -1
  67. manim/mobject/three_d/three_d_utils.py +4 -4
  68. manim/mobject/three_d/three_dimensions.py +62 -34
  69. manim/mobject/types/image_mobject.py +42 -24
  70. manim/mobject/types/point_cloud_mobject.py +105 -67
  71. manim/mobject/types/vectorized_mobject.py +496 -228
  72. manim/mobject/value_tracker.py +5 -4
  73. manim/mobject/vector_field.py +5 -5
  74. manim/opengl/__init__.py +3 -3
  75. manim/plugins/__init__.py +14 -1
  76. manim/plugins/plugins_flags.py +14 -8
  77. manim/renderer/cairo_renderer.py +20 -10
  78. manim/renderer/opengl_renderer.py +21 -23
  79. manim/renderer/opengl_renderer_window.py +2 -0
  80. manim/renderer/shader.py +2 -3
  81. manim/renderer/shader_wrapper.py +5 -2
  82. manim/renderer/vectorized_mobject_rendering.py +5 -0
  83. manim/scene/moving_camera_scene.py +23 -0
  84. manim/scene/scene.py +90 -43
  85. manim/scene/scene_file_writer.py +316 -165
  86. manim/scene/section.py +17 -15
  87. manim/scene/three_d_scene.py +13 -21
  88. manim/scene/vector_space_scene.py +22 -9
  89. manim/typing.py +830 -70
  90. manim/utils/bezier.py +1667 -399
  91. manim/utils/caching.py +13 -5
  92. manim/utils/color/AS2700.py +2 -0
  93. manim/utils/color/BS381.py +3 -0
  94. manim/utils/color/DVIPSNAMES.py +96 -0
  95. manim/utils/color/SVGNAMES.py +179 -0
  96. manim/utils/color/X11.py +3 -0
  97. manim/utils/color/XKCD.py +3 -0
  98. manim/utils/color/__init__.py +8 -5
  99. manim/utils/color/core.py +844 -309
  100. manim/utils/color/manim_colors.py +7 -9
  101. manim/utils/commands.py +48 -20
  102. manim/utils/config_ops.py +18 -13
  103. manim/utils/debug.py +8 -7
  104. manim/utils/deprecation.py +90 -40
  105. manim/utils/docbuild/__init__.py +17 -0
  106. manim/utils/docbuild/autoaliasattr_directive.py +234 -0
  107. manim/utils/docbuild/autocolor_directive.py +21 -17
  108. manim/utils/docbuild/manim_directive.py +50 -35
  109. manim/utils/docbuild/module_parsing.py +245 -0
  110. manim/utils/exceptions.py +6 -0
  111. manim/utils/family.py +5 -3
  112. manim/utils/family_ops.py +17 -4
  113. manim/utils/file_ops.py +26 -16
  114. manim/utils/hashing.py +9 -7
  115. manim/utils/images.py +10 -4
  116. manim/utils/ipython_magic.py +14 -8
  117. manim/utils/iterables.py +161 -119
  118. manim/utils/module_ops.py +57 -19
  119. manim/utils/opengl.py +83 -24
  120. manim/utils/parameter_parsing.py +32 -0
  121. manim/utils/paths.py +21 -23
  122. manim/utils/polylabel.py +168 -0
  123. manim/utils/qhull.py +218 -0
  124. manim/utils/rate_functions.py +74 -39
  125. manim/utils/simple_functions.py +24 -15
  126. manim/utils/sounds.py +7 -1
  127. manim/utils/space_ops.py +125 -69
  128. manim/utils/testing/__init__.py +17 -0
  129. manim/utils/testing/_frames_testers.py +13 -8
  130. manim/utils/testing/_show_diff.py +5 -3
  131. manim/utils/testing/_test_class_makers.py +33 -18
  132. manim/utils/testing/frames_comparison.py +27 -19
  133. manim/utils/tex.py +127 -197
  134. manim/utils/tex_file_writing.py +47 -45
  135. manim/utils/tex_templates.py +2 -1
  136. manim/utils/unit.py +6 -5
  137. {manim-0.18.0.post0.dist-info → manim-0.19.0.dist-info}/LICENSE.community +1 -1
  138. {manim-0.18.0.post0.dist-info → manim-0.19.0.dist-info}/METADATA +40 -39
  139. manim-0.19.0.dist-info/RECORD +221 -0
  140. {manim-0.18.0.post0.dist-info → manim-0.19.0.dist-info}/WHEEL +1 -1
  141. manim/cli/new/__init__.py +0 -0
  142. manim/cli/new/group.py +0 -189
  143. manim/plugins/import_plugins.py +0 -43
  144. manim-0.18.0.post0.dist-info/RECORD +0 -217
  145. {manim-0.18.0.post0.dist-info → manim-0.19.0.dist-info}/LICENSE +0 -0
  146. {manim-0.18.0.post0.dist-info → manim-0.19.0.dist-info}/entry_points.txt +0 -0
@@ -0,0 +1,234 @@
1
+ """A directive for documenting type aliases and other module-level attributes."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from typing import TYPE_CHECKING
6
+
7
+ from docutils import nodes
8
+ from docutils.parsers.rst import Directive
9
+ from docutils.statemachine import ViewList
10
+
11
+ from manim.utils.docbuild.module_parsing import parse_module_attributes
12
+
13
+ if TYPE_CHECKING:
14
+ from sphinx.application import Sphinx
15
+
16
+ __all__ = ["AliasAttrDocumenter"]
17
+
18
+
19
+ ALIAS_DOCS_DICT, DATA_DICT, TYPEVAR_DICT = parse_module_attributes()
20
+ ALIAS_LIST = [
21
+ alias_name
22
+ for module_dict in ALIAS_DOCS_DICT.values()
23
+ for category_dict in module_dict.values()
24
+ for alias_name in category_dict
25
+ ]
26
+
27
+
28
+ def smart_replace(base: str, alias: str, substitution: str) -> str:
29
+ """Auxiliary function for substituting type aliases into a base
30
+ string, when there are overlaps between the aliases themselves.
31
+
32
+ Parameters
33
+ ----------
34
+ base
35
+ The string in which the type aliases will be located and
36
+ replaced.
37
+ alias
38
+ The substring to be substituted.
39
+ substitution
40
+ The string which will replace every occurrence of ``alias``.
41
+
42
+ Returns
43
+ -------
44
+ str
45
+ The new string after the alias substitution.
46
+ """
47
+ occurrences = []
48
+ len_alias = len(alias)
49
+ len_base = len(base)
50
+
51
+ def condition(char: str) -> bool:
52
+ return not char.isalnum() and char != "_"
53
+
54
+ start = 0
55
+ i = 0
56
+ while True:
57
+ i = base.find(alias, start)
58
+ if i == -1:
59
+ break
60
+ if (i == 0 or condition(base[i - 1])) and (
61
+ i + len_alias == len_base or condition(base[i + len_alias])
62
+ ):
63
+ occurrences.append(i)
64
+ start = i + len_alias
65
+
66
+ for o in occurrences[::-1]:
67
+ base = base[:o] + substitution + base[o + len_alias :]
68
+
69
+ return base
70
+
71
+
72
+ def setup(app: Sphinx) -> None:
73
+ app.add_directive("autoaliasattr", AliasAttrDocumenter)
74
+
75
+
76
+ class AliasAttrDocumenter(Directive):
77
+ """Directive which replaces Sphinx's Autosummary for module-level
78
+ attributes: instead, it manually crafts a new "Type Aliases"
79
+ section, where all the module-level attributes which are explicitly
80
+ annotated as :class:`TypeAlias` are considered as such, for their
81
+ use all around the Manim docs.
82
+
83
+ These type aliases are separated from the "regular" module-level
84
+ attributes, which get their traditional "Module Attributes"
85
+ section autogenerated with Sphinx's Autosummary under "Type
86
+ Aliases".
87
+
88
+ See ``docs/source/_templates/autosummary/module.rst`` to watch
89
+ this directive in action.
90
+
91
+ See :func:`~.parse_module_attributes` for more information on how
92
+ the modules are parsed to obtain the :class:`TypeAlias` information
93
+ and separate it from the other attributes.
94
+ """
95
+
96
+ objtype = "autoaliasattr"
97
+ required_arguments = 1
98
+ has_content = True
99
+
100
+ def run(self) -> list[nodes.Element]:
101
+ module_name = self.arguments[0]
102
+ # not present in the keys of the DICTs
103
+ module_name = module_name.removeprefix("manim.")
104
+ module_alias_dict = ALIAS_DOCS_DICT.get(module_name, None)
105
+ module_attrs_list = DATA_DICT.get(module_name, None)
106
+ module_typevars = TYPEVAR_DICT.get(module_name, None)
107
+
108
+ content = nodes.container()
109
+
110
+ # Add "Type Aliases" section
111
+ if module_alias_dict is not None:
112
+ module_alias_section = nodes.section(ids=[f"{module_name}.alias"])
113
+ content += module_alias_section
114
+
115
+ # Use a rubric (title-like), just like in `module.rst`
116
+ module_alias_section += nodes.rubric(text="Type Aliases")
117
+
118
+ # category_name: str
119
+ # category_dict: AliasCategoryDict = dict[str, AliasInfo]
120
+ for category_name, category_dict in module_alias_dict.items():
121
+ category_section = nodes.section(
122
+ ids=[category_name.lower().replace(" ", "_")]
123
+ )
124
+ module_alias_section += category_section
125
+ # category_name can be possibly "" for uncategorized aliases
126
+ if category_name:
127
+ category_section += nodes.title(text=category_name)
128
+
129
+ category_alias_container = nodes.container()
130
+ category_section += category_alias_container
131
+
132
+ # alias_name: str
133
+ # alias_info: AliasInfo = dict[str, str]
134
+ # Contains "definition": str
135
+ # Can possibly contain "doc": str
136
+ for alias_name, alias_info in category_dict.items():
137
+ # Replace all occurrences of type aliases in the
138
+ # definition for automatic cross-referencing!
139
+ alias_def = alias_info["definition"]
140
+ for A in ALIAS_LIST:
141
+ alias_def = smart_replace(alias_def, A, f":class:`~.{A}`")
142
+
143
+ # Using the `.. class::` directive is CRUCIAL, since
144
+ # function/method parameters are always annotated via
145
+ # classes - therefore Sphinx expects a class
146
+ unparsed = ViewList(
147
+ [
148
+ f".. class:: {alias_name}",
149
+ "",
150
+ " .. parsed-literal::",
151
+ "",
152
+ f" {alias_def}",
153
+ "",
154
+ ]
155
+ )
156
+
157
+ if "doc" in alias_info:
158
+ # Replace all occurrences of type aliases in
159
+ # the docs for automatic cross-referencing!
160
+ alias_doc = alias_info["doc"]
161
+ for A in ALIAS_LIST:
162
+ alias_doc = alias_doc.replace(f"`{A}`", f":class:`~.{A}`")
163
+
164
+ # also hyperlink the TypeVars from that module
165
+ if module_typevars is not None:
166
+ for T in module_typevars:
167
+ alias_doc = alias_doc.replace(f"`{T}`", f":class:`{T}`")
168
+
169
+ # Add all the lines with 4 spaces behind, to consider all the
170
+ # documentation as a paragraph INSIDE the `.. class::` block
171
+ doc_lines = alias_doc.split("\n")
172
+ unparsed.extend(ViewList([f" {line}" for line in doc_lines]))
173
+
174
+ # Parse the reST text into a fresh container
175
+ # https://www.sphinx-doc.org/en/master/extdev/markupapi.html#parsing-directive-content-as-rest
176
+ alias_container = nodes.container()
177
+ self.state.nested_parse(unparsed, 0, alias_container)
178
+ category_alias_container += alias_container
179
+
180
+ # then add the module TypeVars section
181
+ if module_typevars is not None:
182
+ module_typevars_section = nodes.section(ids=[f"{module_name}.typevars"])
183
+ content += module_typevars_section
184
+
185
+ # Use a rubric (title-like), just like in `module.rst`
186
+ module_typevars_section += nodes.rubric(text="TypeVar's")
187
+
188
+ # name: str
189
+ # definition: TypeVarDict = dict[str, str]
190
+ for name, definition in module_typevars.items():
191
+ # Using the `.. class::` directive is CRUCIAL, since
192
+ # function/method parameters are always annotated via
193
+ # classes - therefore Sphinx expects a class
194
+ unparsed = ViewList(
195
+ [
196
+ f".. class:: {name}",
197
+ "",
198
+ " .. parsed-literal::",
199
+ "",
200
+ f" {definition}",
201
+ "",
202
+ ]
203
+ )
204
+
205
+ # Parse the reST text into a fresh container
206
+ # https://www.sphinx-doc.org/en/master/extdev/markupapi.html#parsing-directive-content-as-rest
207
+ typevar_container = nodes.container()
208
+ self.state.nested_parse(unparsed, 0, typevar_container)
209
+ module_typevars_section += typevar_container
210
+
211
+ # Then, add the traditional "Module Attributes" section
212
+ if module_attrs_list is not None:
213
+ module_attrs_section = nodes.section(ids=[f"{module_name}.data"])
214
+ content += module_attrs_section
215
+
216
+ # Use the same rubric (title-like) as in `module.rst`
217
+ module_attrs_section += nodes.rubric(text="Module Attributes")
218
+ # Let Sphinx Autosummary do its thing as always
219
+ # Add all the attribute names with 4 spaces behind, so that
220
+ # they're considered as INSIDE the `.. autosummary::` block
221
+ unparsed = ViewList(
222
+ [
223
+ ".. autosummary::",
224
+ *(f" {attr}" for attr in module_attrs_list),
225
+ ]
226
+ )
227
+
228
+ # Parse the reST text into a fresh container
229
+ # https://www.sphinx-doc.org/en/master/extdev/markupapi.html#parsing-directive-content-as-rest
230
+ data_container = nodes.container()
231
+ self.state.nested_parse(unparsed, 0, data_container)
232
+ module_attrs_section += data_container
233
+
234
+ return [content]
@@ -1,13 +1,20 @@
1
+ """A directive for documenting colors in Manim."""
2
+
1
3
  from __future__ import annotations
2
4
 
3
5
  import inspect
6
+ from typing import TYPE_CHECKING
4
7
 
5
8
  from docutils import nodes
6
9
  from docutils.parsers.rst import Directive
7
- from sphinx.application import Sphinx
8
10
 
9
11
  from manim import ManimColor
10
12
 
13
+ if TYPE_CHECKING:
14
+ from sphinx.application import Sphinx
15
+
16
+ __all__ = ["ManimColorModuleDocumenter"]
17
+
11
18
 
12
19
  def setup(app: Sphinx) -> None:
13
20
  app.add_directive("automanimcolormodule", ManimColorModuleDocumenter)
@@ -19,11 +26,11 @@ class ManimColorModuleDocumenter(Directive):
19
26
  has_content = True
20
27
 
21
28
  def add_directive_header(self, sig: str) -> None:
22
- super().add_directive_header(sig)
29
+ # TODO: The Directive class has no method named
30
+ # add_directive_header.
31
+ super().add_directive_header(sig) # type: ignore[misc]
23
32
 
24
- def run(
25
- self,
26
- ) -> None:
33
+ def run(self) -> list[nodes.Element]:
27
34
  module_name = self.arguments[0]
28
35
  try:
29
36
  import importlib
@@ -32,8 +39,8 @@ class ManimColorModuleDocumenter(Directive):
32
39
  except ImportError:
33
40
  return [
34
41
  nodes.error(
35
- None,
36
- nodes.paragraph(text="Failed to import module '%s'" % module_name),
42
+ None, # type: ignore[arg-type]
43
+ nodes.paragraph(text=f"Failed to import module '{module_name}'"),
37
44
  )
38
45
  ]
39
46
 
@@ -48,13 +55,13 @@ class ManimColorModuleDocumenter(Directive):
48
55
 
49
56
  # Create header rows for the table
50
57
  thead = nodes.thead()
51
- row = nodes.row()
58
+ header_row = nodes.row()
52
59
  for _ in range(num_color_cols):
53
- col1 = nodes.paragraph(text="Color Name")
54
- col2 = nodes.paragraph(text="RGB Hex Code")
55
- row += nodes.entry("", col1)
56
- row += nodes.entry("", col2)
57
- thead += row
60
+ header_col1 = nodes.paragraph(text="Color Name")
61
+ header_col2 = nodes.paragraph(text="RGB Hex Code")
62
+ header_row += nodes.entry("", header_col1)
63
+ header_row += nodes.entry("", header_col2)
64
+ thead += header_row
58
65
  tgroup += thead
59
66
 
60
67
  color_elements = []
@@ -64,10 +71,7 @@ class ManimColorModuleDocumenter(Directive):
64
71
  luminance = 0.2126 * r + 0.7152 * g + 0.0722 * b
65
72
 
66
73
  # Choose the font color based on the background luminance
67
- if luminance > 0.5:
68
- font_color = "black"
69
- else:
70
- font_color = "white"
74
+ font_color = "black" if luminance > 0.5 else "white"
71
75
 
72
76
  color_elements.append((member_name, member_obj.to_hex(), font_color))
73
77
 
@@ -77,27 +77,40 @@ directive:
77
77
  that is rendered in a reference block after the source code.
78
78
 
79
79
  """
80
+
80
81
  from __future__ import annotations
81
82
 
82
83
  import csv
83
84
  import itertools as it
84
- import os
85
85
  import re
86
86
  import shutil
87
87
  import sys
88
88
  import textwrap
89
89
  from pathlib import Path
90
90
  from timeit import timeit
91
+ from typing import TYPE_CHECKING, Any, TypedDict
91
92
 
92
93
  import jinja2
93
94
  from docutils import nodes
94
- from docutils.parsers.rst import Directive, directives # type: ignore
95
+ from docutils.parsers.rst import Directive, directives
95
96
  from docutils.statemachine import StringList
96
97
 
97
98
  from manim import QUALITIES
98
99
  from manim import __version__ as manim_version
99
100
 
100
- classnamedict = {}
101
+ if TYPE_CHECKING:
102
+ from sphinx.application import Sphinx
103
+
104
+
105
+ __all__ = ["ManimDirective"]
106
+
107
+
108
+ classnamedict: dict[str, int] = {}
109
+
110
+
111
+ class SetupMetadata(TypedDict):
112
+ parallel_read_safe: bool
113
+ parallel_write_safe: bool
101
114
 
102
115
 
103
116
  class SkipManimNode(nodes.Admonition, nodes.Element):
@@ -110,14 +123,16 @@ class SkipManimNode(nodes.Admonition, nodes.Element):
110
123
  pass
111
124
 
112
125
 
113
- def visit(self, node, name=""):
114
- self.visit_admonition(node, name)
126
+ def visit(self: SkipManimNode, node: nodes.Element, name: str = "") -> None:
127
+ # TODO: Parent classes don't have a visit_admonition() method.
128
+ self.visit_admonition(node, name) # type: ignore[attr-defined]
115
129
  if not isinstance(node[0], nodes.title):
116
130
  node.insert(0, nodes.title("skip-manim", "Example Placeholder"))
117
131
 
118
132
 
119
- def depart(self, node):
120
- self.depart_admonition(node)
133
+ def depart(self: SkipManimNode, node: nodes.Element) -> None:
134
+ # TODO: Parent classes don't have a depart_admonition() method.
135
+ self.depart_admonition(node) # type: ignore[attr-defined]
121
136
 
122
137
 
123
138
  def process_name_list(option_input: str, reference_type: str) -> list[str]:
@@ -143,6 +158,7 @@ class ManimDirective(Directive):
143
158
 
144
159
  See the module docstring for documentation.
145
160
  """
161
+
146
162
  has_content = True
147
163
  required_arguments = 1
148
164
  optional_arguments = 0
@@ -162,11 +178,11 @@ class ManimDirective(Directive):
162
178
  }
163
179
  final_argument_whitespace = True
164
180
 
165
- def run(self):
181
+ def run(self) -> list[nodes.Element]:
166
182
  # Rendering is skipped if the tag skip-manim is present,
167
183
  # or if we are making the pot-files
168
184
  should_skip = (
169
- "skip-manim" in self.state.document.settings.env.app.builder.tags.tags
185
+ "skip-manim" in self.state.document.settings.env.app.builder.tags
170
186
  or self.state.document.settings.env.app.builder.name == "gettext"
171
187
  )
172
188
  if should_skip:
@@ -217,14 +233,10 @@ class ManimDirective(Directive):
217
233
  + self.options.get("ref_functions", [])
218
234
  + self.options.get("ref_methods", [])
219
235
  )
220
- if ref_content:
221
- ref_block = "References: " + " ".join(ref_content)
222
-
223
- else:
224
- ref_block = ""
236
+ ref_block = "References: " + " ".join(ref_content) if ref_content else ""
225
237
 
226
238
  if "quality" in self.options:
227
- quality = f'{self.options["quality"]}_quality'
239
+ quality = f"{self.options['quality']}_quality"
228
240
  else:
229
241
  quality = "example_quality"
230
242
  frame_rate = QUALITIES[quality]["frame_rate"]
@@ -235,13 +247,13 @@ class ManimDirective(Directive):
235
247
  document = state_machine.document
236
248
 
237
249
  source_file_name = Path(document.attributes["source"])
238
- source_rel_name = source_file_name.relative_to(setup.confdir)
250
+ source_rel_name = source_file_name.relative_to(setup.confdir) # type: ignore[attr-defined]
239
251
  source_rel_dir = source_rel_name.parents[0]
240
- dest_dir = Path(setup.app.builder.outdir, source_rel_dir).absolute()
252
+ dest_dir = Path(setup.app.builder.outdir, source_rel_dir).absolute() # type: ignore[attr-defined]
241
253
  if not dest_dir.exists():
242
254
  dest_dir.mkdir(parents=True, exist_ok=True)
243
255
 
244
- source_block = [
256
+ source_block_in = [
245
257
  ".. code-block:: python",
246
258
  "",
247
259
  " from manim import *\n",
@@ -254,13 +266,13 @@ class ManimDirective(Directive):
254
266
  "",
255
267
  " </pre>",
256
268
  ]
257
- source_block = "\n".join(source_block)
269
+ source_block = "\n".join(source_block_in)
258
270
 
259
- config.media_dir = (Path(setup.confdir) / "media").absolute()
271
+ config.media_dir = (Path(setup.confdir) / "media").absolute() # type: ignore[attr-defined,assignment]
260
272
  config.images_dir = "{media_dir}/images"
261
273
  config.video_dir = "{media_dir}/videos/{quality}"
262
274
  output_file = f"{clsname}-{classnamedict[clsname]}"
263
- config.assets_dir = Path("_static")
275
+ config.assets_dir = Path("_static") # type: ignore[assignment]
264
276
  config.progress_bar = "none"
265
277
  config.verbosity = "WARNING"
266
278
 
@@ -278,7 +290,7 @@ class ManimDirective(Directive):
278
290
  if save_as_gif:
279
291
  example_config["format"] = "gif"
280
292
 
281
- user_code = self.content
293
+ user_code = list(self.content)
282
294
  if user_code[0].startswith(">>> "): # check whether block comes from doctest
283
295
  user_code = [
284
296
  line[4:] for line in user_code if line.startswith((">>> ", "... "))
@@ -322,7 +334,7 @@ class ManimDirective(Directive):
322
334
  clsname=clsname,
323
335
  clsname_lowercase=clsname.lower(),
324
336
  hide_source=hide_source,
325
- filesrc_rel=Path(filesrc).relative_to(setup.confdir).as_posix(),
337
+ filesrc_rel=Path(filesrc).relative_to(setup.confdir).as_posix(), # type: ignore[attr-defined]
326
338
  no_autoplay=no_autoplay,
327
339
  output_file=output_file,
328
340
  save_last_frame=save_last_frame,
@@ -341,18 +353,18 @@ class ManimDirective(Directive):
341
353
  rendering_times_file_path = Path("../rendering_times.csv")
342
354
 
343
355
 
344
- def _write_rendering_stats(scene_name, run_time, file_name):
356
+ def _write_rendering_stats(scene_name: str, run_time: float, file_name: str) -> None:
345
357
  with rendering_times_file_path.open("a") as file:
346
358
  csv.writer(file).writerow(
347
359
  [
348
360
  re.sub(r"^(reference\/)|(manim\.)", "", file_name),
349
361
  scene_name,
350
- "%.3f" % run_time,
362
+ f"{run_time:.3f}",
351
363
  ],
352
364
  )
353
365
 
354
366
 
355
- def _log_rendering_times(*args):
367
+ def _log_rendering_times(*args: tuple[Any]) -> None:
356
368
  if rendering_times_file_path.exists():
357
369
  with rendering_times_file_path.open() as file:
358
370
  data = list(csv.reader(file))
@@ -365,9 +377,9 @@ def _log_rendering_times(*args):
365
377
  data = [row for row in data if row]
366
378
 
367
379
  max_file_length = max(len(row[0]) for row in data)
368
- for key, group in it.groupby(data, key=lambda row: row[0]):
380
+ for key, group_iter in it.groupby(data, key=lambda row: row[0]):
369
381
  key = key.ljust(max_file_length + 1, ".")
370
- group = list(group)
382
+ group = list(group_iter)
371
383
  if len(group) == 1:
372
384
  row = group[0]
373
385
  print(f"{key}{row[2].rjust(7, '.')}s {row[1]}")
@@ -377,21 +389,21 @@ def _log_rendering_times(*args):
377
389
  f"{key}{f'{time_sum:.3f}'.rjust(7, '.')}s => {len(group)} EXAMPLES",
378
390
  )
379
391
  for row in group:
380
- print(f"{' '*(max_file_length)} {row[2].rjust(7)}s {row[1]}")
392
+ print(f"{' ' * max_file_length} {row[2].rjust(7)}s {row[1]}")
381
393
  print("")
382
394
 
383
395
 
384
- def _delete_rendering_times(*args):
396
+ def _delete_rendering_times(*args: tuple[Any]) -> None:
385
397
  if rendering_times_file_path.exists():
386
398
  rendering_times_file_path.unlink()
387
399
 
388
400
 
389
- def setup(app):
401
+ def setup(app: Sphinx) -> SetupMetadata:
390
402
  app.add_node(SkipManimNode, html=(visit, depart))
391
403
 
392
- setup.app = app
393
- setup.config = app.config
394
- setup.confdir = app.confdir
404
+ setup.app = app # type: ignore[attr-defined]
405
+ setup.config = app.config # type: ignore[attr-defined]
406
+ setup.confdir = app.confdir # type: ignore[attr-defined]
395
407
 
396
408
  app.add_directive("manim", ManimDirective)
397
409
 
@@ -408,7 +420,10 @@ def setup(app):
408
420
  ).strip(),
409
421
  )
410
422
 
411
- metadata = {"parallel_read_safe": False, "parallel_write_safe": True}
423
+ metadata: SetupMetadata = {
424
+ "parallel_read_safe": False,
425
+ "parallel_write_safe": True,
426
+ }
412
427
  return metadata
413
428
 
414
429