manim 0.17.0__py3-none-any.whl → 0.19.1__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.
Files changed (163) hide show
  1. manim/__init__.py +11 -6
  2. manim/__main__.py +62 -19
  3. manim/_config/__init__.py +10 -9
  4. manim/_config/cli_colors.py +26 -9
  5. manim/_config/default.cfg +1 -3
  6. manim/_config/logger_utils.py +23 -13
  7. manim/_config/utils.py +662 -468
  8. manim/animation/animation.py +164 -18
  9. manim/animation/changing.py +34 -23
  10. manim/animation/composition.py +265 -67
  11. manim/animation/creation.py +208 -26
  12. manim/animation/fading.py +16 -18
  13. manim/animation/growing.py +35 -15
  14. manim/animation/indication.py +150 -76
  15. manim/animation/movement.py +56 -22
  16. manim/animation/numbers.py +64 -6
  17. manim/animation/rotation.py +78 -7
  18. manim/animation/specialized.py +6 -7
  19. manim/animation/speedmodifier.py +13 -10
  20. manim/animation/transform.py +14 -11
  21. manim/animation/transform_matching_parts.py +3 -4
  22. manim/animation/updaters/mobject_update_utils.py +152 -30
  23. manim/animation/updaters/update.py +10 -7
  24. manim/camera/camera.py +182 -118
  25. manim/camera/mapping_camera.py +34 -3
  26. manim/camera/moving_camera.py +95 -74
  27. manim/camera/multi_camera.py +23 -15
  28. manim/camera/three_d_camera.py +70 -52
  29. manim/cli/__init__.py +17 -0
  30. manim/cli/cfg/group.py +76 -44
  31. manim/cli/checkhealth/checks.py +192 -0
  32. manim/cli/checkhealth/commands.py +90 -0
  33. manim/cli/default_group.py +158 -25
  34. manim/cli/init/commands.py +33 -25
  35. manim/cli/plugins/commands.py +16 -3
  36. manim/cli/render/commands.py +72 -60
  37. manim/cli/render/ease_of_access_options.py +4 -3
  38. manim/cli/render/global_options.py +59 -17
  39. manim/cli/render/output_options.py +6 -5
  40. manim/cli/render/render_options.py +98 -33
  41. manim/constants.py +109 -59
  42. manim/data_structures.py +31 -0
  43. manim/mobject/frame.py +8 -5
  44. manim/mobject/geometry/__init__.py +1 -0
  45. manim/mobject/geometry/arc.py +277 -135
  46. manim/mobject/geometry/boolean_ops.py +32 -31
  47. manim/mobject/geometry/labeled.py +376 -0
  48. manim/mobject/geometry/line.py +192 -87
  49. manim/mobject/geometry/polygram.py +224 -58
  50. manim/mobject/geometry/shape_matchers.py +61 -25
  51. manim/mobject/geometry/tips.py +122 -48
  52. manim/mobject/graph.py +1027 -419
  53. manim/mobject/graphing/coordinate_systems.py +533 -278
  54. manim/mobject/graphing/functions.py +53 -32
  55. manim/mobject/graphing/number_line.py +123 -65
  56. manim/mobject/graphing/probability.py +88 -62
  57. manim/mobject/graphing/scale.py +33 -19
  58. manim/mobject/logo.py +118 -28
  59. manim/mobject/matrix.py +87 -83
  60. manim/mobject/mobject.py +912 -442
  61. manim/mobject/opengl/dot_cloud.py +16 -5
  62. manim/mobject/opengl/opengl_compatibility.py +4 -2
  63. manim/mobject/opengl/opengl_geometry.py +254 -153
  64. manim/mobject/opengl/opengl_image_mobject.py +3 -1
  65. manim/mobject/opengl/opengl_mobject.py +779 -482
  66. manim/mobject/opengl/opengl_point_cloud_mobject.py +41 -14
  67. manim/mobject/opengl/opengl_surface.py +14 -92
  68. manim/mobject/opengl/opengl_three_dimensions.py +12 -8
  69. manim/mobject/opengl/opengl_vectorized_mobject.py +98 -100
  70. manim/mobject/svg/brace.py +173 -41
  71. manim/mobject/svg/svg_mobject.py +139 -53
  72. manim/mobject/table.py +61 -68
  73. manim/mobject/text/code_mobject.py +193 -539
  74. manim/mobject/text/numbers.py +81 -34
  75. manim/mobject/text/tex_mobject.py +130 -78
  76. manim/mobject/text/text_mobject.py +288 -164
  77. manim/mobject/three_d/polyhedra.py +111 -13
  78. manim/mobject/three_d/three_d_utils.py +17 -8
  79. manim/mobject/three_d/three_dimensions.py +239 -106
  80. manim/mobject/types/image_mobject.py +50 -30
  81. manim/mobject/types/point_cloud_mobject.py +120 -75
  82. manim/mobject/types/vectorized_mobject.py +841 -408
  83. manim/mobject/value_tracker.py +105 -38
  84. manim/mobject/vector_field.py +50 -31
  85. manim/opengl/__init__.py +3 -3
  86. manim/plugins/__init__.py +14 -1
  87. manim/plugins/plugins_flags.py +10 -14
  88. manim/renderer/cairo_renderer.py +65 -50
  89. manim/renderer/opengl_renderer.py +89 -69
  90. manim/renderer/opengl_renderer_window.py +39 -18
  91. manim/renderer/shader.py +123 -87
  92. manim/renderer/shader_wrapper.py +44 -28
  93. manim/renderer/vectorized_mobject_rendering.py +38 -10
  94. manim/scene/moving_camera_scene.py +32 -3
  95. manim/scene/scene.py +507 -242
  96. manim/scene/scene_file_writer.py +371 -220
  97. manim/scene/section.py +20 -16
  98. manim/scene/three_d_scene.py +14 -22
  99. manim/scene/vector_space_scene.py +223 -129
  100. manim/scene/zoomed_scene.py +46 -41
  101. manim/typing.py +990 -0
  102. manim/utils/bezier.py +1823 -371
  103. manim/utils/caching.py +12 -5
  104. manim/utils/color/AS2700.py +236 -0
  105. manim/utils/color/BS381.py +318 -0
  106. manim/utils/color/DVIPSNAMES.py +96 -0
  107. manim/utils/color/SVGNAMES.py +179 -0
  108. manim/utils/color/X11.py +533 -0
  109. manim/utils/color/XKCD.py +952 -0
  110. manim/utils/color/__init__.py +61 -0
  111. manim/utils/color/core.py +1667 -0
  112. manim/utils/color/manim_colors.py +218 -0
  113. manim/utils/commands.py +48 -20
  114. manim/utils/config_ops.py +39 -19
  115. manim/utils/debug.py +8 -7
  116. manim/utils/deprecation.py +86 -39
  117. manim/utils/docbuild/__init__.py +17 -0
  118. manim/utils/docbuild/autoaliasattr_directive.py +236 -0
  119. manim/utils/docbuild/autocolor_directive.py +99 -0
  120. manim/utils/docbuild/manim_directive.py +94 -41
  121. manim/utils/docbuild/module_parsing.py +245 -0
  122. manim/utils/exceptions.py +6 -0
  123. manim/utils/family.py +5 -3
  124. manim/utils/family_ops.py +17 -4
  125. manim/utils/file_ops.py +27 -17
  126. manim/utils/hashing.py +55 -45
  127. manim/utils/images.py +13 -7
  128. manim/utils/ipython_magic.py +13 -7
  129. manim/utils/iterables.py +163 -120
  130. manim/utils/module_ops.py +66 -24
  131. manim/utils/opengl.py +77 -24
  132. manim/utils/parameter_parsing.py +32 -0
  133. manim/utils/paths.py +30 -33
  134. manim/utils/polylabel.py +235 -0
  135. manim/utils/qhull.py +218 -0
  136. manim/utils/rate_functions.py +98 -32
  137. manim/utils/simple_functions.py +25 -33
  138. manim/utils/sounds.py +7 -1
  139. manim/utils/space_ops.py +188 -115
  140. manim/utils/testing/__init__.py +17 -0
  141. manim/utils/testing/_frames_testers.py +13 -8
  142. manim/utils/testing/_show_diff.py +5 -3
  143. manim/utils/testing/_test_class_makers.py +34 -18
  144. manim/utils/testing/frames_comparison.py +37 -19
  145. manim/utils/tex.py +130 -198
  146. manim/utils/tex_file_writing.py +77 -47
  147. manim/utils/tex_templates.py +2 -1
  148. manim/utils/unit.py +6 -5
  149. {manim-0.17.0.dist-info → manim-0.19.1.dist-info}/METADATA +64 -65
  150. manim-0.19.1.dist-info/RECORD +220 -0
  151. {manim-0.17.0.dist-info → manim-0.19.1.dist-info}/WHEEL +1 -1
  152. manim-0.19.1.dist-info/entry_points.txt +3 -0
  153. {manim-0.17.0.dist-info → manim-0.19.1.dist-info/licenses}/LICENSE.community +1 -1
  154. manim/cli/new/group.py +0 -189
  155. manim/communitycolors.py +0 -9
  156. manim/gui/__init__.py +0 -0
  157. manim/gui/gui.py +0 -82
  158. manim/plugins/import_plugins.py +0 -43
  159. manim/utils/color.py +0 -552
  160. manim-0.17.0.dist-info/RECORD +0 -206
  161. manim-0.17.0.dist-info/entry_points.txt +0 -4
  162. /manim/cli/{new → checkhealth}/__init__.py +0 -0
  163. {manim-0.17.0.dist-info → manim-0.19.1.dist-info/licenses}/LICENSE +0 -0
manim/__init__.py CHANGED
@@ -1,14 +1,17 @@
1
1
  #!/usr/bin/env python
2
-
3
-
4
2
  from __future__ import annotations
5
3
 
6
- import pkg_resources
7
-
8
- __version__: str = pkg_resources.get_distribution(__name__).version
4
+ from importlib.metadata import PackageNotFoundError, version
9
5
 
6
+ # Use installed distribution version if available; otherwise fall back to a
7
+ # sensible default so that importing from a source checkout works without an
8
+ # editable install (pip install -e .).
9
+ try:
10
+ __version__ = version(__name__)
11
+ except PackageNotFoundError:
12
+ # Package is not installed; provide a fallback version string.
13
+ __version__ = "0.0.0+unknown"
10
14
 
11
- import sys
12
15
 
13
16
  # isort: off
14
17
 
@@ -20,6 +23,7 @@ from ._config import *
20
23
  from .utils.commands import *
21
24
 
22
25
  # isort: on
26
+ import numpy as np
23
27
 
24
28
  from .animation.animation import *
25
29
  from .animation.changing import *
@@ -46,6 +50,7 @@ from .constants import *
46
50
  from .mobject.frame import *
47
51
  from .mobject.geometry.arc import *
48
52
  from .mobject.geometry.boolean_ops import *
53
+ from .mobject.geometry.labeled import *
49
54
  from .mobject.geometry.line import *
50
55
  from .mobject.geometry.polygram import *
51
56
  from .mobject.geometry.shape_matchers import *
manim/__main__.py CHANGED
@@ -1,26 +1,54 @@
1
1
  from __future__ import annotations
2
2
 
3
- import sys
4
-
5
3
  import click
6
4
  import cloup
7
5
 
8
- from . import __version__, cli_ctx_settings, console
9
- from .cli.cfg.group import cfg
10
- from .cli.default_group import DefaultGroup
11
- from .cli.init.commands import init
12
- from .cli.new.group import new
13
- from .cli.plugins.commands import plugins
14
- from .cli.render.commands import render
15
- from .constants import EPILOG
6
+ from manim import __version__
7
+ from manim._config import cli_ctx_settings, console
8
+ from manim.cli.cfg.group import cfg
9
+ from manim.cli.checkhealth.commands import checkhealth
10
+ from manim.cli.default_group import DefaultGroup
11
+ from manim.cli.init.commands import init
12
+ from manim.cli.plugins.commands import plugins
13
+ from manim.cli.render.commands import render
14
+ from manim.constants import EPILOG
15
+
16
16
 
17
+ def show_splash(ctx: click.Context, param: click.Option, value: str | None) -> None:
18
+ """When giving a value by console, show an initial message with the Manim
19
+ version before executing any other command: ``Manim Community vA.B.C``.
17
20
 
18
- def exit_early(ctx, param, value):
21
+ Parameters
22
+ ----------
23
+ ctx
24
+ The Click context.
25
+ param
26
+ A Click option.
27
+ value
28
+ A string value given by console, or None.
29
+ """
19
30
  if value:
