Repository: ManimCommunity/manim

Issue:
Mobject.add() should perform input type validation
Currently, the implementation of `Mobject.add` seems to assume that all its arguments are of type `Mobject`, but this is never guaranteed:

```python
    def add(self, *mobjects):
        if self in mobjects:
            raise ValueError("Mobject cannot contain self")
        self.submobjects = list_update(self.submobjects, mobjects)
        return self
```

Should we add this validation? I'm leaning toward yes, but I don't want to litter the whole codebase with input validation everywhere...


Discussion:
Correct me if I'm wrong, but isn't `Mobject.add` supposed to be a private thing? Like, people will use Groupe if they want to add mobjects together. `Mobject.add` is used only in `Group`, and there is already an input validation there (see l.1121 `mobject.py`).
I think it's a matter of good OOP design and encapsulation/responsibilities.
If I `add` something to a Mobject, am I responsible for checking I'm adding the right thing, or is the Mobject responsible for telling me? I think the latter. The fact that it's only used once by one other class is incidental.

Also, I don't think this is supposed to be private since it is inherited from the parent Container class, which makes it public. Overriding it and making it private would be very very weird...
alrighty, we can just `isinstance(obj, Mobject)` and be done with it
Yes, the fix is easy, but I just wanted to see if anyone had objections to adding that typecheck.
If consensus is reached on #325, this should be closed.

Resolving patch:
diff --git a/manim/mobject/mobject.py b/manim/mobject/mobject.py
--- a/manim/mobject/mobject.py
+++ b/manim/mobject/mobject.py
@@ -96,6 +96,9 @@ def add(self, *mobjects):
         ------
         :class:`ValueError`
             When a mobject tries to add itself.
+        :class:`TypeError`
+            When trying to add an object that is not an instance of :class:`Mobject`.
+
 
         Notes
         -----
@@ -130,8 +133,11 @@ def add(self, *mobjects):
             ValueError: Mobject cannot contain self
 
         """
-        if self in mobjects:
-            raise ValueError("Mobject cannot contain self")
+        for m in mobjects:
+            if not isinstance(m, Mobject):
+                raise TypeError("All submobjects must be of type Mobject")
+            if m is self:
+                raise ValueError("Mobject cannot contain self")
         self.submobjects = list_update(self.submobjects, mobjects)
         return self
 
@@ -1242,7 +1248,5 @@ class Group(Mobject):
     """Groups together multiple Mobjects."""
 
     def __init__(self, *mobjects, **kwargs):
-        if not all([isinstance(m, Mobject) for m in mobjects]):
-            raise TypeError("All submobjects must be of type Mobject")
         Mobject.__init__(self, **kwargs)
         self.add(*mobjects)


Test patch:
diff --git a/tests/test_container.py b/tests/test_container.py
--- a/tests/test_container.py
+++ b/tests/test_container.py
@@ -58,6 +58,10 @@ def test_mobject_add():
     with pytest.raises(ValueError):
         obj.add(obj)
 
+    # can only add Mobjects
+    with pytest.raises(TypeError):
+        obj.add("foo")
+
 
 def test_mobject_remove():
     """Test Mobject.remove()."""