Skip to content

SVGMobject passing NoneType to svgelements #3949

Open
@jpmikhail

Description

@jpmikhail

Description of bug / unexpected behavior

When using an SVG plot with a legend (below), Manim passes a NoneType to svgelements. This results in an error:
TypeError: object of type 'NoneType' has no len()

When using an otherwise identical plot with no legend (below), there is no such error and Manim is able to parse the SVG image successfully.

I'm unsure if this is fundamentally an issue with Manim or svgelements.

Expected behavior

Both plots should be easily parsable without modifying svgelements.

How to reproduce the issue

Create the following simple scene with Manim using the two attached plots.

Code for reproducing the problem
class LoadSVGPlots(Scene):
    def construct(self):
        plot0 = SVGMobject('plot_without_legend.svg')
        print('loaded plot_without_legend')
        plot1 = SVGMobject('plot_with_legend.svg')
        print('loaded plot_with_legend')

Additional media files

Images/GIFs

plot_with_legend
plot_without_legend

Logs

Terminal output

After running the command: manim -qh -v DEBUG scene.py LoadSVGPlotsmanim -qh -v DEBUG scene.py LoadSVGPlots

Manim Community v0.18.1

loaded plot_without_legend
╭─────────────────────────────── Traceback (most recent call last) ────────────────────────────────╮
│ /home/mikhailj/Downloads/anaconda3/envs/my-manim-environment/lib/python3.10/site-packages/manim/ │
│ cli/render/commands.py:120 in render                                                             │
│                                                                                                  │
│   117 │   │   │   try:                                                                           │
│   118 │   │   │   │   with tempconfig({}):                                                       │
│   119 │   │   │   │   │   scene = SceneClass()                                                   │
│ ❱ 120 │   │   │   │   │   scene.render()                                                         │
│   121 │   │   │   except Exception:                                                              │
│   122 │   │   │   │   error_console.print_exception()                                            │
│   123 │   │   │   │   sys.exit(1)                                                                │
│                                                                                                  │
│ /home/mikhailj/Downloads/anaconda3/envs/my-manim-environment/lib/python3.10/site-packages/manim/ │
│ scene/scene.py:229 in render                                                                     │
│                                                                                                  │
│    226 │   │   """                                                                               │
│    227 │   │   self.setup()                                                                      │
│    228 │   │   try:                                                                              │
│ ❱  229 │   │   │   self.construct()                                                              │
│    230 │   │   except EndSceneEarlyException:                                                    │
│    231 │   │   │   pass                                                                          │
│    232 │   │   except RerunSceneException as e:                                                  │
│                                                                                                  │
│ /home/mikhailj/Videos/Manim/uniform_combination/scene.py:331 in construct                        │
│                                                                                                  │
│   328 │   def construct(self):                                                                   │
│   329 │   │   plot0 = SVGMobject('plot_without_legend.svg')                                      │
│   330 │   │   print('loaded plot_without_legend')                                                │
│ ❱ 331 │   │   plot1 = SVGMobject('plot_with_legend.svg')                                         │
│   332 │   │   print('loaded plot_with_legend')                                                   │
│   333                                                                                            │
│   334 #####################                                                                      │
│                                                                                                  │
│ /home/mikhailj/Downloads/anaconda3/envs/my-manim-environment/lib/python3.10/site-packages/manim/ │
│ mobject/svg/svg_mobject.py:145 in __init__                                                       │
│                                                                                                  │
│   142 │   │   │   path_string_config = {}                                                        │
│   143 │   │   self.path_string_config = path_string_config                                       │
│   144 │   │                                                                                      │
│ ❱ 145 │   │   self.init_svg_mobject(use_svg_cache=use_svg_cache)                                 │
│   146 │   │                                                                                      │
│   147 │   │   self.set_style(                                                                    │
│   148 │   │   │   fill_color=fill_color,                                                         │
│                                                                                                  │
│ /home/mikhailj/Downloads/anaconda3/envs/my-manim-environment/lib/python3.10/site-packages/manim/ │
│ mobject/svg/svg_mobject.py:171 in init_svg_mobject                                               │
│                                                                                                  │
│   168 │   │   │   │   self.add(*mob)                                                             │
│   169 │   │   │   │   return                                                                     │
│   170 │   │                                                                                      │
│ ❱ 171 │   │   self.generate_mobject()                                                            │
│   172 │   │   if use_svg_cache:                                                                  │
│   173 │   │   │   SVG_HASH_TO_MOB_MAP[hash_val] = self.copy()                                    │
│   174                                                                                            │
│                                                                                                  │
│ /home/mikhailj/Downloads/anaconda3/envs/my-manim-environment/lib/python3.10/site-packages/manim/ │
│ mobject/svg/svg_mobject.py:199 in generate_mobject                                               │
│                                                                                                  │
│   196 │   │   modified_file_path = file_path.with_name(f"{file_path.stem}_{file_path.suffix}")   │
│   197 │   │   new_tree.write(modified_file_path)                                                 │
│   198 │   │                                                                                      │
│ ❱ 199 │   │   svg = se.SVG.parse(modified_file_path)                                             │
│   200 │   │   modified_file_path.unlink()                                                        │
│   201 │   │                                                                                      │
│   202 │   │   mobjects = self.get_mobjects_from(svg)                                             │
│                                                                                                  │
│ /home/mikhailj/Downloads/anaconda3/envs/my-manim-environment/lib/python3.10/site-packages/svgele │
│ ments/svgelements.py:9236 in parse                                                               │
│                                                                                                  │
│   9233 │   │   │   │   │   │   if SVG_TAG_PATH == tag:                                           │
│   9234 │   │   │   │   │   │   │   # Delayed path parsing, for partial paths.                    │
│   9235 │   │   │   │   │   │   │   s = Path(values, pathd_loaded=True)                           │
│ ❱ 9236 │   │   │   │   │   │   │   s.parse(values.get(SVG_ATTR_DATA))                            │
│   9237 │   │   │   │   │   │   elif SVG_TAG_CIRCLE == tag:                                       │
│   9238 │   │   │   │   │   │   │   s = Circle(values)                                            │
│   9239 │   │   │   │   │   │   elif SVG_TAG_ELLIPSE == tag:                                      │
│                                                                                                  │
│ /home/mikhailj/Downloads/anaconda3/envs/my-manim-environment/lib/python3.10/site-packages/svgele │
│ ments/svgelements.py:5969 in parse                                                               │
│                                                                                                  │
│   5966 │   def parse(self, pathdef):                                                             │
│   5967 │   │   """Parses the SVG path."""                                                        │
│   5968 │   │   tokens = SVGLexicalParser()                                                       │
│ ❱ 5969 │   │   tokens.parse(self, pathdef)                                                       │
│   5970 │                                                                                         │
│   5971 │   def validate_connections(self):                                                       │
│   5972 │   │   """                                                                               │
│                                                                                                  │
│ /home/mikhailj/Downloads/anaconda3/envs/my-manim-environment/lib/python3.10/site-packages/svgele │
│ ments/svgelements.py:347 in parse                                                                │
│                                                                                                  │
│    344 │   │   self.parser.start()                                                               │
│    345 │   │   self.pathd = pathd                                                                │
│    346 │   │   self.pos = 0                                                                      │
│ ❱  347 │   │   self.limit = len(pathd)                                                           │
│    348 │   │   while True:                                                                       │
│    349 │   │   │   cmd = self._command()                                                         │
│    350 │   │   │   if cmd is None:                                                               │
╰──────────────────────────────────────────────────────────────────────────────────────────────────╯
TypeError: object of type 'NoneType' has no len()

