Repository: Textualize/textual
File: src/textual/dom.py (Python)

Code before:

    def _initialize_data_bind(self) -> None:
        """initialize a data binding.

        Args:
            compose_parent: The node doing the binding.
        """
        if not self._reactive_connect:
            return
        for variable_name, (compose_parent, reactive) in self._reactive_connect.items():

            def make_setter(variable_name: str) -> Callable[[object], None]:
                """Make a setter for the given variable name.

                Args:
                    variable_name: Name of variable being set.

                Returns:
                    A callable which takes the value to set.
                """

                def setter(value: object) -> None:
                    """Set bound data."""
                    _rich_traceback_omit = True
                    Reactive._initialize_object(self)
                    # Copy the bound value
                    # Sharing the same instance with mutable objects can lead to confusing behavior
                    # Wrap the value in `_Mutated` so the setter knows to invoke watchers etc
                    setattr(self, variable_name, _Mutated(copy(value)))

                return setter

            assert isinstance(compose_parent, DOMNode)
            setter = make_setter(variable_name)
            if isinstance(reactive, Reactive):
                self.watch(
                    compose_parent,
                    reactive.name,
                    setter,
                    init=True,
                )
            else:
                self.call_later(partial(setter, reactive))
        self._reactive_connect = None

    def compose_add_child(self, widget: Widget) -> None:
        """Add a node to children.

        This is used by the compose process when it adds children.
        There is no need to use it directly, but you may want to override it in a subclass
        if you want children to be attached to a different node.

Review comment (bug):
I feel like this should be documented since copying can be expensive or potentially crash the program.

I'm guessing shallow copying means it also wouldn't fix the confusion for e.g. a list of mutable objects. For example, if one of the objects in the list is mutated and I call `mutate_reactive`, the prior confusing scenario still arises.

Code after:
            self._initialize_data_bind()
        else:
            self.call_later(self._initialize_data_bind)
        return self

    def _initialize_data_bind(self) -> None:
        """initialize a data binding.

        Args:
            compose_parent: The node doing the binding.
        """
        if not self._reactive_connect:
            return
        for variable_name, (compose_parent, reactive) in self._reactive_connect.items():

            def make_setter(variable_name: str) -> Callable[[object], None]:
                """Make a setter for the given variable name.

                Args:
                    variable_name: Name of variable being set.

                Returns:
                    A callable which takes the value to set.
                """

                def setter(value: object) -> None:
                    """Set bound data."""
                    _rich_traceback_omit = True
                    Reactive._initialize_object(self)
                    # Wrap the value in `_Mutated` so the setter knows to invoke watchers etc.
                    setattr(self, variable_name, _Mutated(value))

                return setter

            assert isinstance(compose_parent, DOMNode)
            setter = make_setter(variable_name)
            if isinstance(reactive, Reactive):
                self.watch(
                    compose_parent,
                    reactive.name,
                    setter,
                    init=True,
                )
            else:
                self.call_later(partial(setter, reactive))
        self._reactive_connect = None

    def compose_add_child(self, widget: Widget) -> None:
        """Add a node to children.

        This is used by the compose process when it adds children.