<commit_before>from __future__ import annotations

import os
import re
import warnings

from contextlib import suppress
from pathlib import Path
from typing import TYPE_CHECKING
from typing import Iterable
from typing import TypeVar

from poetry.core.packages.constraints import (
    parse_constraint as parse_generic_constraint,
)
from poetry.core.packages.dependency_group import MAIN_GROUP
from poetry.core.packages.specification import PackageSpecification
from poetry.core.packages.utils.utils import contains_group_without_marker
from poetry.core.packages.utils.utils import normalize_python_version_markers
from poetry.core.semver.helpers import parse_constraint
from poetry.core.semver.version_range_constraint import VersionRangeConstraint
from poetry.core.version.markers import parse_marker


if TYPE_CHECKING:
    from packaging.utils import NormalizedName

    from poetry.core.packages.constraints import BaseConstraint
    from poetry.core.packages.directory_dependency import DirectoryDependency
    from poetry.core.packages.file_dependency import FileDependency
    from poetry.core.semver.version_constraint import VersionConstraint
    from poetry.core.version.markers import BaseMarker

    T = TypeVar("T", bound="Dependency")


class Dependency(PackageSpecification):
    def __init__(
        self,
        name: str,
        constraint: str | VersionConstraint,
        optional: bool = False,
        groups: Iterable[str] | None = None,
        allows_prereleases: bool = False,
        extras: Iterable[str] | None = None,
        source_type: str | None = None,
        source_url: str | None = None,
        source_reference: str | None = None,
        source_resolved_reference: str | None = None,
        source_subdirectory: str | None = None,
    ) -> None:
        from poetry.core.version.markers import AnyMarker

        super().__init__(
            name,
            source_type=source_type,
            source_url=source_url,
            source_reference=source_reference,
            source_resolved_reference=source_resolved_reference,
            source_subdirectory=source_subdirectory,
            features=extras,
        )

        self._constraint: VersionConstraint
        self._pretty_constraint: str
        self.constraint = constraint  # type: ignore[assignment]

        self._optional = optional

        if not groups:
            groups = [MAIN_GROUP]

        self._groups = frozenset(groups)

        if (
            isinstance(self._constraint, VersionRangeConstraint)
            and self._constraint.min
        ):
            allows_prereleases = (
                allows_prereleases or self._constraint.min.is_unstable()
            )

        self._allows_prereleases = allows_prereleases

        self._python_versions = "*"
        self._python_constraint = parse_constraint("*")
        self._transitive_python_versions: str | None = None
        self._transitive_python_constraint: VersionConstraint | None = None
        self._transitive_marker: BaseMarker | None = None

        self._in_extras: list[str] = []

        self._activated = not self._optional

        self.is_root = False
        self._marker: BaseMarker = AnyMarker()
        self.source_name: str | None = None

    @property
    def name(self) -> NormalizedName:
        return self._name

    @property
    def constraint(self) -> VersionConstraint:
        return self._constraint

    @constraint.setter
    def constraint(self, constraint: str | VersionConstraint) -> None:
        if isinstance(constraint, str):
            self._constraint = parse_constraint(constraint)
        else:
            self._constraint = constraint

        self._pretty_constraint = str(constraint)

    def set_constraint(self, constraint: str | VersionConstraint) -> None:
        warnings.warn(
            "Calling method 'set_constraint' is deprecated and will be removed. "
            "It has been replaced by the property 'constraint' for consistency.",
            DeprecationWarning,
            stacklevel=2,
        )
        self.constraint = constraint  # type: ignore[assignment]

    @property
    def pretty_constraint(self) -> str:
        return self._pretty_constraint

    @property
    def pretty_name(self) -> str:
        return self._pretty_name

    @property
    def groups(self) -> frozenset[str]:
        return self._groups

    @property
    def python_versions(self) -> str:
        return self._python_versions

    @python_versions.setter
    def python_versions(self, value: str) -> None:
        self._python_versions = value
        self._python_constraint = parse_constraint(value)
        if not self._python_constraint.is_any():
            self._marker = self._marker.intersect(
                parse_marker(
                    self._create_nested_marker(
                        "python_version", self._python_constraint
                    )
                )
            )

    @property
    def transitive_python_versions(self) -> str:
        if self._transitive_python_versions is None:
            return self._python_versions

        return self._transitive_python_versions

    @transitive_python_versions.setter
    def transitive_python_versions(self, value: str) -> None:
        self._transitive_python_versions = value
        self._transitive_python_constraint = parse_constraint(value)

    @property
    def marker(self) -> BaseMarker:
        return self._marker

    @marker.setter
    def marker(self, marker: str | BaseMarker) -> None:
        from poetry.core.packages.utils.utils import convert_markers
        from poetry.core.semver.helpers import parse_constraint
        from poetry.core.version.markers import BaseMarker
        from poetry.core.version.markers import parse_marker

        if not isinstance(marker, BaseMarker):
            marker = parse_marker(marker)

        self._marker = marker

        markers = convert_markers(marker)

        if "extra" in markers:
            # If we have extras, the dependency is optional
            self.deactivate()

            for or_ in markers["extra"]:
                for _, extra in or_:
                    self.in_extras.append(extra)

        # Recalculate python versions.
        self._python_versions = "*"
        if not contains_group_without_marker(markers, "python_version"):
            python_version_markers = markers["python_version"]
            self._python_versions = normalize_python_version_markers(
                python_version_markers
            )

        self._python_constraint = parse_constraint(self._python_versions)

    @property
    def transitive_marker(self) -> BaseMarker:
        if self._transitive_marker is None:
            return self.marker

        return self._transitive_marker

    @transitive_marker.setter
    def transitive_marker(self, value: BaseMarker) -> None:
        self._transitive_marker = value

    @property
    def python_constraint(self) -> VersionConstraint:
        return self._python_constraint

    @property
    def transitive_python_constraint(self) -> VersionConstraint:
        if self._transitive_python_constraint is None:
            return self._python_constraint

        return self._transitive_python_constraint

    @property
    def extras(self) -> frozenset[str]:
        # extras activated in a dependency is the same as features
        return self._features

    @property
    def in_extras(self) -> list[str]:
        return self._in_extras

    @property
    def base_pep_508_name(self) -> str:
        from poetry.core.semver.version import Version
        from poetry.core.semver.version_union import VersionUnion

        requirement = self.pretty_name

        if self.extras:
            extras = ",".join(sorted(self.extras))
            requirement += f"[{extras}]"

        constraint = self.constraint
        if isinstance(constraint, VersionUnion):
            if (
                constraint.excludes_single_version()
                or constraint.excludes_single_wildcard_range()
            ):
                # This branch is a short-circuit logic for special cases and
                # avoids having to split and parse constraint again. This has
                # no functional difference with the logic in the else branch.
                requirement += f" ({str(constraint)})"
            else:
                constraints = ",".join(
                    str(parse_constraint(c)) for c in self.pretty_constraint.split(",")
                )
                requirement += f" ({constraints})"
        elif isinstance(constraint, Version):
            requirement += f" (=={constraint.text})"
        elif not constraint.is_any():
            requirement += f" ({str(constraint).replace(' ', '')})"

        return requirement

    def allows_prereleases(self) -> bool:
        return self._allows_prereleases

    def is_optional(self) -> bool:
        return self._optional

    def is_activated(self) -> bool:
        return self._activated

    def is_vcs(self) -> bool:
        return False

    def is_file(self) -> bool:
        return False

    def is_directory(self) -> bool:
        return False

    def is_url(self) -> bool:
        return False

    def to_pep_508(self, with_extras: bool = True) -> str:
        from poetry.core.packages.utils.utils import convert_markers

        requirement = self.base_pep_508_name

        markers = []
        has_extras = False
        if not self.marker.is_any():
            marker = self.marker
            if not with_extras:
                marker = marker.without_extras()

            # we re-check for any marker here since the without extra marker might
            # return an any marker again
            if not marker.is_empty() and not marker.is_any():
                markers.append(str(marker))

            has_extras = "extra" in convert_markers(marker)
        else:
            # Python marker
            if self.python_versions != "*":
                python_constraint = self.python_constraint

                markers.append(
                    self._create_nested_marker("python_version", python_constraint)
                )

        in_extras = " || ".join(self._in_extras)
        if in_extras and with_extras and not has_extras:
            markers.append(
                self._create_nested_marker("extra", parse_generic_constraint(in_extras))
            )

        if markers:
            if self.is_vcs() or self.is_url() or self.is_file():
                requirement += " "

            if len(markers) > 1:
                marker_str = " and ".join(f"({m})" for m in markers)
                requirement += f"; {marker_str}"
            else:
                requirement += f"; {markers[0]}"

        return requirement

    def _create_nested_marker(
        self, name: str, constraint: BaseConstraint | VersionConstraint
    ) -> str:
        from poetry.core.packages.constraints.constraint import Constraint
        from poetry.core.packages.constraints.multi_constraint import MultiConstraint
        from poetry.core.packages.constraints.union_constraint import UnionConstraint
        from poetry.core.semver.version import Version
        from poetry.core.semver.version_union import VersionUnion

        if isinstance(constraint, (MultiConstraint, UnionConstraint)):
            multi_parts = []
            for c in constraint.constraints:
                multi = isinstance(c, (MultiConstraint, UnionConstraint))
                multi_parts.append((multi, self._create_nested_marker(name, c)))

            glue = " and "
            if isinstance(constraint, UnionConstraint):
                parts = [f"({part[1]})" if part[0] else part[1] for part in multi_parts]
                glue = " or "
            else:
                parts = [part[1] for part in multi_parts]

            marker = glue.join(parts)
        elif isinstance(constraint, Constraint):
            marker = f'{name} {constraint.operator} "{constraint.version}"'
        elif isinstance(constraint, VersionUnion):
            parts = [self._create_nested_marker(name, c) for c in constraint.ranges]
            glue = " or "
            parts = [f"({part})" for part in parts]

            marker = glue.join(parts)
        elif isinstance(constraint, Version):
            if constraint.precision >= 3 and name == "python_version":
                name = "python_full_version"

            marker = f'{name} == "{constraint.text}"'
        else:
            assert isinstance(constraint, VersionRangeConstraint)
            if constraint.min is not None:
                min_name = name
                if constraint.min.precision >= 3 and name == "python_version":
                    min_name = "python_full_version"

                    if constraint.max is None:
                        name = min_name

                op = ">="
                if not constraint.include_min:
                    op = ">"

                version = constraint.min.text
                if constraint.max is not None:
                    max_name = name
                    if constraint.max.precision >= 3 and name == "python_version":
                        max_name = "python_full_version"

                    text = f'{min_name} {op} "{version}"'

                    op = "<="
                    if not constraint.include_max:
                        op = "<"

                    version = constraint.max.text

                    text += f' and {max_name} {op} "{version}"'

                    return text
            elif constraint.max is not None:
                if constraint.max.precision >= 3 and name == "python_version":
                    name = "python_full_version"

                op = "<="
                if not constraint.include_max:
                    op = "<"

                version = constraint.max.text
            else:
                return ""

            marker = f'{name} {op} "{version}"'

        return marker

    def activate(self) -> None:
        """
        Set the dependency as mandatory.
        """
        self._activated = True

    def deactivate(self) -> None:
        """
        Set the dependency as optional.<commit_msg>remove duplicate implementation of create_nested_marker<commit_after>from __future__ import annotations

import os
import re
import warnings

from contextlib import suppress
from pathlib import Path
from typing import TYPE_CHECKING
from typing import Iterable
from typing import TypeVar

from poetry.core.packages.constraints import (
    parse_constraint as parse_generic_constraint,
)
from poetry.core.packages.dependency_group import MAIN_GROUP
from poetry.core.packages.specification import PackageSpecification
from poetry.core.packages.utils.utils import contains_group_without_marker
from poetry.core.packages.utils.utils import create_nested_marker
from poetry.core.packages.utils.utils import normalize_python_version_markers
from poetry.core.semver.helpers import parse_constraint
from poetry.core.semver.version_range_constraint import VersionRangeConstraint
from poetry.core.version.markers import parse_marker


if TYPE_CHECKING:
    from packaging.utils import NormalizedName

    from poetry.core.packages.directory_dependency import DirectoryDependency
    from poetry.core.packages.file_dependency import FileDependency
    from poetry.core.semver.version_constraint import VersionConstraint
    from poetry.core.version.markers import BaseMarker

    T = TypeVar("T", bound="Dependency")


class Dependency(PackageSpecification):
    def __init__(
        self,
        name: str,
        constraint: str | VersionConstraint,
        optional: bool = False,
        groups: Iterable[str] | None = None,
        allows_prereleases: bool = False,
        extras: Iterable[str] | None = None,
        source_type: str | None = None,
        source_url: str | None = None,
        source_reference: str | None = None,
        source_resolved_reference: str | None = None,
        source_subdirectory: str | None = None,
    ) -> None:
        from poetry.core.version.markers import AnyMarker

        super().__init__(
            name,
            source_type=source_type,
            source_url=source_url,
            source_reference=source_reference,
            source_resolved_reference=source_resolved_reference,
            source_subdirectory=source_subdirectory,
            features=extras,
        )

        self._constraint: VersionConstraint
        self._pretty_constraint: str
        self.constraint = constraint  # type: ignore[assignment]

        self._optional = optional

        if not groups:
            groups = [MAIN_GROUP]

        self._groups = frozenset(groups)

        if (
            isinstance(self._constraint, VersionRangeConstraint)
            and self._constraint.min
        ):
            allows_prereleases = (
                allows_prereleases or self._constraint.min.is_unstable()
            )

        self._allows_prereleases = allows_prereleases

        self._python_versions = "*"
        self._python_constraint = parse_constraint("*")
        self._transitive_python_versions: str | None = None
        self._transitive_python_constraint: VersionConstraint | None = None
        self._transitive_marker: BaseMarker | None = None

        self._in_extras: list[str] = []

        self._activated = not self._optional

        self.is_root = False
        self._marker: BaseMarker = AnyMarker()
        self.source_name: str | None = None

    @property
    def name(self) -> NormalizedName:
        return self._name

    @property
    def constraint(self) -> VersionConstraint:
        return self._constraint

    @constraint.setter
    def constraint(self, constraint: str | VersionConstraint) -> None:
        if isinstance(constraint, str):
            self._constraint = parse_constraint(constraint)
        else:
            self._constraint = constraint

        self._pretty_constraint = str(constraint)

    def set_constraint(self, constraint: str | VersionConstraint) -> None:
        warnings.warn(
            "Calling method 'set_constraint' is deprecated and will be removed. "
            "It has been replaced by the property 'constraint' for consistency.",
            DeprecationWarning,
            stacklevel=2,
        )
        self.constraint = constraint  # type: ignore[assignment]

    @property
    def pretty_constraint(self) -> str:
        return self._pretty_constraint

    @property
    def pretty_name(self) -> str:
        return self._pretty_name

    @property
    def groups(self) -> frozenset[str]:
        return self._groups

    @property
    def python_versions(self) -> str:
        return self._python_versions

    @python_versions.setter
    def python_versions(self, value: str) -> None:
        self._python_versions = value
        self._python_constraint = parse_constraint(value)
        if not self._python_constraint.is_any():
            self._marker = self._marker.intersect(
                parse_marker(
                    create_nested_marker("python_version", self._python_constraint)
                )
            )

    @property
    def transitive_python_versions(self) -> str:
        if self._transitive_python_versions is None:
            return self._python_versions

        return self._transitive_python_versions

    @transitive_python_versions.setter
    def transitive_python_versions(self, value: str) -> None:
        self._transitive_python_versions = value
        self._transitive_python_constraint = parse_constraint(value)

    @property
    def marker(self) -> BaseMarker:
        return self._marker

    @marker.setter
    def marker(self, marker: str | BaseMarker) -> None:
        from poetry.core.packages.utils.utils import convert_markers
        from poetry.core.semver.helpers import parse_constraint
        from poetry.core.version.markers import BaseMarker
        from poetry.core.version.markers import parse_marker

        if not isinstance(marker, BaseMarker):
            marker = parse_marker(marker)

        self._marker = marker

        markers = convert_markers(marker)

        if "extra" in markers:
            # If we have extras, the dependency is optional
            self.deactivate()

            for or_ in markers["extra"]:
                for _, extra in or_:
                    self.in_extras.append(extra)

        # Recalculate python versions.
        self._python_versions = "*"
        if not contains_group_without_marker(markers, "python_version"):
            python_version_markers = markers["python_version"]
            self._python_versions = normalize_python_version_markers(
                python_version_markers
            )

        self._python_constraint = parse_constraint(self._python_versions)

    @property
    def transitive_marker(self) -> BaseMarker:
        if self._transitive_marker is None:
            return self.marker

        return self._transitive_marker

    @transitive_marker.setter
    def transitive_marker(self, value: BaseMarker) -> None:
        self._transitive_marker = value

    @property
    def python_constraint(self) -> VersionConstraint:
        return self._python_constraint

    @property
    def transitive_python_constraint(self) -> VersionConstraint:
        if self._transitive_python_constraint is None:
            return self._python_constraint

        return self._transitive_python_constraint

    @property
    def extras(self) -> frozenset[str]:
        # extras activated in a dependency is the same as features
        return self._features

    @property
    def in_extras(self) -> list[str]:
        return self._in_extras

    @property
    def base_pep_508_name(self) -> str:
        from poetry.core.semver.version import Version
        from poetry.core.semver.version_union import VersionUnion

        requirement = self.pretty_name

        if self.extras:
            extras = ",".join(sorted(self.extras))
            requirement += f"[{extras}]"

        constraint = self.constraint
        if isinstance(constraint, VersionUnion):
            if (
                constraint.excludes_single_version()
                or constraint.excludes_single_wildcard_range()
            ):
                # This branch is a short-circuit logic for special cases and
                # avoids having to split and parse constraint again. This has
                # no functional difference with the logic in the else branch.
                requirement += f" ({str(constraint)})"
            else:
                constraints = ",".join(
                    str(parse_constraint(c)) for c in self.pretty_constraint.split(",")
                )
                requirement += f" ({constraints})"
        elif isinstance(constraint, Version):
            requirement += f" (=={constraint.text})"
        elif not constraint.is_any():
            requirement += f" ({str(constraint).replace(' ', '')})"

        return requirement

    def allows_prereleases(self) -> bool:
        return self._allows_prereleases

    def is_optional(self) -> bool:
        return self._optional

    def is_activated(self) -> bool:
        return self._activated

    def is_vcs(self) -> bool:
        return False

    def is_file(self) -> bool:
        return False

    def is_directory(self) -> bool:
        return False

    def is_url(self) -> bool:
        return False

    def to_pep_508(self, with_extras: bool = True) -> str:
        from poetry.core.packages.utils.utils import convert_markers

        requirement = self.base_pep_508_name

        markers = []
        has_extras = False
        if not self.marker.is_any():
            marker = self.marker
            if not with_extras:
                marker = marker.without_extras()

            # we re-check for any marker here since the without extra marker might
            # return an any marker again
            if not marker.is_empty() and not marker.is_any():
                markers.append(str(marker))

            has_extras = "extra" in convert_markers(marker)
        else:
            # Python marker
            if self.python_versions != "*":
                python_constraint = self.python_constraint

                markers.append(
                    create_nested_marker("python_version", python_constraint)
                )

        in_extras = " || ".join(self._in_extras)
        if in_extras and with_extras and not has_extras:
            markers.append(
                create_nested_marker("extra", parse_generic_constraint(in_extras))
            )

        if markers:
            if self.is_vcs() or self.is_url() or self.is_file():
                requirement += " "

            if len(markers) > 1:
                marker_str = " and ".join(f"({m})" for m in markers)
                requirement += f"; {marker_str}"
            else:
                requirement += f"; {markers[0]}"

        return requirement

    def activate(self) -> None:
        """
        Set the dependency as mandatory.
        """
        self._activated = True

    def deactivate(self) -> None:
        """
        Set the dependency as optional.
{'fraction_non_alphanumeric': 0.07898079060168281, 'fraction_numerical': 0.0012700428639466582, 'mean_word_length': 2.174225245653817, 'pattern_counts': {'":': 6, '<': 7, '<?xml version=': 0, '>': 74, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '1870973', 'n_tokens_mistral': 6636, 'n_tokens_neox': 6293, 'n_words': 1632}