Repository: ManimCommunity/manim

Issue:
The `-a` flag makes Manim crash
## Description of bug / unexpected behavior
<!-- Add a clear and concise description of the problem you encountered. -->
When using either the `-a` flag or when providing a comma separated list of numbers whose corresponding scenes should be rendered, Manim crashes.

## Expected behavior
<!-- Add a clear and concise description of what you expected to happen. -->
I expect that Manim should cleanly render all files then quit.

## How to reproduce the issue
<!-- Provide a piece of code illustrating the undesired behavior. -->

<details><summary>Code for reproducing the problem</summary>

```py
# filename: example.py
from manim import *

class WriteScene1(Scene):
    def construct(self):
        text = Tex("Alice and Bob").scale(3)
        self.play(Write(text))

class WriteScene2(Scene):
    def construct(self):
        text = Tex("Alpha and Beta").scale(3)
        self.play(Write(text))
```

</details>


## Additional media files
<!-- Paste in the files manim produced on rendering the code above. Note that GitHub doesn't allow posting videos, so you may need to convert it to a GIF or use the `-i` rendering option. -->

<details><summary>Images/GIFs</summary>

<!-- PASTE MEDIA HERE -->

</details>


## Logs
<details><summary>Terminal output</summary>
<!-- Add "-v DEBUG" when calling manim to generate more detailed logs -->

