From 5b4a38426ebabc57302723d1b51fa90e2c3467fd Mon Sep 17 00:00:00 2001 From: Josh Guice Date: Thu, 20 Aug 2020 22:30:08 -0700 Subject: [PATCH 1/3] fix(solver): skip seen "graph" nodes sharply decreases slowness :) --- poetry/puzzle/solver.py | 14 +++++++++++--- tests/puzzle/test_solver.py | 22 +++++++++++----------- 2 files changed, 22 insertions(+), 14 deletions(-) diff --git a/poetry/puzzle/solver.py b/poetry/puzzle/solver.py index 386d8f95a7f..b297ea68d95 100644 --- a/poetry/puzzle/solver.py +++ b/poetry/puzzle/solver.py @@ -221,7 +221,9 @@ def _solve(self, use_latest=None): except SolveFailure as e: raise SolverProblemError(e) - graph = self._build_graph(self._package, packages) + # TODO why is there a lingering variable state / stack during install when invoked via `poetry update`? + # NOTE passing explicit empty array for seen to reset between invocations during update + install cycle + graph = self._build_graph(self._package, packages, seen=[]) depths = [] final_packages = [] @@ -237,7 +239,7 @@ def _solve(self, use_latest=None): return final_packages, depths def _build_graph( - self, package, packages, previous=None, previous_dep=None, dep=None + self, package, packages, previous=None, previous_dep=None, dep=None, seen=[] ): # type: (...) -> Dict[str, Any] if not previous: category = "dev" @@ -254,6 +256,12 @@ def _build_graph( "children": childrens, } + # skip already traversed packages + if package in seen: + return graph + else: + seen.append(package) + if previous_dep and previous_dep is not dep and previous_dep.name == dep.name: return graph @@ -302,7 +310,7 @@ def _build_graph( continue child_graph = self._build_graph( - pkg, packages, graph, dependency, dep or dependency + pkg, packages, graph, dependency, dep or dependency, seen=seen ) if not is_activated: diff --git a/tests/puzzle/test_solver.py b/tests/puzzle/test_solver.py index c3540a36591..9d1d45c35da 100644 --- a/tests/puzzle/test_solver.py +++ b/tests/puzzle/test_solver.py @@ -619,17 +619,17 @@ def test_solver_with_dependency_in_both_main_and_dev_dependencies( check_solver_result( ops, [ - {"job": "install", "package": package_d}, {"job": "install", "package": package_b}, - {"job": "install", "package": package_c}, {"job": "install", "package": package_a}, + {"job": "install", "package": package_c}, + {"job": "install", "package": package_d}, ], ) - b = ops[1].package + b = ops[0].package + a = ops[1].package c = ops[2].package - d = ops[0].package - a = ops[3].package + d = ops[3].package assert d.category == "dev" assert c.category == "dev" @@ -670,18 +670,18 @@ def test_solver_with_dependency_in_both_main_and_dev_dependencies_with_one_more_ check_solver_result( ops, [ - {"job": "install", "package": package_b}, - {"job": "install", "package": package_d}, {"job": "install", "package": package_a}, + {"job": "install", "package": package_b}, {"job": "install", "package": package_c}, + {"job": "install", "package": package_d}, {"job": "install", "package": package_e}, ], ) - b = ops[0].package - c = ops[3].package - d = ops[1].package - a = ops[2].package + a = ops[0].package + b = ops[1].package + c = ops[2].package + d = ops[3].package e = ops[4].package assert d.category == "dev" From a55332e09fa8009e0c184cfbf1607c977d68d30a Mon Sep 17 00:00:00 2001 From: Josh Guice Date: Fri, 26 Mar 2021 19:15:19 -0700 Subject: [PATCH 2/3] fix(solver): skip already "seen" PackageNodes sharply decreases slowness :) --- poetry/puzzle/solver.py | 11 ++++++----- tests/puzzle/test_solver.py | 28 ++++++++++++++-------------- 2 files changed, 20 insertions(+), 19 deletions(-) diff --git a/poetry/puzzle/solver.py b/poetry/puzzle/solver.py index c96798d4a02..371b00bf3f5 100644 --- a/poetry/puzzle/solver.py +++ b/poetry/puzzle/solver.py @@ -263,7 +263,7 @@ def _solve(self, use_latest: List[str] = None) -> Tuple[List[Package], List[int] # NOTE passing explicit empty array for seen to reset between invocations during update + install cycle results = dict( depth_first_search( - PackageNode(self._package, packages), aggregate_package_nodes, seen=[] + PackageNode(self._package, packages, seen=[]), aggregate_package_nodes ) ) @@ -374,6 +374,7 @@ def __init__( self, package: Package, packages: List[Package], + seen: List[Package], previous: Optional["PackageNode"] = None, previous_dep: Optional[ Union[ @@ -393,10 +394,10 @@ def __init__( "Dependency", ] ] = None, - seen: seen, ) -> None: self.package = package self.packages = packages + self.seen = seen self.previous = previous self.previous_dep = previous_dep @@ -420,10 +421,10 @@ def reachable(self) -> List["PackageNode"]: children: List[PackageNode] = [] # skip already traversed packages - if self.package in seen: + if self.package in self.seen: return [] else: - seen.append(self.package) + self.seen.append(self.package) if ( @@ -462,10 +463,10 @@ def reachable(self) -> List["PackageNode"]: PackageNode( pkg, self.packages, + self.seen, self, dependency, self.dep or dependency, - seen ) ) diff --git a/tests/puzzle/test_solver.py b/tests/puzzle/test_solver.py index 1bae8485236..29e9689cf69 100644 --- a/tests/puzzle/test_solver.py +++ b/tests/puzzle/test_solver.py @@ -815,21 +815,21 @@ def test_solver_with_dependency_in_both_main_and_dev_dependencies( check_solver_result( ops, [ + {"job": "install", "package": package_d}, {"job": "install", "package": package_b}, - {"job": "install", "package": package_a}, {"job": "install", "package": package_c}, - {"job": "install", "package": package_d}, + {"job": "install", "package": package_a}, ], ) - b = ops[0].package - a = ops[1].package + d = ops[0].package + b = ops[1].package c = ops[2].package - d = ops[3].package + a = ops[3].package assert d.category == "dev" - assert c.category == "dev" assert b.category == "main" + assert c.category == "dev" assert a.category == "main" @@ -872,24 +872,24 @@ def test_solver_with_dependency_in_both_main_and_dev_dependencies_with_one_more_ check_solver_result( ops, [ - {"job": "install", "package": package_a}, {"job": "install", "package": package_b}, - {"job": "install", "package": package_c}, {"job": "install", "package": package_d}, + {"job": "install", "package": package_a}, + {"job": "install", "package": package_c}, {"job": "install", "package": package_e}, ], ) - a = ops[0].package - b = ops[1].package - c = ops[2].package - d = ops[3].package + b = ops[0].package + d = ops[1].package + a = ops[2].package + c = ops[3].package e = ops[4].package - assert d.category == "dev" - assert c.category == "dev" assert b.category == "main" + assert d.category == "dev" assert a.category == "main" + assert c.category == "dev" assert e.category == "main" From c1891c7157605a1027b697eb2548d49bca1cb509 Mon Sep 17 00:00:00 2001 From: Josh Guice Date: Fri, 26 Mar 2021 19:35:29 -0700 Subject: [PATCH 3/3] fmt(solver): remove extra blank line --- poetry/puzzle/solver.py | 1 - 1 file changed, 1 deletion(-) diff --git a/poetry/puzzle/solver.py b/poetry/puzzle/solver.py index 371b00bf3f5..35b6bd86bb5 100644 --- a/poetry/puzzle/solver.py +++ b/poetry/puzzle/solver.py @@ -426,7 +426,6 @@ def reachable(self) -> List["PackageNode"]: else: self.seen.append(self.package) - if ( self.previous_dep and self.previous_dep is not self.dep