Repository: pytest-dev/pytest
File: src/_pytest/python.py (Python)

Code before:
        resolved_ids = list(self._resolve_ids())
        # All IDs must be unique!
        if len(resolved_ids) != len(set(resolved_ids)):
            if self._require_unique_ids_enabled():
                duplicate_indexs = defaultdict(list)
                for i, val in enumerate(resolved_ids):
                    duplicate_indexs[val].append(i)

                # Keep only duplicates
                duplicates = {k: v for k, v in duplicate_indexs.items() if len(v) > 1}
                arugment_values = [
                    saferepr(param.values) for param in self.parametersets
                ]
                error_msg = f"""
                    When --require-unique-parameterization-ids set, pytest won't generate unique IDs for parameters.
                    test name: {self.nodeid}
                    argument names: {self.argnames}
                    argument values: {arugment_values}
                    resolved (with non-unique) IDs: {resolved_ids}
                    duplicates: {duplicates}
                    you must make sure all parameterization IDs are unique, either by:
                    - providing unique IDs per parameterization via the ids=[...] argument to @pytest.mark.parametrize
                    - providing a custom id function that generates unique IDs for each parameterization
                    - not setting --require-unique-parameterization-ids
                    """
                raise nodes.Collector.CollectError(textwrap.dedent(error_msg))
            # Record the number of occurrences of each ID.
            id_counts = Counter(resolved_ids)
            # Map the ID to its next suffix.
            id_suffixes: dict[str, int] = defaultdict(int)
            # Suffix non-unique IDs to make them unique.
            for index, id in enumerate(resolved_ids):
                if id_counts[id] > 1:
                    if id is HIDDEN_PARAM:
                        self._complain_multiple_hidden_parameter_sets()
                    suffix = ""
                    if id and id[-1].isdigit():
                        suffix = "_"
                    new_id = f"{id}{suffix}{id_suffixes[id]}"
                    while new_id in set(resolved_ids):
                        id_suffixes[id] += 1
                        new_id = f"{id}{suffix}{id_suffixes[id]}"
                    resolved_ids[index] = new_id
                    id_suffixes[id] += 1
        assert len(resolved_ids) == len(set(resolved_ids)), (
            f"Internal error: {resolved_ids=}"
        )
        return resolved_ids

    def _require_unique_ids_enabled(self) -> bool:
        if self.config:

Review comment (suggestion):
It's customary to put the dedent call around the string itself, i.e. `error_msg = textwrap.dedent(f"""...`.

Code after:
        raises CollectError. Otherwise makes the IDs unique as follows:

        Format is <prm_1_token>-...-<prm_n_token>[counter], where prm_x_token is
        - user-provided id, if given
        - else an id derived from the value, applicable for certain types
        - else <argname><parameterset index>
        The counter suffix is appended only in case a string wouldn't be unique
        otherwise.
        """
        resolved_ids = list(self._resolve_ids())
        # All IDs must be unique!
        if len(resolved_ids) != len(set(resolved_ids)):
            # Record the number of occurrences of each ID.
            id_counts = Counter(resolved_ids)

            if self._strict_parametrization_ids_enabled():
                parameters = ", ".join(self.argnames)
                parametersets = ", ".join(
                    [saferepr(list(param.values)) for param in self.parametersets]
                )
                ids = ", ".join(
                    id if id is not HIDDEN_PARAM else "<hidden>" for id in resolved_ids
                )
                duplicates = ", ".join(
                    id if id is not HIDDEN_PARAM else "<hidden>"
                    for id, count in id_counts.items()
                    if count > 1
                )
                msg = textwrap.dedent(f"""
                    Duplicate parametrization IDs detected, but strict_parametrization_ids is set.

                    Test name:      {self.nodeid}
                    Parameters:     {parameters}
                    Parameter sets: {parametersets}
                    IDs:            {ids}
                    Duplicates:     {duplicates}

                    You can fix this problem using `@pytest.mark.parametrize(..., ids=...)` or `pytest.param(..., id=...)`.
                """).strip()  # noqa: E501
                raise nodes.Collector.CollectError(msg)

            # Map the ID to its next suffix.
            id_suffixes: dict[str, int] = defaultdict(int)
            # Suffix non-unique IDs to make them unique.
            for index, id in enumerate(resolved_ids):
                if id_counts[id] > 1:
                    if id is HIDDEN_PARAM:
                        self._complain_multiple_hidden_parameter_sets()
                    suffix = ""
                    if id and id[-1].isdigit():
                        suffix = "_"