20
- sys.exit()
31
+ console.print(f"Manim Community [green]v{__version__}[/green]\n")
32
+
21
33
 
34
+ def print_version_and_exit(
35
+ ctx: click.Context, param: click.Option, value: str | None
36
+ ) -> None:
37
+ """Same as :func:`show_splash`, but also exit when giving a value by
38
+ console.
22
39
 
23
- console.print(f"Manim Community [green]v{__version__}[/green]\n")
40
+ Parameters
41
+ ----------
42
+ ctx
43
+ The Click context.
44
+ param
45
+ A Click option.
46
+ value
47
+ A string value given by console, or None.
48
+ """
49
+ show_splash(ctx, param, value)
50
+ if value:
51
+ ctx.exit()
24
52
 
25
53
 
26
54
  @cloup.group(
@@ -34,24 +62,39 @@ console.print(f"Manim Community [green]v{__version__}[/green]\n")
34
62
  "is specified. Run 'manim render --help' if you would like to know what the "
35
63
  f"'-ql' or '-p' flags do, for example.\n\n{EPILOG}",
36
64
  )
37
- @click.option(
65
+ @cloup.option(
38
66
  "--version",
39
67
  is_flag=True,
40
68
  help="Show version and exit.",
41
- callback=exit_early,
69
+ callback=print_version_and_exit,
42
70
  is_eager=True,
43
71
  expose_value=False,
44
72
  )
45
- @click.pass_context
46
- def main(ctx):
47
- """The entry point for manim."""
73
+ @click.option(
74
+ "--show-splash/--hide-splash",
75
+ is_flag=True,
76
+ default=True,
77
+ help="Print splash message with version information.",
78
+ callback=show_splash,
79
+ is_eager=True,
80
+ expose_value=False,
81
+ )
82
+ @cloup.pass_context
83
+ def main(ctx: click.Context) -> None:
84
+ """The entry point for Manim.
85
+
86
+ Parameters
87
+ ----------
88
+ ctx
89
+ The Click context.
90
+ """
48
91
  pass
49
92
 
50
93
 
94
+ main.add_command(checkhealth)
51
95
  main.add_command(cfg)
52
96
  main.add_command(plugins)
53
97
  main.add_command(init)
54
- main.add_command(new)
55
98
  main.add_command(render)
56
99
 
57
100
  if __name__ == "__main__":
manim/_config/__init__.py CHANGED
@@ -3,7 +3,9 @@
3
3
  from __future__ import annotations
4
4
 
5
5
  import logging
6
- from contextlib import _GeneratorContextManager, contextmanager
6
+ from collections.abc import Generator
7
+ from contextlib import contextmanager
8
+ from typing import Any
7
9
 
8
10
  from .cli_colors import parse_cli_ctx
9
11
  from .logger_utils import make_logger
@@ -20,12 +22,10 @@ __all__ = [
20
22
  ]
21
23
 
22
24
  parser = make_config_parser()
23
- logger: logging.Logger
24
25
 
25
- # The logger can be accessed from anywhere as manim.logger, or as
26
- # logging.getLogger("manim"). The console must be accessed as manim.console.
27
- # Throughout the codebase, use manim.console.print() instead of print().
28
- # Use error_console to print errors so that it outputs to stderr.
26
+ # Logger usage: accessible globally as `manim.logger` or via `logging.getLogger("manim")`.
27
+ # For printing, use `manim.console.print()` instead of the built-in `print()`.
28
+ # For error output, use `error_console`, which prints to stderr.
29
29
  logger, console, error_console = make_logger(
30
30
  parser["logger"],
31
31
  parser["CLI"]["verbosity"],
@@ -36,13 +36,15 @@ logging.getLogger("PIL").setLevel(logging.INFO)
36
36
  logging.getLogger("matplotlib").setLevel(logging.INFO)
37
37
 
38
38
  config = ManimConfig().digest_parser(parser)
39
+ # TODO: to be used in the future - see PR #620
40
+ # https://github.com/ManimCommunity/manim/pull/620
39
41
  frame = ManimFrame(config)
40
42
 
41
43
 
42
44
  # This has to go here because it needs access to this module's config
43
45
  @contextmanager
44
- def tempconfig(temp: ManimConfig | dict) -> _GeneratorContextManager:
45
- """Context manager that temporarily modifies the global ``config`` object.
46
+ def tempconfig(temp: ManimConfig | dict[str, Any]) -> Generator[None, None, None]:
47
+ """Temporarily modifies the global ``config`` object using a context manager.
46
48
 
47
49
  Inside the ``with`` statement, the modified config will be used. After
48
50
  context manager exits, the config will be restored to its original state.
@@ -65,7 +67,6 @@ def tempconfig(temp: ManimConfig | dict) -> _GeneratorContextManager:
65
67
  8.0
66
68
  >>> with tempconfig({"frame_height": 100.0}):
67
69
  ... print(config["frame_height"])
68
- ...
69
70
  100.0
70
71
  >>> config["frame_height"]
71
72
  8.0
@@ -1,10 +1,21 @@
1
+ """Parses CLI context settings from the configuration file and returns a Cloup Context settings dictionary.
2
+
3
+ This module reads configuration values for help formatting, theme styles, and alignment options
4
+ used when rendering command-line interfaces in Manim.
5
+ """
6
+
7
+ from __future__ import annotations
8
+
1
9
  import configparser
10
+ from typing import Any
2
11
 
3
12
  from cloup import Context, HelpFormatter, HelpTheme, Style
4
13
 
14
+ __all__ = ["parse_cli_ctx"]
5
15
 
6
- def parse_cli_ctx(parser: configparser.ConfigParser) -> Context:
7
- formatter_settings = {
16
+
17
+ def parse_cli_ctx(parser: configparser.SectionProxy) -> dict[str, Any]:
18
+ formatter_settings: dict[str, str | int | None] = {
8
19
  "indent_increment": int(parser["indent_increment"]),
9
20
  "width": int(parser["width"]),
10
21
  "col1_max_width": int(parser["col1_max_width"]),
@@ -23,6 +34,7 @@ def parse_cli_ctx(parser: configparser.ConfigParser) -> Context:
23
34
  "col2",
24
35
  "epilog",
25
36
  }
37
+ # Extract and apply any style-related keys defined in the config section.
26
38
  for k, v in parser.items():
27
39
  if k in theme_keys and v:
28
40
  theme_settings.update({k: Style(v)})
@@ -30,21 +42,26 @@ def parse_cli_ctx(parser: configparser.ConfigParser) -> Context:
30
42
  formatter = {}
31
43
  theme = parser["theme"] if parser["theme"] else None
32
44
  if theme is None:
33
- formatter = HelpFormatter().settings(
34
- theme=HelpTheme(**theme_settings), **formatter_settings
45
+ formatter = HelpFormatter.settings(
46
+ theme=HelpTheme(**theme_settings),
47
+ **formatter_settings,
35
48
  )
36
49
  elif theme.lower() == "dark":
37
- formatter = HelpFormatter().settings(
38
- theme=HelpTheme.dark().with_(**theme_settings), **formatter_settings
50
+ formatter = HelpFormatter.settings(
51
+ theme=HelpTheme.dark().with_(**theme_settings),
52
+ **formatter_settings,
39
53
  )
40
54
  elif theme.lower() == "light":
41
- formatter = HelpFormatter().settings(
42
- theme=HelpTheme.light().with_(**theme_settings), **formatter_settings
55
+ formatter = HelpFormatter.settings(
56
+ theme=HelpTheme.light().with_(**theme_settings),
57
+ **formatter_settings,
43
58
  )
44
59
 
45
- return Context.settings(
60
+ return_val: dict[str, Any] = Context.settings(
46
61
  align_option_groups=parser["align_option_groups"].lower() == "true",
47
62
  align_sections=parser["align_sections"].lower() == "true",
48
63
  show_constraints=True,
49
64
  formatter_settings=formatter,
50
65
  )
66
+
67
+ return return_val
manim/_config/default.cfg CHANGED
@@ -221,9 +221,7 @@ repr_number = green
221
221
  # Uncomment the following line to manually set the loglevel for ffmpeg. See
222
222
  # ffmpeg manpage for accepted values
223
223
  loglevel = ERROR
224
- # defaults to the one present in path
225
- ffmpeg_executable = ffmpeg
226
224
 
227
225
  [jupyter]
228
- media_embed =
226
+ media_embed = False
229
227
  media_width = 60%%
@@ -9,14 +9,14 @@ Both ``logger`` and ``console`` use the ``rich`` library to produce rich text
9
9
  format.
10
10
 
11
11
  """
12
+
12
13
  from __future__ import annotations
13
14
 
14
15
  import configparser
15
16
  import copy
16
17
  import json
17
18
  import logging
18
- import sys
19
- from typing import TYPE_CHECKING
19
+ from typing import TYPE_CHECKING, Any
20
20
 
21
21
  from rich import color, errors
22
22
  from rich import print as printf
@@ -26,6 +26,9 @@ from rich.theme import Theme
26
26
 
27
27
  if TYPE_CHECKING:
28
28
  from pathlib import Path
29
+
30
+ __all__ = ["make_logger", "parse_theme", "set_file_logger", "JSONFormatter"]
31
+
29
32
  HIGHLIGHTED_KEYWORDS = [ # these keywords are highlighted specially
30
33
  "Played",
31
34
  "animations",
@@ -49,9 +52,9 @@ Loading the default color configuration.[/logging.level.error]
49
52
 
50
53
 
51
54
  def make_logger(
52
- parser: configparser.ConfigParser,
55
+ parser: configparser.SectionProxy,
53
56
  verbosity: str,
54
- ) -> tuple[logging.Logger, Console]:
57
+ ) -> tuple[logging.Logger, Console, Console]:
55
58
  """Make the manim logger and console.
56
59
 
57
60
  Parameters
@@ -83,25 +86,29 @@ def make_logger(
83
86
  theme = parse_theme(parser)
84
87
  console = Console(theme=theme)
85
88
 
86
- # With rich 9.5.0+ we could pass stderr=True instead
87
- error_console = Console(theme=theme, file=sys.stderr)
89
+ error_console = Console(theme=theme, stderr=True)
88
90
 
89
91
  # set the rich handler
90
- RichHandler.KEYWORDS = HIGHLIGHTED_KEYWORDS
91
92
  rich_handler = RichHandler(
92
93
  console=console,
93
- show_time=parser.getboolean("log_timestamps"),
94
+ show_time=parser.getboolean("log_timestamps", fallback=False),
95
+ keywords=HIGHLIGHTED_KEYWORDS,
94
96
  )
95
97
 
96
98
  # finally, the logger
97
99
  logger = logging.getLogger("manim")
98
100
  logger.addHandler(rich_handler)
99
101
  logger.setLevel(verbosity)
102
+ logger.propagate = False
103
+
104
+ if not (libav_logger := logging.getLogger()).hasHandlers():
105
+ libav_logger.addHandler(rich_handler)
106
+ libav_logger.setLevel(verbosity)
100
107
 
101
108
  return logger, console, error_console
102
109
 
103
110
 
104
- def parse_theme(parser: configparser.ConfigParser) -> Theme:
111
+ def parse_theme(parser: configparser.SectionProxy) -> Theme | None:
105
112
  """Configure the rich style of logger and console output.
106
113
 
107
114
  Parameters
@@ -119,7 +126,7 @@ def parse_theme(parser: configparser.ConfigParser) -> Theme:
119
126
  :func:`make_logger`.
120
127
 
121
128
  """
122
- theme = {key.replace("_", "."): parser[key] for key in parser}
129
+ theme: dict[str, Any] = {key.replace("_", "."): parser[key] for key in parser}
123
130
 
124
131
  theme["log.width"] = None if theme["log.width"] == "-1" else int(theme["log.width"])
125
132
  theme["log.height"] = (
@@ -177,12 +184,15 @@ class JSONFormatter(logging.Formatter):
177
184
 
178
185
  """
179
186
 
180
- def format(self, record: dict) -> str:
187
+ def format(self, record: logging.LogRecord) -> str:
181
188
  """Format the record in a custom JSON format."""
182
189
  record_c = copy.deepcopy(record)
183
190
  if record_c.args:
184
- for arg in record_c.args:
185
- record_c.args[arg] = "<>"
191
+ if isinstance(record_c.args, dict):
192
+ for arg in record_c.args:
193
+ record_c.args[arg] = "<>"
194
+ else:
195
+ record_c.args = ("<>",) * len(record_c.args)
186
196
  return json.dumps(
187
197
  {
188
198
  "levelname": record_c.levelname,