Repository: ManimCommunity/manim
File: manim/mobject/types/vectorized_mobject.py (Python)

Code before:
                    circle_blue.shift(RIGHT)
                    gr = VGroup(circle_red, circle_green)
                    gr2 = VGroup(circle_blue) # Constructor uses add directly
                    self.add(gr,gr2)
                    self.wait()
                    gr += gr2 # Add group to another
                    self.play(
                        gr.animate.shift(DOWN),
                    )
                    gr -= gr2 # Remove group
                    self.play( # Animate groups separately
                        gr.animate.shift(LEFT),
                        gr2.animate.shift(UP),
                    )
                    self.play( #Animate groups without modification
                        (gr+gr2).animate.shift(RIGHT)
                    )
                    self.play( # Animate group without component
                        (gr-circle_red).animate.shift(RIGHT)
                    )
        """

        def get_type_error_message(invalid_obj, invalid_i):
            return (
                f"Only values of type {vmobject_render_type.__name__} can be added "
                f"as submobjects of VGroup, but the value "
                f"{repr(invalid_obj)} (at index {invalid_i}) is of type "
                f"{type(invalid_obj).__name__}."
            )

        vmobject_render_type = (
            OpenGLVMobject if config.renderer == RendererType.OPENGL else VMobject
        )
        valid_vmobjects = []

        for vmobject_i, vmobject in enumerate(vmobjects):
            if isinstance(vmobject, vmobject_render_type):
                valid_vmobjects.append(vmobject)
            elif isinstance(vmobject, Iterable) and not isinstance(
                vmobject, (Mobject, OpenGLMobject)
            ):
                for subvmobject_i, subvmobject in enumerate(vmobject):
                    if not isinstance(subvmobject, vmobject_render_type):
                        raise TypeError(
                            get_type_error_message(subvmobject, subvmobject_i)
                        )
                    valid_vmobjects.append(subvmobject)
            elif isinstance(vmobject, Iterable) and isinstance(
                vmobject, (Mobject, OpenGLMobject)
            ):
                # This is if vmobject is an empty Mobject

Review comment (suggestion):
(nitpck) not sure why linting didn't catch this, but you don't need it to be an f-string here?
```suggestion
                "as submobjects of VGroup, but the value "
```

Code after:
                    circle_red.shift(LEFT)
                    circle_blue.shift(RIGHT)
                    gr = VGroup(circle_red, circle_green)
                    gr2 = VGroup(circle_blue) # Constructor uses add directly
                    self.add(gr,gr2)
                    self.wait()
                    gr += gr2 # Add group to another
                    self.play(
                        gr.animate.shift(DOWN),
                    )
                    gr -= gr2 # Remove group
                    self.play( # Animate groups separately
                        gr.animate.shift(LEFT),
                        gr2.animate.shift(UP),
                    )
                    self.play( #Animate groups without modification
                        (gr+gr2).animate.shift(RIGHT)
                    )
                    self.play( # Animate group without component
                        (gr-circle_red).animate.shift(RIGHT)
                    )

        A `VGroup` can be created using iterables as well. Keep in mind that all generated values from an
        iterable must be an instance of `VMobject`. This is demonstrated below:

        .. manim:: AddIterableToVGroupExample
            :save_last_frame:

            class AddIterableToVGroupExample(Scene):
                def construct(self):
                    v = VGroup(
                        Square(),               # Singular VMobject instance
                        [Circle(), Triangle()], # List of VMobject instances
                        Dot(),
                        (Dot() for _ in range(2)), # Iterable that generates VMobjects
                    )
                    v.arrange()
                    self.add(v)

        To facilitate this, the iterable is unpacked before its individual instances are added to the `VGroup`.
        As a result, when you index a `VGroup`, you will never get back an iterable.
        Instead, you will always receive `VMobject` instances, including those
        that were part of the iterable/s that you originally added to the `VGroup`.
        """

        def get_type_error_message(invalid_obj, invalid_indices):
            return (
                f"Only values of type {vmobject_render_type.__name__} can be added "
                "as submobjects of VGroup, but the value "
                f"{repr(invalid_obj)} (at index {invalid_indices[1]} of "
                f"parameter {invalid_indices[0]}) is of type "