1
1
from __future__ import annotations
2
2
3
+ import functools
3
4
import time
4
5
5
6
from contextlib import suppress
17
18
from poetry .mixology .result import SolverResult
18
19
from poetry .mixology .set_relation import SetRelation
19
20
from poetry .mixology .term import Term
21
+ from poetry .packages import DependencyPackage
20
22
21
23
22
24
if TYPE_CHECKING :
29
31
_conflict = object ()
30
32
31
33
34
+ class DependencyCache :
35
+ """
36
+ A cache of the valid dependencies.
37
+
38
+ The key observation here is that during the search - except at backtracking
39
+ - once we have decided that a dependency is invalid, we never need check it
40
+ again.
41
+ """
42
+
43
+ def __init__ (self , provider : Provider ):
44
+ self .provider = provider
45
+ self .cache : dict [str , list [Package ]] = {}
46
+
47
+ @functools .lru_cache (maxsize = 128 )
48
+ def search_for (self , dependency : Dependency ) -> list [DependencyPackage ]:
49
+ complete_name = dependency .complete_name
50
+ packages = self .cache .get (complete_name )
51
+ if packages is None :
52
+ packages = self .provider .search_for (dependency )
53
+ else :
54
+ packages = [p for p in packages if dependency .constraint .allows (p .version )]
55
+
56
+ self .cache [complete_name ] = packages
57
+
58
+ return packages
59
+
60
+ def clear (self ):
61
+ self .cache .clear ()
62
+
63
+
32
64
class VersionSolver :
33
65
"""
34
66
The version solver that finds a set of package versions that satisfy the
@@ -47,6 +79,7 @@ def __init__(
47
79
):
48
80
self ._root = root
49
81
self ._provider = provider
82
+ self ._dependency_cache = DependencyCache (provider )
50
83
self ._locked = locked or {}
51
84
52
85
if use_latest is None :
@@ -263,6 +296,7 @@ def _resolve_conflict(self, incompatibility: Incompatibility) -> Incompatibility
263
296
):
264
297
self ._solution .backtrack (previous_satisfier_level )
265
298
self ._contradicted_incompatibilities .clear ()
299
+ self ._dependency_cache .clear ()
266
300
if new_incompatibility :
267
301
self ._add_incompatibility (incompatibility )
268
302
@@ -354,7 +388,7 @@ def _get_min(dependency: Dependency) -> tuple[bool, int]:
354
388
try :
355
389
return (
356
390
not dependency .marker .is_any (),
357
- len (self ._provider .search_for (dependency )),
391
+ len (self ._dependency_cache .search_for (dependency )),
358
392
)
359
393
except ValueError :
360
394
return not dependency .marker .is_any (), 0
@@ -367,7 +401,7 @@ def _get_min(dependency: Dependency) -> tuple[bool, int]:
367
401
locked = self ._get_locked (dependency )
368
402
if locked is None or not dependency .constraint .allows (locked .version ):
369
403
try :
370
- packages = self ._provider .search_for (dependency )
404
+ packages = self ._dependency_cache .search_for (dependency )
371
405
except ValueError as e :
372
406
self ._add_incompatibility (
373
407
Incompatibility ([Term (dependency , True )], PackageNotFoundCause (e ))
0 commit comments