We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
poetry add "requests[security]" fails on Poetry 1.2.0b1
If I run `poetry add "requests[security]"` on Poetry 1.2.0b1, it fails with this message:

 Package 'requests' is listed as a dependency of itself.

I downgraded to Poetry 1.1.13 where it works. Thanks for all your work!

--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `src/poetry/puzzle/solver.py`
Content:
```
1 from __future__ import annotations
2 
3 import time
4 
5 from collections import defaultdict
6 from contextlib import contextmanager
7 from typing import TYPE_CHECKING
8 from typing import FrozenSet
9 from typing import Tuple
10 from typing import TypeVar
11 
12 from poetry.core.packages.dependency_group import MAIN_GROUP
13 
14 from poetry.mixology import resolve_version
15 from poetry.mixology.failure import SolveFailure
16 from poetry.packages import DependencyPackage
17 from poetry.puzzle.exceptions import OverrideNeeded
18 from poetry.puzzle.exceptions import SolverProblemError
19 from poetry.puzzle.provider import Provider
20 
21 
22 if TYPE_CHECKING:
23     from collections.abc import Iterator
24 
25     from cleo.io.io import IO
26     from poetry.core.packages.dependency import Dependency
27     from poetry.core.packages.directory_dependency import DirectoryDependency
28     from poetry.core.packages.file_dependency import FileDependency
29     from poetry.core.packages.package import Package
30     from poetry.core.packages.project_package import ProjectPackage
31     from poetry.core.packages.url_dependency import URLDependency
32     from poetry.core.packages.vcs_dependency import VCSDependency
33 
34     from poetry.puzzle.transaction import Transaction
35     from poetry.repositories import Pool
36     from poetry.repositories import Repository
37     from poetry.utils.env import Env
38 
39 
40 class Solver:
41     def __init__(
42         self,
43         package: ProjectPackage,
44         pool: Pool,
45         installed: Repository,
46         locked: Repository,
47         io: IO,
48         provider: Provider | None = None,
49     ) -> None:
50         self._package = package
51         self._pool = pool
52         self._installed = installed
53         self._locked = locked
54         self._io = io
55 
56         if provider is None:
57             provider = Provider(
58                 self._package, self._pool, self._io, installed=installed
59             )
60 
61         self._provider = provider
62         self._overrides: list[dict[DependencyPackage, dict[str, Dependency]]] = []
63 
64     @property
65     def provider(self) -> Provider:
66         return self._provider
67 
68     @contextmanager
69     def use_environment(self, env: Env) -> Iterator[None]:
70         with self.provider.use_environment(env):
71             yield
72 
73     def solve(self, use_latest: list[str] | None = None) -> Transaction:
74         from poetry.puzzle.transaction import Transaction
75 
76         with self._provider.progress():
77             start = time.time()
78             packages, depths = self._solve(use_latest=use_latest)
79             end = time.time()
80 
81             if len(self._overrides) > 1:
82                 self._provider.debug(
83                     f"Complete version solving took {end - start:.3f} seconds with"
84                     f" {len(self._overrides)} overrides"
85                 )
86                 self._provider.debug(
87                     "Resolved with overrides:"
88                     f" {', '.join(f'({b})' for b in self._overrides)}"
89                 )
90 
91         return Transaction(
92             self._locked.packages,
93             list(zip(packages, depths)),
94             installed_packages=self._installed.packages,
95             root_package=self._package,
96         )
97 
98     def solve_in_compatibility_mode(
99         self,
100         overrides: tuple[dict[DependencyPackage, dict[str, Dependency]], ...],
101         use_latest: list[str] | None = None,
102     ) -> tuple[list[Package], list[int]]:
103 
104         packages = []
105         depths = []
106         for override in overrides:
107             self._provider.debug(
108                 "<comment>Retrying dependency resolution "
109                 f"with the following overrides ({override}).</comment>"
110             )
111             self._provider.set_overrides(override)
112             _packages, _depths = self._solve(use_latest=use_latest)
113             for index, package in enumerate(_packages):
114                 if package not in packages:
115                     packages.append(package)
116                     depths.append(_depths[index])
117                     continue
118                 else:
119                     idx = packages.index(package)
120                     pkg = packages[idx]
121                     depths[idx] = max(depths[idx], _depths[index])
122 
123                     for dep in package.requires:
124                         if dep not in pkg.requires:
125                             pkg.add_dependency(dep)
126 
127         return packages, depths
128 
129     def _solve(
130         self, use_latest: list[str] | None = None
131     ) -> tuple[list[Package], list[int]]:
132         if self._provider._overrides:
133             self._overrides.append(self._provider._overrides)
134 
135         locked: dict[str, list[DependencyPackage]] = defaultdict(list)
136         for package in self._locked.packages:
137             locked[package.name].append(
138                 DependencyPackage(package.to_dependency(), package)
139             )
140         for dependency_packages in locked.values():
141             dependency_packages.sort(
142                 key=lambda p: p.package.version,
143                 reverse=True,
144             )
145 
146         try:
147             result = resolve_version(
148                 self._package, self._provider, locked=locked, use_latest=use_latest
149             )
150 
151             packages = result.packages
152         except OverrideNeeded as e:
153             return self.solve_in_compatibility_mode(e.overrides, use_latest=use_latest)
154         except SolveFailure as e:
155             raise SolverProblemError(e)
156 
157         combined_nodes = depth_first_search(PackageNode(self._package, packages))
158         results = dict(aggregate_package_nodes(nodes) for nodes in combined_nodes)
159 
160         # Merging feature packages with base packages
161         final_packages = []
162         depths = []
163         for package in packages:
164             if package.features:
165                 for _package in packages:
166                     if (
167                         _package.name == package.name
168                         and not _package.is_same_package_as(package)
169                         and _package.version == package.version
170                     ):
171                         for dep in package.requires:
172                             if dep.is_same_package_as(_package):
173                                 continue
174 
175                             if dep not in _package.requires:
176                                 _package.add_dependency(dep)
177 
178                 continue
179 
180             final_packages.append(package)
181             depths.append(results[package])
182 
183         # Return the packages in their original order with associated depths
184         return final_packages, depths
185 
186 
187 DFSNodeID = Tuple[str, FrozenSet[str], bool]
188 
189 T = TypeVar("T", bound="DFSNode")
190 
191 
192 class DFSNode:
193     def __init__(self, id: DFSNodeID, name: str, base_name: str) -> None:
194         self.id = id
195         self.name = name
196         self.base_name = base_name
197 
198     def reachable(self: T) -> list[T]:
199         return []
200 
201     def visit(self, parents: list[PackageNode]) -> None:
202         pass
203 
204     def __str__(self) -> str:
205         return str(self.id)
206 
207 
208 def depth_first_search(source: PackageNode) -> list[list[PackageNode]]:
209     back_edges: dict[DFSNodeID, list[PackageNode]] = defaultdict(list)
210     visited: set[DFSNodeID] = set()
211     topo_sorted_nodes: list[PackageNode] = []
212 
213     dfs_visit(source, back_edges, visited, topo_sorted_nodes)
214 
215     # Combine the nodes by name
216     combined_nodes: dict[str, list[PackageNode]] = defaultdict(list)
217     for node in topo_sorted_nodes:
218         node.visit(back_edges[node.id])
219         combined_nodes[node.name].append(node)
220 
221     combined_topo_sorted_nodes: list[list[PackageNode]] = [
222         combined_nodes.pop(node.name)
223         for node in topo_sorted_nodes
224         if node.name in combined_nodes
225     ]
226 
227     return combined_topo_sorted_nodes
228 
229 
230 def dfs_visit(
231     node: PackageNode,
232     back_edges: dict[DFSNodeID, list[PackageNode]],
233     visited: set[DFSNodeID],
234     sorted_nodes: list[PackageNode],
235 ) -> None:
236     if node.id in visited:
237         return
238     visited.add(node.id)
239 
240     for neighbor in node.reachable():
241         back_edges[neighbor.id].append(node)
242         dfs_visit(neighbor, back_edges, visited, sorted_nodes)
243     sorted_nodes.insert(0, node)
244 
245 
246 class PackageNode(DFSNode):
247     def __init__(
248         self,
249         package: Package,
250         packages: list[Package],
251         previous: PackageNode | None = None,
252         previous_dep: None
253         | (
254             DirectoryDependency
255             | FileDependency
256             | URLDependency
257             | VCSDependency
258             | Dependency
259         ) = None,
260         dep: None
261         | (
262             DirectoryDependency
263             | FileDependency
264             | URLDependency
265             | VCSDependency
266             | Dependency
267         ) = None,
268     ) -> None:
269         self.package = package
270         self.packages = packages
271 
272         self.previous = previous
273         self.previous_dep = previous_dep
274         self.dep = dep
275         self.depth = -1
276 
277         if not previous:
278             self.category = "dev"
279             self.groups: frozenset[str] = frozenset()
280             self.optional = True
281         elif dep:
282             self.category = "main" if MAIN_GROUP in dep.groups else "dev"
283             self.groups = dep.groups
284             self.optional = dep.is_optional()
285         else:
286             raise ValueError("Both previous and dep must be passed")
287 
288         super().__init__(
289             (package.complete_name, self.groups, self.optional),
290             package.complete_name,
291             package.name,
292         )
293 
294     def reachable(self) -> list[PackageNode]:
295         children: list[PackageNode] = []
296 
297         if (
298             self.dep
299             and self.previous_dep
300             and self.previous_dep is not self.dep
301             and self.previous_dep.name == self.dep.name
302         ):
303             return []
304 
305         for dependency in self.package.all_requires:
306             if self.previous and self.previous.name == dependency.name:
307                 # We have a circular dependency.
308                 # Since the dependencies are resolved we can
309                 # simply skip it because we already have it
310                 # N.B. this only catches cycles of length 2;
311                 # dependency cycles in general are handled by the DFS traversal
312                 continue
313 
314             for pkg in self.packages:
315                 if (
316                     pkg.complete_name == dependency.complete_name
317                     and (
318                         dependency.constraint.allows(pkg.version)
319                         or dependency.allows_prereleases()
320                         and pkg.version.is_unstable()
321                         and dependency.constraint.allows(pkg.version.stable)
322                     )
323                     and not any(
324                         child.package.complete_name == pkg.complete_name
325                         and child.groups == dependency.groups
326                         for child in children
327                     )
328                 ):
329                     children.append(
330                         PackageNode(
331                             pkg,
332                             self.packages,
333                             self,
334                             dependency,
335                             self.dep or dependency,
336                         )
337                     )
338 
339         return children
340 
341     def visit(self, parents: list[PackageNode]) -> None:
342         # The root package, which has no parents, is defined as having depth -1
343         # So that the root package's top-level dependencies have depth 0.
344         self.depth = 1 + max(
345             [
346                 parent.depth if parent.base_name != self.base_name else parent.depth - 1
347                 for parent in parents
348             ]
349             + [-2]
350         )
351 
352 
353 def aggregate_package_nodes(nodes: list[PackageNode]) -> tuple[Package, int]:
354     package = nodes[0].package
355     depth = max(node.depth for node in nodes)
356     groups: list[str] = []
357     for node in nodes:
358         groups.extend(node.groups)
359 
360     category = "main" if any(MAIN_GROUP in node.groups for node in nodes) else "dev"
361     optional = all(node.optional for node in nodes)
362     for node in nodes:
363         node.depth = depth
364         node.category = category
365         node.optional = optional
366 
367     package.category = category
368     package.optional = optional
369 
370     return package, depth
371
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/src/poetry/puzzle/solver.py b/src/poetry/puzzle/solver.py
--- a/src/poetry/puzzle/solver.py
+++ b/src/poetry/puzzle/solver.py
@@ -164,21 +164,20 @@
             if package.features:
                 for _package in packages:
                     if (
-                        _package.name == package.name
-                        and not _package.is_same_package_as(package)
+                        not _package.features
+                        and _package.name == package.name
                         and _package.version == package.version
                     ):
                         for dep in package.requires:
-                            if dep.is_same_package_as(_package):
+                            # Prevent adding base package as a dependency to itself
+                            if _package.name == dep.name:
                                 continue
 
                             if dep not in _package.requires:
                                 _package.add_dependency(dep)
-
-                continue
-
-            final_packages.append(package)
-            depths.append(results[package])
+            else:
+                final_packages.append(package)
+                depths.append(results[package])
 
         # Return the packages in their original order with associated depths
         return final_packages, depths

{"golden_diff": "diff --git a/src/poetry/puzzle/solver.py b/src/poetry/puzzle/solver.py\n--- a/src/poetry/puzzle/solver.py\n+++ b/src/poetry/puzzle/solver.py\n@@ -164,21 +164,20 @@\n             if package.features:\n                 for _package in packages:\n                     if (\n-                        _package.name == package.name\n-                        and not _package.is_same_package_as(package)\n+                        not _package.features\n+                        and _package.name == package.name\n                         and _package.version == package.version\n                     ):\n                         for dep in package.requires:\n-                            if dep.is_same_package_as(_package):\n+                            # Prevent adding base package as a dependency to itself\n+                            if _package.name == dep.name:\n                                 continue\n \n                             if dep not in _package.requires:\n                                 _package.add_dependency(dep)\n-\n-                continue\n-\n-            final_packages.append(package)\n-            depths.append(results[package])\n+            else:\n+                final_packages.append(package)\n+                depths.append(results[package])\n \n         # Return the packages in their original order with associated depths\n         return final_packages, depths\n", "issue": "poetry add \"requests[security]\" fails on Poetry 1.2.0b1\nIf I run `poetry add \"requests[security]\"` on Poetry 1.2.0b1, it fails with this message:\r\n\r\n Package 'requests' is listed as a dependency of itself.\r\n\r\nI downgraded to Poetry 1.1.13 where it works. Thanks for all your work!\n", "before_files": [{"content": "from __future__ import annotations\n\nimport time\n\nfrom collections import defaultdict\nfrom contextlib import contextmanager\nfrom typing import TYPE_CHECKING\nfrom typing import FrozenSet\nfrom typing import Tuple\nfrom typing import TypeVar\n\nfrom poetry.core.packages.dependency_group import MAIN_GROUP\n\nfrom poetry.mixology import resolve_version\nfrom poetry.mixology.failure import SolveFailure\nfrom poetry.packages import DependencyPackage\nfrom poetry.puzzle.exceptions import OverrideNeeded\nfrom poetry.puzzle.exceptions import SolverProblemError\nfrom poetry.puzzle.provider import Provider\n\n\nif TYPE_CHECKING:\n    from collections.abc import Iterator\n\n    from cleo.io.io import IO\n    from poetry.core.packages.dependency import Dependency\n    from poetry.core.packages.directory_dependency import DirectoryDependency\n    from poetry.core.packages.file_dependency import FileDependency\n    from poetry.core.packages.package import Package\n    from poetry.core.packages.project_package import ProjectPackage\n    from poetry.core.packages.url_dependency import URLDependency\n    from poetry.core.packages.vcs_dependency import VCSDependency\n\n    from poetry.puzzle.transaction import Transaction\n    from poetry.repositories import Pool\n    from poetry.repositories import Repository\n    from poetry.utils.env import Env\n\n\nclass Solver:\n    def __init__(\n        self,\n        package: ProjectPackage,\n        pool: Pool,\n        installed: Repository,\n        locked: Repository,\n        io: IO,\n        provider: Provider | None = None,\n    ) -> None:\n        self._package = package\n        self._pool = pool\n        self._installed = installed\n        self._locked = locked\n        self._io = io\n\n        if provider is None:\n            provider = Provider(\n                self._package, self._pool, self._io, installed=installed\n            )\n\n        self._provider = provider\n        self._overrides: list[dict[DependencyPackage, dict[str, Dependency]]] = []\n\n    @property\n    def provider(self) -> Provider:\n        return self._provider\n\n    @contextmanager\n    def use_environment(self, env: Env) -> Iterator[None]:\n        with self.provider.use_environment(env):\n            yield\n\n    def solve(self, use_latest: list[str] | None = None) -> Transaction:\n        from poetry.puzzle.transaction import Transaction\n\n        with self._provider.progress():\n            start = time.time()\n            packages, depths = self._solve(use_latest=use_latest)\n            end = time.time()\n\n            if len(self._overrides) > 1:\n                self._provider.debug(\n                    f\"Complete version solving took {end - start:.3f} seconds with\"\n                    f\" {len(self._overrides)} overrides\"\n                )\n                self._provider.debug(\n                    \"Resolved with overrides:\"\n                    f\" {', '.join(f'({b})' for b in self._overrides)}\"\n                )\n\n        return Transaction(\n            self._locked.packages,\n            list(zip(packages, depths)),\n            installed_packages=self._installed.packages,\n            root_package=self._package,\n        )\n\n    def solve_in_compatibility_mode(\n        self,\n        overrides: tuple[dict[DependencyPackage, dict[str, Dependency]], ...],\n        use_latest: list[str] | None = None,\n    ) -> tuple[list[Package], list[int]]:\n\n        packages = []\n        depths = []\n        for override in overrides:\n            self._provider.debug(\n                \"<comment>Retrying dependency resolution \"\n                f\"with the following overrides ({override}).</comment>\"\n            )\n            self._provider.set_overrides(override)\n            _packages, _depths = self._solve(use_latest=use_latest)\n            for index, package in enumerate(_packages):\n                if package not in packages:\n                    packages.append(package)\n                    depths.append(_depths[index])\n                    continue\n                else:\n                    idx = packages.index(package)\n                    pkg = packages[idx]\n                    depths[idx] = max(depths[idx], _depths[index])\n\n                    for dep in package.requires:\n                        if dep not in pkg.requires:\n                            pkg.add_dependency(dep)\n\n        return packages, depths\n\n    def _solve(\n        self, use_latest: list[str] | None = None\n    ) -> tuple[list[Package], list[int]]:\n        if self._provider._overrides:\n            self._overrides.append(self._provider._overrides)\n\n        locked: dict[str, list[DependencyPackage]] = defaultdict(list)\n        for package in self._locked.packages:\n            locked[package.name].append(\n                DependencyPackage(package.to_dependency(), package)\n            )\n        for dependency_packages in locked.values():\n            dependency_packages.sort(\n                key=lambda p: p.package.version,\n                reverse=True,\n            )\n\n        try:\n            result = resolve_version(\n                self._package, self._provider, locked=locked, use_latest=use_latest\n            )\n\n            packages = result.packages\n        except OverrideNeeded as e:\n            return self.solve_in_compatibility_mode(e.overrides, use_latest=use_latest)\n        except SolveFailure as e:\n            raise SolverProblemError(e)\n\n        combined_nodes = depth_first_search(PackageNode(self._package, packages))\n        results = dict(aggregate_package_nodes(nodes) for nodes in combined_nodes)\n\n        # Merging feature packages with base packages\n        final_packages = []\n        depths = []\n        for package in packages:\n            if package.features:\n                for _package in packages:\n                    if (\n                        _package.name == package.name\n                        and not _package.is_same_package_as(package)\n                        and _package.version == package.version\n                    ):\n                        for dep in package.requires:\n                            if dep.is_same_package_as(_package):\n                                continue\n\n                            if dep not in _package.requires:\n                                _package.add_dependency(dep)\n\n                continue\n\n            final_packages.append(package)\n            depths.append(results[package])\n\n        # Return the packages in their original order with associated depths\n        return final_packages, depths\n\n\nDFSNodeID = Tuple[str, FrozenSet[str], bool]\n\nT = TypeVar(\"T\", bound=\"DFSNode\")\n\n\nclass DFSNode:\n    def __init__(self, id: DFSNodeID, name: str, base_name: str) -> None:\n        self.id = id\n        self.name = name\n        self.base_name = base_name\n\n    def reachable(self: T) -> list[T]:\n        return []\n\n    def visit(self, parents: list[PackageNode]) -> None:\n        pass\n\n    def __str__(self) -> str:\n        return str(self.id)\n\n\ndef depth_first_search(source: PackageNode) -> list[list[PackageNode]]:\n    back_edges: dict[DFSNodeID, list[PackageNode]] = defaultdict(list)\n    visited: set[DFSNodeID] = set()\n    topo_sorted_nodes: list[PackageNode] = []\n\n    dfs_visit(source, back_edges, visited, topo_sorted_nodes)\n\n    # Combine the nodes by name\n    combined_nodes: dict[str, list[PackageNode]] = defaultdict(list)\n    for node in topo_sorted_nodes:\n        node.visit(back_edges[node.id])\n        combined_nodes[node.name].append(node)\n\n    combined_topo_sorted_nodes: list[list[PackageNode]] = [\n        combined_nodes.pop(node.name)\n        for node in topo_sorted_nodes\n        if node.name in combined_nodes\n    ]\n\n    return combined_topo_sorted_nodes\n\n\ndef dfs_visit(\n    node: PackageNode,\n    back_edges: dict[DFSNodeID, list[PackageNode]],\n    visited: set[DFSNodeID],\n    sorted_nodes: list[PackageNode],\n) -> None:\n    if node.id in visited:\n        return\n    visited.add(node.id)\n\n    for neighbor in node.reachable():\n        back_edges[neighbor.id].append(node)\n        dfs_visit(neighbor, back_edges, visited, sorted_nodes)\n    sorted_nodes.insert(0, node)\n\n\nclass PackageNode(DFSNode):\n    def __init__(\n        self,\n        package: Package,\n        packages: list[Package],\n        previous: PackageNode | None = None,\n        previous_dep: None\n        | (\n            DirectoryDependency\n            | FileDependency\n            | URLDependency\n            | VCSDependency\n            | Dependency\n        ) = None,\n        dep: None\n        | (\n            DirectoryDependency\n            | FileDependency\n            | URLDependency\n            | VCSDependency\n            | Dependency\n        ) = None,\n    ) -> None:\n        self.package = package\n        self.packages = packages\n\n        self.previous = previous\n        self.previous_dep = previous_dep\n        self.dep = dep\n        self.depth = -1\n\n        if not previous:\n            self.category = \"dev\"\n            self.groups: frozenset[str] = frozenset()\n            self.optional = True\n        elif dep:\n            self.category = \"main\" if MAIN_GROUP in dep.groups else \"dev\"\n            self.groups = dep.groups\n            self.optional = dep.is_optional()\n        else:\n            raise ValueError(\"Both previous and dep must be passed\")\n\n        super().__init__(\n            (package.complete_name, self.groups, self.optional),\n            package.complete_name,\n            package.name,\n        )\n\n    def reachable(self) -> list[PackageNode]:\n        children: list[PackageNode] = []\n\n        if (\n            self.dep\n            and self.previous_dep\n            and self.previous_dep is not self.dep\n            and self.previous_dep.name == self.dep.name\n        ):\n            return []\n\n        for dependency in self.package.all_requires:\n            if self.previous and self.previous.name == dependency.name:\n                # We have a circular dependency.\n                # Since the dependencies are resolved we can\n                # simply skip it because we already have it\n                # N.B. this only catches cycles of length 2;\n                # dependency cycles in general are handled by the DFS traversal\n                continue\n\n            for pkg in self.packages:\n                if (\n                    pkg.complete_name == dependency.complete_name\n                    and (\n                        dependency.constraint.allows(pkg.version)\n                        or dependency.allows_prereleases()\n                        and pkg.version.is_unstable()\n                        and dependency.constraint.allows(pkg.version.stable)\n                    )\n                    and not any(\n                        child.package.complete_name == pkg.complete_name\n                        and child.groups == dependency.groups\n                        for child in children\n                    )\n                ):\n                    children.append(\n                        PackageNode(\n                            pkg,\n                            self.packages,\n                            self,\n                            dependency,\n                            self.dep or dependency,\n                        )\n                    )\n\n        return children\n\n    def visit(self, parents: list[PackageNode]) -> None:\n        # The root package, which has no parents, is defined as having depth -1\n        # So that the root package's top-level dependencies have depth 0.\n        self.depth = 1 + max(\n            [\n                parent.depth if parent.base_name != self.base_name else parent.depth - 1\n                for parent in parents\n            ]\n            + [-2]\n        )\n\n\ndef aggregate_package_nodes(nodes: list[PackageNode]) -> tuple[Package, int]:\n    package = nodes[0].package\n    depth = max(node.depth for node in nodes)\n    groups: list[str] = []\n    for node in nodes:\n        groups.extend(node.groups)\n\n    category = \"main\" if any(MAIN_GROUP in node.groups for node in nodes) else \"dev\"\n    optional = all(node.optional for node in nodes)\n    for node in nodes:\n        node.depth = depth\n        node.category = category\n        node.optional = optional\n\n    package.category = category\n    package.optional = optional\n\n    return package, depth\n", "path": "src/poetry/puzzle/solver.py"}], "after_files": [{"content": "from __future__ import annotations\n\nimport time\n\nfrom collections import defaultdict\nfrom contextlib import contextmanager\nfrom typing import TYPE_CHECKING\nfrom typing import FrozenSet\nfrom typing import Tuple\nfrom typing import TypeVar\n\nfrom poetry.core.packages.dependency_group import MAIN_GROUP\n\nfrom poetry.mixology import resolve_version\nfrom poetry.mixology.failure import SolveFailure\nfrom poetry.packages import DependencyPackage\nfrom poetry.puzzle.exceptions import OverrideNeeded\nfrom poetry.puzzle.exceptions import SolverProblemError\nfrom poetry.puzzle.provider import Provider\n\n\nif TYPE_CHECKING:\n    from collections.abc import Iterator\n\n    from cleo.io.io import IO\n    from poetry.core.packages.dependency import Dependency\n    from poetry.core.packages.directory_dependency import DirectoryDependency\n    from poetry.core.packages.file_dependency import FileDependency\n    from poetry.core.packages.package import Package\n    from poetry.core.packages.project_package import ProjectPackage\n    from poetry.core.packages.url_dependency import URLDependency\n    from poetry.core.packages.vcs_dependency import VCSDependency\n\n    from poetry.puzzle.transaction import Transaction\n    from poetry.repositories import Pool\n    from poetry.repositories import Repository\n    from poetry.utils.env import Env\n\n\nclass Solver:\n    def __init__(\n        self,\n        package: ProjectPackage,\n        pool: Pool,\n        installed: Repository,\n        locked: Repository,\n        io: IO,\n        provider: Provider | None = None,\n    ) -> None:\n        self._package = package\n        self._pool = pool\n        self._installed = installed\n        self._locked = locked\n        self._io = io\n\n        if provider is None:\n            provider = Provider(\n                self._package, self._pool, self._io, installed=installed\n            )\n\n        self._provider = provider\n        self._overrides: list[dict[DependencyPackage, dict[str, Dependency]]] = []\n\n    @property\n    def provider(self) -> Provider:\n        return self._provider\n\n    @contextmanager\n    def use_environment(self, env: Env) -> Iterator[None]:\n        with self.provider.use_environment(env):\n            yield\n\n    def solve(self, use_latest: list[str] | None = None) -> Transaction:\n        from poetry.puzzle.transaction import Transaction\n\n        with self._provider.progress():\n            start = time.time()\n            packages, depths = self._solve(use_latest=use_latest)\n            end = time.time()\n\n            if len(self._overrides) > 1:\n                self._provider.debug(\n                    f\"Complete version solving took {end - start:.3f} seconds with\"\n                    f\" {len(self._overrides)} overrides\"\n                )\n                self._provider.debug(\n                    \"Resolved with overrides:\"\n                    f\" {', '.join(f'({b})' for b in self._overrides)}\"\n                )\n\n        return Transaction(\n            self._locked.packages,\n            list(zip(packages, depths)),\n            installed_packages=self._installed.packages,\n            root_package=self._package,\n        )\n\n    def solve_in_compatibility_mode(\n        self,\n        overrides: tuple[dict[DependencyPackage, dict[str, Dependency]], ...],\n        use_latest: list[str] | None = None,\n    ) -> tuple[list[Package], list[int]]:\n\n        packages = []\n        depths = []\n        for override in overrides:\n            self._provider.debug(\n                \"<comment>Retrying dependency resolution \"\n                f\"with the following overrides ({override}).</comment>\"\n            )\n            self._provider.set_overrides(override)\n            _packages, _depths = self._solve(use_latest=use_latest)\n            for index, package in enumerate(_packages):\n                if package not in packages:\n                    packages.append(package)\n                    depths.append(_depths[index])\n                    continue\n                else:\n                    idx = packages.index(package)\n                    pkg = packages[idx]\n                    depths[idx] = max(depths[idx], _depths[index])\n\n                    for dep in package.requires:\n                        if dep not in pkg.requires:\n                            pkg.add_dependency(dep)\n\n        return packages, depths\n\n    def _solve(\n        self, use_latest: list[str] | None = None\n    ) -> tuple[list[Package], list[int]]:\n        if self._provider._overrides:\n            self._overrides.append(self._provider._overrides)\n\n        locked: dict[str, list[DependencyPackage]] = defaultdict(list)\n        for package in self._locked.packages:\n            locked[package.name].append(\n                DependencyPackage(package.to_dependency(), package)\n            )\n        for dependency_packages in locked.values():\n            dependency_packages.sort(\n                key=lambda p: p.package.version,\n                reverse=True,\n            )\n\n        try:\n            result = resolve_version(\n                self._package, self._provider, locked=locked, use_latest=use_latest\n            )\n\n            packages = result.packages\n        except OverrideNeeded as e:\n            return self.solve_in_compatibility_mode(e.overrides, use_latest=use_latest)\n        except SolveFailure as e:\n            raise SolverProblemError(e)\n\n        combined_nodes = depth_first_search(PackageNode(self._package, packages))\n        results = dict(aggregate_package_nodes(nodes) for nodes in combined_nodes)\n\n        # Merging feature packages with base packages\n        final_packages = []\n        depths = []\n        for package in packages:\n            if package.features:\n                for _package in packages:\n                    if (\n                        not _package.features\n                        and _package.name == package.name\n                        and _package.version == package.version\n                    ):\n                        for dep in package.requires:\n                            # Prevent adding base package as a dependency to itself\n                            if _package.name == dep.name:\n                                continue\n\n                            if dep not in _package.requires:\n                                _package.add_dependency(dep)\n            else:\n                final_packages.append(package)\n                depths.append(results[package])\n\n        # Return the packages in their original order with associated depths\n        return final_packages, depths\n\n\nDFSNodeID = Tuple[str, FrozenSet[str], bool]\n\nT = TypeVar(\"T\", bound=\"DFSNode\")\n\n\nclass DFSNode:\n    def __init__(self, id: DFSNodeID, name: str, base_name: str) -> None:\n        self.id = id\n        self.name = name\n        self.base_name = base_name\n\n    def reachable(self: T) -> list[T]:\n        return []\n\n    def visit(self, parents: list[PackageNode]) -> None:\n        pass\n\n    def __str__(self) -> str:\n        return str(self.id)\n\n\ndef depth_first_search(source: PackageNode) -> list[list[PackageNode]]:\n    back_edges: dict[DFSNodeID, list[PackageNode]] = defaultdict(list)\n    visited: set[DFSNodeID] = set()\n    topo_sorted_nodes: list[PackageNode] = []\n\n    dfs_visit(source, back_edges, visited, topo_sorted_nodes)\n\n    # Combine the nodes by name\n    combined_nodes: dict[str, list[PackageNode]] = defaultdict(list)\n    for node in topo_sorted_nodes:\n        node.visit(back_edges[node.id])\n        combined_nodes[node.name].append(node)\n\n    combined_topo_sorted_nodes: list[list[PackageNode]] = [\n        combined_nodes.pop(node.name)\n        for node in topo_sorted_nodes\n        if node.name in combined_nodes\n    ]\n\n    return combined_topo_sorted_nodes\n\n\ndef dfs_visit(\n    node: PackageNode,\n    back_edges: dict[DFSNodeID, list[PackageNode]],\n    visited: set[DFSNodeID],\n    sorted_nodes: list[PackageNode],\n) -> None:\n    if node.id in visited:\n        return\n    visited.add(node.id)\n\n    for neighbor in node.reachable():\n        back_edges[neighbor.id].append(node)\n        dfs_visit(neighbor, back_edges, visited, sorted_nodes)\n    sorted_nodes.insert(0, node)\n\n\nclass PackageNode(DFSNode):\n    def __init__(\n        self,\n        package: Package,\n        packages: list[Package],\n        previous: PackageNode | None = None,\n        previous_dep: None\n        | (\n            DirectoryDependency\n            | FileDependency\n            | URLDependency\n            | VCSDependency\n            | Dependency\n        ) = None,\n        dep: None\n        | (\n            DirectoryDependency\n            | FileDependency\n            | URLDependency\n            | VCSDependency\n            | Dependency\n        ) = None,\n    ) -> None:\n        self.package = package\n        self.packages = packages\n\n        self.previous = previous\n        self.previous_dep = previous_dep\n        self.dep = dep\n        self.depth = -1\n\n        if not previous:\n            self.category = \"dev\"\n            self.groups: frozenset[str] = frozenset()\n            self.optional = True\n        elif dep:\n            self.category = \"main\" if MAIN_GROUP in dep.groups else \"dev\"\n            self.groups = dep.groups\n            self.optional = dep.is_optional()\n        else:\n            raise ValueError(\"Both previous and dep must be passed\")\n\n        super().__init__(\n            (package.complete_name, self.groups, self.optional),\n            package.complete_name,\n            package.name,\n        )\n\n    def reachable(self) -> list[PackageNode]:\n        children: list[PackageNode] = []\n\n        if (\n            self.dep\n            and self.previous_dep\n            and self.previous_dep is not self.dep\n            and self.previous_dep.name == self.dep.name\n        ):\n            return []\n\n        for dependency in self.package.all_requires:\n            if self.previous and self.previous.name == dependency.name:\n                # We have a circular dependency.\n                # Since the dependencies are resolved we can\n                # simply skip it because we already have it\n                # N.B. this only catches cycles of length 2;\n                # dependency cycles in general are handled by the DFS traversal\n                continue\n\n            for pkg in self.packages:\n                if (\n                    pkg.complete_name == dependency.complete_name\n                    and (\n                        dependency.constraint.allows(pkg.version)\n                        or dependency.allows_prereleases()\n                        and pkg.version.is_unstable()\n                        and dependency.constraint.allows(pkg.version.stable)\n                    )\n                    and not any(\n                        child.package.complete_name == pkg.complete_name\n                        and child.groups == dependency.groups\n                        for child in children\n                    )\n                ):\n                    children.append(\n                        PackageNode(\n                            pkg,\n                            self.packages,\n                            self,\n                            dependency,\n                            self.dep or dependency,\n                        )\n                    )\n\n        return children\n\n    def visit(self, parents: list[PackageNode]) -> None:\n        # The root package, which has no parents, is defined as having depth -1\n        # So that the root package's top-level dependencies have depth 0.\n        self.depth = 1 + max(\n            [\n                parent.depth if parent.base_name != self.base_name else parent.depth - 1\n                for parent in parents\n            ]\n            + [-2]\n        )\n\n\ndef aggregate_package_nodes(nodes: list[PackageNode]) -> tuple[Package, int]:\n    package = nodes[0].package\n    depth = max(node.depth for node in nodes)\n    groups: list[str] = []\n    for node in nodes:\n        groups.extend(node.groups)\n\n    category = \"main\" if any(MAIN_GROUP in node.groups for node in nodes) else \"dev\"\n    optional = all(node.optional for node in nodes)\n    for node in nodes:\n        node.depth = depth\n        node.category = category\n        node.optional = optional\n\n    package.category = category\n    package.optional = optional\n\n    return package, depth\n", "path": "src/poetry/puzzle/solver.py"}]}