Repository: kedro-org/kedro
File: kedro/pipeline/node.py (Python)

Code before:
        if outputs and not isinstance(outputs, (list, dict, str)):
            raise ValueError(
                _node_error_message(
                    f"'outputs' type must be one of [String, List, Dict, None], "
                    f"not '{type(outputs).__name__}'."
                )
            )

        for _output in _to_list(outputs):
            if not isinstance(_output, str):
                raise ValueError(
                    _node_error_message(
                        f"names of variables used as outputs of the function "
                        f"must be of 'String' type, but {_output} from {outputs} "
                        f"is '{type(_output)}'."
                    )
                )
            if "." in _output and not _output.startswith("params:"):
                output_namespace = ".".join(_output.split(".")[:-1])
                if not namespace or not output_namespace.startswith(namespace):
                    raise ValueError(
                        _node_error_message(
                            f"Invalid output dataset name '{_output}': '.' characters not allowed "
                            f"in the node output parameter."
                        )
                    )

        if not inputs and not outputs:
            raise ValueError(
                _node_error_message("it must have some 'inputs' or 'outputs'.")
            )

        self._validate_inputs(func, inputs)

        self._func = func
        self._inputs = inputs
        # The type of _outputs is picked up as possibly being None, however the checks above prevent that
        # ever being the case. Mypy doesn't get that though, so it complains about the assignment of outputs to
        # _outputs with different types.
        self._outputs: str | list[str] | dict[str, str] = outputs  # type: ignore[assignment]
        if name and not re.match(r"[\w\.-]+$", name):
            raise ValueError(
                f"'{name}' is not a valid node name. It must contain only "
                f"letters, digits, hyphens, underscores and/or fullstops."
            )
        self._name = name
        self._namespace = namespace
        self._tags = set(_to_list(tags))
        for tag in self._tags:
            if not re.match(r"[\w\.-]+$", tag):
                raise ValueError(

Review comment (refactor):
This is essentially the same logic as for the `input` so we can extract this in a helper method to make this method shorter. 

Code after:
        for _output in _to_list(outputs):
            if not isinstance(_output, str):
                raise ValueError(
                    _node_error_message(
                        f"names of variables used as outputs of the function "
                        f"must be of 'String' type, but {_output} from {outputs} "
                        f"is '{type(_output)}'."
                    )
                )
            _node_dataset_name_validation(_output, namespace)

        if not inputs and not outputs:
            raise ValueError(
                _node_error_message("it must have some 'inputs' or 'outputs'.")
            )

        self._validate_inputs(func, inputs)

        self._func = func
        self._inputs = inputs
        # The type of _outputs is picked up as possibly being None, however the checks above prevent that
        # ever being the case. Mypy doesn't get that though, so it complains about the assignment of outputs to
        # _outputs with different types.
        self._outputs: str | list[str] | dict[str, str] = outputs  # type: ignore[assignment]
        if name and not re.match(r"[\w\.-]+$", name):
            raise ValueError(
                f"'{name}' is not a valid node name. It must contain only "
                f"letters, digits, hyphens, underscores and/or fullstops."
            )
        self._name = name
        self._namespace = namespace
        self._tags = set(_to_list(tags))
        for tag in self._tags:
            if not re.match(r"[\w\.-]+$", tag):
                raise ValueError(
                    f"'{tag}' is not a valid node tag. It must contain only "
                    f"letters, digits, hyphens, underscores and/or fullstops."
                )

        self._validate_unique_outputs()
        self._validate_inputs_dif_than_outputs()
        self._confirms = confirms

    def _copy(self, **overwrite_params: Any) -> Node:
        """
        Helper function to copy the node, replacing some values.
        """
        params = {
            "func": self._func,
            "inputs": self._inputs,
            "outputs": self._outputs,