```
{"levelname": "INFO", "module": "logger_utils", "message": "Log file will be saved in <>"}
{"levelname": "DEBUG", "module": "hashing", "message": "Hashing ..."}
{"levelname": "DEBUG", "module": "hashing", "message": "Hashing done in <> s."}
{"levelname": "DEBUG", "module": "hashing", "message": "Hash generated :  <>"}
{"levelname": "INFO", "module": "caching", "message": "Animation 0 : Using cached data (hash : <>)"}
{"levelname": "DEBUG", "module": "caching", "message": "List of the first few animation hashes of the scene: <>"}
{"levelname": "DEBUG", "module": "scene_file_writer", "message": "Partial movie files to combine (1 files): <>"}
{"levelname": "INFO", "module": "scene_file_writer", "message": "\nFile ready at <>\n"}
{"levelname": "INFO", "module": "scene", "message": "Rendered WriteScene1\nPlayed 1 animations"}
```
```
(env_Manim_dev) PS C:\Users\Abel\Manim\dev\Example> manim .\example.py -a -qh -v DEBUG --log_to_file
Manim Community v0.4.0
[03/14/21 16:09:32] INFO     Log file will be saved in media\logs\example.log                                                                                                                                                                                        logger_utils.py:175
                    DEBUG    Hashing ...                                                                                                                                                                                                                                  hashing.py:239
[03/14/21 16:09:33] DEBUG    Hashing done in 0.029161 s.                                                                                                                                                                                                                  hashing.py:252
                    DEBUG    Hash generated :  450974505_815064119_2235554292                                                                                                                                                                                             hashing.py:256
                    INFO     Animation 0 : Using cached data (hash : 450974505_815064119_2235554292)                                                                                                                                                                       caching.py:39
                    DEBUG    List of the first few animation hashes of the scene: ['450974505_815064119_2235554292']                                                                                                                                                       caching.py:48
                    DEBUG    Partial movie files to combine (1 files): ['C:\\Users\\Abel\\Manim\\dev\\Example\\media\\videos\\example\\1080p60\\partial_movie_files\\WriteScene1\\450974505_815064119_2235554292.mp4']                                          scene_file_writer.py:447
                    INFO                                                                                                                                                                                                                                        scene_file_writer.py:579
                             File ready at C:\Users\Abel\Manim\dev\Example\media\videos\example\1080p60\WriteScene1.mp4

                    INFO     Rendered WriteScene1                                                                                                                                                                                                                           scene.py:176
                             Played 1 animations
```
```
Traceback (most recent call last):
╭──────────────────────────────────────────────────────────────────────────────────────╮
│ File "c:\users\abel\manim\dev\env_manim_dev\lib\site-packages\manim\__main__.py",    │
│line 103, in main                                                                     │
│    100         else:                                                                 │
│    101             for SceneClass in scene_classes_from_file(input_file):            │
│    102                 try:                                                          │
│  ❱ 103                     scene = SceneClass()                                      │
│    104                     scene.render()                                            │
│    105                     open_file_if_needed(scene.renderer.file_writer)           │
│    106                 except Exception:                                             │
│ File "c:\users\abel\manim\dev\env_manim_dev\lib\site-packages\manim\scene\scene.py", │
│line 91, in __init__                                                                  │
│     88             )                                                                 │
│     89         else:                                                                 │
│     90             self.renderer = renderer                                          │
│  ❱  91         self.renderer.init_scene(self)                                        │
│     92                                                                               │
│     93         self.mobjects = []                                                    │
│     94         # TODO, remove need for foreground mobjects                           │
│ File "c:\users\abel\manim\dev\env_manim_dev\lib\site-packages\manim\renderer\cairo_re│
│nderer.py", line 69, in init_scene                                                    │
│     66         self.static_image = None                                              │
│     67                                                                               │
│     68     def init_scene(self, scene):                                              │
│  ❱  69         self.file_writer = SceneFileWriter(                                   │
│     70             self,                                                             │
│     71             scene.__class__.__name__,                                         │
│     72         )                                                                     │
│ File "c:\users\abel\manim\dev\env_manim_dev\lib\site-packages\manim\scene\scene_file_│
│writer.py", line 48, in __init__                                                      │
│     45     def __init__(self, renderer, scene_name, **kwargs):                       │
│     46         self.renderer = renderer                                              │
│     47         self.stream_lock = False                                              │
│  ❱  48         self.init_output_directories(scene_name)                              │
│     49         self.init_audio()                                                     │
│     50         self.frame_count = 0                                                  │
│     51         self.partial_movie_files = []                                         │
│ File "c:\users\abel\manim\dev\env_manim_dev\lib\site-packages\manim\scene\scene_file_│
│writer.py", line 102, in init_output_directories                                      │
│     99                     add_extension_if_not_present(default_name, GIF_FILE_EXTENS│
│    100                 )                                                             │
│    101                                                                               │
│  ❱ 102             self.partial_movie_directory = guarantee_existence(               │
│    103                 config.get_dir(                                               │
│    104                     "partial_movie_dir",                                      │
│    105                     scene_name=default_name,                                  │
│ File                                                                                 │
│"c:\users\abel\manim\dev\env_manim_dev\lib\site-packages\manim\utils\file_ops.py",    │
│line 36, in guarantee_existence                                                       │
│    33                                                                                │
│    34 def guarantee_existence(path):                                                 │
│    35     if not os.path.exists(path):                                               │
│  ❱ 36         os.makedirs(path)                                                      │
│    37     return os.path.abspath(path)                                               │
│    38                                                                                │
│    39                                                                                │
│ File "C:\Users\Abel\AppData\Local\Programs\Python\Python39\lib\os.py", line 215, in  │
│makedirs                                                                              │
│     212         head, tail = path.split(head)                                        │
│     213     if head and tail and not path.exists(head):                              │
│     214         try:                                                                 │
│  ❱  215             makedirs(head, exist_ok=exist_ok)                                │
│     216         except FileExistsError:                                              │
│     217             # Defeats race condition when another thread created the path    │
│     218             pass                                                             │
│ File "C:\Users\Abel\AppData\Local\Programs\Python\Python39\lib\os.py", line 215, in  │
│makedirs                                                                              │
│     212         head, tail = path.split(head)                                        │
│     213     if head and tail and not path.exists(head):                              │
│     214         try:                                                                 │
│  ❱  215             makedirs(head, exist_ok=exist_ok)                                │
│     216         except FileExistsError:                                              │
│     217             # Defeats race condition when another thread created the path    │
│     218             pass                                                             │
│ File "C:\Users\Abel\AppData\Local\Programs\Python\Python39\lib\os.py", line 215, in  │
│makedirs                                                                              │
│     212         head, tail = path.split(head)                                        │
│     213     if head and tail and not path.exists(head):                              │
│     214         try:                                                                 │
│  ❱  215             makedirs(head, exist_ok=exist_ok)                                │
│     216         except FileExistsError:                                              │
│     217             # Defeats race condition when another thread created the path    │
│     218             pass                                                             │
│ File "C:\Users\Abel\AppData\Local\Programs\Python\Python39\lib\os.py", line 215, in  │
│makedirs                                                                              │
│     212         head, tail = path.split(head)                                        │
│     213     if head and tail and not path.exists(head):                              │
│     214         try:                                                                 │
│  ❱  215             makedirs(head, exist_ok=exist_ok)                                │
│     216         except FileExistsError:                                              │
│     217             # Defeats race condition when another thread created the path    │
│     218             pass                                                             │
│ File "C:\Users\Abel\AppData\Local\Programs\Python\Python39\lib\os.py", line 215, in  │
│makedirs                                                                              │
│     212         head, tail = path.split(head)                                        │
│     213     if head and tail and not path.exists(head):                              │
│     214         try:                                                                 │
│  ❱  215             makedirs(head, exist_ok=exist_ok)                                │
│     216         except FileExistsError:                                              │
│     217             # Defeats race condition when another thread created the path    │
│     218             pass                                                             │
│ File "C:\Users\Abel\AppData\Local\Programs\Python\Python39\lib\os.py", line 215, in  │
│makedirs                                                                              │
│     212         head, tail = path.split(head)                                        │
│     213     if head and tail and not path.exists(head):                              │
│     214         try:                                                                 │
│  ❱  215             makedirs(head, exist_ok=exist_ok)                                │
│     216         except FileExistsError:                                              │
│     217             # Defeats race condition when another thread created the path    │
│     218             pass                                                             │
│ File "C:\Users\Abel\AppData\Local\Programs\Python\Python39\lib\os.py", line 215, in  │
│makedirs                                                                              │
│     212         head, tail = path.split(head)                                        │
│     213     if head and tail and not path.exists(head):                              │
│     214         try:                                                                 │
│  ❱  215             makedirs(head, exist_ok=exist_ok)                                │
│     216         except FileExistsError:                                              │
│     217             # Defeats race condition when another thread created the path    │
│     218             pass                                                             │
│ File "C:\Users\Abel\AppData\Local\Programs\Python\Python39\lib\os.py", line 215, in  │
│makedirs                                                                              │
│     212         head, tail = path.split(head)                                        │
│     213     if head and tail and not path.exists(head):                              │
│     214         try:                                                                 │
│  ❱  215             makedirs(head, exist_ok=exist_ok)                                │
│     216         except FileExistsError:                                              │
│     217             # Defeats race condition when another thread created the path    │
│     218             pass                                                             │
│ File "C:\Users\Abel\AppData\Local\Programs\Python\Python39\lib\os.py", line 215, in  │
│makedirs                                                                              │
│     212         head, tail = path.split(head)                                        │
│     213     if head and tail and not path.exists(head):                              │
│     214         try:                                                                 │
│  ❱  215             makedirs(head, exist_ok=exist_ok)                                │
│     216         except FileExistsError:                                              │
│     217             # Defeats race condition when another thread created the path    │
│     218             pass                                                             │
│ File "C:\Users\Abel\AppData\Local\Programs\Python\Python39\lib\os.py", line 215, in  │
│makedirs                                                                              │
│     212         head, tail = path.split(head)                                        │
│     213     if head and tail and not path.exists(head):                              │
│     214         try:                                                                 │
│  ❱  215             makedirs(head, exist_ok=exist_ok)                                │
│     216         except FileExistsError:                                              │
│     217             # Defeats race condition when another thread created the path    │
│     218             pass                                                             │
│ File "C:\Users\Abel\AppData\Local\Programs\Python\Python39\lib\os.py", line 225, in  │
│makedirs                                                                              │
│     222         if tail == cdir:           # xxx/newdir/. exists if xxx/newdir exists│
│     223             return                                                           │
│     224     try:                                                                     │
│  ❱  225         mkdir(name, mode)                                                    │
│     226     except OSError:                                                          │
│     227         # Cannot rely on checking for EEXIST, since the operating system     │
│     228         # could give priority to other errors like EACCES or EROFS           │
╰──────────────────────────────────────────────────────────────────────────────────────╯
OSError: [WinError 123] The filename, directory name, or volume label syntax is incorrect: 'media\\videos\\example\\1080p60\\partial_movie_files\\C:'
```

<!-- Insert screenshots here (only when absolutely necessary, we prefer copy/pasted output!) -->

</details>


## System specifications

<details><summary>System Details</summary>

- OS (with version, e.g Windows 10 v2004 or macOS 10.15 (Catalina)): Windows 10 v1909
- RAM: 32GB 3200MHz
- Python version (`python/py/python3 --version`): 3.9.2
- Installed modules (provide output from `pip list`):
```
Package                       Version
----------------------------- ----------
alabaster                     0.7.12
appdirs                       1.4.4
atomicwrites                  1.4.0
attrs                         20.3.0
Babel                         2.9.0
beautifulsoup4                4.9.3
black                         20.8b1
cachetools                    4.2.1
certifi                       2020.12.5
chardet                       4.0.0
click                         7.1.2
colorama                      0.4.4
colour                        0.1.5
commonmark                    0.9.1
decorator                     4.4.2
docutils                      0.16
glcontext                     2.3.3
google                        3.0.0
google-api-core               1.26.1
google-api-python-client      2.0.2
google-auth                   1.27.1
google-auth-httplib2          0.1.0
googleapis-common-protos      1.53.0
grpcio                        1.36.1
guzzle-sphinx-theme           0.7.11
httplib2                      0.19.0
idna                          2.10
imagesize                     1.2.0
importlib-metadata            3.7.2
iniconfig                     1.1.1
Jinja2                        2.11.3
manim                         0.4.0
ManimPango                    0.2.4
mapbox-earcut                 0.12.10
MarkupSafe                    1.1.1
moderngl                      5.6.4
moderngl-window               2.3.0
multipledispatch              0.6.0
mypy-extensions               0.4.3
networkx                      2.5
numpy                         1.20.1
packaging                     20.9
pathspec                      0.8.1
Pillow                        8.1.1
pip                           21.0.1
pluggy                        0.13.1
protobuf                      3.15.5
py                            1.10.0
pyasn1                        0.4.8
pyasn1-modules                0.2.8
pycairo                       1.20.0
pydub                         0.24.1
pyglet                        1.5.15
Pygments                      2.8.0
pyparsing                     2.4.7
pyrr                          0.10.3
pytest                        6.2.2
pytz                          2021.1
recommonmark                  0.7.1
regex                         2020.11.13
requests                      2.25.1
rich                          6.2.0
rsa                           4.7.2
scipy                         1.6.1
setuptools                    49.2.1
six                           1.15.0
snowballstemmer               2.1.0
soupsieve                     2.2
Sphinx                        3.1.2
sphinx-copybutton             0.3.1
sphinxcontrib-applehelp       1.0.2
sphinxcontrib-devhelp         1.0.2
sphinxcontrib-htmlhelp        1.0.3
sphinxcontrib-jsmath          1.0.1
sphinxcontrib-qthelp          1.0.3
sphinxcontrib-serializinghtml 1.1.4
sphinxext-opengraph           0.4.1
toml                          0.10.2
tqdm                          4.58.0
typed-ast                     1.4.2
typing-extensions             3.7.4.3
uritemplate                   3.0.1
urllib3                       1.26.3
zipp                          3.4.1
```
</details>

<details><summary>LaTeX details</summary>

+ LaTeX distribution (e.g. TeX Live 2020): MiKTeX latest
+ Installed LaTeX packages: all of them
<!-- output of `tlmgr list --only-installed` for TeX Live or a screenshot of the Packages page for MikTeX -->

</details>

<details><summary>FFMPEG</summary>

Output of `ffmpeg -version`:

```
ffmpeg version 4.3.2-2021-02-27-full_build-www.gyan.dev Copyright (c) 2000-2021 the FFmpeg developers
built with gcc 10.2.0 (Rev6, Built by MSYS2 project)
configuration: --enable-gpl --enable-version3 --enable-static --disable-w32threads --disable-autodetect --enable-fontconfig --enable-iconv --enable-gnutls --enable-libxml2 --enable-gmp --enable-lzma --enable-libsnappy --enable-zlib --enable-libsrt --enable-libssh --enable-libzmq --enable-avisynth --enable-libbluray --enable-libcaca --enable-sdl2 --enable-libdav1d --enable-libzvbi --enable-librav1e --enable-libwebp --enable-libx264 --enable-libx265 --enable-libxvid --enable-libaom --enable-libopenjpeg --enable-libvpx --enable-libass --enable-frei0r --enable-libfreetype --enable-libfribidi --enable-libvidstab --enable-libvmaf --enable-libzimg --enable-amf --enable-cuda-llvm --enable-cuvid --enable-ffnvcodec --enable-nvdec --enable-nvenc --enable-d3d11va --enable-dxva2 --enable-libmfx --enable-libcdio --enable-libgme --enable-libmodplug --enable-libopenmpt --enable-libopencore-amrwb --enable-libmp3lame --enable-libshine --enable-libtheora --enable-libtwolame --enable-libvo-amrwbenc --enable-libilbc --enable-libgsm --enable-libopencore-amrnb --enable-libopus --enable-libspeex --enable-libvorbis --enable-ladspa --enable-libbs2b --enable-libflite --enable-libmysofa --enable-librubberband --enable-libsoxr --enable-chromaprint
libavutil      56. 51.100 / 56. 51.100
libavcodec     58. 91.100 / 58. 91.100
libavformat    58. 45.100 / 58. 45.100
libavdevice    58. 10.100 / 58. 10.100
libavfilter     7. 85.100 /  7. 85.100
libswscale      5.  7.100 /  5.  7.100
libswresample   3.  7.100 /  3.  7.100
libpostproc    55.  7.100 / 55.  7.100
```
</details>

## Additional comments
<!-- Add further context that you think might be relevant for this issue here. -->
I tested this both on the latest master branch and on version 0.3.0. Both exhibited the issue.



Discussion:
Can reproduce. I tracked the problem down and it seems to be caused due to these lines:
https://github.com/ManimCommunity/manim/blob/776067584e6365db1898d23a2dc7fe20cf07ae5e/manim/scene/scene_file_writer.py#L71-L75

Apparently, in a later section of the same function, this `default_name` parameter is passed to `guarantee_existence` (and subsequently `os.makedirs`:
https://github.com/ManimCommunity/manim/blob/776067584e6365db1898d23a2dc7fe20cf07ae5e/manim/scene/scene_file_writer.py#L102-L109

...which resulted in a path like this: 
![image](https://user-images.githubusercontent.com/65204531/111417496-00572300-870c-11eb-9431-5fdaa01bd9e3.png)

instead of something like this:
![image](https://user-images.githubusercontent.com/65204531/111417742-7eb3c500-870c-11eb-9e2a-caa1b84a5f6a.png)

Since I don't really know the reasoning behind L74 of `SceneFileWriter`, pinging @leotrs (last modifier according to blame) for clarity on this and whether changing it would break anything.
Update: apparently, changing `scene_name=default_name` to `scene_name=scene_name` fixes the problem.

Resolving patch:
diff --git a/manim/scene/scene_file_writer.py b/manim/scene/scene_file_writer.py
--- a/manim/scene/scene_file_writer.py
+++ b/manim/scene/scene_file_writer.py
@@ -68,7 +68,7 @@ def init_output_directories(self, scene_name):
         else:
             module_name = ""
 
-        if config["output_file"]:
+        if config["output_file"] and not config["write_all"]:
             default_name = config.get_dir("output_file")
         else:
             default_name = Path(scene_name)
@@ -102,7 +102,7 @@ def init_output_directories(self, scene_name):
             self.partial_movie_directory = guarantee_existence(
                 config.get_dir(
                     "partial_movie_dir",
-                    scene_name=default_name,
+                    scene_name=scene_name,
                     module_name=module_name,
                 )
             )


Test patch:
diff --git a/tests/test_scene_rendering/conftest.py b/tests/test_scene_rendering/conftest.py
--- a/tests/test_scene_rendering/conftest.py
+++ b/tests/test_scene_rendering/conftest.py
@@ -11,3 +11,8 @@ def manim_cfg_file():
 @pytest.fixture
 def simple_scenes_path():
     return str(Path(__file__).parent / "simple_scenes.py")
+
+
+@pytest.fixture
+def infallible_scenes_path():
+    return str(Path(__file__).parent / "infallible_scenes.py")
diff --git a/tests/test_scene_rendering/infallible_scenes.py b/tests/test_scene_rendering/infallible_scenes.py
new file mode 100644
--- /dev/null
+++ b/tests/test_scene_rendering/infallible_scenes.py
@@ -0,0 +1,11 @@
+from manim import *
+
+
+class Wait1(Scene):
+    def construct(self):
+        self.wait()
+
+
+class Wait2(Scene):
+    def construct(self):
+        self.wait(2)
diff --git a/tests/test_scene_rendering/test_cli_flags.py b/tests/test_scene_rendering/test_cli_flags.py
--- a/tests/test_scene_rendering/test_cli_flags.py
+++ b/tests/test_scene_rendering/test_cli_flags.py
@@ -145,6 +145,34 @@ def test_r_flag(tmp_path, manim_cfg_file, simple_scenes_path):
     assert np.asarray(Image.open(filename)).shape == (100, 200, 4)
 
 
+@pytest.mark.slow
+def test_a_flag(tmp_path, manim_cfg_file, infallible_scenes_path):
+    command = [
+        sys.executable,
+        "-m",
+        "manim",
+        infallible_scenes_path,
+        "-ql",
+        "--media_dir",
+        str(tmp_path),
+        "-a",
+    ]
+    out, err, exit_code = capture(command)
+    assert exit_code == 0, err
+
+    one_is_not_empty = (
+        tmp_path / "videos" / "infallible_scenes" / "480p15" / "Wait1.mp4"
+    ).is_file()
+    assert one_is_not_empty, "running manim with -a flag did not render the first scene"
+
+    two_is_not_empty = (
+        tmp_path / "videos" / "infallible_scenes" / "480p15" / "Wait2.mp4"
+    ).is_file()
+    assert (
+        two_is_not_empty
+    ), "running manim with -a flag did not render the second scene"
+
+
 @pytest.mark.slow
 def test_custom_folders(tmp_path, manim_cfg_file, simple_scenes_path):
     scene_name = "SquareToCircle"