Coverage for britney2/installability/universe.py: 100%
38 statements
« prev ^ index » next coverage.py v6.5.0, created at 2024-04-18 20:48 +0000
« prev ^ index » next coverage.py v6.5.0, created at 2024-04-18 20:48 +0000
1# -*- coding: utf-8 -*-
3# Copyright (C) 2012 Niels Thykier <niels@thykier.net>
5# This program is free software; you can redistribute it and/or modify
6# it under the terms of the GNU General Public License as published by
7# the Free Software Foundation; either version 2 of the License, or
8# (at your option) any later version.
10# This program is distributed in the hope that it will be useful,
11# but WITHOUT ANY WARRANTY; without even the implied warranty of
12# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13# GNU General Public License for more details.
16class BinaryPackageRelation(object):
17 """All relations of a given binary package"""
19 __slots__ = ['pkg_ids', 'dependencies', 'negative_dependencies', 'reverse_dependencies']
21 def __init__(self, pkg_ids, dependencies, negative_dependencies, reverse_dependencies):
22 self.pkg_ids = pkg_ids
23 self.dependencies = dependencies
24 self.negative_dependencies = negative_dependencies
25 self.reverse_dependencies = reverse_dependencies
28class BinaryPackageUniverse(object):
29 """A "universe" of all binary packages and their relations
31 The package universe is a read-only ("immutable") data structure
32 that knows of all binary packages and their internal relations.
33 The relations are either in Conjunctive Normal Form (CNF) represented
34 via sets of sets of the package ids or simply sets of package ids.
36 Being immutable, the universe does *not* track stateful data such
37 as "which package is in what suite?" nor "is this package installable
38 in that suite?".
40 The universe also includes some packages that are considered "broken".
41 These packages have been identified to always be uninstallability
42 regardless of the selection of package available (e.g. the depend
43 on a non-existent package or has a relation that is impossible to
44 satisfy).
46 For these packages, the universe only tracks that they
47 exist and that they are broken. This implies that their relations
48 have been nulled into empty sets and they have been removed from
49 the relations of other packages. This optimizes analysis of the
50 universe on packages that is/can be installable at the expense
51 of a "minor" lie about the "broken" packages.
52 """
54 def __init__(self, relations, essential_packages, broken_packages, equivalent_packages):
55 self._relations = relations
56 self._essential_packages = essential_packages
57 self._broken_packages = broken_packages
58 self._equivalent_packages = equivalent_packages
60 def dependencies_of(self, pkg_id):
61 """Returns the set of dependencies of a given package
63 :param pkg_id: The BinaryPackageId of a binary package.
64 :return: A set containing the package ids all of the dependencies
65 of the input package in CNF.
66 """
67 return self._relations[pkg_id].dependencies
69 def negative_dependencies_of(self, pkg_id):
70 """Returns the set of negative dependencies of a given package
72 Note that there is no "reverse_negative_dependencies_of" method,
73 since negative dependencies have no "direction" unlike positive
74 dependencies.
76 :param pkg_id: The BinaryPackageId of a binary package.
77 :return: A set containing the package ids all of the negative
78 dependencies of the input package.
79 """
80 return self._relations[pkg_id].negative_dependencies
82 def reverse_dependencies_of(self, pkg_id):
83 """Returns the set of reverse dependencies of a given package
85 Note that a package is considered a reverse dependency of the
86 given package as long as at least one of its dependency relations
87 *could* be satisfied by the given package.
89 :param pkg_id: The BinaryPackageId of a binary package.
90 :return: A set containing the package ids all of the reverse
91 dependencies of the input package.
92 """
93 return self._relations[pkg_id].reverse_dependencies
95 def are_equivalent(self, pkg_id1, pkg_id2):
96 """Test if pkg_id1 and pkg_id2 are equivalent
98 :param pkg_id1 The id of the first package
99 :param pkg_id2 The id of the second package
100 :return: True if pkg_id1 and pkg_id2 have the same "signature" in
101 the package dependency graph (i.e. relations can not tell
102 them apart semantically except for their name). Otherwise False.
104 Note that this can return True even if pkg_id1 and pkg_id2 can
105 tell each other apart.
106 """
107 return pkg_id2 in self.packages_equivalent_to(pkg_id1)
109 def packages_equivalent_to(self, pkg_id):
110 """Determine which packages are equivalent to a given package
112 :param pkg_id: The BinaryPackageId of a binary package.
113 :return: A frozenset of all package ids that are equivalent to the
114 input package.
115 """
116 return self._relations[pkg_id].pkg_ids
118 def relations_of(self, pkg_id):
119 """Get the direct relations of a given package
121 :param pkg_id: The BinaryPackageId of a binary package.
122 :return: A BinaryPackageRelation describing all known direct
123 relations for the package.
124 """
125 return self._relations[pkg_id]
127 @property
128 def essential_packages(self):
129 """A frozenset of all "Essential: yes" binaries in the universe
131 :return A frozenset of BinaryPackageIds of all binaries that are
132 marked as essential.
133 """
134 return self._essential_packages
136 @property
137 def broken_packages(self):
138 """A frozenset of all broken binaries in the universe
140 :return A frozenset of BinaryPackageIds of all binaries that are
141 considered "broken" and had their relations nulled.
142 """
143 return self._broken_packages
145 @property
146 def equivalent_packages(self):
147 """A frozenset of all binary packages that are equivalent to at least one other package
149 The binary packages in this set has the property that "universe.packages_equivalent_to(pkg_id)"
150 will return a set of at least 2 or more elements for each of them.
152 :return A frozenset of BinaryPackageIds of packages that are equivalent to other packages.
153 """
154 return self._equivalent_packages
156 def __contains__(self, pkg_id):
157 return pkg_id in self._relations
159 def __iter__(self):
160 yield from self._relations