Repository: ManimCommunity/manim

Issue:
`SVGMobject._ensure_valid_file` cannot handle `~` or non-existant files gracefully
In case a specified file does not exist, or if a `~` is used to represent the user's home directory, `SVGMobject._ensure_valid_file` raises a rather unhelpful exception:

```py
╭─────────────────────────────── Traceback (most recent call last) ────────────────────────────────╮
│ /home/behackl/code/manim/manim/cli/render/commands.py:121 in render                              │
│                                                                                                  │
│   118 │   │   │   try:                                                                           │
│   119 │   │   │   │   with tempconfig(config):                                                   │
│   120 │   │   │   │   │   scene = SceneClass()                                                   │
│ ❱ 121 │   │   │   │   │   scene.render()                                                         │
│   122 │   │   │   except Exception:                                                              │
│   123 │   │   │   │   error_console.print_exception()                                            │
│   124 │   │   │   │   sys.exit(1)                                                                │
│                                                                                                  │
│ /home/behackl/code/manim/manim/scene/scene.py:222 in render                                      │
│                                                                                                  │
│    219 │   │   """                                                                               │
│    220 │   │   self.setup()                                                                      │
│    221 │   │   try:                                                                              │
│ ❱  222 │   │   │   self.construct()                                                              │
│    223 │   │   except EndSceneEarlyException:                                                    │
│    224 │   │   │   pass                                                                          │
│    225 │   │   except RerunSceneException as e:                                                  │
│                                                                                                  │
│ /home/behackl/code/manim/example_scenes/ex.py:5 in construct                                     │
│                                                                                                  │
│    2                                                                                             │
│    3 class SVGT(Scene):                                                                          │
│    4 │   def construct(self):                                                                    │
│ ❱  5 │   │   svg = SVGMobject("~/code/manim/tests/test_graphical_units/img_svg_resources/aabb    │
│    6 │   │   self.play(DrawBorderThenFill(svg))                                                  │
│    7 │   │   self.wait()                                                                         │
│    8                                                                                             │
│                                                                                                  │
│ /home/behackl/code/manim/manim/mobject/svg/svg_mobject.py:92 in __init__                         │
│                                                                                                  │
│    89 │   │                                                                                      │
│    90 │   │   self.def_map = {}                                                                  │
│    91 │   │   self.file_name = Path(file_name)                                                   │
│ ❱  92 │   │   self._ensure_valid_file()                                                          │
│    93 │   │   self.should_center = should_center                                                 │
│    94 │   │   self.unpack_groups = unpack_groups                                                 │
│    95 │   │   self.path_string_config = (                                                        │
│                                                                                                  │
│ /home/behackl/code/manim/manim/mobject/svg/svg_mobject.py:141 in _ensure_valid_file              │
│                                                                                                  │
│   138 │   │   │   config.get_dir("assets_dir") / self.file_name,                                 │
│   139 │   │   │   config.get_dir("assets_dir") / self.file_name.with_suffix(".svg"),             │
│   140 │   │   │   config.get_dir("assets_dir") / self.file_name.with_suffix(".xdv"),             │
│ ❱ 141 │   │   │   self.file_path,                                                                │
│   142 │   │   │   self.file_path.with_suffix(".svg"),                                            │
│   143 │   │   │   self.file_path.with_suffix(".xdv"),                                            │
│   144 │   │   ]                                                                                  │
│                                                                                                  │
│ /home/behackl/code/manim/manim/mobject/mobject.py:663 in __getattr__                             │
│                                                                                                  │
│    660 │   │   │   return types.MethodType(setter, self)                                         │
│    661 │   │                                                                                     │
│    662 │   │   # Unhandled attribute, therefore error                                            │
│ ❱  663 │   │   raise AttributeError(f"{type(self).__name__} object has no attribute '{attr}'")   │
│    664 │                                                                                         │
│    665 │   @property                                                                             │
│    666 │   def width(self):                                                                      │
╰──────────────────────────────────────────────────────────────────────────────────────────────────╯
AttributeError: SVGMobject object has no attribute 'file_path'
```

The method should be fixed to recognize `~` and in case the file does not exist a more specific error should be raised.


Discussion:
Could I take this issue?

Resolving patch:
diff --git a/manim/mobject/svg/svg_mobject.py b/manim/mobject/svg/svg_mobject.py
--- a/manim/mobject/svg/svg_mobject.py
+++ b/manim/mobject/svg/svg_mobject.py
@@ -125,8 +125,8 @@ def _ensure_valid_file(self):
         if self.file_name is None:
             raise Exception("Must specify file for SVGMobject")
 
-        if self.file_name.exists():
-            self.file_path = self.file_name
+        if self.file_name.expanduser().exists():
+            self.file_path = self.file_name.expanduser()
             return
 
         relative = Path.cwd() / self.file_name
diff --git a/manim/mobject/text/code_mobject.py b/manim/mobject/text/code_mobject.py
--- a/manim/mobject/text/code_mobject.py
+++ b/manim/mobject/text/code_mobject.py
@@ -285,7 +285,7 @@ def _ensure_valid_file(self):
             raise Exception("Must specify file for Code")
         possible_paths = [
             os.path.join(os.path.join("assets", "codes"), self.file_name),
-            self.file_name,
+            os.path.expanduser(self.file_name),
         ]
         for path in possible_paths:
             if os.path.exists(path):
diff --git a/manim/utils/file_ops.py b/manim/utils/file_ops.py
--- a/manim/utils/file_ops.py
+++ b/manim/utils/file_ops.py
@@ -161,7 +161,7 @@ def guarantee_empty_existence(path: Path) -> Path:
 def seek_full_path_from_defaults(
     file_name: str, default_dir: Path, extensions: list[str]
 ) -> Path:
-    possible_paths = [Path(file_name)]
+    possible_paths = [Path(file_name).expanduser()]
     possible_paths += [
         Path(default_dir) / f"{file_name}{extension}" for extension in ["", *extensions]
     ]


Test patch: