Browse Source

console: Provide helpers to locate BinaryPackageIDs

Signed-off-by: Niels Thykier <>
Niels Thykier 11 months ago
No known key found for this signature in database GPG Key ID: A65B78DBE67C7AAC
1 changed files with 94 additions and 0 deletions
  1. +94

+ 94
- 0
britney2/ View File

@@ -1,19 +1,111 @@
import code
import collections

class SubInterpreterExit(SystemExit):

def fuzzy_match(string, collection, thing_being_matched):
if len(collection) < 1:
raise ValueError("No valid candidates to match!?")
matches = collection
if string:
if string in collection:
return string
matches = [x for x in collection if string in x]
if not matches:
raise ValueError("No matches for %s (%s), possible valid values are: %s" % (
string, thing_being_matched, sorted(collection)))
if string is not None and len(matches) > 1:
matches = [x for x in collection if x.starts(string)]
if len(matches) > 1:
if string is None:
raise ValueError("More than one item and no search criteria for %s, possible valid values are: %s" % (
thing_being_matched, sorted(matches)))
raise ValueError("Too many matches for %s (%s), matching candidates are: %s" % (
string, thing_being_matched, sorted(matches)))
return next(iter(matches))

class ConsoleUtils(object):

def __init__(self, britney):
self._britney = britney
self._pkgid_cache = collections.defaultdict(list)

def _build_cache(self):
for pkg_id in self._britney.all_binaries:

def pkg_id(self, package_name, package_version=None, package_architecture=None, *,
fuzzy_match_version=True, fuzzy_match_arch=True):
"""Look up BinaryPackageID

pkg_id("lintian", "2.5", "amd64") -> BinaryPackageID("lintian", "2.5.10", "amd64")
(Note that difference in version is intentional and a part of the fuzzy matching)

:param package_name: Name of the package (e.g. "lintian")
:param package_version: Version of the package (e.g. "2.5")
:param package_architecture: Architecture of the package (note arch:all packages are always split
in to a per-architecture package)
:param fuzzy_match_version: If true, any version string is accepted as long as it uniquely identified
the package.
:param fuzzy_match_arch: If true, any architecture string is accepted as long as it uniquely identified
the package.
:return: Exactly one BinaryPackageId
if package_name not in self._pkgid_cache:
raise ValueError("Unknown package name: %s" % package_name)

package_candidates = self._pkgid_cache[package_name]
unique_versions = {x.version for x in package_candidates}

real_version = package_version
if package_version is None or package_version not in unique_versions:
if not fuzzy_match_version:
if unique_versions is None:
raise ValueError("unique_versions cannot be None when fuzzy_match_version is False")
raise ValueError("Unknown version %s, valid options are: %s" % (
package_version, sorted(unique_versions)))
real_version = fuzzy_match(package_architecture, unique_versions, 'package_version')

unique_architectures = {x.architecture for x in package_candidates if x.version == real_version}

real_architecture = package_architecture
if package_architecture is None or package_architecture not in unique_architectures:
if not fuzzy_match_arch:
if package_architecture is None:
raise ValueError("package_architecture cannot be None when fuzzy_match_arch is False")
raise ValueError("Unknown architecture %s" % package_architecture)
real_architecture = fuzzy_match(package_architecture, unique_architectures, 'package_architecture')

match = [x for x in package_candidates if x.version == real_version and x.architecture == real_architecture]
if len(match) != 1:
if not match:
raise ValueError("Package %s, version %s (%s) is not available on architecture %s (%s)" %
package_name, package_version, real_version, package_architecture, real_architecture)
raise ValueError("The terms %s, %s (%s) and %s (%s) did not result in a unique package!? All matches: %s" %
package_name, package_version, real_version, package_architecture, real_architecture,

return match[0]

def console_quit():
raise SubInterpreterExit()

def run_python_console(britney_obj):
console_utils = ConsoleUtils(britney_obj)
console_locals = {
'britney': britney_obj,
'__name__': '__console__',
'__doc__': None,
'all_bin_pkg_ids': britney_obj.all_binaries.keys(),
'pkg_id': console_utils.pkg_id,
'quit': console_quit,
'exit': console_quit,
@@ -23,6 +115,8 @@ Interactive python (REPL) shell in britney.

Locals available
* britney: Instance of the Britney object.
* all_bin_pkg_ids: Set of all BinaryPackageIDs
* pkg_id: Lookup a BinaryPackageID
* quit()/exit(): leave this REPL console.