System specifications

System Details
  • OS (with version, e.g., Windows 10 v2004 or macOS 10.15 (Catalina)): Debian GNU/Linux 10 (buster)
  • RAM:
  • Python version (python/py/python3 --version): Python 3.10.14
  • Installed modules (provide output from pip list):
Package              Version
-------------------- -----------
Brotli               1.1.0
build                1.2.2
CacheControl         0.14.0
certifi              2024.8.30
cffi                 1.17.1
charset-normalizer   3.3.2
cleo                 2.1.0
click                8.1.7
click-default-group  1.2.4
cloup                3.0.5
colorama             0.4.6
crashtest            0.4.1
cryptography         43.0.1
decorator            5.1.1
distlib              0.3.8
dulwich              0.21.7
fastjsonschema       2.20.0
filelock             3.16.0
future               1.0.0
glcontext            3.0.0
h2                   4.1.0
hpack                4.0.0
hyperframe           6.0.1
idna                 3.10
importlib_metadata   8.5.0
installer            0.7.0
isosurfaces          0.1.2
jaraco.classes       3.4.0
jeepney              0.8.0
keyring              24.3.1
manim                0.18.1
ManimPango           0.6.0
mapbox_earcut        1.0.1
markdown-it-py       3.0.0
mdurl                0.1.2
moderngl             5.10.0
moderngl-window      2.4.1
more-itertools       10.5.0
msgpack              1.1.0
multipledispatch     0.6.0
networkx             3.3
numpy                1.26.4
packaging            24.1
pexpect              4.9.0
pillow               10.4.0
pip                  24.2
pkginfo              1.11.1
platformdirs         4.3.3
poetry               1.8.3
poetry-core          1.9.0
poetry-plugin-export 1.8.0
ptyprocess           0.7.0
pycairo              1.27.0
pycparser            2.22
pydub                0.25.1
pyglet               1.5.16
Pygments             2.18.0
pyproject_hooks      1.1.0
pyrr                 0.10.3
PySocks              1.7.1
PyYAML               6.0.2
rapidfuzz            3.9.7
requests             2.32.3
requests-toolbelt    1.0.0
rich                 13.8.1
scipy                1.14.1
screeninfo           0.8.1
SecretStorage        3.3.3
setuptools           73.0.1
shellingham          1.5.4
six                  1.16.0
skia-pathops         0.8.0.post1
srt                  3.5.3
svg.path             6.3
svgelements          1.9.6
tomli                2.0.1
tomlkit              0.13.2
tqdm                 4.66.5
trove-classifiers    2024.9.12
typing_extensions    4.12.2
urllib3              2.2.3
virtualenv           20.26.4
watchdog             5.0.2
wheel                0.44.0
zipp                 3.20.2
zstandard            0.23.0
LaTeX details
  • LaTeX distribution (e.g. TeX Live 2020):
  • Installed LaTeX packages:

Additional comments

I was able to avoid the error by modifying svgelements/svgelements.py class SVGLexicalParser method parse. I replaced

    def parse(self, parser, pathd):
        self.parser = parser
        self.parser.start()
        self.pathd = pathd
        self.pos = 0
        self.limit = len(pathd)
        # the rest of the method

with

    def parse(self, parser, pathd):
        self.parser = parser
        self.parser.start()
        self.pathd = pathd
        self.pos = 0
        if pathd is not None:
            self.limit = len(pathd)
        else:
            self.limit = 0
        # the rest of the method

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    Status

    🆕 New

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions