diff options
author | 2024-02-22 15:50:40 -0500 | |
---|---|---|
committer | 2024-03-07 18:49:06 +0000 | |
commit | 3a53501625e73483a86f2ee00696047a20682745 (patch) | |
tree | 73dd46cc960bea65897dac7862ab3fa85d0d8d7a | |
parent | dependencies.py: introduce unit testing for graph_reverse_depends (diff) | |
download | gentoolkit-3a53501625e73483a86f2ee00696047a20682745.tar.gz gentoolkit-3a53501625e73483a86f2ee00696047a20682745.tar.bz2 gentoolkit-3a53501625e73483a86f2ee00696047a20682745.zip |
dependencies.py: rewrite graph_reverse_depends to pass tests
The graph_reverse_depends method was not able to pass the unit tests
introduced in the previous commits. It has been rewritten to pass them.
This also has adding types to the method, and yields the results as an
iterator rather than collecting them into a list in one shot.
The printer callback parameter has been removed. This callback most
likely existed so that results would be shown to the user as soon as
they were available instead of delaying printing until the method
completed, which could take seconds or minutes depending on the
parameters. By making this method an iterator, the same effect is
acheived by having the caller print every item as its yielded
from the method.
Signed-off-by: John Turner <jturner.usa@gmail.com>
Signed-off-by: Sam James <sam@gentoo.org>
-rw-r--r-- | pym/gentoolkit/dependencies.py | 102 |
1 files changed, 36 insertions, 66 deletions
diff --git a/pym/gentoolkit/dependencies.py b/pym/gentoolkit/dependencies.py index c6abff0..be5c71f 100644 --- a/pym/gentoolkit/dependencies.py +++ b/pym/gentoolkit/dependencies.py @@ -14,7 +14,7 @@ __all__ = ("Dependencies",) import itertools from functools import cache from enum import Enum -from typing import List, Dict +from typing import List, Dict, Iterable, Iterator, Set, Optional, Any, Union import portage from portage.dep import paren_reduce @@ -22,6 +22,7 @@ from portage.dep import paren_reduce from gentoolkit import errors from gentoolkit.atom import Atom from gentoolkit.query import Query +from gentoolkit.cpv import CPV # ======= # Classes @@ -52,10 +53,11 @@ class Dependencies(Query): """ - def __init__(self, query, parser=None): + def __init__(self, query: Any, parser: Any = None) -> None: Query.__init__(self, query) - self.use = [] - self.depatom = "" + self.use: List[str] = [] + self.depatom: Optional[Atom] = None + self.depth: Optional[int] = None # Allow a custom parser function: self.parser = parser if parser else self._parser @@ -185,15 +187,13 @@ class Dependencies(Query): def graph_reverse_depends( self, - pkgset=None, - max_depth=-1, - only_direct=True, - printer_fn=None, + pkgset: Iterable[Union[str, CPV]], + max_depth: Optional[int] = None, + only_direct: bool = True, # The rest of these are only used internally: - depth=0, - seen=None, - result=None, - ): + depth: int = 0, + seen: Optional[Set[str]] = None, + ) -> Iterator["Dependencies"]: """Graph direct reverse dependencies for self. Example usage: @@ -201,10 +201,10 @@ class Dependencies(Query): >>> ffmpeg = Dependencies('media-video/ffmpeg-9999') >>> # I only care about installed packages that depend on me: ... from gentoolkit.helpers import get_installed_cpvs - >>> # I want to pass in a sorted list. We can pass strings or - ... # Package or Atom types, so I'll use Package to sort: + >>> # I want to pass in a list. We can pass strings or + ... # Package or Atom types. ... from gentoolkit.package import Package - >>> installed = sorted(get_installed_cpvs()) + >>> installed = get_installed_cpvs() >>> deptree = ffmpeg.graph_reverse_depends( ... only_direct=False, # Include indirect revdeps ... pkgset=installed) # from installed pkgset @@ -212,82 +212,52 @@ class Dependencies(Query): 24 @type pkgset: iterable - @keyword pkgset: sorted pkg cpv strings or anything sublassing + @keyword pkgset: pkg cpv strings or anything sublassing L{gentoolkit.cpv.CPV} to use for calculate our revdep graph. - @type max_depth: int + @type max_depth: optional @keyword max_depth: Maximum depth to recurse if only_direct=False. - -1 means no maximum depth; - 0 is the same as only_direct=True; + None means no maximum depth; + 0 is the same as only_direct=True; >0 means recurse only this many times; @type only_direct: bool @keyword only_direct: to recurse or not to recurse - @type printer_fn: callable - @keyword printer_fn: If None, no effect. If set, it will be applied to - each L{gentoolkit.atom.Atom} object as it is added to the results. - @rtype: list + @rtype: iterable @return: L{gentoolkit.dependencies.Dependencies} objects """ - if not pkgset: - err = ( - "%s kwarg 'pkgset' must be set. " - "Can be list of cpv strings or any 'intersectable' object." - ) - raise errors.GentoolkitFatalError(err % (self.__class__.__name__,)) if seen is None: seen = set() - if result is None: - result = list() - if depth == 0: - pkgset = tuple(Dependencies(x) for x in pkgset) - - pkgdep = None - for pkgdep in pkgset: + for pkgdep in (Dependencies(pkg) for pkg in pkgset): if self.cp not in pkgdep.get_raw_depends(): # fast path for obviously non-matching packages. This saves # us the work of instantiating a whole Atom() for *every* # dependency of *every* package in pkgset. continue - all_depends = pkgdep.get_all_depends() - dep_is_displayed = False - for dep in all_depends: - # TODO: Add ability to determine if dep is enabled by USE flag. - # Check portage.dep.use_reduce + found_match = False + for dep in pkgdep.get_all_depends(): if dep.intersects(self): + pkgdep.depatom = dep pkgdep.depth = depth - pkgdep.matching_dep = dep - if printer_fn is not None: - printer_fn(pkgdep, dep_is_displayed=dep_is_displayed) - result.append(pkgdep) - dep_is_displayed = True - - # if --indirect specified, call ourselves again with the dep - # Do not call if we have already called ourselves. + yield pkgdep + found_match = True + if ( - dep_is_displayed - and not only_direct + found_match and pkgdep.cpv not in seen - and (depth < max_depth or max_depth == -1) + and only_direct is False + and (max_depth is None or depth < max_depth) ): seen.add(pkgdep.cpv) - result.append( - pkgdep.graph_reverse_depends( - pkgset=pkgset, - max_depth=max_depth, - only_direct=only_direct, - printer_fn=printer_fn, - depth=depth + 1, - seen=seen, - result=result, - ) + yield from pkgdep.graph_reverse_depends( + pkgset=pkgset, + only_direct=False, + max_depth=max_depth, + depth=depth + 1, + seen=seen, ) - if depth == 0: - return result - return pkgdep - def _parser(self, deps, use_conditional=None, depth=0): """?DEPEND file parser. |