From 3b4330920da718d2dbb2a4a94577c07eaa58a8c5 Mon Sep 17 00:00:00 2001 From: Michael Vogt Date: Thu, 10 Jun 2010 11:55:18 +0200 Subject: * apt/utils.py: - fix end date calculation for releases in june --- apt/utils.py | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'apt') diff --git a/apt/utils.py b/apt/utils.py index 80ba6d65..8fc69215 100644 --- a/apt/utils.py +++ b/apt/utils.py @@ -28,11 +28,16 @@ def get_maintenance_end_date(release_date, m_months): ends. Needs the data of the release and the number of months that its is supported as input """ + # calc end date years = m_months / 12 months = m_months % 12 support_end_year = (release_date.year + years + (release_date.month + months)/12) support_end_month = (release_date.month + months) % 12 + # special case: this happens when e.g. doing 2010-06 + 18 months + if support_end_month == 0: + support_end_month = 12 + support_end_year -= 1 return (support_end_year, support_end_month) -- cgit v1.2.3 From d16f52e874378f1ebf1fe0b2491585b46bfe2e55 Mon Sep 17 00:00:00 2001 From: Michael Vogt Date: Fri, 27 Aug 2010 11:18:44 +0200 Subject: * apt/debfile.py: - add missing init for _installed_conflicts (LP: #618597) --- apt/debfile.py | 1 + debian/changelog | 7 +++++++ 2 files changed, 8 insertions(+) (limited to 'apt') diff --git a/apt/debfile.py b/apt/debfile.py index 33f0f04d..9bf27da3 100644 --- a/apt/debfile.py +++ b/apt/debfile.py @@ -598,6 +598,7 @@ class DscSrcPackage(DebPackage): self.filename = filename self._depends = [] self._conflicts = [] + self._installed_conflicts = set() self.pkgname = "" self.binaries = [] if self.filename is not None: diff --git a/debian/changelog b/debian/changelog index af215801..1fb41d07 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,3 +1,10 @@ +python-apt (0.7.96.1ubuntu6) maverick; urgency=low + + * apt/debfile.py: + - add missing init for _installed_conflicts (LP: #618597) + + -- Michael Vogt Fri, 27 Aug 2010 11:18:13 +0200 + python-apt (0.7.96.1ubuntu5) maverick; urgency=low * python/acquire.cc: -- cgit v1.2.3 From 4b91eac101b839c188835aa0e8d5284e32c6bdc1 Mon Sep 17 00:00:00 2001 From: Michael Vogt Date: Tue, 31 Aug 2010 15:31:26 +0200 Subject: * cherry pick debfile fix from lp:~mvo/python-apt/mvo (402..405): * apt/debfile.py: - fix error when reading binary content and add regresion test --- apt/debfile.py | 4 ++-- debian/changelog | 8 ++++++++ tests/data/test_debs/gdebi-test11.deb | Bin 0 -> 634 bytes tests/data/test_debs/gdebi-test12.deb | Bin 0 -> 966 bytes tests/test_debfile.py | 13 +++++++++++++ 5 files changed, 23 insertions(+), 2 deletions(-) create mode 100644 tests/data/test_debs/gdebi-test11.deb create mode 100644 tests/data/test_debs/gdebi-test12.deb (limited to 'apt') diff --git a/apt/debfile.py b/apt/debfile.py index 9bf27da3..f4a31379 100644 --- a/apt/debfile.py +++ b/apt/debfile.py @@ -512,7 +512,7 @@ class DebPackage(object): return sorted(content) @staticmethod - def to_hex(self, in_data): + def to_hex(in_data): hex = "" for (i, c) in enumerate(in_data): if i%80 == 0: @@ -521,7 +521,7 @@ class DebPackage(object): return hex @staticmethod - def to_strish(self, in_data): + def to_strish(in_data): s = "" for c in in_data: if ord(c) < 10 or ord(c) > 127: diff --git a/debian/changelog b/debian/changelog index 1fb41d07..e926c124 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,3 +1,11 @@ +python-apt (0.7.96.1ubuntu7) UNRELEASED; urgency=low + + * cherry pick debfile fix from lp:~mvo/python-apt/mvo (402..405): + * apt/debfile.py: + - fix error when reading binary content and add regresion test + + -- Michael Vogt Tue, 31 Aug 2010 15:21:08 +0200 + python-apt (0.7.96.1ubuntu6) maverick; urgency=low * apt/debfile.py: diff --git a/tests/data/test_debs/gdebi-test11.deb b/tests/data/test_debs/gdebi-test11.deb new file mode 100644 index 00000000..af9b441f Binary files /dev/null and b/tests/data/test_debs/gdebi-test11.deb differ diff --git a/tests/data/test_debs/gdebi-test12.deb b/tests/data/test_debs/gdebi-test12.deb new file mode 100644 index 00000000..36544cc7 Binary files /dev/null and b/tests/data/test_debs/gdebi-test12.deb differ diff --git a/tests/test_debfile.py b/tests/test_debfile.py index 5874dfc4..42cda6f6 100644 --- a/tests/test_debfile.py +++ b/tests/test_debfile.py @@ -77,6 +77,19 @@ class TestDebfilee(unittest.TestCase): "Unexpected result for package '%s' (got %s wanted %s)\n%s" % ( filename, res, expected_res, deb._failure_string)) + def testContent(self): + # normal + deb = apt.debfile.DebPackage(cache=self.cache) + deb.open(os.path.join("data", "test_debs", "gdebi-test11.deb")) + self.assertEqual('#!/bin/sh\necho "test"\n', + deb.data_content("usr/bin/test")) + # binary + deb = apt.debfile.DebPackage(cache=self.cache) + deb.open(os.path.join("data", "test_debs", "gdebi-test12.deb")) + content = deb.data_content("usr/bin/binary") + self.assertTrue(content.startswith("Automatically converted to printable ascii:\n\x7fELF ")) + + if __name__ == "__main__": #logging.basicConfig(level=logging.DEBUG) unittest.main() -- cgit v1.2.3 From bc301a8f07e50c7a14973b10fa4cb95f7a2beff2 Mon Sep 17 00:00:00 2001 From: Michael Vogt Date: Tue, 7 Sep 2010 11:56:17 +0200 Subject: * apt/debfile: - don't fail if we conflict with the pkgs we are reinstalling --- apt/debfile.py | 3 ++- debian/changelog | 8 ++++++++ 2 files changed, 10 insertions(+), 1 deletion(-) (limited to 'apt') diff --git a/apt/debfile.py b/apt/debfile.py index f4a31379..d8159546 100644 --- a/apt/debfile.py +++ b/apt/debfile.py @@ -347,7 +347,8 @@ class DebPackage(object): 'targetver' : c_or.target_ver } self._cache.op_progress.done() return False - if c_or.target_pkg.name in provides: + if (c_or.target_pkg.name in provides and + self.pkgname != pkg.name): self._dbg(2, "would break (conflicts) %s" % provides) self._failure_string += _("Breaks existing package '%(pkgname)s' that conflict: '%(targetpkg)s'. But the '%(debfile)s' provides it via: '%(provides)s'") % { 'provides' : ",".join(provides), diff --git a/debian/changelog b/debian/changelog index dcd87b64..44be9f23 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,3 +1,11 @@ +python-apt (0.7.97.2) UNRELEASED; urgency=low + + [ Kiwinote ] + * apt/debfile: + - don't fail if we conflict with the pkgs we are reinstalling + + -- Michael Vogt Fri, 27 Aug 2010 11:22:23 +0200 + python-apt (0.7.96.1ubuntu8) maverick; urgency=low * debian/control: -- cgit v1.2.3 From 3bf6b76611bf0b148d495e2e4aae08546e54fa94 Mon Sep 17 00:00:00 2001 From: Michael Vogt Date: Mon, 18 Oct 2010 11:51:44 +0200 Subject: fix compat issues with python3 --- apt/utils.py | 4 ++-- aptsources/distinfo.py | 28 ++++++++++++++-------------- aptsources/distro.py | 5 +++-- aptsources/sourceslist.py | 15 ++++++++------- debian/changelog | 5 +++-- tests/test_utils.py | 12 ++++++------ 6 files changed, 36 insertions(+), 33 deletions(-) (limited to 'apt') diff --git a/apt/utils.py b/apt/utils.py index 8fc69215..49e8bed5 100644 --- a/apt/utils.py +++ b/apt/utils.py @@ -29,10 +29,10 @@ def get_maintenance_end_date(release_date, m_months): its is supported as input """ # calc end date - years = m_months / 12 + years = m_months // 12 months = m_months % 12 support_end_year = (release_date.year + years + - (release_date.month + months)/12) + (release_date.month + months)//12) support_end_month = (release_date.month + months) % 12 # special case: this happens when e.g. doing 2010-06 + 18 months if support_end_month == 0: diff --git a/aptsources/distinfo.py b/aptsources/distinfo.py index 6374f185..a69f944a 100644 --- a/aptsources/distinfo.py +++ b/aptsources/distinfo.py @@ -22,11 +22,11 @@ # USA import errno +import logging import os import gettext from os import getenv from subprocess import Popen, PIPE -import ConfigParser import re import apt_pkg @@ -166,9 +166,9 @@ class DistInfo(object): try: dist = Popen(["lsb_release", "-i", "-s"], stdout=PIPE).communicate()[0].strip() - except OSError, exc: + except OSError as exc: if exc.errno != errno.ENOENT: - print 'WARNING: lsb_release failed, using defaults:', exc + logging.warn('lsb_release failed, using defaults:' % exc) dist = "Debian" self.dist = dist @@ -232,7 +232,7 @@ class DistInfo(object): mirror_data = filter(match_mirror_line.match, [x.strip() for x in open(value)]) except Exception: - print "WARNING: Failed to read mirror file" + logging.warn("Failed to read mirror file") mirror_data = [] for line in mirror_data: if line.startswith("#LOC:"): @@ -286,17 +286,17 @@ class DistInfo(object): if __name__ == "__main__": d = DistInfo("Ubuntu", "/usr/share/python-apt/templates") - print d.changelogs_uri + logging.info(d.changelogs_uri) for template in d.templates: - print "\nSuite: %s" % template.name - print "Desc: %s" % template.description - print "BaseURI: %s" % template.base_uri - print "MatchURI: %s" % template.match_uri + logging.info("\nSuite: %s" % template.name) + logging.info("Desc: %s" % template.description) + logging.info("BaseURI: %s" % template.base_uri) + logging.info("MatchURI: %s" % template.match_uri) if template.mirror_set != {}: - print "Mirrors: %s" % template.mirror_set.keys() + logging.info("Mirrors: %s" % template.mirror_set.keys()) for comp in template.components: - print " %s -%s -%s" % (comp.name, - comp.description, - comp.description_long) + logging.info(" %s -%s -%s" % (comp.name, + comp.description, + comp.description_long)) for child in template.children: - print " %s" % child.description + logging.info(" %s" % child.description) diff --git a/aptsources/distro.py b/aptsources/distro.py index 23192f50..d4b65645 100644 --- a/aptsources/distro.py +++ b/aptsources/distro.py @@ -22,6 +22,7 @@ # USA import gettext +import logging import re import os import sys @@ -451,9 +452,9 @@ def _lsb_release(): # Convert to unicode string, needed for Python 3.1 out = out.decode("utf-8") result.update(l.split(":\t") for l in out.split("\n") if ':\t' in l) - except OSError, exc: + except OSError as exc: if exc.errno != errno.ENOENT: - print 'WARNING: lsb_release failed, using defaults:', exc + logging.warn('lsb_release failed, using defaults:' % exc) return result diff --git a/aptsources/sourceslist.py b/aptsources/sourceslist.py index 76bea43a..0c4335ec 100644 --- a/aptsources/sourceslist.py +++ b/aptsources/sourceslist.py @@ -25,6 +25,7 @@ import gettext import glob +import logging import os.path import re import shutil @@ -346,7 +347,7 @@ class SourcesList(object): source = SourceEntry(line, file) self.list.append(source) except: - print "could not open file '%s'" % file + logging.error("could not open file '%s'" % file) else: f.close() @@ -437,14 +438,14 @@ if __name__ == "__main__": sources = SourcesList() for entry in sources: - print entry.str() + logging.info("entry %s" % entry.str()) #print entry.uri mirror = is_mirror("http://archive.ubuntu.com/ubuntu/", "http://de.archive.ubuntu.com/ubuntu/") - print "is_mirror(): %s" % mirror + logging.info("is_mirror(): %s" % mirror) - print is_mirror("http://archive.ubuntu.com/ubuntu", - "http://de.archive.ubuntu.com/ubuntu/") - print is_mirror("http://archive.ubuntu.com/ubuntu/", - "http://de.archive.ubuntu.com/ubuntu") + logging.info(is_mirror("http://archive.ubuntu.com/ubuntu", + "http://de.archive.ubuntu.com/ubuntu/")) + logging.info(is_mirror("http://archive.ubuntu.com/ubuntu/", + "http://de.archive.ubuntu.com/ubuntu")) diff --git a/debian/changelog b/debian/changelog index 817d34bd..3bf12153 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,14 +1,15 @@ -python-apt (0.7.98.1ubuntu1) UNRELEASEDmaverick; urgency=low +python-apt (0.7.98.1ubuntu1) natty; urgency=low [ Michael Vogt ] * merged from debian/sid, remaining changes: - updated mirror list + - compat fixes for 3.x (pending upstream inclusion) [ Jeremy Bicha ] * data/templates/Ubuntu.info.in: - add natty, LP:661578 - -- Michael Vogt Fri, 15 Oct 2010 10:30:58 +0200 + -- Michael Vogt Mon, 18 Oct 2010 10:55:00 +0200 python-apt (0.7.98.1) unstable; urgency=low diff --git a/tests/test_utils.py b/tests/test_utils.py index 19dd977d..23511f32 100644 --- a/tests/test_utils.py +++ b/tests/test_utils.py @@ -20,7 +20,7 @@ class TestUtils(unittest.TestCase): from apt.utils import get_maintenance_end_date months_of_support = 18 # test historic releases, jaunty - release_date = datetime.datetime(2009, 04, 23) + release_date = datetime.datetime(2009, 4, 23) (end_year, end_month) = get_maintenance_end_date(release_date, months_of_support) self.assertEqual(end_year, 2010) self.assertEqual(end_month, 10) @@ -28,25 +28,25 @@ class TestUtils(unittest.TestCase): release_date = datetime.datetime(2009, 10, 29) (end_year, end_month) = get_maintenance_end_date(release_date, months_of_support) self.assertEqual(end_year, 2011) - self.assertEqual(end_month, 04) + self.assertEqual(end_month, 4) # test maverick release_date = datetime.datetime(2010, 10, 10) (end_year, end_month) = get_maintenance_end_date(release_date, months_of_support) self.assertEqual(end_year, 2012) - self.assertEqual(end_month, 04) + self.assertEqual(end_month, 4) # test with modulo zero - release_date = datetime.datetime(2010, 06, 10) + release_date = datetime.datetime(2010, 6, 10) (end_year, end_month) = get_maintenance_end_date(release_date, months_of_support) self.assertEqual(end_year, 2011) self.assertEqual(end_month, 12) # test dapper months_of_support = 60 - release_date = datetime.datetime(2008, 04, 24) + release_date = datetime.datetime(2008, 4, 24) (end_year, end_month) = get_maintenance_end_date(release_date, months_of_support) self.assertEqual(end_year, 2013) - self.assertEqual(end_month, 04) + self.assertEqual(end_month, 4) # what datetime says #d = datetime.timedelta(18*30) -- cgit v1.2.3 From 19e2e0f210da3fb4cb87cfe1ddd4bbc6c61d8cd1 Mon Sep 17 00:00:00 2001 From: Michael Vogt Date: Thu, 26 May 2011 18:01:26 +0200 Subject: merge from debian, omit disable of the 0.7 API --- apt/cache.py | 44 ++++- apt/cdrom.py | 7 +- apt/debfile.py | 2 +- apt/package.py | 50 ++++-- apt/progress/old.py | 5 +- apt/progress/text.py | 5 +- aptsources/distinfo.py | 167 +++++++++---------- aptsources/sourceslist.py | 87 +++++----- debian/changelog | 63 ++++++++ debian/rules | 8 +- doc/examples/architecture.py | 12 ++ doc/source/c++/api.rst | 60 +++++++ doc/source/library/apt.package.rst | 31 +++- doc/source/library/apt_pkg.rst | 306 ++++++++++++++++++++++++++++++++++- doc/source/tutorials/apt-cdrom.rst | 2 +- doc/source/whatsnew/0.8.0.rst | 38 +++++ pre-build.sh | 4 +- python/acquire-item.cc | 10 +- python/acquire.cc | 14 +- python/apt_pkgmodule.cc | 106 +++++++++---- python/apt_pkgmodule.h | 8 + python/arfile.cc | 12 +- python/cache.cc | 265 +++++++++++++++++++++++-------- python/cachegroup.cc | 188 ++++++++++++++++++++++ python/configuration.cc | 6 +- python/depcache.cc | 12 +- python/generic.h | 16 ++ python/indexfile.cc | 2 +- python/indexrecords.cc | 2 +- python/orderlist.cc | 317 +++++++++++++++++++++++++++++++++++++ python/pkgmanager.cc | 279 +++++++++++++++++++++++++++----- python/pkgsrcrecords.cc | 10 +- python/policy.cc | 2 +- python/progress.cc | 60 +++---- python/python-apt-helpers.cc | 2 + python/python-apt.h | 16 ++ python/string.cc | 14 +- python/tag.cc | 5 +- python/tarfile.cc | 14 +- setup.py | 3 +- tests/test_apt_cache.py | 31 ++-- tests/test_cache_invocation.py | 4 +- tests/test_configuration.py | 30 ++++ tests/test_group.py | 32 ++++ tests/test_hashes.py | 7 +- tests/test_progress.py | 3 +- 46 files changed, 1964 insertions(+), 397 deletions(-) create mode 100644 doc/examples/architecture.py create mode 100644 doc/source/whatsnew/0.8.0.rst create mode 100644 python/cachegroup.cc create mode 100644 python/orderlist.cc create mode 100644 tests/test_configuration.py create mode 100644 tests/test_group.py (limited to 'apt') diff --git a/apt/cache.py b/apt/cache.py index bfa41edc..be137b76 100644 --- a/apt/cache.py +++ b/apt/cache.py @@ -66,7 +66,11 @@ class Cache(object): self._weakref = weakref.WeakValueDictionary() self._set = set() self._fullnameset = set() + self._changes_count = -1 self._sorted_set = None + + self.connect("cache_post_open", self._inc_changes_count) + self.connect("cache_post_change", self._inc_changes_count) if memonly: # force apt to build its caches in memory apt_pkg.config.set("Dir::Cache::pkgcache", "") @@ -87,6 +91,11 @@ class Cache(object): # recognized (LP: #320665) apt_pkg.init_system() self.open(progress) + + + def _inc_changes_count(self): + """Increase the number of changes""" + self._changes_count += 1 def _check_and_create_required_dirs(self, rootdir): """ @@ -107,7 +116,7 @@ class Cache(object): os.makedirs(rootdir + d) for f in files: if not os.path.exists(rootdir + f): - open(rootdir + f, "w") + open(rootdir + f, "w").close() def _run_callbacks(self, name): """ internal helper to run a callback """ @@ -288,6 +297,30 @@ class Cache(object): finally: os.close(lock) + def fetch_archives(self, progress=None, fetcher=None): + """Fetch the archives for all packages marked for install/upgrade. + + You can specify either an :class:`apt.progress.base.AcquireProgress()` + object for the parameter *progress*, or specify an already + existing :class:`apt_pkg.Acquire` object for the parameter *fetcher*. + + The return value of the function is undefined. If an error occured, + an exception of type :class:`FetchFailedException` or + :class:`FetchCancelledException` is raised. + + .. versionadded:: 0.8.0 + """ + if progress is not None and fetcher is not None: + raise ValueError("Takes a progress or a an Acquire object") + if progress is None: + progress = apt.progress.text.AcquireProgress() + if fetcher is None: + fetcher = apt_pkg.Acquire(progress) + + + return self._fetch_archives(fetcher, + apt_pkg.PackageManager(self._depcache)) + def is_virtual_package(self, pkgname): """Return whether the package is a virtual package.""" try: @@ -339,6 +372,10 @@ class Cache(object): raise_on_error=True, sources_list=None): """Run the equivalent of apt-get update. + You probably want to call open() afterwards, in order to utilise the + new cache. Otherwise, the old cache will be used which can lead to + strange bugs. + The first parameter *fetch_progress* may be set to an instance of apt.progress.FetchProgress, the default is apt.progress.FetchProgress() . @@ -548,6 +585,7 @@ class ProblemResolver(object): def __init__(self, cache): self._resolver = apt_pkg.ProblemResolver(cache._depcache) + self._cache = cache def clear(self, package): """Reset the package to the default state.""" @@ -567,11 +605,15 @@ class ProblemResolver(object): def resolve(self): """Resolve dependencies, try to remove packages where needed.""" + self._cache.cache_pre_change() self._resolver.resolve() + self._cache.cache_post_change() def resolve_by_keep(self): """Resolve dependencies, do not try to remove packages.""" + self._cache.cache_pre_change() self._resolver.resolve_by_keep() + self._cache.cache_post_change() # ----------------------------- experimental interface diff --git a/apt/cdrom.py b/apt/cdrom.py index 01caa12f..9688de9e 100644 --- a/apt/cdrom.py +++ b/apt/cdrom.py @@ -79,9 +79,10 @@ class Cdrom(apt_pkg.Cdrom): src.append(apt_pkg.config.find_file("Dir::Etc::sourcelist")) # Check each file for fname in src: - for line in open(fname): - if not line.lstrip().startswith("#") and cd_id in line: - return True + with open(fname) as fobj: + for line in fobj: + if not line.lstrip().startswith("#") and cd_id in line: + return True return False if apt_pkg._COMPAT_0_7: diff --git a/apt/debfile.py b/apt/debfile.py index fb4312a1..d0f41def 100644 --- a/apt/debfile.py +++ b/apt/debfile.py @@ -64,7 +64,7 @@ class DebPackage(object): self._installed_conflicts = set() self._failure_string = "" self.filename = filename - self._debfile = apt_inst.DebFile(open(self.filename)) + self._debfile = apt_inst.DebFile(self.filename) control = self._debfile.control.extractdata("control") self._sections = apt_pkg.TagSection(control) self.pkgname = self._sections["Package"] diff --git a/apt/package.py b/apt/package.py index d7d5d167..14e80594 100644 --- a/apt/package.py +++ b/apt/package.py @@ -50,9 +50,9 @@ __all__ = ('BaseDependency', 'Dependency', 'Origin', 'Package', 'Record', def _file_is_same(path, size, md5): """Return ``True`` if the file is the same.""" - if (os.path.exists(path) and os.path.getsize(path) == size and - apt_pkg.md5sum(open(path)) == md5): - return True + if os.path.exists(path) and os.path.getsize(path) == size: + with open(path) as fobj: + return apt_pkg.md5sum(fobj) == md5 class FetchError(Exception): @@ -162,10 +162,23 @@ class Origin(object): class Record(Mapping): - """Represent a pkgRecord. + """Record in a Packages file + + Represent a record as stored in a Packages file. You can use this like + a dictionary mapping the field names of the record to their values:: + + >>> record = Record("Package: python-apt\\nVersion: 0.8.0\\n\\n") + >>> record["Package"] + 'python-apt' + >>> record["Version"] + '0.8.0' + + For example, to get the tasks of a package from a cache, you could do:: + + package.candidate.record["Tasks"].split() + + Of course, you can also use the :attr:`Version.tasks` property. - It can be accessed like a dictionary and can also give the original package - record if accessed as a string. """ def __init__(self, record_str): @@ -209,6 +222,9 @@ class Record(Mapping): class Version(object): """Representation of a package version. + The Version class contains all information related to a + specific package version. + .. versionadded:: 0.7.9 """ @@ -393,7 +409,11 @@ class Version(object): @property def record(self): - """Return a Record() object for this version.""" + """Return a Record() object for this version. + + Return a Record() object for this version which provides access + to the raw attributes of the candidate version + """ return Record(self._records.record) def get_dependencies(self, *types): @@ -474,6 +494,16 @@ class Version(object): """ return self._records.sha256_hash + @property + def tasks(self): + """Get the tasks of the package. + + A set of the names of the tasks this package belongs to. + + .. versionadded:: 0.8.0 + """ + return set(self.record["Task"].split()) + def _uris(self): """Return an iterator over all available urls. @@ -994,11 +1024,8 @@ class Package(object): """ path = "/var/lib/dpkg/info/%s.list" % self.name try: - file_list = open(path, "rb") - try: + with open(path, "rb") as file_list: return file_list.read().decode("utf-8").split(u"\n") - finally: - file_list.close() except EnvironmentError: return [] @@ -1104,6 +1131,7 @@ class Package(object): # Check if the download was canceled if cancel_lock and cancel_lock.isSet(): return u"" + # FIXME: python3.2: Should be closed manually changelog_file = urllib2.urlopen(uri) # do only get the lines that are new changelog = u"" diff --git a/apt/progress/old.py b/apt/progress/old.py index 4bd79f2e..364cd2ce 100644 --- a/apt/progress/old.py +++ b/apt/progress/old.py @@ -149,10 +149,11 @@ class TextFetchProgress(FetchProgress): Return True to continue or False to cancel. """ FetchProgress.pulse(self) + if self.currentCPS > 0: s = "[%2.f%%] %sB/s %s" % (self.percent, - apt_pkg.size_to_str(int(self.currentCPS)), - apt_pkg.time_to_str(int(self.eta))) + apt_pkg.size_to_str(self.currentCPS), + apt_pkg.time_to_str(long(self.eta))) else: s = "%2.f%% [Working]" % (self.percent) print "\r%s" % (s), diff --git a/apt/progress/text.py b/apt/progress/text.py index d777837c..c5eec092 100644 --- a/apt/progress/text.py +++ b/apt/progress/text.py @@ -157,11 +157,10 @@ class AcquireProgress(base.AcquireProgress, TextProgress): shown = False tval = '%i%%' % percent - end = "" if self.current_cps: - eta = int(float(self.total_bytes - self.current_bytes) / - self.current_cps) + eta = long(float(self.total_bytes - self.current_bytes) / + self.current_cps) end = " %sB/s %s" % (apt_pkg.size_to_str(self.current_cps), apt_pkg.time_to_str(eta)) diff --git a/aptsources/distinfo.py b/aptsources/distinfo.py index a69f944a..249985e7 100644 --- a/aptsources/distinfo.py +++ b/aptsources/distinfo.py @@ -176,89 +176,90 @@ class DistInfo(object): map_mirror_sets = {} dist_fname = "%s/%s.info" % (base_dir, dist) - dist_file = open(dist_fname) - if not dist_file: - return - template = None - component = None - for line in dist_file: - tokens = line.split(':', 1) - if len(tokens) < 2: - continue - field = tokens[0].strip() - value = tokens[1].strip() - if field == 'ChangelogURI': - self.changelogs_uri = _(value) - elif field == 'MetaReleaseURI': - self.metarelease_uri = value - elif field == 'Suite': - self.finish_template(template, component) - component=None - template = Template() - template.name = value - template.distribution = dist - template.match_name = "^%s$" % value - elif field == 'MatchName': - template.match_name = value - elif field == 'ParentSuite': - template.child = True - for nanny in self.templates: - # look for parent and add back ref to it - if nanny.name == value: - template.parents.append(nanny) - nanny.children.append(template) - elif field == 'Available': - template.available = apt_pkg.string_to_bool(value) - elif field == 'Official': - template.official = apt_pkg.string_to_bool(value) - elif field == 'RepositoryType': - template.type = value - elif field == 'BaseURI' and not template.base_uri: - template.base_uri = value - elif field == 'BaseURI-%s' % self.arch: - template.base_uri = value - elif field == 'MatchURI' and not template.match_uri: - template.match_uri = value - elif field == 'MatchURI-%s' % self.arch: - template.match_uri = value - elif (field == 'MirrorsFile' or - field == 'MirrorsFile-%s' % self.arch): - # Make the path absolute. - value = os.path.isabs(value) and value or \ - os.path.abspath(os.path.join(base_dir, value)) - if value not in map_mirror_sets: - mirror_set = {} - try: - mirror_data = filter(match_mirror_line.match, - [x.strip() for x in open(value)]) - except Exception: - logging.warn("Failed to read mirror file") - mirror_data = [] - for line in mirror_data: - if line.startswith("#LOC:"): - location = match_loc.sub(r"\1", line) - continue - (proto, hostname, dir) = split_url(line) - if hostname in mirror_set: - mirror_set[hostname].add_repository(proto, dir) - else: - mirror_set[hostname] = Mirror( - proto, hostname, dir, location) - map_mirror_sets[value] = mirror_set - template.mirror_set = map_mirror_sets[value] - elif field == 'Description': - template.description = _(value) - elif field == 'Component': - if component and not template.has_component(component.name): - template.components.append(component) - component = Component(value) - elif field == 'CompDescription': - component.set_description(_(value)) - elif field == 'CompDescriptionLong': - component.set_description_long(_(value)) - self.finish_template(template, component) - template=None - component=None + with open(dist_fname) as dist_file: + template = None + component = None + for line in dist_file: + tokens = line.split(':', 1) + if len(tokens) < 2: + continue + field = tokens[0].strip() + value = tokens[1].strip() + if field == 'ChangelogURI': + self.changelogs_uri = _(value) + elif field == 'MetaReleaseURI': + self.metarelease_uri = value + elif field == 'Suite': + self.finish_template(template, component) + component=None + template = Template() + template.name = value + template.distribution = dist + template.match_name = "^%s$" % value + elif field == 'MatchName': + template.match_name = value + elif field == 'ParentSuite': + template.child = True + for nanny in self.templates: + # look for parent and add back ref to it + if nanny.name == value: + template.parents.append(nanny) + nanny.children.append(template) + elif field == 'Available': + template.available = apt_pkg.string_to_bool(value) + elif field == 'Official': + template.official = apt_pkg.string_to_bool(value) + elif field == 'RepositoryType': + template.type = value + elif field == 'BaseURI' and not template.base_uri: + template.base_uri = value + elif field == 'BaseURI-%s' % self.arch: + template.base_uri = value + elif field == 'MatchURI' and not template.match_uri: + template.match_uri = value + elif field == 'MatchURI-%s' % self.arch: + template.match_uri = value + elif (field == 'MirrorsFile' or + field == 'MirrorsFile-%s' % self.arch): + # Make the path absolute. + value = os.path.isabs(value) and value or \ + os.path.abspath(os.path.join(base_dir, value)) + if value not in map_mirror_sets: + mirror_set = {} + try: + with open(value) as value_f: + mirror_data = filter(match_mirror_line.match, + [x.strip() for x in + value_f]) + except Exception: + print "WARNING: Failed to read mirror file" + mirror_data = [] + for line in mirror_data: + if line.startswith("#LOC:"): + location = match_loc.sub(r"\1", line) + continue + (proto, hostname, dir) = split_url(line) + if hostname in mirror_set: + mirror_set[hostname].add_repository(proto, dir) + else: + mirror_set[hostname] = Mirror( + proto, hostname, dir, location) + map_mirror_sets[value] = mirror_set + template.mirror_set = map_mirror_sets[value] + elif field == 'Description': + template.description = _(value) + elif field == 'Component': + if (component and not + template.has_component(component.name)): + template.components.append(component) + component = Component(value) + elif field == 'CompDescription': + component.set_description(_(value)) + elif field == 'CompDescriptionLong': + component.set_description_long(_(value)) + self.finish_template(template, component) + template=None + component=None def finish_template(self, template, component): " finish the current tempalte " diff --git a/aptsources/sourceslist.py b/aptsources/sourceslist.py index 04f6a5a7..b85e6947 100644 --- a/aptsources/sourceslist.py +++ b/aptsources/sourceslist.py @@ -1,4 +1,4 @@ -# aptsource.py - Provide an abstraction of the sources.list +# sourceslist.py - Provide an abstraction of the sources.list # # Copyright (c) 2004-2009 Canonical Ltd. # Copyright (c) 2004 Michiel Sikkes @@ -276,6 +276,12 @@ class SourcesList(object): yield entry raise StopIteration + def __find(self, *predicates, **attrs): + for source in self.list: + if (all(getattr(source, key) == attrs[key] for key in attrs) and + all(predicate(source) for predicate in predicates)): + yield source + def add(self, type, uri, dist, orig_comps, comment="", pos=-1, file=None, architectures=[]): """ Add a new source to the sources.list. @@ -287,39 +293,32 @@ class SourcesList(object): # create a working copy of the component list so that # we can modify it later comps = orig_comps[:] + sources = self.__find(lambda s: set(s.architectures) == architectures, + disabled=False, invalid=False, type=type, uri=uri, + dist=dist) # check if we have this source already in the sources.list - for source in self.list: - if not source.disabled and not source.invalid and \ - source.type == type and uri == source.uri and \ - source.dist == dist and \ - set(source.architectures) == architectures: - for new_comp in comps: - if new_comp in source.comps: - # we have this component already, delete it - # from the new_comps list - del comps[comps.index(new_comp)] - if len(comps) == 0: - return source - for source in self.list: + for source in sources: + for new_comp in comps: + if new_comp in source.comps: + # we have this component already, delete it + # from the new_comps list + del comps[comps.index(new_comp)] + if len(comps) == 0: + return source + + sources = self.__find(lambda s: set(s.architectures) == architectures, + invalid=False, type=type, uri=uri, dist=dist) + + for source in sources: # if there is a repo with the same (type, uri, dist) just add the # components - if not source.disabled and not source.invalid and \ - source.type == type and uri == source.uri and \ - source.dist == dist and \ - set(source.architectures) == architectures: - comps = uniq(source.comps + comps) - source.comps = comps - return source - # if there is a corresponding repo which is disabled, enable it - elif source.disabled and not source.invalid and \ - source.type == type and uri == source.uri and \ - set(source.architectures) == architectures and \ - source.dist == dist and \ - len(set(source.comps) & set(comps)) == len(comps): + if source.disabled and set(source.comps) == comps: source.disabled = False return source + elif not source.disabled: + source.comps = uniq(source.comps + comps) + return source # there isn't any matching source, so create a new line and parse it - line = type if architectures: line += " [arch=%s]" % ",".join(architectures) @@ -370,15 +369,12 @@ class SourcesList(object): def load(self, file): """ (re)load the current sources """ try: - f = open(file, "r") - lines = f.readlines() - for line in lines: - source = SourceEntry(line, file) - self.list.append(source) + with open(file, "r") as f: + for line in f: + source = SourceEntry(line, file) + self.list.append(source) except: - logging.error("could not open file '%s'" % file) - else: - f.close() + print "could not open file '%s'" % file def save(self): """ save the current sources """ @@ -390,14 +386,19 @@ class SourcesList(object): "## See sources.list(5) for more information, especialy\n" "# Remember that you can only use http, ftp or file URIs\n" "# CDROMs are managed through the apt-cdrom tool.\n") - open(path, "w").write(header) + + with open(path, "w") as f: + f.write(header) return - for source in self.list: - if source.file not in files: - files[source.file] = open(source.file, "w") - files[source.file].write(source.str()) - for f in files: - files[f].close() + + try: + for source in self.list: + if source.file not in files: + files[source.file] = open(source.file, "w") + files[source.file].write(source.str()) + finally: + for f in files: + files[f].close() def check_for_relations(self, sources_list): """get all parent and child channels in the sources list""" diff --git a/debian/changelog b/debian/changelog index 11e8819d..edd0081f 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,3 +1,66 @@ +python-apt (0.8.0~exp4ubuntu1) oneiric; urgency=low + + * Merged from debian/experimental, remaining changes: + - updated mirror list + - do not disable 0.7 compat API yet + + -- Michael Vogt Tue, 24 May 2011 10:08:56 +0200 + +python-apt (0.8.0~exp4) experimental; urgency=low + + * apt_pkg: Add OrderList, wanted for mancoosi (Closes: #623485) + * apt_pkg: Add subclassing fun to PackageManager, for #623485 as well + * apt.cache: Emit change signals in ProblemResolver + * apt.Cache: Add a _changes_count member for later use + + -- Julian Andres Klode Fri, 29 Apr 2011 13:57:30 +0200 + +python-apt (0.8.0~exp3) experimental; urgency=low + + [ Stéphane Graber ] + * Update enable_component to also apply to -src entries (LP: #758732) + + [ Julian Andres Klode ] + * apt_pkg: Add apt_pkg.Version.multi_arch and friends + + -- Julian Andres Klode Thu, 21 Apr 2011 15:33:38 +0200 + +python-apt (0.8.0~exp2) experimental; urgency=low + + * aptsources: Parse multi-arch sources.list files correctly + * aptsources: Allow insertion of new multi-arch entries + * aptsources: Various cleanup work + * all: Fix all instances of ResourceWarning about unclosed files + * tests/test_apt_cache.py: Use assertTrue() instead of assert_() + * apt_pkg: Raise error when parse_commandline gets empty argv (LP: #707416) + * apt_pkg: Fix time_to_str, time_rfc1123 to accept more correct values + (time_to_str accepts unsigned long, time_rfc1123 long long, y2k31-correct). + * apt.progress: Use long for ETA, natural type for size (LP: #377375) + * aptsources/sourceslist.py: s/aptsource.py/sourceslist.py/ (LP: #309603) + * doc/examples: Add example on how to get architecture names (LP: #194374) + * apt_pkg: Fix unsigned/long-vs-int issues (LP: #610820) + * apt.cache: Document that update() may need an open() (Closes: #622342) + * apt.cache: Add a fetch_archives() method (Closes: #622347) + * doc: Fix a minor formatting error, patch by Jakub Wilk (Closes: #608914) + * apt.package: Add 'tasks' to Version, improve doc (Closes: #619574) + * doc: Fix documentation of BaseDependency.relation (Closes: #607031) + + -- Julian Andres Klode Tue, 12 Apr 2011 15:25:38 +0200 + +python-apt (0.8.0~exp1) experimental; urgency=low + + * Disable the old-style API, and break all packages using it + * Add an 'is_multi_arch' attribute to apt_pkg.Cache + * Add apt_pkg.Group class, wrapping pkgCache::GrpIterator + * Change apt_pkg.Cache() so that passing None for 'progress' results in + no progress output + * Support (name, arch) tuples in apt_pkg.Cache mappings, wrapping + FindPkg() with two string parameters. + * Introduce apt_pkg.Cache.groups and apt_pkg.Cache.group_count + * Fix debian/rules to work correctly with tilde in version number + + -- Julian Andres Klode Tue, 05 Apr 2011 16:21:45 +0200 + python-apt (0.7.100.3ubuntu7) oneiric; urgency=low * data/templates/Ubuntu.info.in: diff --git a/debian/rules b/debian/rules index 5428375d..a85d1eb2 100755 --- a/debian/rules +++ b/debian/rules @@ -2,8 +2,8 @@ # Should be include-links, but that somehow fails. export DEBVER=$(shell dpkg-parsechangelog | sed -n -e 's/^Version: //p') export CFLAGS=-Wno-write-strings -DCOMPAT_0_7 -export PATH :=$(CURDIR)/utils:$(PATH) -export SHELL = env PATH=$(PATH) sh +export PATH := $(CURDIR)/utils:$(PATH) +export pyversions := $(CURDIR)/utils/pyversions %: dh --with python2,python3 $@ @@ -28,14 +28,14 @@ override_dh_installdocs: override_dh_strip: dh_strip -p python-apt --dbg-package=python-apt-dbg dh_strip -p python3-apt --dbg-package=python3-apt-dbg - + override_dh_compress: dh_compress -X.js -X_static/* -X _sources/* -X_sources/*/* -X.inv # We ignore failures on hurd, since its locking is broken override_dh_auto_test: ifeq (,$(findstring nocheck, $(DEB_BUILD_OPTIONS))) - set -e; for python in $(shell pyversions -r); do \ + set -e; for python in $(shell $(pyversions) -r); do \ $$python tests/test_all.py -q || [ "$(DEB_BUILD_ARCH_OS)" = "hurd" ]; \ done; else diff --git a/doc/examples/architecture.py b/doc/examples/architecture.py new file mode 100644 index 00000000..75afb2d1 --- /dev/null +++ b/doc/examples/architecture.py @@ -0,0 +1,12 @@ +import apt_pkg + + +def main(): + apt_pkg.init_config() + + print "Native architecture:", apt_pkg.config["APT::Architecture"] + print "All architectures:", apt_pkg.config.value_list("APT::Architectures") + + +if __name__ == '__main__': + main() diff --git a/doc/source/c++/api.rst b/doc/source/c++/api.rst index 97ab24d1..63ed1599 100644 --- a/doc/source/c++/api.rst +++ b/doc/source/c++/api.rst @@ -389,6 +389,38 @@ Description (pkgCache::DescIterator) Return the :ctype:`pkgCache::DescIterator` reference contained in the Python object *object*. + +Group (pkgCache::GrpIterator) +---------------------------------- +.. versionadded:: 0.8.0 + +.. cvar:: PyTypeObject PyGroup_Type + + The type object for :class:`apt_pkg.Group` objects. + +.. cfunction:: int PyGroup_Check(PyObject *object) + + Check that the object *object* is an :class:`apt_pkg.Group` object, or + a subclass thereof. + +.. cfunction:: int PyGroup_CheckExact(PyObject *object) + + Check that the object *object* is an :class:`apt_pkg.Group` object + and no subclass thereof. + +.. cfunction:: PyObject* PyGroup_FromCpp(pkgCache::GrpIterator &cpp, bool delete, PyObject *owner) + + Create a new :class:`apt_pkg.Group` object from the :ctype:`pkgCache::GrpIterator` + reference given by the parameter *cpp*. If the parameter *delete* is + true, *cpp* will be deleted when the reference + count of the returned object reaches 0. The parameter *owner* should be + a PyObject of the type :cdata:`PyCache_Type`. + +.. cfunction:: pkgCache::GrpIterator& PyGroup_ToCpp(PyObject *object) + + Return the :ctype:`pkgCache::GrpIterator` reference contained in the + Python object *object*. + Hashes (Hashes) ---------------------------------- .. cvar:: PyTypeObject PyHashes_Type @@ -590,6 +622,34 @@ IndexFile (pkgIndexFile) Return the :ctype:`pkgIndexFile` pointer contained in the Python object *object*. +OrderList (pkgOrderList) +--------------------------- +.. cvar:: PyTypeObject PyOrderList_Type + + The type object for :class:`apt_pkg.OrderList` objects. + +.. cfunction:: int PyOrderList_Check(PyObject *object) + + Check that the object *object* is an :class:`apt_pkg.OrderList` object, or + a subclass thereof. + +.. cfunction:: int PyOrderList_CheckExact(PyObject *object) + + Check that the object *object* is an :class:`apt_pkg.OrderList` object + and no subclass thereof. + +.. cfunction:: PyObject* PyOrderList_FromCpp(pkgOrderList *cpp, bool delete, PyObject *owner) + + Create a new :class:`apt_pkg.OrderList` object from the :ctype:`pkgOrderList` + pointer given by the parameter *cpp*. If the parameter *delete* is + true, the object pointed to by *cpp* will be deleted when the reference + count of the returned object reaches 0. The owner must be a + :class:`apt_pkg.DepCache` object. + +.. cfunction:: pkgOrderList* PyOrderList_ToCpp(PyObject *object) + + Return the :ctype:`pkgOrderList` pointer contained in the Python object + *object*. PackageManager (pkgPackageManager) ---------------------------------- diff --git a/doc/source/library/apt.package.rst b/doc/source/library/apt.package.rst index 4b143b8a..01a26973 100644 --- a/doc/source/library/apt.package.rst +++ b/doc/source/library/apt.package.rst @@ -34,7 +34,7 @@ Dependency Information .. attribute:: relation - The relation (>>,>=,==,<<,<=,) + The relation (>,>=,==,<,<=,) .. attribute:: version @@ -90,6 +90,35 @@ Origin Information it provides a GPG-signed Release file and the GPG-key used is in the keyring used by apt (see apt-key). + + +The Record class +----------------- +.. autoclass:: Record + :members: + + .. note:: + .. versionchanged:: 0.7.100 + This class is a subclass of :class:`collections.Mapping` when used + in Python 2.6 or newer. + + .. describe:: record[name] + + Return the value of the field with the name *name*. + + .. describe:: name in record + + Return whether a field *name* exists in record. + + .. describe:: len(record) + + The number of fields in the record + + .. describe:: str(record) + + Display the record as a string + + Examples --------- .. code-block:: python diff --git a/doc/source/library/apt_pkg.rst b/doc/source/library/apt_pkg.rst index 426cb97e..81f2d408 100644 --- a/doc/source/library/apt_pkg.rst +++ b/doc/source/library/apt_pkg.rst @@ -40,17 +40,43 @@ Working with the cache The constructor takes an optional argument which must be a subclass of :class:`apt.progress.base.OpProgress`. This object will then be used to display information during the cache opening process (or possible creation - of the cache). + of the cache). It may also be ``None``, in which case no progress will + be emitted. If not given, progress will be printed to standard output. + + .. note:: + + The cache supports colon-seperated name:architecture pairs. For + normal architectures, they are equal to a (name, architecture) + tuple. For the the "any" architecture behavior is different, as + "name:any" is equivalent to ("name:any", "any"). This is done so + that "name:any" matches all packages with that name which have + Multi-Arch: allowed set. .. describe:: cache[pkgname] Return the :class:`Package()` object for the package name given by - *pkgname*. + *pkgname*. If *pkgname* includes a colon, the part after the colon + is used as the architecture. + + .. describe:: cache[name, architecture] + + Return the :class:`Package()` object for the package with the given + name and architecture. + + .. versionadded: 0.8.0 .. describe:: pkgname in cache Check whether a package with the name given by *pkgname* exists in - the cache. + the cache for the native architecture. If *pkgname* includes a + colon, the part after the colon is used as the architecture. + + .. describe:: (name, architecture) in cache + + Check whether a package with the given name and architecture exists + in the cache. + + .. versionadded: 0.8.0 .. method:: update(progress, sources [, pulse_interval]) -> bool @@ -73,6 +99,44 @@ Working with the cache A list of all :class:`PackageFile` objects stored in the cache. + .. attribute:: group_count + + The number of groups in the cache. + + .. versionadded: 0.8.0 + + .. attribute:: groups + + A sequence of :class:`Group` objects, implemented as a + :class:`GroupList` object. + + .. versionadded: 0.8.0 + + .. class:: GroupList + + A simple sequence-like object which only provides a length and + an implementation of ``__getitem__`` for accessing groups at + a certain index. Apart from being iterable, it can be used in + the following ways: + + .. versionadded: 0.8.0 + + .. describe:: list[index] + + Get the :class:`Group` object for the group at the position + given by *index* in the GroupList *list*. + + .. describe:: len(list) + + Return the length of the GroupList object *list*. + + + .. attribute:: is_multi_arch + + An attribute determining whether the cache supports multi-arch. + + .. versionadded: 0.8.0 + .. attribute:: package_count The total number of packages available in the cache. This value is @@ -358,6 +422,158 @@ Installing with :class:`PackageManager` A constant for checking whether the the result of the call to :meth:`do_install` is 'incomplete'. + + All instances of this class also support the following methods: + + .. note:: + + This methods are provided mainly for subclassing purposes + and should not be used in most programs. This class is a + subclass of an internal :class:`_PackageManager` which does + not provide that methods. As the public C++ API creates such + an object without those methods, you should not rely on those + methods to be available unless you used the constructor of + :class:`PackageManager` to create the object. + + .. method:: configure(pkg: Package) -> bool + + Notify the package manager that the :class:`Package` given + by *pkg* is to be configured. Must return a ``True`` value + or ``None`` to continue, or a value which is ``False`` if + evaluated as boolean to abort. + + .. versionadded:: 0.8.0 + + .. method:: install(pkg: Package, filename: str) -> bool + + Notify the package manager that the :class:`Package` given + by *pkg* is to be installed from the .deb located at + *filename*. Must return a ``True`` value or ``None`` to + continue, or a value which is ``False`` if evaluated as + boolean to abort. + + + .. versionadded:: 0.8.0 + + .. method:: remove(pkg: Package, purge: bool) -> bool + + Notify the package manager that the :class:`Package` given + by *pkg* is to be removed. If *purge* is ``True``, the package + shall be purged. Must return a ``True`` value or ``None`` to + continue, or a value which is ``False`` if evaluated as boolean + to abort. + + + .. versionadded:: 0.8.0 + + .. method:: go(status_fd: int) -> bool + + Start dpkg, writing status information to the file descriptor + given by *status_fd*. Must return a ``True`` value or ``None`` to + continue, or a value which is ``False`` if evaluated as boolean + to abort. + + .. versionadded:: 0.8.0 + + .. method:: reset() + + Reset the package manager for a new round. + + .. versionadded:: 0.8.0 + + +Installation ordering with :class:`OrderList` +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +.. class:: OrderList(depcache: DepCache) + + Represent a :ctype:`pkgOrderList`, used for installation + ordering. This class provides several methods and attributes, + is complicated and should not be used by normal programs. + + .. versionadded:: 0.8.0 + + This class is a sequence and supports the following operations: + + .. describe:: list[index] + + Get the package at the given index in the list. Negative + index is supported. + + .. describe:: len(list) + + The length of the list. + + It also supports the append() method from :class:`list`: + + .. method:: append(pkg: Package) + + Append a new package to the end of the list. Please note that + you may not append a package twice, as only as much packages + as in the cache can be added. + + The class also defines several specific attributes and methods, + to be described hereinafter. + + .. method:: score(pkg: Package) + + Return the score of the package. Packages are basically + ordered by descending score. + + This class allows flags to be set on packages. Those flags are: + + .. attribute:: FLAG_ADDED + .. attribute:: FLAG_ADD_PENDING + .. attribute:: FLAG_IMMEDIATE + .. attribute:: FLAG_LOOP + .. attribute:: FLAG_UNPACKED + .. attribute:: FLAG_CONFIGURED + .. attribute:: FLAG_REMOVED + .. attribute:: FLAG_STATES_MASK + + Same as ``FLAG_UNPACKED | FLAG_CONFIGURED | FLAG_REMOVED`` + + .. attribute:: FLAG_IN_LIST + .. attribute:: FLAG_AFTER + + The methods to work with those flags are: + + .. method:: flag(pkg: Package, flag: int[, unset_flags: int]) + + Flag a package. Sets the flags given in *flag* and unsets + any flags given in *unset_flags*. + + .. method:: is_flag(pkg: Package, flag: int) + + Check whether the flags in *flag* are set for the package. + + .. method:: wipe_flags(flags: int) + + Remove the flags in *flags* from all packages. + + .. method:: is_missing(pkg: Package) + + Check if the package is missing (not really usable right now) + + .. method:: is_now(pkg: Package) + + Check if the package is flagged for any state but removal. + + The following methods for ordering are provided: + + .. method:: order_critical() + + Order the packages for critical unpacking; that is, only + respect pre-dependencies. + + .. method:: order_unpack() + + Order the packages for unpacking, repecting Pre-Depends and + Conflicts. + + .. method:: order_configure() + + Order the packages for configuration, respecting Depends. Improve performance with :class:`ActionGroup` ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -432,6 +648,47 @@ Resolving Dependencies with :class:`ProblemResolver` Try to resolve the problems without installing or removing packages. +:class:`Group` of packages with the same name +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +.. class:: Group(cache: Cache, name: str) + + .. versionadded:: 0.8.0 + + A collection of packages in which all packages have the same name. Groups + are used in multi-arch environments, where two or more packages have the + same name, but different architectures. + + Group objects provide the following parts for sequential access: + + .. describe:: group[index] + + Get the package at the given **index** in the group. + + .. note:: + Groups are internally implemented using a linked list. The object + keeps a pointer to the current object and the first object, so + access to the first element, or accesses in order have a + complexity of O(1). Random-access complexity is ranges from + O(1) to O(n). + + Group objects also provide special methods to find single packages: + + .. method:: find_package(architecture: str) -> Package + + Find a package with the groups name and the architecture given + in the argument *architecture*. If no such package exists, return + ``None``. + + .. method:: find_preferred_package(prefer_nonvirtual: bool = True) -> Package + + Find the preferred package. This is the package of the native + architecture (specified in ``APT::Architecture``) if available, + or the package from the first foreign architecture. If no package + could be found, return ``None`` + + If **prefer_nonvirtual** is ``True``, the preferred package + will be a non-virtual package, if one exists. + :class:`Package` information ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -653,6 +910,49 @@ Example: .. attribute:: installed_size The size of the package (in kilobytes), when unpacked on the disk. + + .. attribute:: multi_arch + + The multi-arch state of the package. Can be one of the following + attributes. + + .. attribute:: MULTI_ARCH_NONE + + No multi-arch + + .. attribute:: MULTI_ARCH_ALL + + An ``Architecture: all`` package + + + .. attribute:: MULTI_ARCH_FOREIGN + + Can satisfy dependencies of foreign-architecture + packages + + .. attribute:: MULTI_ARCH_ALL_FOREIGN + + :attr:`MULTI_ARCH_FOREIGN` for ``Architecture: all`` + packages. + + .. attribute:: MULTI_ARCH_SAME + + Multiple versions from different architectures may be + installed in parallel, but may only satisfy dependencies + of packages from the same architecture + + .. attribute:: MULTI_ARCH_ALLOWED + + Installation in parallel and satisfying ``pkg:any`` + style dependencies is allowed. + + .. attribute:: MULTI_ARCH_ALL_ALLOWED + + :attr:`MULTI_ARCH_ALLOWED` for ``Architecture: all`` + packages. + + + .. attribute:: parent_pkg diff --git a/doc/source/tutorials/apt-cdrom.rst b/doc/source/tutorials/apt-cdrom.rst index 0561e082..5dd88743 100644 --- a/doc/source/tutorials/apt-cdrom.rst +++ b/doc/source/tutorials/apt-cdrom.rst @@ -98,7 +98,7 @@ it is a boolean argument. Afterwards you could use location of the mount pint. ``('c',"config-file","","ConfigFile")`` shows how to include configuration files. This option takes a parameter which points to a configuration file which will be added to the configuration space. -('o',"option","","ArbItem") is yet another type of option, which allows users +``('o',"option","","ArbItem")`` is yet another type of option, which allows users to set configuration options on the commandline. Now we have to check whether help or version is specified, and print a message diff --git a/doc/source/whatsnew/0.8.0.rst b/doc/source/whatsnew/0.8.0.rst new file mode 100644 index 00000000..2eeb135e --- /dev/null +++ b/doc/source/whatsnew/0.8.0.rst @@ -0,0 +1,38 @@ +What's New In python-apt 0.8 +============================ +Python-apt 0.8 is a new major release of the python bindings for the APT +package management libraries. + + +Removal of old API +------------------ +The old API that was deprecated in 0.7.100 is no longer available. Applications +that have not yet updated to the new API should do so. + +Multi-arch support +------------------ +This version of python-apt introduces multi-arch support: + + * A new class, :class:`apt_pkg.Group` has been added. + * :class:`apt_pkg.Cache` can now be indexed by ``(name, architecture)`` + tuples + +Features for mancoosi +---------------------- +Several new features related to ordering have been added on request +of the mancoosi project: + + * A new class :class:`apt_pkg.OrderList` has been added + * The :class:`apt_pkg.PackageManager` class now provides new methods + for registering install/remove/configure actions which can be + subclassed to check ordering. + +Other changes +------------- +This release of python-apt also features several other, smaller changes: + + * apt_pkg.Cache() now takes None for the progress parameter, preventing + progress reporting. + +There have been various other changes, see the changelog for a complete list +of changes. diff --git a/pre-build.sh b/pre-build.sh index 026a491e..a150272d 100755 --- a/pre-build.sh +++ b/pre-build.sh @@ -7,5 +7,5 @@ if [ -n "$https_proxy" ]; then fi utils/get_ubuntu_mirrors_from_lp.py > data/templates/Ubuntu.mirrors -echo "updating Debian mirror list" -utils/get_debian_mirrors.py > data/templates/Debian.mirrors +#echo "updating Debian mirror list" +#utils/get_debian_mirrors.py > data/templates/Debian.mirrors diff --git a/python/acquire-item.cc b/python/acquire-item.cc index 895d4a21..5e7423ab 100644 --- a/python/acquire-item.cc +++ b/python/acquire-item.cc @@ -65,13 +65,13 @@ static PyObject *acquireitem_get_error_text(PyObject *self, void *closure) static PyObject *acquireitem_get_filesize(PyObject *self, void *closure) { pkgAcquire::Item *item = acquireitem_tocpp(self); - return item ? Py_BuildValue("K", item->FileSize) : 0; + return item ? MkPyNumber(item->FileSize) : 0; } static PyObject *acquireitem_get_id(PyObject *self, void *closure) { pkgAcquire::Item *item = acquireitem_tocpp(self); - return item ? Py_BuildValue("k", item->ID) : 0; + return item ? MkPyNumber(item->ID) : 0; } static PyObject *acquireitem_get_mode(PyObject *self, void *closure) @@ -95,13 +95,13 @@ static PyObject *acquireitem_get_local(PyObject *self, void *closure) static PyObject *acquireitem_get_partialsize(PyObject *self, void *closure) { pkgAcquire::Item *item = acquireitem_tocpp(self); - return item ? Py_BuildValue("K", item->PartialSize) : 0; + return item ? MkPyNumber(item->PartialSize) : 0; } static PyObject *acquireitem_get_status(PyObject *self, void *closure) { pkgAcquire::Item *item = acquireitem_tocpp(self); - return item ? Py_BuildValue("i", item->Status) : 0; + return item ? MkPyNumber(item->Status) : 0; } static int acquireitem_set_id(PyObject *self, PyObject *value, void *closure) @@ -110,7 +110,7 @@ static int acquireitem_set_id(PyObject *self, PyObject *value, void *closure) if (Itm == 0) return -1; if (PyLong_Check(value)) { - Itm->ID = PyLong_AsLong(value); + Itm->ID = PyLong_AsUnsignedLong(value); } else if (PyInt_Check(value)) { Itm->ID = PyInt_AsLong(value); diff --git a/python/acquire.cc b/python/acquire.cc index ab90bbdd..6169ff40 100644 --- a/python/acquire.cc +++ b/python/acquire.cc @@ -51,17 +51,17 @@ static PyObject *acquireworker_get_status(PyObject *self, void *closure) static PyObject *acquireworker_get_current_size(PyObject *self, void *closure) { - return Py_BuildValue("k",GetCpp(self)->CurrentSize); + return MkPyNumber(GetCpp(self)->CurrentSize); } static PyObject *acquireworker_get_total_size(PyObject *self, void *closure) { - return Py_BuildValue("k",GetCpp(self)->TotalSize); + return MkPyNumber(GetCpp(self)->TotalSize); } static PyObject *acquireworker_get_resumepoint(PyObject *self, void *closure) { - return Py_BuildValue("k",GetCpp(self)->ResumePoint); + return MkPyNumber(GetCpp(self)->ResumePoint); } static PyGetSetDef acquireworker_getset[] = { @@ -225,7 +225,7 @@ static PyObject *PkgAcquireRun(PyObject *Self,PyObject *Args) pkgAcquire::RunResult run = fetcher->Run(pulseInterval); - return HandleErrors(Py_BuildValue("i",run)); + return HandleErrors(MkPyNumber(run)); } @@ -259,15 +259,15 @@ static PyMethodDef PkgAcquireMethods[] = { #define fetcher (GetCpp(Self)) static PyObject *PkgAcquireGetTotalNeeded(PyObject *Self,void*) { - return Py_BuildValue("L", fetcher->TotalNeeded()); + return MkPyNumber(fetcher->TotalNeeded()); } static PyObject *PkgAcquireGetFetchNeeded(PyObject *Self,void*) { - return Py_BuildValue("L", fetcher->FetchNeeded()); + return MkPyNumber(fetcher->FetchNeeded()); } static PyObject *PkgAcquireGetPartialPresent(PyObject *Self,void*) { - return Py_BuildValue("L", fetcher->PartialPresent()); + return MkPyNumber(fetcher->PartialPresent()); } #undef fetcher diff --git a/python/apt_pkgmodule.cc b/python/apt_pkgmodule.cc index d1ac33e0..e8490b4e 100644 --- a/python/apt_pkgmodule.cc +++ b/python/apt_pkgmodule.cc @@ -24,6 +24,7 @@ #include #include #include +#include #include #include @@ -83,7 +84,7 @@ static PyObject *VersionCompare(PyObject *Self,PyObject *Args) return 0; } - return Py_BuildValue("i",_system->VS->DoCmpVersion(A,A+LenA,B,B+LenB)); + return MkPyNumber(_system->VS->DoCmpVersion(A,A+LenA,B,B+LenB)); } static char *doc_CheckDep = @@ -459,7 +460,7 @@ static PyObject *GetLock(PyObject *Self,PyObject *Args) int fd = GetLock(file, errors); - return HandleErrors(Py_BuildValue("i", fd)); + return HandleErrors(MkPyNumber(fd)); } static char *doc_PkgSystemLock = @@ -732,6 +733,12 @@ static struct _PyAptPkgAPIStruct API = { &PyVersion_Type, // version_type &PyVersion_FromCpp, // version_tocpp &PyVersion_ToCpp, // version_tocpp + &PyGroup_Type, // group_type + &PyGroup_FromCpp, // group_fromcpp + &PyGroup_ToCpp, // group_tocpp + &PyOrderList_Type, // orderlist_type + &PyOrderList_FromCpp, // orderlist_fromcpp + &PyOrderList_ToCpp // orderlist_tocpp }; @@ -811,6 +818,8 @@ extern "C" void initapt_pkg() ADDTYPE(Module,"DependencyList",&PyDependencyList_Type); // NO __new__(), internal ADDTYPE(Module,"Package",&PyPackage_Type); // NO __new__() ADDTYPE(Module,"Version",&PyVersion_Type); // NO __new__() + ADDTYPE(Module,"Group", &PyGroup_Type); + ADDTYPE(Module,"GroupList", &PyGroupList_Type); /* ============================ cdrom.cc ============================ */ ADDTYPE(Module,"Cdrom",&PyCdrom_Type); /* ========================= configuration.cc ========================= */ @@ -824,7 +833,8 @@ extern "C" void initapt_pkg() /* ========================= metaindex.cc ========================= */ ADDTYPE(Module,"MetaIndex",&PyMetaIndex_Type); // NO __new__() /* ========================= pkgmanager.cc ========================= */ - ADDTYPE(Module,"PackageManager",&PyPackageManager_Type); + ADDTYPE(Module,"_PackageManager",&PyPackageManager_Type); + ADDTYPE(Module,"PackageManager",&PyPackageManager2_Type); /* ========================= pkgrecords.cc ========================= */ ADDTYPE(Module,"PackageRecords",&PyPackageRecords_Type); /* ========================= pkgsrcrecords.cc ========================= */ @@ -838,6 +848,7 @@ extern "C" void initapt_pkg() ADDTYPE(Module,"AcquireItemDesc",&PyAcquireItemDesc_Type); ADDTYPE(Module,"SystemLock",&PySystemLock_Type); ADDTYPE(Module,"FileLock",&PyFileLock_Type); + ADDTYPE(Module,"OrderList",&PyOrderList_Type); // Tag file constants PyModule_AddObject(Module,"REWRITE_PACKAGE_ORDER", CharCharToList(TFRewritePackageOrder)); @@ -845,86 +856,110 @@ extern "C" void initapt_pkg() PyModule_AddObject(Module,"REWRITE_SOURCE_ORDER", CharCharToList(TFRewriteSourceOrder)); + PyDict_SetItemString(PyOrderList_Type.tp_dict, "FLAG_ADDED", MkPyNumber(pkgOrderList::Added)); + PyDict_SetItemString(PyOrderList_Type.tp_dict, "FLAG_ADD_PENDIG", MkPyNumber(pkgOrderList::AddPending)); + PyDict_SetItemString(PyOrderList_Type.tp_dict, "FLAG_IMMEDIATE", MkPyNumber(pkgOrderList::Immediate)); + PyDict_SetItemString(PyOrderList_Type.tp_dict, "FLAG_LOOP", MkPyNumber(pkgOrderList::Loop)); + PyDict_SetItemString(PyOrderList_Type.tp_dict, "FLAG_UNPACKED", MkPyNumber(pkgOrderList::UnPacked)); + PyDict_SetItemString(PyOrderList_Type.tp_dict, "FLAG_CONFIGURED", MkPyNumber(pkgOrderList::Configured)); + PyDict_SetItemString(PyOrderList_Type.tp_dict, "FLAG_REMOVED", MkPyNumber(pkgOrderList::Removed)); + PyDict_SetItemString(PyOrderList_Type.tp_dict, "FLAG_IN_LIST", MkPyNumber(pkgOrderList::InList)); + PyDict_SetItemString(PyOrderList_Type.tp_dict, "FLAG_AFTER", MkPyNumber(pkgOrderList::After)); + PyDict_SetItemString(PyOrderList_Type.tp_dict, "FLAG_STATES_MASK", MkPyNumber(pkgOrderList::States)); // Acquire constants. // some constants PyDict_SetItemString(PyAcquire_Type.tp_dict, "RESULT_CANCELLED", - Py_BuildValue("i", pkgAcquire::Cancelled)); + MkPyNumber(pkgAcquire::Cancelled)); PyDict_SetItemString(PyAcquire_Type.tp_dict, "RESULT_CONTINUE", - Py_BuildValue("i", pkgAcquire::Continue)); + MkPyNumber(pkgAcquire::Continue)); PyDict_SetItemString(PyAcquire_Type.tp_dict, "RESULT_FAILED", - Py_BuildValue("i", pkgAcquire::Failed)); + MkPyNumber(pkgAcquire::Failed)); #ifdef COMPAT_0_7 PyDict_SetItemString(PyAcquire_Type.tp_dict, "ResultCancelled", - Py_BuildValue("i", pkgAcquire::Cancelled)); + MkPyNumber(pkgAcquire::Cancelled)); PyDict_SetItemString(PyAcquire_Type.tp_dict, "ResultContinue", - Py_BuildValue("i", pkgAcquire::Continue)); + MkPyNumber(pkgAcquire::Continue)); PyDict_SetItemString(PyAcquire_Type.tp_dict, "ResultFailed", - Py_BuildValue("i", pkgAcquire::Failed)); + MkPyNumber(pkgAcquire::Failed)); #endif // Dependency constants PyDict_SetItemString(PyDependency_Type.tp_dict, "TYPE_DEPENDS", - Py_BuildValue("i", pkgCache::Dep::Depends)); + MkPyNumber(pkgCache::Dep::Depends)); PyDict_SetItemString(PyDependency_Type.tp_dict, "TYPE_PREDEPENDS", - Py_BuildValue("i", pkgCache::Dep::PreDepends)); + MkPyNumber(pkgCache::Dep::PreDepends)); PyDict_SetItemString(PyDependency_Type.tp_dict, "TYPE_SUGGESTS", - Py_BuildValue("i", pkgCache::Dep::Suggests)); + MkPyNumber(pkgCache::Dep::Suggests)); PyDict_SetItemString(PyDependency_Type.tp_dict, "TYPE_RECOMMENDS", - Py_BuildValue("i", pkgCache::Dep::Suggests)); + MkPyNumber(pkgCache::Dep::Suggests)); PyDict_SetItemString(PyDependency_Type.tp_dict, "TYPE_CONFLICTS", - Py_BuildValue("i", pkgCache::Dep::Conflicts)); + MkPyNumber(pkgCache::Dep::Conflicts)); PyDict_SetItemString(PyDependency_Type.tp_dict, "TYPE_REPLACES", - Py_BuildValue("i", pkgCache::Dep::Replaces)); + MkPyNumber(pkgCache::Dep::Replaces)); PyDict_SetItemString(PyDependency_Type.tp_dict, "TYPE_OBSOLETES", - Py_BuildValue("i", pkgCache::Dep::Obsoletes)); + MkPyNumber(pkgCache::Dep::Obsoletes)); PyDict_SetItemString(PyDependency_Type.tp_dict, "TYPE_DPKG_BREAKS", - Py_BuildValue("i", pkgCache::Dep::DpkgBreaks)); + MkPyNumber(pkgCache::Dep::DpkgBreaks)); PyDict_SetItemString(PyDependency_Type.tp_dict, "TYPE_ENHANCES", - Py_BuildValue("i", pkgCache::Dep::Enhances)); + MkPyNumber(pkgCache::Dep::Enhances)); // PackageManager constants PyDict_SetItemString(PyPackageManager_Type.tp_dict, "RESULT_COMPLETED", - Py_BuildValue("i", pkgPackageManager::Completed)); + MkPyNumber(pkgPackageManager::Completed)); PyDict_SetItemString(PyPackageManager_Type.tp_dict, "RESULT_FAILED", - Py_BuildValue("i", pkgPackageManager::Failed)); + MkPyNumber(pkgPackageManager::Failed)); PyDict_SetItemString(PyPackageManager_Type.tp_dict, "RESULT_INCOMPLETE", - Py_BuildValue("i", pkgPackageManager::Incomplete)); + MkPyNumber(pkgPackageManager::Incomplete)); #ifdef COMPAT_0_7 PyDict_SetItemString(PyPackageManager_Type.tp_dict, "ResultCompleted", - Py_BuildValue("i", pkgPackageManager::Completed)); + MkPyNumber(pkgPackageManager::Completed)); PyDict_SetItemString(PyPackageManager_Type.tp_dict, "ResultFailed", - Py_BuildValue("i", pkgPackageManager::Failed)); + MkPyNumber(pkgPackageManager::Failed)); PyDict_SetItemString(PyPackageManager_Type.tp_dict, "ResultIncomplete", - Py_BuildValue("i", pkgPackageManager::Incomplete)); + MkPyNumber(pkgPackageManager::Incomplete)); #endif + PyDict_SetItemString(PyVersion_Type.tp_dict, "MULTI_ARCH_NONE", + MkPyNumber(pkgCache::Version::None)); + PyDict_SetItemString(PyVersion_Type.tp_dict, "MULTI_ARCH_ALL", + MkPyNumber(pkgCache::Version::All)); + PyDict_SetItemString(PyVersion_Type.tp_dict, "MULTI_ARCH_FOREIGN", + MkPyNumber(pkgCache::Version::Foreign)); + PyDict_SetItemString(PyVersion_Type.tp_dict, "MULTI_ARCH_SAME", + MkPyNumber(pkgCache::Version::Same)); + PyDict_SetItemString(PyVersion_Type.tp_dict, "MULTI_ARCH_ALLOWED", + MkPyNumber(pkgCache::Version::Allowed)); + PyDict_SetItemString(PyVersion_Type.tp_dict, "MULTI_ARCH_ALL_FOREIGN", + MkPyNumber(pkgCache::Version::AllForeign)); + PyDict_SetItemString(PyVersion_Type.tp_dict, "MULTI_ARCH_ALL_ALLOWED", + MkPyNumber(pkgCache::Version::AllAllowed)); // AcquireItem Constants. PyDict_SetItemString(PyAcquireItem_Type.tp_dict, "STAT_IDLE", - Py_BuildValue("i", pkgAcquire::Item::StatIdle)); + MkPyNumber(pkgAcquire::Item::StatIdle)); PyDict_SetItemString(PyAcquireItem_Type.tp_dict, "STAT_FETCHING", - Py_BuildValue("i", pkgAcquire::Item::StatFetching)); + MkPyNumber(pkgAcquire::Item::StatFetching)); PyDict_SetItemString(PyAcquireItem_Type.tp_dict, "STAT_DONE", - Py_BuildValue("i", pkgAcquire::Item::StatDone)); + MkPyNumber(pkgAcquire::Item::StatDone)); PyDict_SetItemString(PyAcquireItem_Type.tp_dict, "STAT_TRANSIENT_NETWORK_ERROR", - Py_BuildValue("i", pkgAcquire::Item::StatTransientNetworkError)); + MkPyNumber(pkgAcquire::Item::StatTransientNetworkError)); PyDict_SetItemString(PyAcquireItem_Type.tp_dict, "STAT_ERROR", - Py_BuildValue("i", pkgAcquire::Item::StatError)); + MkPyNumber(pkgAcquire::Item::StatError)); PyDict_SetItemString(PyAcquireItem_Type.tp_dict, "STAT_AUTH_ERROR", - Py_BuildValue("i", pkgAcquire::Item::StatAuthError)); + MkPyNumber(pkgAcquire::Item::StatAuthError)); #ifdef COMPAT_0_7 PyDict_SetItemString(PyAcquireItem_Type.tp_dict, "StatIdle", - Py_BuildValue("i", pkgAcquire::Item::StatIdle)); + MkPyNumber(pkgAcquire::Item::StatIdle)); PyDict_SetItemString(PyAcquireItem_Type.tp_dict, "StatFetching", - Py_BuildValue("i", pkgAcquire::Item::StatFetching)); + MkPyNumber(pkgAcquire::Item::StatFetching)); PyDict_SetItemString(PyAcquireItem_Type.tp_dict, "StatDone", - Py_BuildValue("i", pkgAcquire::Item::StatDone)); + MkPyNumber(pkgAcquire::Item::StatDone)); PyDict_SetItemString(PyAcquireItem_Type.tp_dict, "StatError", - Py_BuildValue("i", pkgAcquire::Item::StatError)); + MkPyNumber(pkgAcquire::Item::StatError)); PyDict_SetItemString(PyAcquireItem_Type.tp_dict, "StatAuthError", - Py_BuildValue("i", pkgAcquire::Item::StatAuthError)); + MkPyNumber(pkgAcquire::Item::StatAuthError)); #endif #if PY_MAJOR_VERSION >= 3 && PY_MINOR_VERSION >= 1 @@ -964,6 +999,7 @@ extern "C" void initapt_pkg() PyModule_AddIntConstant(Module,"INSTSTATE_HOLD",pkgCache::State::Hold); PyModule_AddIntConstant(Module,"INSTSTATE_HOLD_REINSTREQ",pkgCache::State::HoldReInstReq); + // DEPRECATED API #ifdef COMPAT_0_7 PyModule_AddObject(Module,"RewritePackageOrder", diff --git a/python/apt_pkgmodule.h b/python/apt_pkgmodule.h index da647c3f..b3534a30 100644 --- a/python/apt_pkgmodule.h +++ b/python/apt_pkgmodule.h @@ -70,6 +70,8 @@ extern PyTypeObject PyCache_Type; extern PyTypeObject PyCacheFile_Type; extern PyTypeObject PyPackageList_Type; extern PyTypeObject PyDescription_Type; +extern PyTypeObject PyGroup_Type; +extern PyTypeObject PyGroupList_Type; /* internal */ extern PyTypeObject PyPackage_Type; extern PyTypeObject PyPackageFile_Type; extern PyTypeObject PyDependency_Type; @@ -100,6 +102,7 @@ PyObject *GetPkgAcqFile(PyObject *Self, PyObject *Args, PyObject *kwds); // packagemanager extern PyTypeObject PyPackageManager_Type; +extern PyTypeObject PyPackageManager2_Type; PyObject *GetPkgManager(PyObject *Self,PyObject *Args); @@ -132,6 +135,7 @@ extern PyTypeObject PyAcquireItemDesc_Type; extern PyTypeObject PyAcquireWorker_Type; extern PyTypeObject PySystemLock_Type; extern PyTypeObject PyFileLock_Type; +extern PyTypeObject PyOrderList_Type; // Functions to be exported in the public API. @@ -149,6 +153,7 @@ extern PyTypeObject PyFileLock_Type; # define PyDependency_ToCpp GetCpp # define PyDependencyList_ToCpp GetCpp // TODO # define PyDescription_ToCpp GetCpp +# define PyGroup_ToCpp GetCpp # define PyHashes_ToCpp GetCpp # define PyHashString_ToCpp GetCpp # define PyIndexRecords_ToCpp GetCpp @@ -156,6 +161,7 @@ extern PyTypeObject PyFileLock_Type; # define PyPackage_ToCpp GetCpp # define PyPackageFile_ToCpp GetCpp # define PyIndexFile_ToCpp GetCpp +# define PyOrderList_ToCpp GetCpp # define PyPackageList_ToCpp GetCpp // TODO # define PyPackageManager_ToCpp GetCpp # define PyPackageRecords_ToCpp GetCpp // TODO @@ -186,7 +192,9 @@ PyObject* PyHashString_FromCpp(HashString* const &obj, bool Delete, PyObject *Ow PyObject* PyIndexRecords_FromCpp(indexRecords* const &obj, bool Delete, PyObject *Owner); PyObject* PyMetaIndex_FromCpp(metaIndex* const &obj, bool Delete, PyObject *Owner); PyObject* PyPackage_FromCpp(pkgCache::PkgIterator const &obj, bool Delete, PyObject *Owner); +PyObject* PyGroup_FromCpp(pkgCache::GrpIterator const &obj, bool Delete, PyObject *Owner); PyObject* PyIndexFile_FromCpp(pkgIndexFile* const &obj, bool Delete, PyObject *Owner); +PyObject* PyOrderList_FromCpp(pkgOrderList* const &obj, bool Delete, PyObject *Owner); PyObject* PyPackageFile_FromCpp(pkgCache::PkgFileIterator const &obj, bool Delete, PyObject *Owner); //PyObject* PyPackageList_FromCpp(PkgListStruct const &obj, bool Delete, PyObject *Owner); PyObject* PyPackageManager_FromCpp(pkgPackageManager* const &obj, bool Delete, PyObject *Owner); diff --git a/python/arfile.cc b/python/arfile.cc index 5377ca8d..c3aa74d1 100644 --- a/python/arfile.cc +++ b/python/arfile.cc @@ -39,32 +39,32 @@ static PyObject *armember_get_name(PyObject *self, void *closure) static PyObject *armember_get_mtime(PyObject *self, void *closure) { - return Py_BuildValue("k", GetCpp(self)->MTime); + return MkPyNumber(GetCpp(self)->MTime); } static PyObject *armember_get_uid(PyObject *self, void *closure) { - return Py_BuildValue("k", GetCpp(self)->UID); + return MkPyNumber(GetCpp(self)->UID); } static PyObject *armember_get_gid(PyObject *self, void *closure) { - return Py_BuildValue("k", GetCpp(self)->GID); + return MkPyNumber(GetCpp(self)->GID); } static PyObject *armember_get_mode(PyObject *self, void *closure) { - return Py_BuildValue("k", GetCpp(self)->Mode); + return MkPyNumber(GetCpp(self)->Mode); } static PyObject *armember_get_size(PyObject *self, void *closure) { - return Py_BuildValue("k", GetCpp(self)->Size); + return MkPyNumber(GetCpp(self)->Size); } static PyObject *armember_get_start(PyObject *self, void *closure) { - return Py_BuildValue("k", GetCpp(self)->Start); + return MkPyNumber(GetCpp(self)->Start); } static PyObject *armember_repr(PyObject *self) diff --git a/python/cache.cc b/python/cache.cc index 190d4f27..b263d320 100644 --- a/python/cache.cc +++ b/python/cache.cc @@ -39,13 +39,57 @@ const char *UntranslatedDepTypes[] = }; /*}}}*/ -struct PkgListStruct + +template struct IterListStruct { - pkgCache::PkgIterator Iter; + T Iter; unsigned long LastIndex; - PkgListStruct(pkgCache::PkgIterator const &I) : Iter(I), LastIndex(0) {} - PkgListStruct() {abort();}; // G++ Bug.. + IterListStruct(T const &I) : Iter(I), LastIndex(0) {} + IterListStruct() {}; + + bool move(unsigned long Index) { + if (Index < 0 || (unsigned)Index >= Count()) + { + PyErr_SetNone(PyExc_IndexError); + return false; + } + + if ((unsigned)Index < LastIndex) + { + LastIndex = 0; + Iter = Begin(); + } + + while ((unsigned)Index > LastIndex) + { + LastIndex++; + Iter++; + if (Iter.end() == true) + { + PyErr_SetNone(PyExc_IndexError); + return false; + } + } + return true; + } + + virtual unsigned Count() = 0; + virtual T Begin() = 0; + +}; + +struct PkgListStruct : public IterListStruct { + unsigned Count() { return Iter.Cache()->HeaderP->PackageCount; } + pkgCache::PkgIterator Begin() { return Iter.Cache()->PkgBegin(); } + + PkgListStruct(pkgCache::PkgIterator const &I) { Iter = I; } +}; + +struct GrpListStruct : public IterListStruct { + unsigned Count() { return Iter.Cache()->HeaderP->GroupCount; } + pkgCache::GrpIterator Begin() { return Iter.Cache()->GrpBegin(); } + GrpListStruct(pkgCache::GrpIterator const &I) { Iter = I; } }; struct RDepListStruct @@ -175,6 +219,16 @@ static PyMethodDef PkgCacheMethods[] = {} }; +static PyObject *PkgCacheGetGroupCount(PyObject *Self, void*) { + pkgCache *Cache = GetCpp(Self); + return MkPyNumber(Cache->HeaderP->GroupCount); +} + +static PyObject *PkgCacheGetGroups(PyObject *Self, void*) { + pkgCache *Cache = GetCpp(Self); + return CppPyObject_NEW(Self,&PyGroupList_Type,Cache->GrpBegin()); +} + static PyObject *PkgCacheGetPackages(PyObject *Self, void*) { pkgCache *Cache = GetCpp(Self); return CppPyObject_NEW(Self,&PyPackageList_Type,Cache->PkgBegin()); @@ -182,31 +236,31 @@ static PyObject *PkgCacheGetPackages(PyObject *Self, void*) { static PyObject *PkgCacheGetPackageCount(PyObject *Self, void*) { pkgCache *Cache = GetCpp(Self); - return Py_BuildValue("i",Cache->HeaderP->PackageCount); + return MkPyNumber((int)Cache->HeaderP->PackageCount); } static PyObject *PkgCacheGetVersionCount(PyObject *Self, void*) { pkgCache *Cache = GetCpp(Self); - return Py_BuildValue("i",Cache->HeaderP->VersionCount); + return MkPyNumber(Cache->HeaderP->VersionCount); } static PyObject *PkgCacheGetDependsCount(PyObject *Self, void*) { pkgCache *Cache = GetCpp(Self); - return Py_BuildValue("i",Cache->HeaderP->DependsCount); + return MkPyNumber(Cache->HeaderP->DependsCount); } static PyObject *PkgCacheGetPackageFileCount(PyObject *Self, void*) { pkgCache *Cache = GetCpp(Self); - return Py_BuildValue("i",Cache->HeaderP->PackageFileCount); + return MkPyNumber(Cache->HeaderP->PackageFileCount); } static PyObject *PkgCacheGetVerFileCount(PyObject *Self, void*) { pkgCache *Cache = GetCpp(Self); - return Py_BuildValue("i",Cache->HeaderP->VerFileCount); + return MkPyNumber(Cache->HeaderP->VerFileCount); } static PyObject *PkgCacheGetProvidesCount(PyObject *Self, void*) { pkgCache *Cache = GetCpp(Self); - return Py_BuildValue("i",Cache->HeaderP->ProvidesCount); + return MkPyNumber(Cache->HeaderP->ProvidesCount); } static PyObject *PkgCacheGetFileList(PyObject *Self, void*) { @@ -222,11 +276,21 @@ static PyObject *PkgCacheGetFileList(PyObject *Self, void*) { return List; } +static PyObject *PkgCacheGetIsMultiArch(PyObject *Self, void*) { + pkgCache *Cache = GetCpp(Self); + PyBool_FromLong(Cache->MultiArchCache()); +} + static PyGetSetDef PkgCacheGetSet[] = { {"depends_count",PkgCacheGetDependsCount,0, "The number of apt_pkg.Dependency objects stored in the cache."}, {"file_list",PkgCacheGetFileList,0, "A list of apt_pkg.PackageFile objects stored in the cache."}, + {"group_count",PkgCacheGetGroupCount,0, + "The number of apt_pkg.Group objects stored in the cache."}, + {"groups", PkgCacheGetGroups, 0, "A list of Group objects in the cache"}, + {"is_multi_arch", PkgCacheGetIsMultiArch, 0, + "Whether the cache supports multi-arch."}, {"package_count",PkgCacheGetPackageCount,0, "The number of apt_pkg.Package objects stored in the cache."}, {"package_file_count",PkgCacheGetPackageFileCount,0, @@ -242,24 +306,37 @@ static PyGetSetDef PkgCacheGetSet[] = { {} }; +// Helper to call FindPkg(name) or FindPkg(name, architecture) +static pkgCache::PkgIterator CacheFindPkg(PyObject *self, PyObject *arg) +{ + const char *name; + const char *architecture; + pkgCache *cache = GetCpp(self); + name = PyObject_AsString(arg); -// Map access, operator [] -static PyObject *CacheMapOp(PyObject *Self,PyObject *Arg) -{ - pkgCache *Cache = GetCpp(Self); + if (name != NULL) + return cache->FindPkg(name); - // Get the name of the package, unicode and normal strings. - const char *Name = PyObject_AsString(Arg); - if (Name == NULL) - return 0; + PyErr_Clear(); + if (PyArg_ParseTuple(arg, "ss", &name, &architecture) == 0) { + PyErr_Clear(); + PyErr_Format(PyExc_TypeError, "Expected a string or a pair of strings"); + return pkgCache::PkgIterator(); + } + + return cache->FindPkg(name, architecture); +} - // Search for the package - pkgCache::PkgIterator Pkg = Cache->FindPkg(Name); +// Map access, operator [] +static PyObject *CacheMapOp(PyObject *Self,PyObject *Arg) +{ + pkgCache::PkgIterator Pkg = CacheFindPkg(Self, Arg); if (Pkg.end() == true) { - PyErr_SetString(PyExc_KeyError,Name); + if (!PyErr_Occurred()) + PyErr_SetObject(PyExc_KeyError,Arg); return 0; } @@ -269,11 +346,9 @@ static PyObject *CacheMapOp(PyObject *Self,PyObject *Arg) // Check whether the cache contains a package with a given name. static int CacheContains(PyObject *Self,PyObject *Arg) { - // Get the name of the package, unicode and normal strings. - const char *Name = PyObject_AsString(Arg); - if (Name == NULL) - return 0; - return (GetCpp(Self)->FindPkg(Name).end() == false); + bool res = (CacheFindPkg(Self, Arg).end() == false); + PyErr_Clear(); + return res; } static PyObject *PkgCacheNew(PyTypeObject *type,PyObject *Args,PyObject *kwds) @@ -292,7 +367,11 @@ static PyObject *PkgCacheNew(PyTypeObject *type,PyObject *Args,PyObject *kwds) pkgCacheFile *Cache = new pkgCacheFile(); - if(pyCallbackInst != 0) { + if (pyCallbackInst == Py_None) { + OpProgress Prog; + if (Cache->Open(Prog,false) == false) + return HandleErrors(); + } else if(pyCallbackInst != 0) { // sanity check for the progress object, see #497049 if (PyObject_HasAttrString(pyCallbackInst, "done") != true) { PyErr_SetString(PyExc_ValueError, @@ -342,9 +421,11 @@ static char *doc_PkgCache = "Cache([progress]) -> Cache() object.\n\n" "apt.progress.base.OpProgress() object (or similar) which reports\n" "progress information while the cache is being opened. If this\n" "parameter is not supplied, the progress will be reported in simple,\n" - "human-readable text to standard output.\n\n" + "human-readable text to standard output. If it is None, no output\n" + "will be made.\n\n" "The cache can be used like a mapping from package names to Package\n" - "objects (although only getting items is supported)."; + "objects (although only getting items is supported). Instead of a name,\n" + "a tuple of a name and an architecture may be used."; static PySequenceMethods CacheSeq = {0,0,0,0,0,0,0,CacheContains,0,0}; static PyMappingMethods CacheMap = {CacheMapLen,CacheMapOp,0}; PyTypeObject PyCache_Type = @@ -418,7 +499,7 @@ PyTypeObject PyCacheFile_Type = 0, // tp_as_buffer Py_TPFLAGS_DEFAULT, // tp_flags }; - /*}}}*/ + // Package List Class /*{{{*/ // --------------------------------------------------------------------- static Py_ssize_t PkgListLen(PyObject *Self) @@ -429,29 +510,9 @@ static Py_ssize_t PkgListLen(PyObject *Self) static PyObject *PkgListItem(PyObject *iSelf,Py_ssize_t Index) { PkgListStruct &Self = GetCpp(iSelf); - if (Index < 0 || (unsigned)Index >= Self.Iter.Cache()->HeaderP->PackageCount) - { - PyErr_SetNone(PyExc_IndexError); - return 0; - } - - if ((unsigned)Index < Self.LastIndex) - { - Self.LastIndex = 0; - Self.Iter = Self.Iter.Cache()->PkgBegin(); - } - - while ((unsigned)Index > Self.LastIndex) - { - Self.LastIndex++; - Self.Iter++; - if (Self.Iter.end() == true) - { - PyErr_SetNone(PyExc_IndexError); - return 0; - } - } + if (!Self.move(Index)) + return 0; return CppPyObject_NEW(GetOwner(iSelf),&PyPackage_Type, Self.Iter); } @@ -501,6 +562,68 @@ PyTypeObject PyPackageList_Type = CppClear, // tp_clear }; +/* The same for groups */ +static Py_ssize_t GrpListLen(PyObject *Self) +{ + return GetCpp(Self).Iter.Cache()->HeaderP->GroupCount; +} + +static PyObject *GrpListItem(PyObject *iSelf,Py_ssize_t Index) +{ + GrpListStruct &Self = GetCpp(iSelf); + + if (!Self.move(Index)) + return 0; + return CppPyObject_NEW(GetOwner(iSelf),&PyGroup_Type, + Self.Iter); +} + +static PySequenceMethods GrpListSeq = +{ + GrpListLen, + 0, // concat + 0, // repeat + GrpListItem, + 0, // slice + 0, // assign item + 0 // assign slice +}; + +static const char *grouplist_doc = + "A GroupList is an internally used structure to represent\n" + "the 'groups' attribute of apt_pkg.Cache objects in a more\n" + "efficient manner by creating Group objects only when they\n" + "are accessed."; + +PyTypeObject PyGroupList_Type = +{ + PyVarObject_HEAD_INIT(&PyType_Type, 0) + "apt_pkg.GroupList", // tp_name + sizeof(CppPyObject), // tp_basicsize + 0, // tp_itemsize + // Methods + CppDealloc, // tp_dealloc + 0, // tp_print + 0, // tp_getattr + 0, // tp_setattr + 0, // tp_compare + 0, // tp_repr + 0, // tp_as_number + &GrpListSeq, // tp_as_sequence + 0, // tp_as_mapping + 0, // tp_hash + 0, // tp_call + 0, // tp_str + 0, // tp_getattro + 0, // tp_setattro + 0, // tp_as_buffer + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC, // tp_flags + grouplist_doc, // tp_doc + CppTraverse, // tp_traverse + CppClear, // tp_clear +}; + + #define Owner (GetOwner(Self)) #define MkGet(PyFunc,Ret) static PyObject *PyFunc(PyObject *Self,void*) \ { \ @@ -514,10 +637,10 @@ MkGet(PackageGetSection,Safe_FromString(Pkg.Section())) MkGet(PackageGetRevDependsList,CppPyObject_NEW(Owner, &PyDependencyList_Type, Pkg.RevDependsList())) MkGet(PackageGetProvidesList,CreateProvides(Owner,Pkg.ProvidesList())) -MkGet(PackageGetSelectedState,Py_BuildValue("i",Pkg->SelectedState)) -MkGet(PackageGetInstState,Py_BuildValue("i",Pkg->InstState)) -MkGet(PackageGetCurrentState,Py_BuildValue("i",Pkg->CurrentState)) -MkGet(PackageGetID,Py_BuildValue("i",Pkg->ID)) +MkGet(PackageGetSelectedState,MkPyNumber(Pkg->SelectedState)) +MkGet(PackageGetInstState,MkPyNumber(Pkg->InstState)) +MkGet(PackageGetCurrentState,MkPyNumber(Pkg->CurrentState)) +MkGet(PackageGetID,MkPyNumber(Pkg->ID)) # MkGet(PackageGetAuto,PyBool_FromLong((Pkg->Flags & pkgCache::Flag::Auto) != 0)) MkGet(PackageGetEssential,PyBool_FromLong((Pkg->Flags & pkgCache::Flag::Essential) != 0)) @@ -714,7 +837,7 @@ static PyObject *DescriptionGetFileList(PyObject *Self,void*) PyObject *DescFile; PyObject *Obj; DescFile = CppPyObject_NEW(Owner,&PyPackageFile_Type,I.File()); - Obj = Py_BuildValue("Nl",DescFile,I.Index()); + Obj = Py_BuildValue("NN",DescFile,MkPyNumber(I.Index())); PyList_Append(List,Obj); Py_DECREF(Obj); } @@ -869,7 +992,7 @@ static PyObject *VersionGetFileList(PyObject *Self, void*) { PyObject *PkgFile; PyObject *Obj; PkgFile = CppPyObject_NEW(Owner,&PyPackageFile_Type,I.File()); - Obj = Py_BuildValue("Nl",PkgFile,I.Index()); + Obj = Py_BuildValue("NN",PkgFile,MkPyNumber(I.Index())); PyList_Append(List,Obj); Py_DECREF(Obj); } @@ -896,19 +1019,19 @@ static PyObject *VersionGetProvidesList(PyObject *Self, void*) { return CreateProvides(Owner,Version_GetVer(Self).ProvidesList()); } static PyObject *VersionGetSize(PyObject *Self, void*) { - return Py_BuildValue("i", Version_GetVer(Self)->Size); + return MkPyNumber(Version_GetVer(Self)->Size); } static PyObject *VersionGetInstalledSize(PyObject *Self, void*) { - return Py_BuildValue("i", Version_GetVer(Self)->InstalledSize); + return MkPyNumber(Version_GetVer(Self)->InstalledSize); } static PyObject *VersionGetHash(PyObject *Self, void*) { - return Py_BuildValue("i", Version_GetVer(Self)->Hash); + return MkPyNumber(Version_GetVer(Self)->Hash); } static PyObject *VersionGetID(PyObject *Self, void*) { - return Py_BuildValue("i", Version_GetVer(Self)->ID); + return MkPyNumber(Version_GetVer(Self)->ID); } static PyObject *VersionGetPriority(PyObject *Self, void*) { - return Py_BuildValue("i",Version_GetVer(Self)->Priority); + return MkPyNumber(Version_GetVer(Self)->Priority); } static PyObject *VersionGetPriorityStr(PyObject *Self, void*) { return Safe_FromString(Version_GetVer(Self).PriorityType()); @@ -924,6 +1047,11 @@ static PyObject *VersionGetTranslatedDescription(PyObject *Self, void*) { Ver.TranslatedDescription()); } +static PyObject *VersionGetMultiArch(PyObject *Self, void*) +{ + return MkPyNumber(Version_GetVer(Self)->MultiArch); +} + #if 0 // FIXME: enable once pkgSourceList is stored somewhere static PyObject *VersionGetIsTrusted(PyObject *Self, void*) { else if (strcmp("IsTrusted", Name) == 0) @@ -999,6 +1127,9 @@ static PyGetSetDef VersionGetSet[] = { "The numeric ID of the package."}, {"installed_size",VersionGetInstalledSize,0, "The installed size of this package version."}, + {"multi_arch",VersionGetMultiArch,0, + "Multi-arch state of this package, as an integer. See\n" + "the various MULTI_ARCH_* members."}, {"parent_pkg",VersionGetParentPkg,0, "The parent package of this version."}, {"priority",VersionGetPriority,0, @@ -1118,7 +1249,7 @@ static PyObject *PackageFile_GetIndexType(PyObject *Self,void*) static PyObject *PackageFile_GetSize(PyObject *Self,void*) { pkgCache::PkgFileIterator &File = GetCpp(Self); - return Py_BuildValue("i",File->Size); + return MkPyNumber(File->Size); } static PyObject *PackageFile_GetNotSource(PyObject *Self,void*) @@ -1135,7 +1266,7 @@ static PyObject *PackageFile_GetNotAutomatic(PyObject *Self,void*) static PyObject *PackageFile_GetID(PyObject *Self,void*) { pkgCache::PkgFileIterator &File = GetCpp(Self); - return Py_BuildValue("i",File->ID); + return MkPyNumber(File->ID); } #define S(s) (s == NULL ? "" : s) @@ -1344,13 +1475,13 @@ static PyObject *DependencyGetDepTypeUntranslated(PyObject *Self,void*) static PyObject *DependencyGetDepTypeEnum(PyObject *Self,void*) { pkgCache::DepIterator &Dep = GetCpp(Self); - return Py_BuildValue("i", Dep->Type); + return MkPyNumber(Dep->Type); } static PyObject *DependencyGetID(PyObject *Self,void*) { pkgCache::DepIterator &Dep = GetCpp(Self); - return Py_BuildValue("i",Dep->ID); + return MkPyNumber(Dep->ID); } static PyGetSetDef DependencyGetSet[] = { diff --git a/python/cachegroup.cc b/python/cachegroup.cc new file mode 100644 index 00000000..4fc6c378 --- /dev/null +++ b/python/cachegroup.cc @@ -0,0 +1,188 @@ +/* + * cachegroup.cc - Wrapper around pkgCache::GrpIterator + * + * Copyright 2011 Julian Andres Klode + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301, USA. + */ +#include +#include "apt_pkgmodule.h" +#include "generic.h" +#include + +struct PyGroup : CppPyObject { + pkgCache::PkgIterator current; + int nextIndex; +}; + +static PyObject *group_new(PyTypeObject *type,PyObject *args, + PyObject *kwds) +{ + PyObject *pyCache; + char *name; + char *kwlist[] = {"cache", "name", NULL}; + if (PyArg_ParseTupleAndKeywords(args, kwds, "O!s", kwlist, + &PyCache_Type, &pyCache, + &name) == 0) + return 0; + + pkgCache *cache = GetCpp(pyCache); + + pkgCache::GrpIterator grp = cache->FindGrp(name); + + if (!grp.end()) { + return PyGroup_FromCpp(grp, true, pyCache); + } else { + PyErr_SetString(PyExc_KeyError, name); + return NULL; + } +} + +static const char group_find_package_doc[] = + "find_package(architecture: str) -> Package\n\n" + "Return a package for the given architecture, or None if none exists"; +static PyObject *group_find_package(PyObject *self,PyObject *args) +{ + pkgCache::GrpIterator grp = GetCpp(self); + PyObject *owner = GetOwner(self); + + char *architecture; + if (PyArg_ParseTuple(args, "s", &architecture) == 0) + return 0; + + pkgCache::PkgIterator pkg = grp.FindPkg(architecture); + + if (pkg.end()) { + Py_RETURN_NONE; + } else { + return PyPackage_FromCpp(pkg, true, owner ? owner : self); + } +} + +static const char group_find_preferred_package_doc[] = + "find_preferred_package(prefer_non_virtual: bool = True) -> Package\n\n" + "Return a package for the best architecture, either the native one\n" + "or the first found one. If none exists, return None. If non_virtual\n" + "is True, prefer non-virtual packages over virtual ones."; +static PyObject *group_find_preferred_package(PyObject *self,PyObject *args, + PyObject *kwds) +{ + pkgCache::GrpIterator grp = GetCpp(self); + PyObject *owner = GetOwner(self); + char nonvirtual = 1; + char *kwlist[] = {"prefer_non_virtual", NULL}; + if (PyArg_ParseTupleAndKeywords(args, kwds, "|b", kwlist, &nonvirtual) == 0) + return 0; + pkgCache::PkgIterator pkg = grp.FindPreferredPkg(nonvirtual); + + if (pkg.end()) { + Py_RETURN_NONE; + } else { + return PyPackage_FromCpp(pkg, true, owner); + } +} + +static PyMethodDef group_methods[] = { + {"find_package",group_find_package,METH_VARARGS,group_find_package_doc}, + {"find_preferred_package",(PyCFunction) group_find_preferred_package, + METH_VARARGS|METH_KEYWORDS,group_find_preferred_package_doc}, + {} +}; + +static PyObject *group_seq_item(PyObject *pySelf,Py_ssize_t index) +{ + PyGroup *self = static_cast(pySelf); + pkgCache::GrpIterator grp = GetCpp(self); + PyObject *owner = GetOwner(self); + + if (self->nextIndex > index || self->nextIndex == 0) { + self->nextIndex = 1; + new (&self->current) pkgCache::PkgIterator(grp.PackageList()); + } + + if (self->nextIndex != index + 1) { + while (self->nextIndex <= index && !self->current.end()) { + self->current = grp.NextPkg(self->current); + self->nextIndex++; + } + } + + if (self->current.end()) + return PyErr_Format(PyExc_IndexError, "Out of range: %zd", index); + + return PyPackage_FromCpp(self->current, true, owner); +} + + +static PySequenceMethods group_as_sequence = +{ + 0, + 0, // concat + 0, // repeat + group_seq_item, + 0, // slice + 0, // assign item + 0 // assign slice +}; + + +static const char group_doc[] = "Group(cache, name)\n\n" + "Group of packages with the same name.\n\n" + "Provides access to all packages sharing a name. Can be used this\n" + "like a list, or by using the special find_*() methods. If you use\n" + "it as a sequence, make sure to access it linearly, as this uses a\n" + "linked list internally."; +PyTypeObject PyGroup_Type = { + PyVarObject_HEAD_INIT(&PyType_Type, 0) + "apt_pkg.Group", // tp_name + sizeof(PyGroup), // tp_basicsize + 0, // tp_itemsize + // Methods + CppDealloc, // tp_dealloc + 0, // tp_print + 0, // tp_getattr + 0, // tp_setattr + 0, // tp_compare + 0, // tp_repr + 0, // tp_as_number + &group_as_sequence, // tp_as_sequence + 0, // tp_as_mapping + 0, // tp_hash + 0, // tp_call + 0, // tp_str + 0, // tp_getattro + 0, // tp_setattro + 0, // tp_as_buffer + Py_TPFLAGS_DEFAULT, // tp_flags + group_doc, // tp_doc + 0, // tp_traverse + 0, // tp_clear + 0, // tp_richcompare + 0, // tp_weaklistoffset + 0, // tp_iter + 0, // tp_iternext + group_methods, // tp_methods + 0, // tp_members + 0, // tp_getset + 0, // tp_base + 0, // tp_dict + 0, // tp_descr_get + 0, // tp_descr_set + 0, // tp_dictoffset + 0, // tp_init + 0, // tp_alloc + group_new, // tp_new +}; diff --git a/python/configuration.cc b/python/configuration.cc index 93e92efa..9000f71f 100644 --- a/python/configuration.cc +++ b/python/configuration.cc @@ -92,7 +92,7 @@ static PyObject *CnfFindI(PyObject *Self,PyObject *Args) int Default = 0; if (PyArg_ParseTuple(Args,"s|i",&Name,&Default) == 0) return 0; - return Py_BuildValue("i",GetSelf(Self).FindI(Name,Default)); + return MkPyNumber(GetSelf(Self).FindI(Name,Default)); } static const char *doc_FindB = @@ -438,6 +438,10 @@ PyObject *ParseCommandLine(PyObject *Self,PyObject *Args) return 0; } + if (PySequence_Length(Pargv) < 1) { + PyErr_SetString(PyExc_ValueError,"argv is an empty sequence"); + return 0; + } // Convert the option list int Length = PySequence_Length(POList); CommandLine::Args *OList = new CommandLine::Args[Length+1]; diff --git a/python/depcache.cc b/python/depcache.cc index 12c13a73..e6113429 100644 --- a/python/depcache.cc +++ b/python/depcache.cc @@ -668,22 +668,22 @@ static PyMethodDef PkgDepCacheMethods[] = #define depcache (GetCpp(Self)) static PyObject *PkgDepCacheGetKeepCount(PyObject *Self,void*) { - return Py_BuildValue("l", depcache->KeepCount()); + return MkPyNumber(depcache->KeepCount()); } static PyObject *PkgDepCacheGetInstCount(PyObject *Self,void*) { - return Py_BuildValue("l", depcache->InstCount()); + return MkPyNumber(depcache->InstCount()); } static PyObject *PkgDepCacheGetDelCount(PyObject *Self,void*) { - return Py_BuildValue("l", depcache->DelCount()); + return MkPyNumber(depcache->DelCount()); } static PyObject *PkgDepCacheGetBrokenCount(PyObject *Self,void*) { - return Py_BuildValue("l", depcache->BrokenCount()); + return MkPyNumber(depcache->BrokenCount()); } static PyObject *PkgDepCacheGetUsrSize(PyObject *Self,void*) { - return Py_BuildValue("L", depcache->UsrSize()); + return MkPyNumber(depcache->UsrSize()); } static PyObject *PkgDepCacheGetDebSize(PyObject *Self,void*) { - return Py_BuildValue("L", depcache->DebSize()); + return MkPyNumber(depcache->DebSize()); } #undef depcache diff --git a/python/generic.h b/python/generic.h index ce9e5091..f9680ca5 100644 --- a/python/generic.h +++ b/python/generic.h @@ -57,6 +57,7 @@ typedef int Py_ssize_t; #define PyString_Type PyUnicode_Type #define PyInt_Check PyLong_Check #define PyInt_AsLong PyLong_AsLong +#define PyInt_FromLong PyLong_FromLong // Force 0.7 compatibility to be off in Python 3 builds #undef COMPAT_0_7 #else @@ -231,6 +232,21 @@ PyObject *HandleErrors(PyObject *Res = 0); const char **ListToCharChar(PyObject *List,bool NullTerm = false); PyObject *CharCharToList(const char **List,unsigned long Size = 0); +/* Happy number conversion, thanks to overloading */ +inline PyObject *MkPyNumber(unsigned long long o) { return PyLong_FromUnsignedLongLong(o); } +inline PyObject *MkPyNumber(unsigned long o) { return PyLong_FromUnsignedLong(o); } +inline PyObject *MkPyNumber(unsigned int o) { return PyLong_FromUnsignedLong(o); } +inline PyObject *MkPyNumber(unsigned short o) { return PyInt_FromLong(o); } +inline PyObject *MkPyNumber(unsigned char o) { return PyInt_FromLong(o); } + +inline PyObject *MkPyNumber(long long o) { return PyLong_FromLongLong(o); } +inline PyObject *MkPyNumber(long o) { return PyInt_FromLong(o); } +inline PyObject *MkPyNumber(int o) { return PyInt_FromLong(o); } +inline PyObject *MkPyNumber(short o) { return PyInt_FromLong(o); } +inline PyObject *MkPyNumber(char o) { return PyInt_FromLong(o); } + +inline PyObject *MkPyNumber(double o) { return PyFloat_FromDouble(o); } + # ifdef COMPAT_0_7 PyObject *_PyAptObject_getattro(PyObject *self, PyObject *attr); # else diff --git a/python/indexfile.cc b/python/indexfile.cc index 037be210..bf0df516 100644 --- a/python/indexfile.cc +++ b/python/indexfile.cc @@ -47,7 +47,7 @@ static PyObject *IndexFileGetHasPackages(PyObject *Self,void*) { return PyBool_FromLong((File->HasPackages())); } static PyObject *IndexFileGetSize(PyObject *Self,void*) { - return Py_BuildValue("i",(File->Size())); + return MkPyNumber((File->Size())); } static PyObject *IndexFileGetIsTrusted(PyObject *Self,void*) { return PyBool_FromLong((File->IsTrusted())); diff --git a/python/indexrecords.cc b/python/indexrecords.cc index d6a3263c..c7623cfd 100644 --- a/python/indexrecords.cc +++ b/python/indexrecords.cc @@ -63,7 +63,7 @@ static PyObject *indexrecords_lookup(PyObject *self,PyObject *args) // Copy the HashString(), to prevent crashes and to not require the // indexRecords object to exist. PyObject *py_hash = PyHashString_FromCpp(new HashString(result->Hash), true, NULL); - PyObject *value = Py_BuildValue("(Oi)",py_hash,result->Size); + PyObject *value = Py_BuildValue("(ON)",py_hash,MkPyNumber(result->Size)); Py_DECREF(py_hash); return value; } diff --git a/python/orderlist.cc b/python/orderlist.cc new file mode 100644 index 00000000..9fbed5f2 --- /dev/null +++ b/python/orderlist.cc @@ -0,0 +1,317 @@ +/* + * orderlist.cc - Wrapper around pkgOrderList + * + * Copyright 2011 Julian Andres Klode + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301, USA. + */ + +#include +#include "apt_pkgmodule.h" +#include "generic.h" +#include + +struct PyOrderList : CppPyObject { + pkgCache::PkgIterator current; + int nextIndex; +}; + +static PyObject *order_list_new(PyTypeObject *type,PyObject *args, + PyObject *kwds) +{ + PyObject *pyDepCache = NULL; + char *kwlist[] = {"depcache", NULL}; + if (PyArg_ParseTupleAndKeywords(args, kwds, "O!", kwlist, + &PyDepCache_Type, &pyDepCache) + == 0) + return 0; + + pkgDepCache *depCache = PyDepCache_ToCpp(pyDepCache); + return PyOrderList_FromCpp(new pkgOrderList(depCache), true, + pyDepCache); +} + +static const char order_list_append_doc[] = + "append(pkg: Package)\n\n" + "Append a package to the end of the list."; +static PyObject *order_list_append(PyObject *self,PyObject *args) +{ + pkgOrderList *list = GetCpp(self); + PyObject *pyPackage = NULL; + if (PyArg_ParseTuple(args, "O!", &PyPackage_Type, &pyPackage) == 0) + return 0; + + list->push_back(PyPackage_ToCpp(pyPackage)); + Py_RETURN_NONE; +} + +static const char order_list_score_doc[] = + "score(pkg: Package) -> int\n\n" + "Return the score of the package."; +static PyObject *order_list_score(PyObject *self,PyObject *args) +{ + pkgOrderList *list = GetCpp(self); + PyObject *pyPackage = NULL; + if (PyArg_ParseTuple(args, "O!", &PyPackage_Type, &pyPackage) == 0) + return 0; + + return MkPyNumber(list->Score(PyPackage_ToCpp(pyPackage))); +} + +static const char order_list_order_critical_doc[] = + "order_critical()\n\n" + "Order by PreDepends only (critical unpack order)."; +static PyObject *order_list_order_critical(PyObject *self,PyObject *args) +{ + pkgOrderList *list = GetCpp(self); + if (PyArg_ParseTuple(args, "") == 0) + return 0; + + list->OrderCritical(); + + Py_INCREF(Py_None); + return HandleErrors(Py_None); +} + +static const char order_list_order_unpack_doc[] = + "order_unpack()\n\n" + "Order the packages for unpacking (see Debian Policy)."; +static PyObject *order_list_order_unpack(PyObject *self,PyObject *args) +{ + pkgOrderList *list = GetCpp(self); + if (PyArg_ParseTuple(args, "") == 0) + return 0; + + list->OrderUnpack(); + Py_INCREF(Py_None); + return HandleErrors(Py_None); +} + +static const char order_list_order_configure_doc[] = + "order_configure()\n\n" + "Order the packages for configuration (see Debian Policy)."; +static PyObject *order_list_order_configure(PyObject *self,PyObject *args) +{ + pkgOrderList *list = GetCpp(self); + if (PyArg_ParseTuple(args, "") == 0) + return 0; + + list->OrderConfigure(); + + Py_INCREF(Py_None); + return HandleErrors(Py_None); +} + +static bool valid_flags(unsigned int flags) { + return (flags & ~pkgOrderList::Added + & ~pkgOrderList::AddPending + & ~pkgOrderList::Immediate + & ~pkgOrderList::Loop + & ~pkgOrderList::UnPacked + & ~pkgOrderList::Configured + & ~pkgOrderList::Removed + & ~pkgOrderList::InList + & ~pkgOrderList::After + & ~pkgOrderList::States) == 0; +} + +static const char order_list_flag_doc[] = + "flag(pkg: Package, flag: int[, unset_flags: int])\n\n" + "Flag the package, set flags in 'flag' and remove flags in\n" + "'unset_flags'."; +static PyObject *order_list_flag(PyObject *self,PyObject *args) +{ + pkgOrderList *list = GetCpp(self); + + PyObject *pyPkg = NULL; + unsigned int flags = 0; + unsigned int unset_flags = 0; + if (PyArg_ParseTuple(args, "O!I|I", &PyPackage_Type, &pyPkg, + &flags, &unset_flags) == 0) + return 0; + + if (!valid_flags(flags)) + return PyErr_Format(PyExc_ValueError, "flags (%u) is" + " not a valid combination of flags.", + flags); + if (!valid_flags(unset_flags)) + return PyErr_Format(PyExc_ValueError, "unset_flags (%u) is" + " not a valid combination of flags.", + unset_flags); + + list->Flag(PyPackage_ToCpp(pyPkg), flags, unset_flags); + + Py_RETURN_NONE; +} + +static const char order_list_is_flag_doc[] = + "is_flag(pkg: Package, flag: int)\n\n" + "Check if the flag(s) are set."; +static PyObject *order_list_is_flag(PyObject *self,PyObject *args) +{ + pkgOrderList *list = GetCpp(self); + PyObject *pyPkg = NULL; + unsigned int flags = 0; + if (PyArg_ParseTuple(args, "O!I", &PyPackage_Type, &pyPkg, + &flags) == 0) + return 0; + + if (!valid_flags(flags)) + return PyErr_Format(PyExc_ValueError, "flags (%u) is" + " not a valid combination of flags.", + flags); + + return PyBool_FromLong(list->IsFlag(PyPackage_ToCpp(pyPkg), flags)); +} + +static const char order_list_wipe_flags_doc[] = + "wipe_flags(flags: int)\n\n" + "Remove the flags in 'flags' from all packages in this list"; +static PyObject *order_list_wipe_flags(PyObject *self,PyObject *args) +{ + pkgOrderList *list = GetCpp(self); + unsigned int flags = 0; + if (PyArg_ParseTuple(args, "I", &flags) == 0) + return 0; + + if (!valid_flags(flags)) + return PyErr_Format(PyExc_ValueError, "flags (%u) is" + " not a valid combination of flags.", + flags); + + list->WipeFlags(flags); + Py_RETURN_NONE; +} + +static const char order_list_is_now_doc[] = + "is_now(pkg: Package)\n\n" + "Check if the package is flagged for any state but removal."; +static PyObject *order_list_is_now(PyObject *self,PyObject *args) +{ + pkgOrderList *list = GetCpp(self); + PyObject *pyPkg = NULL; + if (PyArg_ParseTuple(args, "O!", &PyPackage_Type, &pyPkg) == 0) + return 0; + + return PyBool_FromLong(list->IsNow(PyPackage_ToCpp(pyPkg))); +} + +static const char order_list_is_missing_doc[] = + "is_now(pkg: Package)\n\n" + "Check if the package is marked for install."; +static PyObject *order_list_is_missing(PyObject *self,PyObject *args) +{ + pkgOrderList *list = GetCpp(self); + PyObject *pyPkg = NULL; + if (PyArg_ParseTuple(args, "O!", &PyPackage_Type, &pyPkg) == 0) + return 0; + + return PyBool_FromLong(list->IsMissing(PyPackage_ToCpp(pyPkg))); +} + + +#define METHOD(name) {#name, order_list_##name, METH_VARARGS,\ + order_list_##name##_doc} + +static PyMethodDef order_list_methods[] = { + METHOD(append), + METHOD(score), + METHOD(order_critical), + METHOD(order_unpack), + METHOD(order_configure), + METHOD(flag), + METHOD(is_flag), + METHOD(is_now), + METHOD(is_missing), + METHOD(wipe_flags), + {} +}; + +static PyObject *order_list_seq_item(PyObject *self,Py_ssize_t index) +{ + pkgOrderList *list = GetCpp(self); + PyObject *owner = GetOwner(self); + PyObject *pycache = GetOwner(owner); + pkgCache *cache = PyCache_ToCpp(pycache); + + if (index < 0 || index >= list->size()) + return PyErr_Format(PyExc_IndexError, "Out of range: %zd", index); + + return PyPackage_FromCpp(pkgCache::PkgIterator(*cache, + *(list->begin() + index)), + true, owner); +} + +Py_ssize_t order_list_seq_length(PyObject *self) +{ + return GetCpp(self)->size(); +} + +static PySequenceMethods order_list_as_sequence = +{ + order_list_seq_length, // sq_length + 0, // sq_concat + 0, // sq_repeat + order_list_seq_item, // sq_item + 0, // sq_ass_item + 0, // sq_contains + 0, // sq_inplace_concat + 0 // sq_inplace_repeat +}; + +static const char order_list_doc[] = "OrderList(depcache: DepCache)\n\n" + "Sequence type for packages with special ordering methods."; +PyTypeObject PyOrderList_Type = { + PyVarObject_HEAD_INIT(&PyType_Type, 0) + "apt_pkg.OrderList", // tp_name + sizeof(CppPyObject), // tp_basicsize + 0, // tp_itemsize + // Methods + CppDeallocPtr, // tp_dealloc + 0, // tp_print + 0, // tp_getattr + 0, // tp_setattr + 0, // tp_compare + 0, // tp_repr + 0, // tp_as_number + &order_list_as_sequence, // tp_as_sequence + 0, // tp_as_mapping + 0, // tp_hash + 0, // tp_call + 0, // tp_str + 0, // tp_getattro + 0, // tp_setattro + 0, // tp_as_buffer + Py_TPFLAGS_DEFAULT, // tp_flags + order_list_doc, // tp_doc + 0, // tp_traverse + 0, // tp_clear + 0, // tp_richcompare + 0, // tp_weaklistoffset + 0, // tp_iter + 0, // tp_iternext + order_list_methods, // tp_methods + 0, // tp_members + 0, // tp_getset + 0, // tp_base + 0, // tp_dict + 0, // tp_descr_get + 0, // tp_descr_set + 0, // tp_dictoffset + 0, // tp_init + 0, // tp_alloc + order_list_new, // tp_new +}; diff --git a/python/pkgmanager.cc b/python/pkgmanager.cc index 95e8c27e..b7bed658 100644 --- a/python/pkgmanager.cc +++ b/python/pkgmanager.cc @@ -17,36 +17,10 @@ #include #include #include +#include #include -static PyObject *PkgManagerNew(PyTypeObject *type,PyObject *Args,PyObject *kwds) -{ - PyObject *Owner; - char *kwlist[] = {"depcache",0}; - if (PyArg_ParseTupleAndKeywords(Args,kwds,"O!",kwlist,&PyDepCache_Type, - &Owner) == 0) - return 0; - - pkgPackageManager *pm = _system->CreatePM(GetCpp(Owner)); - - CppPyObject *PkgManagerObj = - CppPyObject_NEW(NULL, type,pm); - - return PkgManagerObj; -} - -#ifdef COMPAT_0_7 -PyObject *GetPkgManager(PyObject *Self,PyObject *Args) -{ - PyErr_WarnEx(PyExc_DeprecationWarning, "apt_pkg.GetPackageManager() is " - "deprecated. Please see apt_pkg.PackageManager() for the " - "replacement.", 1); - return PkgManagerNew(&PyPackageManager_Type,Args,0); -} -#endif - - static PyObject *PkgManagerGetArchives(PyObject *Self,PyObject *Args) { pkgPackageManager *pm = GetCpp(Self); @@ -79,7 +53,7 @@ static PyObject *PkgManagerDoInstall(PyObject *Self,PyObject *Args) pkgPackageManager::OrderResult res = pm->DoInstall(status_fd); - return HandleErrors(Py_BuildValue("i",res)); + return HandleErrors(MkPyNumber(res)); } static PyObject *PkgManagerFixMissing(PyObject *Self,PyObject *Args) @@ -114,17 +88,16 @@ static PyMethodDef PkgManagerMethods[] = {} }; - static const char *packagemanager_doc = - "PackageManager(depcache: apt_pkg.DepCache)\n\n" - "PackageManager objects allow the fetching of packages marked for\n" - "installation and the installation of those packages. The parameter\n" - "'depcache' specifies an apt_pkg.DepCache object where information\n" - "about the package selections is retrieved from."; + "_PackageManager objects allow the fetching of packages marked for\n" + "installation and the installation of those packages.\n" + "This is an abstract base class that cannot be subclassed\n" + "in Python. The only subclass is apt_pkg.PackageManager. This\n" + "class is an implementation-detail and not part of the API."; PyTypeObject PyPackageManager_Type = { PyVarObject_HEAD_INIT(&PyType_Type, 0) - "apt_pkg.PackageManager", // tp_name + "apt_pkg._PackageManager", // tp_name sizeof(CppPyObject), // tp_basicsize 0, // tp_itemsize // Methods @@ -143,8 +116,7 @@ PyTypeObject PyPackageManager_Type = _PyAptObject_getattro, // tp_getattro 0, // tp_setattro 0, // tp_as_buffer - (Py_TPFLAGS_DEFAULT | // tp_flags - Py_TPFLAGS_BASETYPE), + Py_TPFLAGS_DEFAULT, // tp_flag, packagemanager_doc, // tp_doc 0, // tp_traverse 0, // tp_clear @@ -162,9 +134,240 @@ PyTypeObject PyPackageManager_Type = 0, // tp_dictoffset 0, // tp_init 0, // tp_alloc - PkgManagerNew, // tp_new + 0, // tp_new +}; + + +struct CppPyRef { + PyObject *o; + CppPyRef(const CppPyRef &o) { Py_XINCREF(o); this->o = o; } + CppPyRef(PyObject *o) : o(o) {} + ~CppPyRef() { Py_XDECREF(o); } + operator PyObject *() const { return o; } + PyObject *operator->() const { return o; } }; +class PyPkgManager : public pkgDPkgPM { + bool res(CppPyRef result) { + if (result == NULL) { + std::cerr << "Error in function: " << std::endl; + PyErr_Print(); + PyErr_Clear(); + return false; + } + return (result != NULL && + (result == Py_None || PyObject_IsTrue(result) == 1)); + } + + + PyObject *GetPyPkg(const PkgIterator &Pkg) { + PyObject *depcache = NULL; + PyObject *cache = NULL; + + depcache = GetOwner(pyinst); + if (depcache != NULL && PyDepCache_Check(depcache)) + cache = GetOwner(depcache); + + return PyPackage_FromCpp(Pkg, true, cache); + } + + /* Call through to Python */ + virtual bool Install(PkgIterator Pkg,string File) { + return res(PyObject_CallMethod(pyinst, "install", "(NN)", + GetPyPkg(Pkg), + CppPyString(File))); + } + virtual bool Configure(PkgIterator Pkg) { + return res(PyObject_CallMethod(pyinst, "configure", "(N)", + GetPyPkg(Pkg))); + } + virtual bool Remove(PkgIterator Pkg,bool Purge = false) { + return res(PyObject_CallMethod(pyinst, "remove", "(NN)", + GetPyPkg(Pkg), + PyBool_FromLong(Purge))); + } + virtual bool Go(int StatusFd=-1) { + return res(PyObject_CallMethod(pyinst, "go", "(i)", + StatusFd)); + } + virtual void Reset() { + CppPyRef(PyObject_CallMethod(pyinst, "reset", NULL)); + } + +public: + /* Those call the protected functions from the parent class */ + bool callInstall(PkgIterator Pkg,string File) { return pkgDPkgPM::Install(Pkg, File); } + bool callRemove(PkgIterator Pkg, bool Purge) { return pkgDPkgPM::Remove(Pkg, Purge); } + bool callGo(int StatusFd=-1) { return pkgDPkgPM::Go(StatusFd); } + void callReset() { return pkgDPkgPM::Reset(); } + bool callConfigure(PkgIterator Pkg) { return pkgDPkgPM::Configure(Pkg); } + pkgOrderList *getOrderList() { return pkgPackageManager::List; } + + PyPkgManager(pkgDepCache *Cache) : pkgDPkgPM(Cache) {}; + PyObject *pyinst; +}; + +static PyObject *PkgManagerNew(PyTypeObject *type,PyObject *Args,PyObject *kwds) +{ + PyObject *Owner; + char *kwlist[] = {"depcache",0}; + if (PyArg_ParseTupleAndKeywords(Args,kwds,"O!",kwlist,&PyDepCache_Type, + &Owner) == 0) + return 0; + + PyPkgManager *pm = new PyPkgManager(GetCpp(Owner)); + + CppPyObject *PkgManagerObj = + CppPyObject_NEW(NULL, type,pm); + + pm->pyinst = PkgManagerObj; + + return PkgManagerObj; +} + +#ifdef COMPAT_0_7 +PyObject *GetPkgManager(PyObject *Self,PyObject *Args) +{ + PyErr_WarnEx(PyExc_DeprecationWarning, "apt_pkg.GetPackageManager() is " + "deprecated. Please see apt_pkg.PackageManager() for the " + "replacement.", 1); + return PkgManagerNew(&PyPackageManager2_Type,Args,0); +} +#endif + +static PyObject *PkgManagerInstall(PyObject *Self,PyObject *Args) +{ + PyPkgManager *pm = GetCpp(Self); + PyObject *pkg; + const char *file; + + if (PyArg_ParseTuple(Args, "O!s", &PyPackage_Type,&pkg, &file) == 0) + return 0; + + return HandleErrors(PyBool_FromLong(pm->callInstall(PyPackage_ToCpp(pkg), file))); +} + + +static PyObject *PkgManagerConfigure(PyObject *Self,PyObject *Args) +{ + PyPkgManager *pm = GetCpp(Self); + PyObject *pkg; + + if (PyArg_ParseTuple(Args, "O!", &PyPackage_Type,&pkg) == 0) + return 0; + + return HandleErrors(PyBool_FromLong(pm->callConfigure(PyPackage_ToCpp(pkg)))); +} + +static PyObject *PkgManagerRemove(PyObject *Self,PyObject *Args) +{ + PyPkgManager *pm = GetCpp(Self); + PyObject *pkg; + char purge; + + if (PyArg_ParseTuple(Args, "O!b", &PyPackage_Type,&pkg, &purge) == 0) + return 0; + + return HandleErrors(PyBool_FromLong(pm->callRemove(PyPackage_ToCpp(pkg), purge))); +} + +static PyObject *PkgManagerGo(PyObject *Self,PyObject *Args) +{ + PyPkgManager *pm = GetCpp(Self); + int fd; + + if (PyArg_ParseTuple(Args, "i", &fd) == 0) + return 0; + + return HandleErrors(PyBool_FromLong(pm->callGo(fd))); +} + +static PyObject *PkgManagerReset(PyObject *Self,PyObject *Args) +{ + PyPkgManager *pm = GetCpp(Self); + + pm->callReset(); + Py_INCREF(Py_None); + return HandleErrors(Py_None); +} + +static PyMethodDef PkgManager2Methods[] = +{ + {"install",PkgManagerInstall,METH_VARARGS, + "install(pkg: Package, filename: str) -> bool \n\n" + "Add a install action. Can be overriden in subclasses.\n\n" + "New in version 0.8.0."}, + {"configure",PkgManagerConfigure,METH_VARARGS, + "configure(pkg: Package) -> bool \n\n" + "Add a configure action. Can be overriden in subclasses.\n\n" + "New in version 0.8.0."}, + {"remove",PkgManagerRemove,METH_VARARGS, + "remove(pkg: Package, purge: bool) -> bool \n\n" + "Add a removal action. Can be overriden in subclasses.\n\n" + "New in version 0.8.0."}, + {"go",PkgManagerGo,METH_VARARGS, + "go(status_fd: int) -> bool \n\n" + "Start dpkg. Can be overriden in subclasses.\n\n" + "New in version 0.8.0."}, + {"reset",PkgManagerReset,METH_VARARGS, + "reset()\n\n" + "Reset the package manager for a new round.\n" + "Can be overriden in subclasses.\n\n" + "New in version 0.8.0."}, + {} +}; + +static const char *packagemanager2_doc = + "PackageManager(depcache: apt_pkg.DepCache)\n\n" + "PackageManager objects allow the fetching of packages marked for\n" + "installation and the installation of those packages. The parameter\n" + "'depcache' specifies an apt_pkg.DepCache object where information\n" + "about the package selections is retrieved from.\n\n" + "Methods in this class can be overriden in sub classes\n" + "to implement behavior different from APT's dpkg implementation."; +PyTypeObject PyPackageManager2_Type = +{ + PyVarObject_HEAD_INIT(&PyType_Type, 0) + "apt_pkg.PackageManager", // tp_name + sizeof(CppPyObject), // tp_basicsize + 0, // tp_itemsize + // Methods + CppDeallocPtr, // tp_dealloc + 0, // tp_print + 0, // tp_getattr + 0, // tp_setattr + 0, // tp_compare + 0, // tp_repr + 0, // tp_as_number + 0, // tp_as_sequence + 0, // tp_as_mapping + 0, // tp_hash + 0, // tp_call + 0, // tp_str + 0, // tp_getattro + 0, // tp_setattro + 0, // tp_as_buffer + (Py_TPFLAGS_DEFAULT | // tp_flags + Py_TPFLAGS_BASETYPE), + packagemanager2_doc, // tp_doc + 0, // tp_traverse + 0, // tp_clear + 0, // tp_richcompare + 0, // tp_weaklistoffset + 0, // tp_iter + 0, // tp_iternext + PkgManager2Methods, // tp_methods + 0, // tp_members + 0, // tp_getset + &PyPackageManager_Type, // tp_base + 0, // tp_dict + 0, // tp_descr_get + 0, // tp_descr_set + 0, // tp_dictoffset + 0, // tp_init + 0, // tp_alloc + PkgManagerNew, // tp_new +}; /*}}}*/ diff --git a/python/pkgsrcrecords.cc b/python/pkgsrcrecords.cc index aad3ef7e..4c889129 100644 --- a/python/pkgsrcrecords.cc +++ b/python/pkgsrcrecords.cc @@ -147,9 +147,9 @@ static PyObject *PkgSrcRecordsGetFiles(PyObject *Self,void*) { PyObject *v; for(unsigned int i=0;i(self); if (PyObject_TypeCheck(arg, &PyPackage_Type)) { pkgCache::PkgIterator pkg = GetCpp(arg); - return Py_BuildValue("i", policy->GetPriority(pkg)); + return MkPyNumber(policy->GetPriority(pkg)); } else { PyErr_SetString(PyExc_TypeError,"Argument must be of Package()."); return 0; diff --git a/python/progress.cc b/python/progress.cc index 5700a1b6..bd3c2ad6 100644 --- a/python/progress.cc +++ b/python/progress.cc @@ -92,12 +92,12 @@ void PyOpProgress::Update() setattr(callbackInst, "op", "s", Op.c_str()); setattr(callbackInst, "subop", "s", SubOp.c_str()); setattr(callbackInst, "major_change", "b", MajorChange); - setattr(callbackInst, "percent", "f", Percent); + setattr(callbackInst, "percent", "N", MkPyNumber(Percent)); #ifdef COMPAT_0_7 setattr(callbackInst, "Op", "s", Op.c_str()); setattr(callbackInst, "subOp", "s", SubOp.c_str()); setattr(callbackInst, "majorChange", "b", MajorChange); - PyObject *arglist = Py_BuildValue("(f)", Percent); + PyObject *arglist = Py_BuildValue("(N)", MkPyNumber(Percent)); RunSimpleCallback("update", arglist); #else RunSimpleCallback("update"); @@ -156,19 +156,19 @@ void PyFetchProgress::UpdateStatus(pkgAcquire::ItemDesc &Itm, int status) // Added object file size and object partial size to // parameters that are passed to updateStatus. // -- Stephan - PyObject *arglist = Py_BuildValue("(sssikk)", Itm.URI.c_str(), + PyObject *arglist = Py_BuildValue("(sssNNN)", Itm.URI.c_str(), Itm.Description.c_str(), Itm.ShortDesc.c_str(), - status, - Itm.Owner->FileSize, - Itm.Owner->PartialSize); + MkPyNumber(status), + MkPyNumber(Itm.Owner->FileSize), + MkPyNumber(Itm.Owner->PartialSize)); RunSimpleCallback("update_status_full", arglist); // legacy version of the interface - arglist = Py_BuildValue("(sssi)", Itm.URI.c_str(), Itm.Description.c_str(), - Itm.ShortDesc.c_str(), status); + arglist = Py_BuildValue("(sssN)", Itm.URI.c_str(), Itm.Description.c_str(), + Itm.ShortDesc.c_str(), MkPyNumber(status)); if(PyObject_HasAttrString(callbackInst, "updateStatus")) RunSimpleCallback("updateStatus", arglist); @@ -240,11 +240,11 @@ void PyFetchProgress::Start() pkgAcquireStatus::Start(); #ifdef COMPAT_0_7 - setattr(callbackInst, "currentCPS", "d", 0); - setattr(callbackInst, "currentBytes", "d", 0); - setattr(callbackInst, "currentItems", "k", 0); - setattr(callbackInst, "totalItems", "k", 0); - setattr(callbackInst, "totalBytes", "d", 0); + setattr(callbackInst, "currentCPS", "N", MkPyNumber(0)); + setattr(callbackInst, "currentBytes", "N", MkPyNumber(0)); + setattr(callbackInst, "currentItems", "N", MkPyNumber(0)); + setattr(callbackInst, "totalItems", "N", MkPyNumber(0)); + setattr(callbackInst, "totalBytes", "N", MkPyNumber(0)); #endif RunSimpleCallback("start"); @@ -280,14 +280,14 @@ bool PyFetchProgress::Pulse(pkgAcquire * Owner) return false; } - setattr(callbackInst, "last_bytes", "d", LastBytes); - setattr(callbackInst, "current_cps", "d", CurrentCPS); - setattr(callbackInst, "current_bytes", "d", CurrentBytes); - setattr(callbackInst, "total_bytes", "d", TotalBytes); - setattr(callbackInst, "fetched_bytes", "d", FetchedBytes); - setattr(callbackInst, "elapsed_time", "k", ElapsedTime); - setattr(callbackInst, "current_items", "k", CurrentItems); - setattr(callbackInst, "total_items", "k", TotalItems); + setattr(callbackInst, "last_bytes", "N", MkPyNumber(LastBytes)); + setattr(callbackInst, "current_cps", "N", MkPyNumber(CurrentCPS)); + setattr(callbackInst, "current_bytes", "N", MkPyNumber(CurrentBytes)); + setattr(callbackInst, "total_bytes", "N", MkPyNumber(TotalBytes)); + setattr(callbackInst, "fetched_bytes", "N", MkPyNumber(FetchedBytes)); + setattr(callbackInst, "elapsed_time", "N", MkPyNumber(ElapsedTime)); + setattr(callbackInst, "current_items", "N", MkPyNumber(CurrentItems)); + setattr(callbackInst, "total_items", "N", MkPyNumber(TotalItems)); // New style if (!PyObject_HasAttrString(callbackInst, "updateStatus")) { @@ -313,12 +313,12 @@ bool PyFetchProgress::Pulse(pkgAcquire * Owner) return true; } #ifdef COMPAT_0_7 - setattr(callbackInst, "currentCPS", "d", CurrentCPS); - setattr(callbackInst, "currentBytes", "d", CurrentBytes); - setattr(callbackInst, "totalBytes", "d", TotalBytes); - setattr(callbackInst, "fetchedBytes", "d", FetchedBytes); - setattr(callbackInst, "currentItems", "k", CurrentItems); - setattr(callbackInst, "totalItems", "k", TotalItems); + setattr(callbackInst, "currentCPS", "N", MkPyNumber(CurrentCPS)); + setattr(callbackInst, "currentBytes", "N", MkPyNumber(CurrentBytes)); + setattr(callbackInst, "totalBytes", "N", MkPyNumber(TotalBytes)); + setattr(callbackInst, "fetchedBytes", "N", MkPyNumber(FetchedBytes)); + setattr(callbackInst, "currentItems", "N", MkPyNumber(CurrentItems)); + setattr(callbackInst, "totalItems", "N", MkPyNumber(TotalItems)); // Go through the list of items and add active items to the // activeItems vector. map activeItemMap; @@ -351,11 +351,11 @@ bool PyFetchProgress::Pulse(pkgAcquire * Owner) pkgAcquire::Worker *worker = iter->first; pkgAcquire::ItemDesc *itm = iter->second; - PyObject *itmTuple = Py_BuildValue("(ssskk)", itm->URI.c_str(), + PyObject *itmTuple = Py_BuildValue("(sssNN)", itm->URI.c_str(), itm->Description.c_str(), itm->ShortDesc.c_str(), - worker->TotalSize, - worker->CurrentSize); + MkPyNumber(worker->TotalSize), + MkPyNumber(worker->CurrentSize)); PyTuple_SetItem(itemsTuple, tuplePos, itmTuple); } diff --git a/python/python-apt-helpers.cc b/python/python-apt-helpers.cc index 7a0f20c4..079b93cf 100644 --- a/python/python-apt-helpers.cc +++ b/python/python-apt-helpers.cc @@ -52,7 +52,9 @@ NEW_FROM(PyHashString_FromCpp,&PyHashString_Type,HashString*) NEW_FROM(PyIndexRecords_FromCpp,&PyIndexRecords_Type,indexRecords*) NEW_FROM(PyMetaIndex_FromCpp,&PyMetaIndex_Type,metaIndex*) NEW_FROM(PyPackage_FromCpp,&PyPackage_Type,pkgCache::PkgIterator) +NEW_FROM(PyGroup_FromCpp,&PyGroup_Type,pkgCache::GrpIterator) NEW_FROM(PyIndexFile_FromCpp,&PyIndexFile_Type,pkgIndexFile*) +NEW_FROM(PyOrderList_FromCpp,&PyOrderList_Type,pkgOrderList*) NEW_FROM(PyPackageFile_FromCpp,&PyPackageFile_Type,pkgCache::PkgFileIterator) //NEW_FROM(PyPackageList_FromCpp,&PyPackageList_Type,PkgListStruct) NEW_FROM(PyPackageManager_FromCpp,&PyPackageManager_Type,pkgPackageManager*) diff --git a/python/python-apt.h b/python/python-apt.h index b9fc9212..6f2a02df 100644 --- a/python/python-apt.h +++ b/python/python-apt.h @@ -167,6 +167,13 @@ struct _PyAptPkgAPIStruct { PyObject* (*version_fromcpp)(pkgCache::VerIterator const &obj, bool Delete, PyObject *Owner); pkgCache::VerIterator& (*version_tocpp)(PyObject *self); + PyTypeObject *group_type; + PyObject* (*group_fromcpp)(pkgCache::GrpIterator const &obj, bool Delete, PyObject *Owner); + pkgCache::GrpIterator& (*group_tocpp)(PyObject *self); + + PyTypeObject *orderlist_type; + PyObject* (*orderlist_fromcpp)(pkgOrderList* const &obj, bool Delete, PyObject *Owner); + pkgOrderList*& (*orderlist_tocpp)(PyObject *self); }; // Checking macros. @@ -184,6 +191,7 @@ struct _PyAptPkgAPIStruct { # define PyDependency_Check(op) PyObject_TypeCheck(op, &PyDependency_Type) # define PyDependencyList_Check(op) PyObject_TypeCheck(op, &PyDependencyList_Type) # define PyDescription_Check(op) PyObject_TypeCheck(op, &PyDescription_Type) +# define PyGroup_Check(op) PyObject_TypeCheck(op, &PyGroup_Type) # define PyHashes_Check(op) PyObject_TypeCheck(op, &PyHashes_Type) # define PyHashString_Check(op) PyObject_TypeCheck(op, &PyHashString_Type) # define PyIndexRecords_Check(op) PyObject_TypeCheck(op, &PyIndexRecords_Type) @@ -217,12 +225,14 @@ struct _PyAptPkgAPIStruct { # define PyDependencyList_CheckExact(op) (op->op_type == &PyDependencyList_Type) # define PyDescription_CheckExact(op) (op->op_type == &PyDescription_Type) # define PyHashes_CheckExact(op) (op->op_type == &PyHashes_Type) +# define PyGroup_CheckExact(op) (op->op_type == &PyGroup_Type) # define PyHashString_CheckExact(op) (op->op_type == &PyHashString_Type) # define PyIndexRecords_CheckExact(op) (op->op_type == &PyIndexRecords_Type) # define PyMetaIndex_CheckExact(op) (op->op_type == &PyMetaIndex_Type) # define PyPackage_CheckExact(op) (op->op_type == &PyPackage_Type) # define PyPackageFile_CheckExact(op) (op->op_type == &PyPackageFile_Type) # define PyIndexFile_CheckExact(op) (op->op_type == &PyIndexFile_Type) +# define PyOrderList_CheckExact(op) (op->op_type == &PyOrderList_Type) # define PyPackageList_CheckExact(op) (op->op_type == &PyPackageList_Type) # define PyPackageManager_CheckExact(op) (op->op_type == &PyPackageManager_Type) # define PyPackageRecords_CheckExact(op) (op->op_type == &PyPackageRecords_Type) @@ -260,6 +270,7 @@ static int import_apt_pkg(void) { # define PyDependency_Type *(_PyAptPkg_API->dependency_type) # define PyDependencyList_Type *(_PyAptPkg_API->dependencylist_type) # define PyDescription_Type *(_PyAptPkg_API->description_type) +# define PyGroup_Type *(_PyAptPkg_API->group_type) # define PyHashes_Type *(_PyAptPkg_API->hashes_type) # define PyHashString_Type *(_PyAptPkg_API->hashstring_type) # define PyIndexRecords_Type *(_PyAptPkg_API->indexrecords_type) @@ -267,6 +278,7 @@ static int import_apt_pkg(void) { # define PyPackage_Type *(_PyAptPkg_API->package_type) # define PyPackageFile_Type *(_PyAptPkg_API->packagefile_type) # define PyIndexFile_Type *(_PyAptPkg_API->packageindexfile_type) +# define PyOrderList_Type *(_PyAptPkg_API->orderlist_type) # define PyPackageList_Type *(_PyAptPkg_API->packagelist_type) # define PyPackageManager_Type *(_PyAptPkg_API->packagemanager_type) # define PyPackageRecords_Type *(_PyAptPkg_API->packagerecords_type) @@ -292,6 +304,7 @@ static int import_apt_pkg(void) { # define PyDependency_ToCpp _PyAptPkg_API->dependency_tocpp # define PyDependencyList_ToCpp _PyAptPkg_API->dependencylist_tocpp // NULL # define PyDescription_ToCpp _PyAptPkg_API->description_tocpp +# define PyGroup_ToCpp _PyAptPkg_API->group_tocpp # define PyHashes_ToCpp _PyAptPkg_API->hashes_tocpp # define PyHashString_ToCpp _PyAptPkg_API->hashstring_tocpp # define PyIndexRecords_ToCpp _PyAptPkg_API->indexrecords_tocpp @@ -299,6 +312,7 @@ static int import_apt_pkg(void) { # define PyPackage_ToCpp _PyAptPkg_API->package_tocpp # define PyPackageFile_ToCpp _PyAptPkg_API->packagefile_tocpp # define PyIndexFile_ToCpp _PyAptPkg_API->packageindexfile_tocpp +# define PyOrderList_ToCpp _PyAptPkg_API->orderlist_tocpp // NULL # define PyPackageList_ToCpp _PyAptPkg_API->packagelist_tocpp // NULL # define PyPackageManager_ToCpp _PyAptPkg_API->packagemanager_tocpp # define PyPackageRecords_ToCpp _PyAptPkg_API->packagerecords_tocpp @@ -324,6 +338,7 @@ static int import_apt_pkg(void) { # define PyDependency_FromCpp _PyAptPkg_API->dependency_fromcpp # define PyDependencyList_FromCpp _PyAptPkg_API->dependencylist_fromcpp // NULL # define PyDescription_FromCpp _PyAptPkg_API->description_fromcpp +# define PyGroup_FromCpp _PyAptPkg_API->group_fromcpp # define PyHashes_FromCpp _PyAptPkg_API->hashes_fromcpp # define PyHashString_FromCpp _PyAptPkg_API->hashstring_fromcpp # define PyIndexRecords_FromCpp _PyAptPkg_API->indexrecords_fromcpp @@ -331,6 +346,7 @@ static int import_apt_pkg(void) { # define PyPackage_FromCpp _PyAptPkg_API->package_fromcpp # define PyPackageFile_FromCpp _PyAptPkg_API->packagefile_fromcpp # define PyIndexFile_FromCpp _PyAptPkg_API->packageindexfile_fromcpp +# define PyOrderList_FromCpp _PyAptPkg_API->orderlist_fromcpp // NULL # define PyPackageList_FromCpp _PyAptPkg_API->packagelist_fromcpp // NULL # define PyPackageManager_FromCpp _PyAptPkg_API->packagemanager_fromcpp # define PyPackageRecords_FromCpp _PyAptPkg_API->packagerecords_fromcpp diff --git a/python/string.cc b/python/string.cc index 6a1ce4e2..7abe2d17 100644 --- a/python/string.cc +++ b/python/string.cc @@ -28,11 +28,11 @@ PyObject *Python(PyObject *Self,PyObject *Args) \ return CppPyString(CFunc(Str)); \ } -#define MkInt(Python,CFunc) \ +#define MkInt(Python,CFunc, ctype, pytype) \ PyObject *Python(PyObject *Self,PyObject *Args) \ { \ - int Val = 0; \ - if (PyArg_ParseTuple(Args,"i",&Val) == 0) \ + ctype Val = 0; \ + if (PyArg_ParseTuple(Args,pytype,&Val) == 0) \ return 0; \ return CppPyString(CFunc(Val)); \ } @@ -56,8 +56,8 @@ PyObject *StrBase64Encode(PyObject *Self,PyObject *Args) { MkStr(StrURItoFileName,URItoFileName); //MkFloat(StrSizeToStr,SizeToStr); -MkInt(StrTimeToStr,TimeToStr); -MkInt(StrTimeRFC1123,TimeRFC1123); +MkInt(StrTimeToStr,TimeToStr, unsigned long, "k"); +MkInt(StrTimeRFC1123,TimeRFC1123, long long, "L"); /*}}}*/ // Other String functions /*{{{*/ @@ -91,7 +91,7 @@ PyObject *StrStringToBool(PyObject *Self,PyObject *Args) char *Str = 0; if (PyArg_ParseTuple(Args,"s",&Str) == 0) return 0; - return Py_BuildValue("i",StringToBool(Str)); + return MkPyNumber(StringToBool(Str)); } PyObject *StrStrToTime(PyObject *Self,PyObject *Args) @@ -107,7 +107,7 @@ PyObject *StrStrToTime(PyObject *Self,PyObject *Args) return Py_None; } - return Py_BuildValue("i",Result); + return MkPyNumber(Result); } PyObject *StrCheckDomainList(PyObject *Self,PyObject *Args) diff --git a/python/tag.cc b/python/tag.cc index 44cd06af..94554400 100644 --- a/python/tag.cc +++ b/python/tag.cc @@ -247,7 +247,7 @@ static PyObject *TagSecBytes(PyObject *Self,PyObject *Args) if (PyArg_ParseTuple(Args,"") == 0) return 0; - return Py_BuildValue("i",GetCpp(Self).size()); + return MkPyNumber(GetCpp(Self).size()); } static PyObject *TagSecStr(PyObject *Self) @@ -319,7 +319,8 @@ static PyObject *TagFileOffset(PyObject *Self,PyObject *Args) { if (PyArg_ParseTuple(Args,"") == 0) return 0; - return Py_BuildValue("i",((TagFileData *)Self)->Object.Offset()); + return MkPyNumber(((TagFileData *)Self)->Object.Offset()); + } static char *doc_Jump = diff --git a/python/tarfile.cc b/python/tarfile.cc index 215d3a8c..cdfe0a7c 100644 --- a/python/tarfile.cc +++ b/python/tarfile.cc @@ -189,35 +189,35 @@ static PyObject *tarmember_get_linkname(PyObject *self, void *closure) static PyObject *tarmember_get_mode(PyObject *self, void *closure) { - return Py_BuildValue("k", GetCpp(self).Mode); + return MkPyNumber(GetCpp(self).Mode); } static PyObject *tarmember_get_uid(PyObject *self, void *closure) { - return Py_BuildValue("k", GetCpp(self).UID); + return MkPyNumber(GetCpp(self).UID); } static PyObject *tarmember_get_gid(PyObject *self, void *closure) { - return Py_BuildValue("k", GetCpp(self).GID); + return MkPyNumber(GetCpp(self).GID); } static PyObject *tarmember_get_size(PyObject *self, void *closure) { - return Py_BuildValue("k", GetCpp(self).Size); + return MkPyNumber(GetCpp(self).Size); } static PyObject *tarmember_get_mtime(PyObject *self, void *closure) { - return Py_BuildValue("k", GetCpp(self).MTime); + return MkPyNumber(GetCpp(self).MTime); } static PyObject *tarmember_get_major(PyObject *self, void *closure) { - return Py_BuildValue("k", GetCpp(self).Major); + return MkPyNumber(GetCpp(self).Major); } static PyObject *tarmember_get_minor(PyObject *self, void *closure) { - return Py_BuildValue("k", GetCpp(self).Minor); + return MkPyNumber(GetCpp(self).Minor); } static PyObject *tarmember_repr(PyObject *self) diff --git a/setup.py b/setup.py index 9c6eda60..eff78379 100644 --- a/setup.py +++ b/setup.py @@ -33,7 +33,8 @@ files = ['apt_pkgmodule.cc', 'acquire.cc', 'cache.cc', 'cdrom.cc', 'hashstring.cc', 'indexfile.cc', 'indexrecords.cc', 'metaindex.cc', 'pkgmanager.cc', 'pkgrecords.cc', 'pkgsrcrecords.cc', 'policy.cc', 'progress.cc', 'sourcelist.cc', 'string.cc', 'tag.cc', - 'lock.cc', 'acquire-item.cc', 'python-apt-helpers.cc'] + 'lock.cc', 'acquire-item.cc', 'python-apt-helpers.cc', + 'cachegroup.cc', 'orderlist.cc'] files = sorted(['python/' + fname for fname in files], key=lambda s: s[:-3]) apt_pkg = Extension("apt_pkg", files, libraries=["apt-pkg"]) diff --git a/tests/test_apt_cache.py b/tests/test_apt_cache.py index cccfc9c8..aaa9f601 100644 --- a/tests/test_apt_cache.py +++ b/tests/test_apt_cache.py @@ -48,9 +48,9 @@ class TestAptCache(unittest.TestCase): # tons of seek operations r = pkg.candidate.record self.assertEqual(r['Package'], pkg.shortname) - self.assert_('Version' in r) - self.assert_(len(r['Description']) > 0) - self.assert_(str(r).startswith('Package: %s\n' % pkg.shortname)) + self.assertTrue('Version' in r) + self.assertTrue(len(r['Description']) > 0) + self.assertTrue(str(r).startswith('Package: %s\n' % pkg.shortname)) def test_get_provided_packages(self): cache = apt.Cache() @@ -70,7 +70,7 @@ class TestAptCache(unittest.TestCase): def test_low_level_pkg_provides(self): # low level cache provides list of the pkg - cache = apt_pkg.Cache() + cache = apt_pkg.Cache(progress=None) l = cache["mail-transport-agent"].provides_list # arbitrary number, just needs to be higher enough self.assertTrue(len(l), 5) @@ -89,17 +89,17 @@ class TestAptCache(unittest.TestCase): tmpdir = tempfile.mkdtemp() dpkg_dir = os.path.join(tmpdir,"var","lib","dpkg") os.makedirs(os.path.join(dpkg_dir,"updates")) - open(os.path.join(dpkg_dir,"status"), "w") + open(os.path.join(dpkg_dir,"status"), "w").close() apt_pkg.config.set("Dir::State::status", os.path.join(dpkg_dir,"status")) cache = apt.Cache() # test empty self.assertFalse(cache.dpkg_journal_dirty) # that is ok, only [0-9] are dpkg jounral entries - open(os.path.join(dpkg_dir,"updates","xxx"), "w") + open(os.path.join(dpkg_dir,"updates","xxx"), "w").close() self.assertFalse(cache.dpkg_journal_dirty) # that is a dirty journal - open(os.path.join(dpkg_dir,"updates","000"), "w") + open(os.path.join(dpkg_dir,"updates","000"), "w").close() self.assertTrue(cache.dpkg_journal_dirty) # reset config value apt_pkg.config.set("Dir::State::status", old_status) @@ -121,20 +121,19 @@ class TestAptCache(unittest.TestCase): apt_pkg.config.set("dir::etc::sourceparts", "xxx") # main sources.list sources_list = base_sources - f=open(sources_list, "w") - repo = os.path.abspath("./data/test-repo2") - f.write("deb copy:%s /\n" % repo) - f.close() + with open(sources_list, "w") as f: + repo = os.path.abspath("./data/test-repo2") + f.write("deb copy:%s /\n" % repo) # test single sources.list fetching sources_list = os.path.join(rootdir, "test.list") - f=open(sources_list, "w") - repo_dir = os.path.abspath("./data/test-repo") - f.write("deb copy:%s /\n" % repo_dir) - f.close() + with open(sources_list, "w") as f: + repo_dir = os.path.abspath("./data/test-repo") + f.write("deb copy:%s /\n" % repo_dir) + self.assertTrue(os.path.exists(sources_list)) # write marker to ensure listcleaner is not run - open("./data/tmp/var/lib/apt/lists/marker", "w") + open("./data/tmp/var/lib/apt/lists/marker", "w").close() # update a single sources.list cache = apt.Cache() diff --git a/tests/test_cache_invocation.py b/tests/test_cache_invocation.py index 6f4014de..a89ef557 100644 --- a/tests/test_cache_invocation.py +++ b/tests/test_cache_invocation.py @@ -14,7 +14,7 @@ class TestCache(unittest.TestCase): def test_wrong_invocation(self): """cache_invocation: Test wrong invocation.""" - apt_cache = apt_pkg.Cache(apt.progress.base.OpProgress()) + apt_cache = apt_pkg.Cache(progress=None) self.assertRaises(ValueError, apt_pkg.Cache, apt_cache) self.assertRaises(ValueError, apt_pkg.Cache, @@ -23,7 +23,7 @@ class TestCache(unittest.TestCase): def test_proper_invocation(self): """cache_invocation: Test correct invocation.""" - apt_cache = apt_pkg.Cache(apt.progress.base.OpProgress()) + apt_cache = apt_pkg.Cache(progress=None) apt_depcache = apt_pkg.DepCache(apt_cache) if __name__ == "__main__": diff --git a/tests/test_configuration.py b/tests/test_configuration.py new file mode 100644 index 00000000..80509cff --- /dev/null +++ b/tests/test_configuration.py @@ -0,0 +1,30 @@ +#!/usr/bin/python +# +# Copyright (C) 2011 Julian Andres Klode +# +# Copying and distribution of this file, with or without modification, +# are permitted in any medium without royalty provided the copyright +# notice and this notice are preserved. +"""Unit tests for verifying the correctness of apt_pkg.Configuration""" +import unittest + +import apt_pkg + + +class TestConfiguration(unittest.TestCase): + """Test various configuration things""" + + def setUp(self): + """Prepare the tests, create reference values...""" + apt_pkg.init_config() + + def test_lp707416(self): + """configuration: Test empty arguments (LP: #707416)""" + self.assertRaises(ValueError, apt_pkg.parse_commandline, + apt_pkg.config,[], []) + self.assertRaises(SystemError, apt_pkg.parse_commandline, + apt_pkg.config,[], ["cmd", "--arg0"]) + + +if __name__ == "__main__": + unittest.main() diff --git a/tests/test_group.py b/tests/test_group.py new file mode 100644 index 00000000..3c3a5b7a --- /dev/null +++ b/tests/test_group.py @@ -0,0 +1,32 @@ +import unittest + +import apt_pkg + + +class TestGroup(unittest.TestCase): + + def setUp(self): + apt_pkg.init() + self.cache = apt_pkg.Cache(progress=None) + + def test_pkgingroup(self): + """Check that each package belongs to the corresponding group""" + for pkg in self.cache.packages: + group = apt_pkg.Group(self.cache, pkg.name) + assert any(pkg.id == p.id for p in group) + + def test_iteration(self): + """Check that iteration works correctly.""" + for pkg in self.cache.packages: + group = apt_pkg.Group(self.cache, pkg.name) + + list(group) == list(group) + + + def test_cache_groups(self): + """group: Iterate over all groups""" + assert len(list(self.cache.groups)) == self.cache.group_count + + +if __name__ == "__main__": + unittest.main() diff --git a/tests/test_hashes.py b/tests/test_hashes.py index e0aabe09..660373cb 100644 --- a/tests/test_hashes.py +++ b/tests/test_hashes.py @@ -82,11 +82,16 @@ class TestHashString(unittest.TestCase): def setUp(self): """Prepare the test by reading the file.""" - self.hashes = apt_pkg.Hashes(open(apt_pkg.__file__)) + self.file = open(apt_pkg.__file__) + self.hashes = apt_pkg.Hashes(self.file) self.md5 = apt_pkg.HashString("MD5Sum", self.hashes.md5) self.sha1 = apt_pkg.HashString("SHA1", self.hashes.sha1) self.sha256 = apt_pkg.HashString("SHA256", self.hashes.sha256) + def tearDown(self): + """Cleanup, Close the file object used for the tests.""" + self.file.close() + def test_md5(self): """hashes: Test apt_pkg.HashString().md5""" self.assertEqual("MD5Sum:%s" % self.hashes.md5, str(self.md5)) diff --git a/tests/test_progress.py b/tests/test_progress.py index ffab5bc0..73853dfa 100644 --- a/tests/test_progress.py +++ b/tests/test_progress.py @@ -34,7 +34,8 @@ class TestProgress(unittest.TestCase): apt_pkg.config.set("Dir::state::lists", "./tmp") # create artifical line deb_line = "deb file:%s/data/fake-packages/ /\n" % basedir - open("fetch_sources.list","w").write(deb_line) + with open("fetch_sources.list","w") as fobj: + fobj.write(deb_line) apt_pkg.config.set("Dir::Etc::sourcelist", "fetch_sources.list") def test_acquire_progress(self): -- cgit v1.2.3 From 3a0e654a8a5935c148bc95a9f9cb5d7786aae825 Mon Sep 17 00:00:00 2001 From: Michael Vogt Date: Tue, 9 Aug 2011 09:12:29 +0200 Subject: apt/package.py: make print statements py3 compatbile --- apt/package.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'apt') diff --git a/apt/package.py b/apt/package.py index 9223ed22..dbc5b23e 100644 --- a/apt/package.py +++ b/apt/package.py @@ -545,8 +545,8 @@ class Version(object): base = os.path.basename(self._records.filename) destfile = os.path.join(destdir, base) if _file_is_same(destfile, self.size, self._records.md5_hash): - print 'Ignoring already existing file:', destfile - return + print('Ignoring already existing file: %s' % destfile) + return os.path.abspath(destfile) acq = apt_pkg.Acquire(progress or apt.progress.text.AcquireProgress()) acqfile = apt_pkg.AcquireFile(acq, self.uri, self._records.md5_hash, self.size, base, destfile=destfile) @@ -555,7 +555,7 @@ class Version(object): if acqfile.status != acqfile.STAT_DONE: raise FetchError("The item %r could not be fetched: %s" % (acqfile.destfile, acqfile.error_text)) - print self._records.filename + return os.path.abspath(destfile) def fetch_source(self, destdir="", progress=None, unpack=True): @@ -594,7 +594,7 @@ class Version(object): if type_ == 'dsc': dsc = destfile if _file_is_same(destfile, size, md5): - print 'Ignoring already existing file:', destfile + print('Ignoring already existing file: %s' % destfile) continue files.append(apt_pkg.AcquireFile(acq, src.index.archive_uri(path), md5, size, base, destfile=destfile)) -- cgit v1.2.3 From 8c5ea9518e38ac7fd3d5d4b547e0b6bad4b0c18d Mon Sep 17 00:00:00 2001 From: Michael Vogt Date: Wed, 28 Sep 2011 13:58:36 +0200 Subject: * apt/package.py: - packages in marked_install state can also be auto-removable --- apt/package.py | 4 ++-- debian/changelog | 2 ++ 2 files changed, 4 insertions(+), 2 deletions(-) (limited to 'apt') diff --git a/apt/package.py b/apt/package.py index dbc5b23e..4104f93e 100644 --- a/apt/package.py +++ b/apt/package.py @@ -973,8 +973,8 @@ class Package(object): another package, and if no packages depend on it anymore, the package is no longer required. """ - return self.is_installed and \ - self._pcache._depcache.is_garbage(self._pkg) + return ((self.is_installed or self.marked_install) and + self._pcache._depcache.is_garbage(self._pkg)) @property def is_auto_installed(self): diff --git a/debian/changelog b/debian/changelog index 242e4058..05f2dc8e 100644 --- a/debian/changelog +++ b/debian/changelog @@ -20,6 +20,8 @@ python-apt (0.8.1) UNRELEASED; urgency=low - print library dir to hunt down build failure on amd64 * aptsources/distinfo.py: - make mirror a valid protocol name + * apt/package.py: + - packages in marked_install state can also be auto-removable -- Julian Andres Klode Tue, 07 Jun 2011 14:00:22 +0200 -- cgit v1.2.3 From a2363f7e30c93599af6366413bad965846a12d83 Mon Sep 17 00:00:00 2001 From: Michael Vogt Date: Tue, 4 Oct 2011 16:34:56 +0200 Subject: * apt/progress/gtk2.py: - update to the latest vte API for child-exited (LP: #865388) --- apt/progress/gtk2.py | 9 +++++---- debian/changelog | 2 ++ 2 files changed, 7 insertions(+), 4 deletions(-) (limited to 'apt') diff --git a/apt/progress/gtk2.py b/apt/progress/gtk2.py index 9137ef76..b5794e92 100644 --- a/apt/progress/gtk2.py +++ b/apt/progress/gtk2.py @@ -34,6 +34,7 @@ except ImportError: import gobject as glib import gobject import pango +import time import vte import apt_pkg @@ -127,16 +128,15 @@ class GInstallProgress(gobject.GObject, base.InstallProgress): self.apt_status = -1 self.time_last_update = time.time() self.term = term - reaper = vte.reaper_get() - reaper.connect("child-exited", self.child_exited) + self.term.connect("child-exited", self.child_exited) self.env = ["VTE_PTY_KEEP_FD=%s" % self.writefd, "DEBIAN_FRONTEND=gnome", "APT_LISTCHANGES_FRONTEND=gtk"] self._context = glib.main_context_default() - def child_exited(self, term, pid, status): + def child_exited(self, term): """Called when a child process exits""" - self.apt_status = os.WEXITSTATUS(status) + self.apt_status = term.get_child_exit_status() self.finished = True def error(self, pkg, errormsg): @@ -204,6 +204,7 @@ class GInstallProgress(gobject.GObject, base.InstallProgress): """Wait for the child process to exit.""" while not self.finished: self.update_interface() + time.sleep(0.02) return self.apt_status if apt_pkg._COMPAT_0_7: diff --git a/debian/changelog b/debian/changelog index 88faaca3..0864d725 100644 --- a/debian/changelog +++ b/debian/changelog @@ -24,6 +24,8 @@ python-apt (0.8.1) UNRELEASED; urgency=low - packages in marked_install state can also be auto-removable * add concept of "ParentComponent" for e.g. ubuntu/multiverse that needs universe enabled as well (plus add test) + * apt/progress/gtk2.py: + - update to the latest vte API for child-exited (LP: #865388) -- Julian Andres Klode Tue, 07 Jun 2011 14:00:22 +0200 -- cgit v1.2.3 From 53aea1b695035db409775f9f1fbfadabbbf4d593 Mon Sep 17 00:00:00 2001 From: Michael Vogt Date: Tue, 4 Oct 2011 16:36:48 +0200 Subject: * apt/progress/gtk2.py: - update to the latest vte API for child-exited (LP: #865388) --- apt/progress/gtk2.py | 9 +++++---- debian/changelog | 7 +++++++ 2 files changed, 12 insertions(+), 4 deletions(-) (limited to 'apt') diff --git a/apt/progress/gtk2.py b/apt/progress/gtk2.py index 9137ef76..b5794e92 100644 --- a/apt/progress/gtk2.py +++ b/apt/progress/gtk2.py @@ -34,6 +34,7 @@ except ImportError: import gobject as glib import gobject import pango +import time import vte import apt_pkg @@ -127,16 +128,15 @@ class GInstallProgress(gobject.GObject, base.InstallProgress): self.apt_status = -1 self.time_last_update = time.time() self.term = term - reaper = vte.reaper_get() - reaper.connect("child-exited", self.child_exited) + self.term.connect("child-exited", self.child_exited) self.env = ["VTE_PTY_KEEP_FD=%s" % self.writefd, "DEBIAN_FRONTEND=gnome", "APT_LISTCHANGES_FRONTEND=gtk"] self._context = glib.main_context_default() - def child_exited(self, term, pid, status): + def child_exited(self, term): """Called when a child process exits""" - self.apt_status = os.WEXITSTATUS(status) + self.apt_status = term.get_child_exit_status() self.finished = True def error(self, pkg, errormsg): @@ -204,6 +204,7 @@ class GInstallProgress(gobject.GObject, base.InstallProgress): """Wait for the child process to exit.""" while not self.finished: self.update_interface() + time.sleep(0.02) return self.apt_status if apt_pkg._COMPAT_0_7: diff --git a/debian/changelog b/debian/changelog index 906f86df..d7a4d46f 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,3 +1,10 @@ +python-apt (0.8.0ubuntu9) oneiric; urgency=low + + * apt/progress/gtk2.py: + - update to the latest vte API for child-exited (LP: #865388) + + -- Michael Vogt Tue, 04 Oct 2011 16:35:52 +0200 + python-apt (0.8.0ubuntu8) oneiric; urgency=low * add concept of "ParentComponent" for e.g. ubuntu/multiverse -- cgit v1.2.3 From f17fc818cca41668bb6358e490ff3278e64fa493 Mon Sep 17 00:00:00 2001 From: Michael Vogt Date: Wed, 5 Oct 2011 10:32:04 +0200 Subject: * tests/test_apt_cache.py: - add __cmp__ to apt.Package so that sort() sorts by name on list of package objects --- apt/package.py | 3 +++ debian/changelog | 3 +++ tests/test_apt_cache.py | 11 +++++++++++ 3 files changed, 17 insertions(+) (limited to 'apt') diff --git a/apt/package.py b/apt/package.py index 4104f93e..cb373c2e 100644 --- a/apt/package.py +++ b/apt/package.py @@ -707,6 +707,9 @@ class Package(object): return '' % (self._pkg.name, self._pkg.architecture, self._pkg.id) + def __cmp__(self, other): + return cmp(self.name, other.name) + def candidate(self): """Return the candidate version of the package. diff --git a/debian/changelog b/debian/changelog index 0864d725..b7876e51 100644 --- a/debian/changelog +++ b/debian/changelog @@ -26,6 +26,9 @@ python-apt (0.8.1) UNRELEASED; urgency=low that needs universe enabled as well (plus add test) * apt/progress/gtk2.py: - update to the latest vte API for child-exited (LP: #865388) + * tests/test_apt_cache.py: + - add __cmp__ to apt.Package so that sort() sorts by name + on list of package objects -- Julian Andres Klode Tue, 07 Jun 2011 14:00:22 +0200 diff --git a/tests/test_apt_cache.py b/tests/test_apt_cache.py index 2f812059..9c1f549f 100644 --- a/tests/test_apt_cache.py +++ b/tests/test_apt_cache.py @@ -181,5 +181,16 @@ class TestAptCache(unittest.TestCase): apt_pkg.config.set("dir::etc::sourcelist", old_source_list) apt_pkg.config.set("dir::etc::sourceparts", old_source_parts) + def test_package_cmp(self): + cache = apt.Cache() + l = [] + l.append(cache["libc6"]) + l.append(cache["xterm"]) + l.append(cache["apt"]) + l.sort() + self.assertEqual([p.name for p in l], + ["apt", "libc6", "xterm"]) + + if __name__ == "__main__": unittest.main() -- cgit v1.2.3 From e99da71e5f242787ec39714e55b5fa08bfe071eb Mon Sep 17 00:00:00 2001 From: Michael Vogt Date: Wed, 19 Oct 2011 18:24:24 +0200 Subject: rm usage of camelcase in cache.py doc (closes: #626617) --- apt/cache.py | 4 ++-- debian/changelog | 4 ++++ 2 files changed, 6 insertions(+), 2 deletions(-) (limited to 'apt') diff --git a/apt/cache.py b/apt/cache.py index be137b76..49097dd2 100644 --- a/apt/cache.py +++ b/apt/cache.py @@ -507,7 +507,7 @@ class Cache(object): self._callbacks[name].append(callback) def actiongroup(self): - """Return an ActionGroup() object for the current cache. + """Return an action group object for the current cache. Action groups can be used to speedup actions. The action group is active as soon as it is created, and disabled when the object is @@ -520,7 +520,7 @@ class Cache(object): for package in my_selected_packages: package.mark_install() - This way, the ActionGroup is automatically released as soon as the + This way, the action group is automatically released as soon as the with statement block is left. It also has the benefit of making it clear which parts of the code run with a action group and which don't. diff --git a/debian/changelog b/debian/changelog index 758c49b9..7fa0f624 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,5 +1,6 @@ python-apt (0.8.2) UNRELEASED; urgency=low + [ Michael Vogt ] * merged from ubuntu: - use logging instead of print - update distro template Ubuntu.info.in @@ -10,6 +11,9 @@ python-apt (0.8.2) UNRELEASED; urgency=low - updated, thanks to Sergio Cipolla (closes: #628398) * po/da.po: - updated, thanks to Joe Dalton (closes: #631309) + + [ Tshepang Lekhonkhobe ] + * rm usage of camelcase in cache.py doc (closes: #626617) -- Michael Vogt Wed, 19 Oct 2011 18:03:42 +0200 -- cgit v1.2.3 From 2089dda15fd6dd2734c395a9840c06e467c76325 Mon Sep 17 00:00:00 2001 From: Michael Vogt Date: Wed, 19 Oct 2011 18:25:43 +0200 Subject: grammar fix in the cache.py doc (closes: #626610) --- apt/cache.py | 3 +-- debian/changelog | 1 + 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'apt') diff --git a/apt/cache.py b/apt/cache.py index 49097dd2..bab5c277 100644 --- a/apt/cache.py +++ b/apt/cache.py @@ -46,8 +46,7 @@ class LockFailedException(IOError): class Cache(object): """Dictionary-like package cache. - This class has all the packages that are available in it's - dictionary. + The dictionary of this class contains all available packages. Keyword arguments: progress -- a OpProgress object diff --git a/debian/changelog b/debian/changelog index 7fa0f624..15562c04 100644 --- a/debian/changelog +++ b/debian/changelog @@ -14,6 +14,7 @@ python-apt (0.8.2) UNRELEASED; urgency=low [ Tshepang Lekhonkhobe ] * rm usage of camelcase in cache.py doc (closes: #626617) + * grammar fix in the cache.py doc (closes: #626610) -- Michael Vogt Wed, 19 Oct 2011 18:03:42 +0200 -- cgit v1.2.3 From cc6c62f5bd954d11ec0b0a1d90a190463d36b0e6 Mon Sep 17 00:00:00 2001 From: Michael Vogt Date: Wed, 19 Oct 2011 18:26:57 +0200 Subject: apt/debfile.py: Remove the need to explcitly call check() in order to get output from require_changes and missing_deps (closes: #624379) --- apt/debfile.py | 4 +++- debian/changelog | 3 +++ 2 files changed, 6 insertions(+), 1 deletion(-) (limited to 'apt') diff --git a/apt/debfile.py b/apt/debfile.py index d0f41def..104b0814 100644 --- a/apt/debfile.py +++ b/apt/debfile.py @@ -472,7 +472,7 @@ class DebPackage(object): def missing_deps(self): """Return missing dependencies.""" self._dbg(1, "Installing: %s" % self._need_pkgs) - if self._need_pkgs is None: + if not self._need_pkgs: self.check() return self._need_pkgs @@ -485,6 +485,8 @@ class DebPackage(object): install = [] remove = [] unauthenticated = [] + if not self._cache: + self.check() for pkg in self._cache: if pkg.marked_install or pkg.marked_upgrade: install.append(pkg.name) diff --git a/debian/changelog b/debian/changelog index 15562c04..50d3c80f 100644 --- a/debian/changelog +++ b/debian/changelog @@ -15,6 +15,9 @@ python-apt (0.8.2) UNRELEASED; urgency=low [ Tshepang Lekhonkhobe ] * rm usage of camelcase in cache.py doc (closes: #626617) * grammar fix in the cache.py doc (closes: #626610) + * apt/debfile.py: Remove the need to explcitly call check() in order + to get output from require_changes and missing_deps + (closes: #624379) -- Michael Vogt Wed, 19 Oct 2011 18:03:42 +0200 -- cgit v1.2.3 From cf026ec1840a0d858b48e35fa98b248323ab9d90 Mon Sep 17 00:00:00 2001 From: Michael Vogt Date: Fri, 21 Oct 2011 11:07:53 +0200 Subject: first cut of multiarch support, tests still fail though --- apt/debfile.py | 34 ++++++++++++++++--- tests/data/test_debs/multiarch-test1_i386.deb | Bin 0 -> 978 bytes tests/test_debfile.py | 8 +++-- tests/test_debfile_multiarch.py | 46 ++++++++++++++++++++++++++ 4 files changed, 80 insertions(+), 8 deletions(-) create mode 100644 tests/data/test_debs/multiarch-test1_i386.deb create mode 100644 tests/test_debfile_multiarch.py (limited to 'apt') diff --git a/apt/debfile.py b/apt/debfile.py index 104b0814..1a9b471a 100644 --- a/apt/debfile.py +++ b/apt/debfile.py @@ -54,6 +54,7 @@ class DebPackage(object): self._sections = {} self._need_pkgs = [] self._failure_string = "" + self._multiarch = None if filename: self.open(filename) @@ -83,6 +84,14 @@ class DebPackage(object): self.filename)] return files + # helper that will return a pkgname with a multiarch suffix if needed + def _maybe_append_multiarch_suffix(self, pkgname): + if (self._multiarch and + not self._cache.is_virtual_package(pkgname) and + self._cache[pkgname].candidate.architecture != "all"): + return "%s:%s" % (pkgname, self._multiarch) + return pkgname + def _is_or_group_satisfied(self, or_group): """Return True if at least one dependency of the or-group is satisfied. @@ -96,6 +105,9 @@ class DebPackage(object): ver = dep[1] oper = dep[2] + # multiarch + depname = self._maybe_append_multiarch_suffix(depname) + # check for virtual pkgs if not depname in self._cache: if self._cache.is_virtual_package(depname): @@ -131,6 +143,9 @@ class DebPackage(object): for dep in or_group: depname, ver, oper = dep + # multiarch + depname = self._maybe_append_multiarch_suffix(depname) + # if we don't have it in the cache, it may be virtual if not depname in self._cache: if not self._cache.is_virtual_package(depname): @@ -203,6 +218,10 @@ class DebPackage(object): ver = dep[1] oper = dep[2] + # FIXME: is this good enough? i.e. will apt always populate + # the cache with conflicting pkgnames for our arch? + depname = self._maybe_append_multiarch_suffix(depname) + # check conflicts with virtual pkgs if not depname in self._cache: # FIXME: we have to check for virtual replaces here as @@ -400,9 +419,14 @@ class DebPackage(object): return False arch = self._sections["Architecture"] if arch != "all" and arch != apt_pkg.config.find("APT::Architecture"): - self._dbg(1, "ERROR: Wrong architecture dude!") - self._failure_string = _("Wrong architecture '%s'") % arch - return False + if arch in apt_pkg.get_architectures(): + self._multiarch = arch + self.pkgname = "%s:%s" % (self.pkgname, self._multiarch) + self._dbg(1, "Found multiarch arch: '%s'" % arch) + else: + self._dbg(1, "ERROR: Wrong architecture dude!") + self._failure_string = _("Wrong architecture '%s'") % arch + return False # check version if self.compare_to_version_in_cache() == self.VERSION_OUTDATED: @@ -656,7 +680,7 @@ class DscSrcPackage(DebPackage): def _test(): """Test function""" from apt.cache import Cache - from apt.progress import DpkgInstallProgress + from apt.progress.base import InstallProgress cache = Cache() @@ -678,7 +702,7 @@ def _test(): print d.filelist print "Installing ..." - ret = d.install(DpkgInstallProgress()) + ret = d.install(InstallProgress()) print ret #s = DscSrcPackage(cache, "../tests/3ddesktop_0.2.9-6.dsc") diff --git a/tests/data/test_debs/multiarch-test1_i386.deb b/tests/data/test_debs/multiarch-test1_i386.deb new file mode 100644 index 00000000..439a9f46 Binary files /dev/null and b/tests/data/test_debs/multiarch-test1_i386.deb differ diff --git a/tests/test_debfile.py b/tests/test_debfile.py index 951c2afe..72d5e0b0 100644 --- a/tests/test_debfile.py +++ b/tests/test_debfile.py @@ -17,8 +17,8 @@ sys.path.insert(0, get_library_dir()) import apt_pkg import apt.debfile -class TestDebfilee(unittest.TestCase): - """ test the apt cache """ +class TestDebfile(unittest.TestCase): + """ test the debfile """ TEST_DEBS = [ # conflicts with apt @@ -87,7 +87,7 @@ class TestDebfilee(unittest.TestCase): self.assertEqual(deb["Maintainer"], "Samuel Lidén Borell ") - def testContent(self): + def test_content(self): # no python-debian for python3 yet, so fail gracefully try: import debian @@ -130,6 +130,8 @@ Description: testpackage for gdebi - contains usr/bin/binary for file reading # we need to support python2.6 self.assertTrue(raised) + + if __name__ == "__main__": #logging.basicConfig(level=logging.DEBUG) unittest.main() diff --git a/tests/test_debfile_multiarch.py b/tests/test_debfile_multiarch.py new file mode 100644 index 00000000..1b468a45 --- /dev/null +++ b/tests/test_debfile_multiarch.py @@ -0,0 +1,46 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- +# +# Copyright (C) 2010 Michael Vogt +# +# Copying and distribution of this file, with or without modification, +# are permitted in any medium without royalty provided the copyright +# notice and this notice are preserved. +"""Unit tests for verifying the correctness of DebPackage in apt.debfile.""" +import os +import logging +import unittest + +from test_all import get_library_dir +import sys +sys.path.insert(0, get_library_dir()) +import apt +import apt_pkg +import apt.debfile + +class TestDebfileMultiarch(unittest.TestCase): + """ test the multiarch debfile """ + + def test_multiarch_deb(self): + if apt_pkg.get_architectures() != ["amd64", "i386"]: + logging.warn("skipping test because running on a non-multiarch system") + return + deb = apt.debfile.DebPackage( + "./data/test_debs/multiarch-test1_i386.deb") + missing = deb.missing_deps + print missing + self.assertFalse("dpkg:i386" in missing) + + def test_multiarch_conflicts(self): + cache = apt.Cache() + # WARNING: this assumes that lib3ds-1-3 is a non-multiarch lib + # use "lib3ds-1-3" as a test to see if non-multiach lib conflicts work + cache["lib3ds-1-3"].mark_install() + deb = apt.debfile.DebPackage( + "./data/test_debs/multiarch-test1_i386.deb", cache=cache) + # this deb should now not be installable + self.assertFalse(deb.check()) + + +if __name__ == "__main__": + unittest.main() -- cgit v1.2.3 From c96acf6d26beb8c2c2e6eb9a868a4ea6b053a9d4 Mon Sep 17 00:00:00 2001 From: Michael Vogt Date: Fri, 21 Oct 2011 11:47:03 +0200 Subject: make the tests pass --- apt/debfile.py | 36 +++++++++++++++++++++++++++++------- tests/test_debfile_multiarch.py | 11 ++++++++--- 2 files changed, 37 insertions(+), 10 deletions(-) (limited to 'apt') diff --git a/apt/debfile.py b/apt/debfile.py index 1a9b471a..0e8bfcad 100644 --- a/apt/debfile.py +++ b/apt/debfile.py @@ -85,12 +85,33 @@ class DebPackage(object): return files # helper that will return a pkgname with a multiarch suffix if needed - def _maybe_append_multiarch_suffix(self, pkgname): - if (self._multiarch and - not self._cache.is_virtual_package(pkgname) and - self._cache[pkgname].candidate.architecture != "all"): - return "%s:%s" % (pkgname, self._multiarch) - return pkgname + def _maybe_append_multiarch_suffix(self, pkgname, + in_conflict_checking=False): + # trivial cases + if not self._multiarch: + return pkgname + elif self._cache.is_virtual_package(pkgname): + return pkgname + elif self._cache[pkgname].candidate == "all": + return pkgname + # now do the real multiarch checking + multiarch_pkgname = "%s:%s" % (pkgname, self._multiarch) + # the upper layers will handle this + if not multiarch_pkgname in self._cache: + return multiarch_pkgname + # now check the multiarch state + cand = self._cache[multiarch_pkgname].candidate._cand + #print pkgname, multiarch_pkgname, cand.multi_arch + # the default is to add the suffix, unless its a pkg that can satify + # foreign dependencies + if cand.multi_arch & cand.MULTI_ARCH_FOREIGN: + return pkgname + # for conflicts we need a special case here, any not multiarch enabled + # package has a implicit conflict + if (in_conflict_checking and + not (cand.multi_arch & cand.MULTI_ARCH_SAME)): + return pkgname + return multiarch_pkgname def _is_or_group_satisfied(self, or_group): """Return True if at least one dependency of the or-group is satisfied. @@ -220,7 +241,8 @@ class DebPackage(object): # FIXME: is this good enough? i.e. will apt always populate # the cache with conflicting pkgnames for our arch? - depname = self._maybe_append_multiarch_suffix(depname) + depname = self._maybe_append_multiarch_suffix( + depname, in_conflict_checking=True) # check conflicts with virtual pkgs if not depname in self._cache: diff --git a/tests/test_debfile_multiarch.py b/tests/test_debfile_multiarch.py index 1b468a45..db777692 100644 --- a/tests/test_debfile_multiarch.py +++ b/tests/test_debfile_multiarch.py @@ -21,14 +21,14 @@ import apt.debfile class TestDebfileMultiarch(unittest.TestCase): """ test the multiarch debfile """ - def test_multiarch_deb(self): + def test_multiarch_deb_check(self): if apt_pkg.get_architectures() != ["amd64", "i386"]: logging.warn("skipping test because running on a non-multiarch system") return deb = apt.debfile.DebPackage( "./data/test_debs/multiarch-test1_i386.deb") missing = deb.missing_deps - print missing + #print missing self.assertFalse("dpkg:i386" in missing) def test_multiarch_conflicts(self): @@ -39,7 +39,12 @@ class TestDebfileMultiarch(unittest.TestCase): deb = apt.debfile.DebPackage( "./data/test_debs/multiarch-test1_i386.deb", cache=cache) # this deb should now not be installable - self.assertFalse(deb.check()) + installable = deb.check() + #print deb._failure_string + self.assertFalse(installable) + self.assertEqual(deb._failure_string, + "Conflicts with the installed package 'lib3ds-1-3'") + if __name__ == "__main__": -- cgit v1.2.3 From 5416003d5acd3630bff5b48f69277b5e1d917f20 Mon Sep 17 00:00:00 2001 From: Michael Vogt Date: Fri, 21 Oct 2011 12:14:29 +0200 Subject: apt/debfile.py: really check against architecture --- apt/debfile.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'apt') diff --git a/apt/debfile.py b/apt/debfile.py index 0e8bfcad..1d7a4a16 100644 --- a/apt/debfile.py +++ b/apt/debfile.py @@ -92,7 +92,7 @@ class DebPackage(object): return pkgname elif self._cache.is_virtual_package(pkgname): return pkgname - elif self._cache[pkgname].candidate == "all": + elif self._cache[pkgname].candidate.architecture == "all": return pkgname # now do the real multiarch checking multiarch_pkgname = "%s:%s" % (pkgname, self._multiarch) -- cgit v1.2.3 From 1948d5d03887eb81ea7797c0f23049ef6c3895ce Mon Sep 17 00:00:00 2001 From: Michael Vogt Date: Thu, 10 Nov 2011 10:05:55 +0100 Subject: * apt/cache.py: - remove "print" when creating dirs in apt.Cache(rootdir=dir), thanks to Martin Pitt --- apt/cache.py | 2 +- debian/changelog | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) (limited to 'apt') diff --git a/apt/cache.py b/apt/cache.py index bab5c277..a4585277 100644 --- a/apt/cache.py +++ b/apt/cache.py @@ -111,7 +111,7 @@ class Cache(object): ] for d in dirs: if not os.path.exists(rootdir + d): - print "creating: ", rootdir + d + #print "creating: ", rootdir + d os.makedirs(rootdir + d) for f in files: if not os.path.exists(rootdir + f): diff --git a/debian/changelog b/debian/changelog index 6fbc5241..f5ae2b79 100644 --- a/debian/changelog +++ b/debian/changelog @@ -15,6 +15,9 @@ python-apt (0.8.2) UNRELEASED; urgency=low - updated, thanks to Nikola Nenadic (closes: #638308) * python/apt_pkgmodule.cc: - add apt_pkg.get_architectures() call + * apt/cache.py: + - remove "print" when creating dirs in apt.Cache(rootdir=dir), + thanks to Martin Pitt [ Tshepang Lekhonkhobe ] * rm usage of camelcase in cache.py doc (closes: #626617) -- cgit v1.2.3 From fcd268692450231c98bfb6ac2e573ce466e014c7 Mon Sep 17 00:00:00 2001 From: Michael Vogt Date: Mon, 14 Nov 2011 14:07:38 +0100 Subject: * apt/cache.py: - set Dir::bin::dpkg if a alternate rootdir is given (LP: #885895) --- apt/cache.py | 4 ++++ debian/changelog | 3 +++ 2 files changed, 7 insertions(+) (limited to 'apt') diff --git a/apt/cache.py b/apt/cache.py index a4585277..8a456715 100644 --- a/apt/cache.py +++ b/apt/cache.py @@ -83,6 +83,10 @@ class Cache(object): apt_pkg.config.set("Dir", rootdir) apt_pkg.config.set("Dir::State::status", rootdir + "/var/lib/dpkg/status") + # also set dpkg to the rootdir path so that its called for the + # --print-foreign-architectures call + apt_pkg.config.set("Dir::bin::dpkg", + os.path.join(rootdir, "usr", "bin", "dpkg")) # create required dirs/files when run with special rootdir # automatically self._check_and_create_required_dirs(rootdir) diff --git a/debian/changelog b/debian/changelog index 58326dc2..14ee705e 100644 --- a/debian/changelog +++ b/debian/changelog @@ -19,6 +19,9 @@ python-apt (0.8.2) UNRELEASED; urgency=low - remove "print" when creating dirs in apt.Cache(rootdir=dir), thanks to Martin Pitt * fix build against apt in experimental + * apt/cache.py: + - set Dir::bin::dpkg if a alternate rootdir is given + (LP: #885895) [ Tshepang Lekhonkhobe ] * rm usage of camelcase in cache.py doc (closes: #626617) -- cgit v1.2.3 From f2cd844828de310dc510505612395ef60ded8731 Mon Sep 17 00:00:00 2001 From: Michael Vogt Date: Thu, 17 Nov 2011 16:46:53 +0100 Subject: * apt/package.py: - add new "suggests" property, thanks to Christop Groth --- apt/package.py | 5 +++++ debian/changelog | 2 ++ 2 files changed, 7 insertions(+) (limited to 'apt') diff --git a/apt/package.py b/apt/package.py index cb373c2e..e09acca4 100644 --- a/apt/package.py +++ b/apt/package.py @@ -454,6 +454,11 @@ class Version(object): """Return the recommends of the package version.""" return self.get_dependencies("Recommends") + @property + def suggests(self): + """Return the suggests of the package version.""" + return self.get_dependencies("Suggests") + @property def origins(self): """Return a list of origins for the package version.""" diff --git a/debian/changelog b/debian/changelog index 14ee705e..afffdf12 100644 --- a/debian/changelog +++ b/debian/changelog @@ -22,6 +22,8 @@ python-apt (0.8.2) UNRELEASED; urgency=low * apt/cache.py: - set Dir::bin::dpkg if a alternate rootdir is given (LP: #885895) + * apt/package.py: + - add new "suggests" property, thanks to Christop Groth [ Tshepang Lekhonkhobe ] * rm usage of camelcase in cache.py doc (closes: #626617) -- cgit v1.2.3 From add08632b8a66c7ba5de7ab44d8a10ec9462692e Mon Sep 17 00:00:00 2001 From: Michael Vogt Date: Thu, 17 Nov 2011 17:57:34 +0100 Subject: allow Dependency object to be iteratable, this allows to write code like: for or_dep_group in pkg.candidate.dependencies: for dep in or_dep_group: do_something() --- apt/package.py | 3 +++ debian/changelog | 4 ++++ tests/test_apt_cache.py | 4 ++-- 3 files changed, 9 insertions(+), 2 deletions(-) (limited to 'apt') diff --git a/apt/package.py b/apt/package.py index e09acca4..ab692788 100644 --- a/apt/package.py +++ b/apt/package.py @@ -107,6 +107,9 @@ class Dependency(object): def __repr__(self): return repr(self.or_dependencies) + def __iter__(self): + return self.or_dependencies.__iter__() + class DeprecatedProperty(property): """A property which gives DeprecationWarning on access. diff --git a/debian/changelog b/debian/changelog index afffdf12..7d939702 100644 --- a/debian/changelog +++ b/debian/changelog @@ -24,6 +24,10 @@ python-apt (0.8.2) UNRELEASED; urgency=low (LP: #885895) * apt/package.py: - add new "suggests" property, thanks to Christop Groth + - allow Dependency object to be iteratable, this allows to write + code like: + for or_dep_group in pkg.candidate.dependencies: + for dep in or_dep_group: do_something() [ Tshepang Lekhonkhobe ] * rm usage of camelcase in cache.py doc (closes: #626617) diff --git a/tests/test_apt_cache.py b/tests/test_apt_cache.py index 0d80f617..7784a25f 100644 --- a/tests/test_apt_cache.py +++ b/tests/test_apt_cache.py @@ -55,8 +55,8 @@ class TestAptCache(unittest.TestCase): # that is possible and does not crash for pkg in cache: if pkg.candidate: - for or_dep in pkg.candidate.dependencies: - for dep in or_dep.or_dependencies: + for or_deps in pkg.candidate.dependencies: + for dep in or_deps: self.assertTrue(dep.name) self.assertTrue(isinstance(dep.relation, str)) self.assertTrue(dep.pre_depend in (True, False)) -- cgit v1.2.3 From a01e314df3ceaf0cc99102beb511a2ecbd973057 Mon Sep 17 00:00:00 2001 From: Michael Vogt Date: Thu, 17 Nov 2011 20:28:10 +0100 Subject: make Dependency a list (it should probably also renamed to DependencyOrGroup or something), thanks to Christop Groth --- apt/package.py | 14 ++++++-------- debian/changelog | 4 +++- 2 files changed, 9 insertions(+), 9 deletions(-) (limited to 'apt') diff --git a/apt/package.py b/apt/package.py index ab692788..8b79c420 100644 --- a/apt/package.py +++ b/apt/package.py @@ -94,7 +94,7 @@ class BaseDependency(object): preDepend = AttributeDeprecatedBy('pre_depend') -class Dependency(object): +class Dependency(list): """Represent an Or-group of dependencies. Attributes defined here: @@ -102,14 +102,12 @@ class Dependency(object): """ def __init__(self, alternatives): - self.or_dependencies = alternatives - - def __repr__(self): - return repr(self.or_dependencies) - - def __iter__(self): - return self.or_dependencies.__iter__() + super(Dependency, self).__init__() + self.extend(alternatives) + @property + def or_dependencies(self): + return self class DeprecatedProperty(property): """A property which gives DeprecationWarning on access. diff --git a/debian/changelog b/debian/changelog index 7d939702..6e750980 100644 --- a/debian/changelog +++ b/debian/changelog @@ -27,7 +27,9 @@ python-apt (0.8.2) UNRELEASED; urgency=low - allow Dependency object to be iteratable, this allows to write code like: for or_dep_group in pkg.candidate.dependencies: - for dep in or_dep_group: do_something() + for dep in or_dep_group: + do_something() + (thanks to Christop Groth) [ Tshepang Lekhonkhobe ] * rm usage of camelcase in cache.py doc (closes: #626617) -- cgit v1.2.3 From b7b608bf2c66e7466313ea02568348aea9cc29ba Mon Sep 17 00:00:00 2001 From: Michael Vogt Date: Tue, 22 Nov 2011 18:01:26 +0100 Subject: cleanup based on feedback from juliank --- apt/cache.py | 10 ++++++++-- apt/debfile.py | 19 +++++++++++++------ debian/changelog | 6 +++--- tests/test_debfile.py | 12 +++++++----- 4 files changed, 31 insertions(+), 16 deletions(-) (limited to 'apt') diff --git a/apt/cache.py b/apt/cache.py index 8a456715..b4d67fa5 100644 --- a/apt/cache.py +++ b/apt/cache.py @@ -46,7 +46,13 @@ class LockFailedException(IOError): class Cache(object): """Dictionary-like package cache. - The dictionary of this class contains all available packages. + The APT cache file contains a hash table mapping names of binary + packages to their metadata. A Cache object is the in-core + representation of the same. It provides access to APTs idea of the + list of available packages. + + The cache can be used like a mapping from package names to Package + objects (although only getting items is supported). Keyword arguments: progress -- a OpProgress object @@ -510,7 +516,7 @@ class Cache(object): self._callbacks[name].append(callback) def actiongroup(self): - """Return an action group object for the current cache. + """Return an `ActionGroup` object for the current cache. Action groups can be used to speedup actions. The action group is active as soon as it is created, and disabled when the object is diff --git a/apt/debfile.py b/apt/debfile.py index 104b0814..5b9274b2 100644 --- a/apt/debfile.py +++ b/apt/debfile.py @@ -53,6 +53,7 @@ class DebPackage(object): self.pkgname = "" self._sections = {} self._need_pkgs = [] + self._check_was_run = False self._failure_string = "" if filename: self.open(filename) @@ -68,7 +69,8 @@ class DebPackage(object): control = self._debfile.control.extractdata("control") self._sections = apt_pkg.TagSection(control) self.pkgname = self._sections["Package"] - + self._check_was_run = False + def __getitem__(self, key): return self._sections[key] @@ -393,6 +395,8 @@ class DebPackage(object): """Check if the package is installable.""" self._dbg(3, "check") + self._check_was_run = True + # check arch if not "Architecture" in self._sections: self._dbg(1, "ERROR: no architecture field") @@ -472,8 +476,8 @@ class DebPackage(object): def missing_deps(self): """Return missing dependencies.""" self._dbg(1, "Installing: %s" % self._need_pkgs) - if not self._need_pkgs: - self.check() + if not self._check_was_run: + raise ValueError("property only available after check() was run") return self._need_pkgs @property @@ -485,8 +489,8 @@ class DebPackage(object): install = [] remove = [] unauthenticated = [] - if not self._cache: - self.check() + if not self._check_was_run: + raise ValueError("property only available after check() was run") for pkg in self._cache: if pkg.marked_install or pkg.marked_upgrade: install.append(pkg.name) @@ -641,7 +645,8 @@ class DscSrcPackage(DebPackage): "source package '%s' that builds %s\n") % (self.pkgname, " ".join(self.binaries)) self._sections["Description"] = s - + self._check_was_run = False + def check(self): """Check if the package is installable..""" if not self.check_conflicts(): @@ -649,6 +654,8 @@ class DscSrcPackage(DebPackage): if self._cache[pkgname]._pkg.essential: raise Exception(_("An essential package would be removed")) self._cache[pkgname].mark_delete() + # properties are ok now + self._check_was_run = True # FIXME: a additional run of the check_conflicts() # after _satisfy_depends() should probably be done return self._satisfy_depends(self.depends) diff --git a/debian/changelog b/debian/changelog index 27eb8e97..cc922735 100644 --- a/debian/changelog +++ b/debian/changelog @@ -26,13 +26,13 @@ python-apt (0.8.2) UNRELEASED; urgency=low - set Dir::bin::dpkg if a alternate rootdir is given (LP: #885895) * build fixes for the apt in experimental + * apt/debfile.py: raise error when accessing require_changes and + missing_deps without calling check() before, thanks to + Tshepang Lekhonkhobe (closes: #624379) [ Tshepang Lekhonkhobe ] * rm usage of camelcase in cache.py doc (closes: #626617) * grammar fix in the cache.py doc (closes: #626610) - * apt/debfile.py: Remove the need to explcitly call check() in order - to get output from require_changes and missing_deps - (closes: #624379) -- Michael Vogt Wed, 19 Oct 2011 18:03:42 +0200 diff --git a/tests/test_debfile.py b/tests/test_debfile.py index 501b8f61..e6c475d3 100644 --- a/tests/test_debfile.py +++ b/tests/test_debfile.py @@ -88,11 +88,6 @@ class TestDebfilee(unittest.TestCase): "Samuel Lidén Borell ") def test_content(self): - # no python-debian for python3 yet, so fail gracefully - try: - import debian - except ImportError: - return # normal deb = apt.debfile.DebPackage(cache=self.cache) deb.open(os.path.join("data", "test_debs", "gdebi-test11.deb")) @@ -119,6 +114,13 @@ Description: testpackage for gdebi - contains usr/bin/binary for file reading deb = apt.debfile.DebPackage("./data/test_debs/data-tar-xz.deb") self.assertEqual(deb.filelist, ["./", "usr/", "usr/bin/"]) + def test_check_exception(self): + deb = apt.debfile.DebPackage("./data/test_debs/data-tar-xz.deb") + with self.assertRaises(ValueError): + deb.missing_deps + deb.check() + deb.missing_deps + def test_no_supported_data_tar(self): # ensure that a unknown data.tar.xxx raises a exception raised = False -- cgit v1.2.3 From 0af443d9a182b0fec5f9963edef3c1f1908e831f Mon Sep 17 00:00:00 2001 From: Michael Vogt Date: Tue, 22 Nov 2011 18:06:30 +0100 Subject: cleanup --- apt/cache.py | 14 +++++++------- apt/debfile.py | 4 ++-- 2 files changed, 9 insertions(+), 9 deletions(-) (limited to 'apt') diff --git a/apt/cache.py b/apt/cache.py index b4d67fa5..96b38a57 100644 --- a/apt/cache.py +++ b/apt/cache.py @@ -46,13 +46,13 @@ class LockFailedException(IOError): class Cache(object): """Dictionary-like package cache. - The APT cache file contains a hash table mapping names of binary - packages to their metadata. A Cache object is the in-core - representation of the same. It provides access to APTs idea of the - list of available packages. - - The cache can be used like a mapping from package names to Package - objects (although only getting items is supported). + The APT cache file contains a hash table mapping names of binary + packages to their metadata. A Cache object is the in-core + representation of the same. It provides access to APTs idea of the + list of available packages. + + The cache can be used like a mapping from package names to Package + objects (although only getting items is supported). Keyword arguments: progress -- a OpProgress object diff --git a/apt/debfile.py b/apt/debfile.py index 5b9274b2..277262d7 100644 --- a/apt/debfile.py +++ b/apt/debfile.py @@ -477,7 +477,7 @@ class DebPackage(object): """Return missing dependencies.""" self._dbg(1, "Installing: %s" % self._need_pkgs) if not self._check_was_run: - raise ValueError("property only available after check() was run") + raise AttributeError("property only available after check() was run") return self._need_pkgs @property @@ -490,7 +490,7 @@ class DebPackage(object): remove = [] unauthenticated = [] if not self._check_was_run: - raise ValueError("property only available after check() was run") + raise AttributeError("property only available after check() was run") for pkg in self._cache: if pkg.marked_install or pkg.marked_upgrade: install.append(pkg.name) -- cgit v1.2.3 From fb4ef05de0a64f75f2896d9e74c88a3bb3db00c7 Mon Sep 17 00:00:00 2001 From: Michael Vogt Date: Tue, 22 Nov 2011 18:36:46 +0100 Subject: apt/package.py: use lt() instead of cmp() --- apt/package.py | 4 ++-- debian/changelog | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) (limited to 'apt') diff --git a/apt/package.py b/apt/package.py index cb373c2e..45d6044b 100644 --- a/apt/package.py +++ b/apt/package.py @@ -707,8 +707,8 @@ class Package(object): return '' % (self._pkg.name, self._pkg.architecture, self._pkg.id) - def __cmp__(self, other): - return cmp(self.name, other.name) + def __lt__(self, other): + return self.name < other.name def candidate(self): """Return the candidate version of the package. diff --git a/debian/changelog b/debian/changelog index cc922735..fece12fb 100644 --- a/debian/changelog +++ b/debian/changelog @@ -18,7 +18,7 @@ python-apt (0.8.2) UNRELEASED; urgency=low * apt/cache.py: - remove "print" when creating dirs in apt.Cache(rootdir=dir), thanks to Martin Pitt - - add __cmp__ to apt.Package so that sort() sorts by name + - add __lt__ to apt.Package so that sort() sorts by name on list of package objects * debian/control: - add recommends to xz-lzma to ensure we have the unlzma command -- cgit v1.2.3 From c4c1197922edb4f82390f7e0f125be34863d24d3 Mon Sep 17 00:00:00 2001 From: Michael Vogt Date: Thu, 1 Dec 2011 12:03:16 +0100 Subject: * apt/progress/base.py: - write exception text to stderr to avoid hidding exceptions like "pre-configure failed" from libapt (thanks to Jean-Baptiste Lallement) --- apt/progress/base.py | 4 +++- debian/changelog | 4 ++++ 2 files changed, 7 insertions(+), 1 deletion(-) (limited to 'apt') diff --git a/apt/progress/base.py b/apt/progress/base.py index 97375431..4943978c 100644 --- a/apt/progress/base.py +++ b/apt/progress/base.py @@ -27,6 +27,7 @@ import fcntl import os import re import select +import sys import apt_pkg @@ -196,7 +197,8 @@ class InstallProgress(object): os._exit(os.spawnlp(os.P_WAIT, "dpkg", "dpkg", "--status-fd", str(self.write_stream.fileno()), "-i", obj)) - except Exception: + except Exception as e: + sys.stderr.write("%s\n" % e) os._exit(apt_pkg.PackageManager.RESULT_FAILED) self.child_pid = pid diff --git a/debian/changelog b/debian/changelog index 7150adf7..569c335d 100644 --- a/debian/changelog +++ b/debian/changelog @@ -30,6 +30,10 @@ python-apt (0.8.2) UNRELEASED; urgency=low for dep in or_dep_group: do_something() (thanks to Christop Groth) + * apt/progress/base.py: + - write exception text to stderr to avoid hidding exceptions + like "pre-configure failed" from libapt (thanks to Jean-Baptiste + Lallement) [ Tshepang Lekhonkhobe ] * rm usage of camelcase in cache.py doc (closes: #626617) -- cgit v1.2.3 From c8bfdda85fbd2df250fbc6d3faeb2bd5875a77d6 Mon Sep 17 00:00:00 2001 From: Michael Vogt Date: Thu, 1 Dec 2011 13:32:42 +0100 Subject: py3 fixes/clean --- apt/progress/base.py | 2 ++ tests/test_debfile.py | 4 ++-- tests/test_utils.py | 1 - 3 files changed, 4 insertions(+), 3 deletions(-) (limited to 'apt') diff --git a/apt/progress/base.py b/apt/progress/base.py index 4943978c..c2a9a9c8 100644 --- a/apt/progress/base.py +++ b/apt/progress/base.py @@ -192,12 +192,14 @@ class InstallProgress(object): # and the execution continues in the # parent code leading to very confusing bugs try: + raise Exception("foo") os._exit(obj.do_install(self.write_stream.fileno())) except AttributeError: os._exit(os.spawnlp(os.P_WAIT, "dpkg", "dpkg", "--status-fd", str(self.write_stream.fileno()), "-i", obj)) except Exception as e: + os.write(self.writefd, "pmerror:::%s" % e) sys.stderr.write("%s\n" % e) os._exit(apt_pkg.PackageManager.RESULT_FAILED) diff --git a/tests/test_debfile.py b/tests/test_debfile.py index 501b8f61..56410153 100644 --- a/tests/test_debfile.py +++ b/tests/test_debfile.py @@ -131,12 +131,12 @@ Description: testpackage for gdebi - contains usr/bin/binary for file reading self.assertTrue(raised) def test_multiarch_deb(self): - print apt_pkg.get_architectures() if apt_pkg.get_architectures() != ["amd64", "i386"]: logging.warn("skipping test because running on a non-multiarch system") return deb = apt.debfile.DebPackage("./data/test_debs/multiarch-test1_i386.deb") - print deb.missing_deps() + res = deb.check() + # FIXME: do something sensible with the multiarch test diff --git a/tests/test_utils.py b/tests/test_utils.py index 23511f32..26ee0bff 100644 --- a/tests/test_utils.py +++ b/tests/test_utils.py @@ -7,7 +7,6 @@ # notice and this notice are preserved. import sys -sys.path.insert(0, "..") import apt_pkg import apt.utils import datetime -- cgit v1.2.3 From fb595849bc7149619b3e47b65f145a1f66fc7dbe Mon Sep 17 00:00:00 2001 From: Michael Vogt Date: Thu, 1 Dec 2011 13:45:58 +0100 Subject: fix debfile binary test for py3 --- apt/debfile.py | 11 +++++++++-- tests/test_debfile.py | 5 ----- 2 files changed, 9 insertions(+), 7 deletions(-) (limited to 'apt') diff --git a/apt/debfile.py b/apt/debfile.py index 104b0814..4a82842a 100644 --- a/apt/debfile.py +++ b/apt/debfile.py @@ -525,12 +525,19 @@ class DebPackage(object): @staticmethod def to_strish(in_data): + # helper for py3 compat, in_data is str in py2 and bytes in py3 + def my_ord(c): + if type(c) == int: + return c + else: + return ord(c) + # convert s = "" for c in in_data: - if ord(c) < 10 or ord(c) > 127: + if my_ord(c) < 10 or my_ord(c) > 127: s += " " else: - s += c + s += chr(c) return s def _get_content(self, part, name, auto_decompress=True, auto_hex=True): diff --git a/tests/test_debfile.py b/tests/test_debfile.py index 56410153..70979689 100644 --- a/tests/test_debfile.py +++ b/tests/test_debfile.py @@ -88,11 +88,6 @@ class TestDebfilee(unittest.TestCase): "Samuel Lidén Borell ") def test_content(self): - # no python-debian for python3 yet, so fail gracefully - try: - import debian - except ImportError: - return # normal deb = apt.debfile.DebPackage(cache=self.cache) deb.open(os.path.join("data", "test_debs", "gdebi-test11.deb")) -- cgit v1.2.3 From 1c6b01aa63e440444f00ec9e3f527e1c65a63787 Mon Sep 17 00:00:00 2001 From: Michael Vogt Date: Thu, 1 Dec 2011 13:55:04 +0100 Subject: apt/progress/base.py: fix silly leftover from last commit --- apt/progress/base.py | 2 -- 1 file changed, 2 deletions(-) (limited to 'apt') diff --git a/apt/progress/base.py b/apt/progress/base.py index c2a9a9c8..4943978c 100644 --- a/apt/progress/base.py +++ b/apt/progress/base.py @@ -192,14 +192,12 @@ class InstallProgress(object): # and the execution continues in the # parent code leading to very confusing bugs try: - raise Exception("foo") os._exit(obj.do_install(self.write_stream.fileno())) except AttributeError: os._exit(os.spawnlp(os.P_WAIT, "dpkg", "dpkg", "--status-fd", str(self.write_stream.fileno()), "-i", obj)) except Exception as e: - os.write(self.writefd, "pmerror:::%s" % e) sys.stderr.write("%s\n" % e) os._exit(apt_pkg.PackageManager.RESULT_FAILED) -- cgit v1.2.3 From d236f527f85b185144875e0e7ad9102c4c2dabd0 Mon Sep 17 00:00:00 2001 From: Michael Vogt Date: Thu, 1 Dec 2011 13:55:28 +0100 Subject: apt/debfile.py: add py3 compat for to_strish() --- apt/debfile.py | 26 ++++++++++++++------------ 1 file changed, 14 insertions(+), 12 deletions(-) (limited to 'apt') diff --git a/apt/debfile.py b/apt/debfile.py index 4a82842a..a7202eba 100644 --- a/apt/debfile.py +++ b/apt/debfile.py @@ -525,19 +525,21 @@ class DebPackage(object): @staticmethod def to_strish(in_data): - # helper for py3 compat, in_data is str in py2 and bytes in py3 - def my_ord(c): - if type(c) == int: - return c - else: - return ord(c) - # convert s = "" - for c in in_data: - if my_ord(c) < 10 or my_ord(c) > 127: - s += " " - else: - s += chr(c) + # py2 compat, in_data is type string + if type(in_data) == str: + for c in in_data: + if ord(c) < 10 or ord(c) > 127: + s += " " + else: + s += c + # py3 compat, in_data is type bytes + else: + for b in in_data: + if b < 10 or b > 127: + s += " " + else: + s += chr(b) return s def _get_content(self, part, name, auto_decompress=True, auto_hex=True): -- cgit v1.2.3 From 350bb4a03d6562ddba12fbb0a34610aac7706b3c Mon Sep 17 00:00:00 2001 From: Michael Vogt Date: Thu, 8 Dec 2011 17:26:57 +0100 Subject: handle architecture-specific conflicts correctly (LP: #829138) --- apt/debfile.py | 3 ++- debian/changelog | 7 +++++++ 2 files changed, 9 insertions(+), 1 deletion(-) (limited to 'apt') diff --git a/apt/debfile.py b/apt/debfile.py index c91ce349..17f1dbd9 100644 --- a/apt/debfile.py +++ b/apt/debfile.py @@ -310,6 +310,7 @@ class DebPackage(object): size = float(len(self._cache)) steps = max(int(size/50), 1) debver = self._sections["Version"] + debarch = self._sections["Architecture"] # store what we provide so that we can later check against that provides = [ x[0][0] for x in self.provides] for (i, pkg) in enumerate(self._cache): @@ -338,7 +339,7 @@ class DebPackage(object): if "Conflicts" in ver.depends_list: for conflicts_ver_list in ver.depends_list["Conflicts"]: for c_or in conflicts_ver_list: - if c_or.target_pkg.name == self.pkgname: + if c_or.target_pkg.name == self.pkgname and c_or.target_pkg.architecture == debarch: if apt_pkg.check_dep(debver, c_or.comp_type, c_or.target_ver): self._dbg(2, "would break (conflicts) %s" % pkg.name) # TRANSLATORS: the first '%s' is the package that conflicts, the second the packagename that it conflicts with (so the name of the deb the user tries to install), the third is the relation (e.g. >=) and the last is the version for the relation diff --git a/debian/changelog b/debian/changelog index 4a90e907..d5b68082 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,3 +1,10 @@ +python-apt (0.8.3) UNRELEASED; urgency=low + + [ Alexey Feldgendler ] + * handle architecture-specific conflicts correctly (LP: #829138) + + -- Michael Vogt Thu, 08 Dec 2011 17:26:37 +0100 + python-apt (0.8.2) unstable; urgency=low [ Michael Vogt ] -- cgit v1.2.3 From 10ec8cd5b5614606b3db9fdc3d6e248984048f0e Mon Sep 17 00:00:00 2001 From: Michael Vogt Date: Mon, 19 Dec 2011 12:06:06 +0100 Subject: * apt/debfile.py: - fix crash in dep multiarch handling --- apt/debfile.py | 3 ++- debian/changelog | 2 ++ 2 files changed, 4 insertions(+), 1 deletion(-) (limited to 'apt') diff --git a/apt/debfile.py b/apt/debfile.py index 160a4a72..cd027c71 100644 --- a/apt/debfile.py +++ b/apt/debfile.py @@ -94,7 +94,8 @@ class DebPackage(object): return pkgname elif self._cache.is_virtual_package(pkgname): return pkgname - elif self._cache[pkgname].candidate.architecture == "all": + elif (pkgname in self._cache and + self._cache[pkgname].candidate.architecture == "all"): return pkgname # now do the real multiarch checking multiarch_pkgname = "%s:%s" % (pkgname, self._multiarch) diff --git a/debian/changelog b/debian/changelog index fcf8ff98..df486b60 100644 --- a/debian/changelog +++ b/debian/changelog @@ -8,6 +8,8 @@ python-apt (0.8.3) unstable; urgency=low - add multiarch support to the debfile.py code * tests/test_apt_cache.py: - add additional check if provides test can actually be run + * apt/debfile.py: + - fix crash in dep multiarch handling -- Michael Vogt Thu, 08 Dec 2011 20:31:52 +0100 -- cgit v1.2.3 From 375920c5172a5f60acba02b73c3b32ffbccc8ab2 Mon Sep 17 00:00:00 2001 From: Michael Vogt Date: Mon, 19 Dec 2011 13:35:59 +0100 Subject: pyflakes cleanup, use apt_pkg.gettext in aptsources too --- apt/debfile.py | 12 ++---------- apt/progress/gtk2.py | 3 --- aptsources/distinfo.py | 6 +----- aptsources/distro.py | 4 +--- aptsources/sourceslist.py | 3 +-- 5 files changed, 5 insertions(+), 23 deletions(-) (limited to 'apt') diff --git a/apt/debfile.py b/apt/debfile.py index cd027c71..ab24c50b 100644 --- a/apt/debfile.py +++ b/apt/debfile.py @@ -160,10 +160,6 @@ class DebPackage(object): def _satisfy_or_group(self, or_group): """Try to satisfy the or_group.""" - - or_found = False - virtual_pkg = None - for dep in or_group: depname, ver, oper = dep @@ -233,10 +229,6 @@ class DebPackage(object): def _check_conflicts_or_group(self, or_group): """Check the or-group for conflicts with installed pkgs.""" self._dbg(2, "_check_conflicts_or_group(): %s " % (or_group)) - - or_found = False - virtual_pkg = None - for dep in or_group: depname = dep[0] ver = dep[1] @@ -514,7 +506,7 @@ class DebPackage(object): for pkg in self._need_pkgs: try: self._cache[pkg].mark_install(from_user=False) - except SystemError as e: + except SystemError: self._failure_string = _("Cannot install '%s'") % pkg self._cache.clear() return False @@ -607,7 +599,7 @@ class DebPackage(object): # auto-convert to hex try: data = unicode(data, "utf-8") - except Exception as e: + except Exception: new_data = _("Automatically converted to printable ascii:\n") new_data += self.to_strish(data) return new_data diff --git a/apt/progress/gtk2.py b/apt/progress/gtk2.py index b5794e92..c2635ca0 100644 --- a/apt/progress/gtk2.py +++ b/apt/progress/gtk2.py @@ -22,9 +22,6 @@ # USA """GObject-powered progress classes and a GTK+ status widget.""" -import os -import time - import pygtk pygtk.require('2.0') import gtk diff --git a/aptsources/distinfo.py b/aptsources/distinfo.py index c8ec5c46..ec162c2d 100644 --- a/aptsources/distinfo.py +++ b/aptsources/distinfo.py @@ -24,17 +24,13 @@ import errno import logging import os -import gettext from os import getenv from subprocess import Popen, PIPE import re import apt_pkg - -def _(s): - return gettext.dgettext("python-apt", s) - +from apt_pkg import gettext as _ class Template(object): diff --git a/aptsources/distro.py b/aptsources/distro.py index f777a4ea..b4056b27 100644 --- a/aptsources/distro.py +++ b/aptsources/distro.py @@ -30,9 +30,7 @@ import sys from xml.etree.ElementTree import ElementTree import gettext - -def _(s): - return gettext.dgettext("python-apt", s) +from apt_pkg import gettext as _ class NoDistroTemplateException(Exception): diff --git a/aptsources/sourceslist.py b/aptsources/sourceslist.py index 40a0379b..e3b8c9be 100644 --- a/aptsources/sourceslist.py +++ b/aptsources/sourceslist.py @@ -23,7 +23,6 @@ # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 # USA -import gettext import glob import logging import os.path @@ -35,6 +34,7 @@ import time import apt_pkg from distinfo import DistInfo from apt.deprecation import function_deprecated_by +from apt_pkg import gettext as _ # some global helpers @@ -446,7 +446,6 @@ class SourceEntryMatcher(object): def match(self, source): """Add a matching template to the source""" - _ = gettext.gettext found = False for template in self.templates: if (re.search(template.match_uri, source.uri) and -- cgit v1.2.3 From accf60bda95e6a1bad53f26412b12a32f136d54d Mon Sep 17 00:00:00 2001 From: Michael Vogt Date: Tue, 31 Jan 2012 14:52:42 +0100 Subject: * apt/debfile.py: - use apt_inst for reading the control_filelist * debian/control: - remove no longer needed dependency on python-debian --- apt/debfile.py | 37 ++++++++++++++++++++----------------- debian/changelog | 4 ++++ debian/control | 1 - 3 files changed, 24 insertions(+), 18 deletions(-) (limited to 'apt') diff --git a/apt/debfile.py b/apt/debfile.py index ab24c50b..6a189502 100644 --- a/apt/debfile.py +++ b/apt/debfile.py @@ -40,8 +40,12 @@ class DebPackage(object): VERSION_SAME, VERSION_NEWER) = range(4) - _supported_data_members = ("data.tar.gz", "data.tar.bz2", "data.tar.lzma", - "data.tar.xz") + _supported_data_members = ( + "data.tar.gz", + "data.tar.bz2", + "data.tar.lzma", + "data.tar.xz", + ) debug = 0 @@ -82,10 +86,22 @@ class DebPackage(object): try: self._debfile.data.go(lambda item, data: files.append(item.name)) except SystemError: - return [_("List of files for '%s' could not be read" % - self.filename)] + return [_("List of files for '%s' could not be read") % + self.filename] return files + @property + def control_filelist(self): + """ return the list of files in control.tar.gt """ + control = [] + try: + self._debfile.control.go(lambda item, data: control.append(item.name)) + except SystemError: + return [_("List of control files for '%s' could not be read") % + self.filename] + return sorted(control) + + # helper that will return a pkgname with a multiarch suffix if needed def _maybe_append_multiarch_suffix(self, pkgname, in_conflict_checking=False): @@ -545,19 +561,6 @@ class DebPackage(object): remove.append(pkg.name) return (install, remove, unauthenticated) - @property - def control_filelist(self): - """ return the list of files in control.tar.gt """ - try: - from debian.debfile import DebFile - except: - raise Exception(_("Python-debian module not available")) - content = [] - for name in DebFile(self.filename).control: - if name and name != ".": - content.append(name) - return sorted(content) - @staticmethod def to_hex(in_data): hex = "" diff --git a/debian/changelog b/debian/changelog index 1e400dfb..17fe379e 100644 --- a/debian/changelog +++ b/debian/changelog @@ -3,6 +3,10 @@ python-apt (0.8.4) UNRELEASED; urgency=low * doc/examples/build-deps.py: - update the build-deps.py example to use the apt API more * add support for apt_pkg.Policy.get_priority(PkgFileIterator) + * apt/debfile.py: + - use apt_inst for reading the control_filelist + * debian/control: + - remove no longer needed dependency on python-debian -- Michael Vogt Wed, 04 Jan 2012 12:07:48 +0100 diff --git a/debian/control b/debian/control index 541e4c36..29392858 100644 --- a/debian/control +++ b/debian/control @@ -27,7 +27,6 @@ Breaks: packagekit-backend-apt (<= 0.4.8-0ubuntu4), computer-janitor (<< 1.14.1-1+), debdelta (<< 0.41+), python-dogtail (<< 0.6.1-3.1+), - python-debian (<< 0.1.18+), python-software-properties (<< 0.70.debian-1+), aptdaemon (<< 0.11+bzr343-1~), apt-forktracer (<< 0.3), -- cgit v1.2.3 From 6f538d96ec35e80e19f1b33cad86b9ae45da54f5 Mon Sep 17 00:00:00 2001 From: Michael Vogt Date: Tue, 31 Jan 2012 14:58:14 +0100 Subject: apt/debfile.py: kill _supported_data_members its actually not used anymore and instead done dynamically in arfile.cc using the APT::Configuration::getCompressionTypes() call (yeah!) --- apt/debfile.py | 7 ------- 1 file changed, 7 deletions(-) (limited to 'apt') diff --git a/apt/debfile.py b/apt/debfile.py index 6a189502..2eb807b8 100644 --- a/apt/debfile.py +++ b/apt/debfile.py @@ -40,13 +40,6 @@ class DebPackage(object): VERSION_SAME, VERSION_NEWER) = range(4) - _supported_data_members = ( - "data.tar.gz", - "data.tar.bz2", - "data.tar.lzma", - "data.tar.xz", - ) - debug = 0 def __init__(self, filename=None, cache=None): -- cgit v1.2.3 From da171aa93337802c19d8caa99a4ab8516bdbc5bd Mon Sep 17 00:00:00 2001 From: Michael Vogt Date: Tue, 27 Mar 2012 11:21:20 +0200 Subject: * apt/package.py: - if there is no Version.uri return None --- apt/package.py | 5 ++++- debian/changelog | 2 ++ 2 files changed, 6 insertions(+), 1 deletion(-) (limited to 'apt') diff --git a/apt/package.py b/apt/package.py index 73f68c87..29eec909 100644 --- a/apt/package.py +++ b/apt/package.py @@ -545,7 +545,10 @@ class Version(object): .. versionadded:: 0.7.10 """ - return iter(self._uris()).next() + try: + return iter(self._uris()).next() + except StopIteration: + return None def fetch_binary(self, destdir='', progress=None): """Fetch the binary version of the package. diff --git a/debian/changelog b/debian/changelog index 5813656a..b1bf28d6 100644 --- a/debian/changelog +++ b/debian/changelog @@ -13,6 +13,8 @@ python-apt (0.8.4) UNRELEASED; urgency=low * python/tag.cc, tests/test_tagfile.py: - add support a filename argument in apt_pkg.TagFile() (in addition to the file object currently supported) + * apt/package.py: + - if there is no Version.uri return None -- Michael Vogt Wed, 04 Jan 2012 12:07:48 +0100 -- cgit v1.2.3 From 23d4a5fc9ca8dc7a35fd96a48dcb261ac0daf46b Mon Sep 17 00:00:00 2001 From: Michael Vogt Date: Wed, 28 Mar 2012 10:19:51 +0200 Subject: * apt/cache.py: - fix _have_multi_arch flag (thanks to Sebastian Heinlein) --- apt/cache.py | 3 +-- debian/changelog | 2 ++ 2 files changed, 3 insertions(+), 2 deletions(-) (limited to 'apt') diff --git a/apt/cache.py b/apt/cache.py index 96b38a57..86a788bb 100644 --- a/apt/cache.py +++ b/apt/cache.py @@ -152,8 +152,7 @@ class Cache(object): self._sorted_set = None self._weakref.clear() - self._have_multi_arch = bool(apt_pkg.config.value_list("APT::" + - "Architectures")) + self._have_multi_arch = len(apt_pkg.get_architectures()) > 1 progress.op = _("Building data structures") i = last = 0 diff --git a/debian/changelog b/debian/changelog index b1bf28d6..a163f03a 100644 --- a/debian/changelog +++ b/debian/changelog @@ -15,6 +15,8 @@ python-apt (0.8.4) UNRELEASED; urgency=low to the file object currently supported) * apt/package.py: - if there is no Version.uri return None + * apt/cache.py: + - fix _have_multi_arch flag (thanks to Sebastian Heinlein) -- Michael Vogt Wed, 04 Jan 2012 12:07:48 +0100 -- cgit v1.2.3 From 8ab2e648e11c321c31d88e4c135066bf85816160 Mon Sep 17 00:00:00 2001 From: Sebastian Heinlein Date: Wed, 30 May 2012 10:15:35 +0200 Subject: Import AptAuth.py from software-properites as auth.py --- apt/auth.py | 98 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 98 insertions(+) create mode 100644 apt/auth.py (limited to 'apt') diff --git a/apt/auth.py b/apt/auth.py new file mode 100644 index 00000000..04ee8b76 --- /dev/null +++ b/apt/auth.py @@ -0,0 +1,98 @@ +# dialog_apt_key.py.in - edit the apt keys +# +# Copyright (c) 2004 Canonical +# +# Author: Michael Vogt +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License as +# published by the Free Software Foundation; either version 2 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 +# USA + +import atexit +import gettext +import os +import shutil +import subprocess +import tempfile + +from subprocess import PIPE + +# gettext convenient +_ = gettext.gettext +def dummy(e): return e +N_ = dummy + +# some known keys +N_("Ubuntu Archive Automatic Signing Key ") +N_("Ubuntu CD Image Automatic Signing Key ") + +class AptAuth: + def __init__(self, rootdir="/"): + self.gpg = ["/usr/bin/gpg"] + self.base_opt = self.gpg + [ + "--no-options", + "--no-default-keyring", + "--no-auto-check-trustdb", + "--trust-model", "always", + "--keyring", os.path.join(rootdir, "etc/apt/trusted.gpg"), + ] + self.tmpdir = tempfile.mkdtemp() + self.base_opt += ["--secret-keyring", + os.path.join(self.tmpdir, "secring.gpg")] + self.base_opt += ["--trustdb-name", + os.path.join(self.tmpdir, "trustdb.gpg")] + self.list_opt = self.base_opt + ["--with-colons", + "--batch", + "--list-keys"] + self.rm_opt = self.base_opt + ["--quiet", + "--batch", + "--delete-key", + "--yes"] + self.add_opt = self.base_opt + ["--quiet", + "--batch", + "--import"] + atexit.register(self._cleanup_tmpdir) + + def _cleanup_tmpdir(self): + shutil.rmtree(self.tmpdir) + + def list(self): + res = [] + #print self.list_opt + p = subprocess.Popen(self.list_opt,stdout=PIPE).stdout + for line in p.readlines(): + fields = line.split(":") + if fields[0] == "pub": + name = fields[9] + res.append("%s %s\n%s" %((fields[4])[-8:],fields[5], _(name))) + return res + + def add(self, filename): + #print "request to add " + filename + cmd = self.add_opt[:] + cmd.append(filename) + p = subprocess.Popen(cmd) + return (p.wait() == 0) + + def update(self): + cmd = ["/usr/bin/apt-key", "update"] + p = subprocess.Popen(cmd) + return (p.wait() == 0) + + def rm(self, key): + #print "request to remove " + key + cmd = self.rm_opt[:] + cmd.append(key) + p = subprocess.Popen(cmd) + return (p.wait() == 0) -- cgit v1.2.3 From 2fa1eb3cfd05e981e1ab6daf2589f8d6e92c4bf6 Mon Sep 17 00:00:00 2001 From: Sebastian Heinlein Date: Thu, 31 May 2012 08:45:24 +0200 Subject: A lot of refactoring --- apt/auth.py | 262 ++++++++++++++++++++++++++++++++++++++++++------------------ 1 file changed, 186 insertions(+), 76 deletions(-) (limited to 'apt') diff --git a/apt/auth.py b/apt/auth.py index 04ee8b76..97f9d578 100644 --- a/apt/auth.py +++ b/apt/auth.py @@ -1,9 +1,12 @@ -# dialog_apt_key.py.in - edit the apt keys -# +#!/usr/bin/env python +# -*- coding: utf-8 -*- +# auth - authentication key management +# # Copyright (c) 2004 Canonical -# +# # Author: Michael Vogt -# +# Sebastian Heinlein +# # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License as # published by the Free Software Foundation; either version 2 of the @@ -13,86 +16,193 @@ # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. -# +# # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 # USA +"""Handle GnuPG keys used to trust signed repositories.""" import atexit -import gettext +import glob import os +import os.path import shutil import subprocess import tempfile -from subprocess import PIPE - -# gettext convenient -_ = gettext.gettext -def dummy(e): return e -N_ = dummy - -# some known keys -N_("Ubuntu Archive Automatic Signing Key ") -N_("Ubuntu CD Image Automatic Signing Key ") - -class AptAuth: - def __init__(self, rootdir="/"): - self.gpg = ["/usr/bin/gpg"] - self.base_opt = self.gpg + [ - "--no-options", - "--no-default-keyring", - "--no-auto-check-trustdb", - "--trust-model", "always", - "--keyring", os.path.join(rootdir, "etc/apt/trusted.gpg"), - ] - self.tmpdir = tempfile.mkdtemp() - self.base_opt += ["--secret-keyring", - os.path.join(self.tmpdir, "secring.gpg")] - self.base_opt += ["--trustdb-name", - os.path.join(self.tmpdir, "trustdb.gpg")] - self.list_opt = self.base_opt + ["--with-colons", - "--batch", - "--list-keys"] - self.rm_opt = self.base_opt + ["--quiet", - "--batch", - "--delete-key", - "--yes"] - self.add_opt = self.base_opt + ["--quiet", - "--batch", - "--import"] - atexit.register(self._cleanup_tmpdir) - - def _cleanup_tmpdir(self): - shutil.rmtree(self.tmpdir) - - def list(self): - res = [] - #print self.list_opt - p = subprocess.Popen(self.list_opt,stdout=PIPE).stdout - for line in p.readlines(): - fields = line.split(":") - if fields[0] == "pub": - name = fields[9] - res.append("%s %s\n%s" %((fields[4])[-8:],fields[5], _(name))) - return res - - def add(self, filename): - #print "request to add " + filename - cmd = self.add_opt[:] - cmd.append(filename) - p = subprocess.Popen(cmd) - return (p.wait() == 0) - - def update(self): - cmd = ["/usr/bin/apt-key", "update"] - p = subprocess.Popen(cmd) - return (p.wait() == 0) - - def rm(self, key): - #print "request to remove " + key - cmd = self.rm_opt[:] - cmd.append(key) - p = subprocess.Popen(cmd) - return (p.wait() == 0) +import apt_pkg +from apt_pkg import gettext as _ + +# Create a temporary dir to store secret keying and trust database. +# APT doesn't use a secrect key ring but GnuPG fails without it. +_TMPDIR = tempfile.mkdtemp() +atexit.register(shutil.rmtree, _TMPDIR) + + +class TrustedKey(object): + + """Represents a trusted key.""" + + def __init__(self, name, keyid, date): + self.raw_name = name + # Allow to translated some known keys + self.name = _(name) + self.keyid = keyid + self.date = date + + def __str__(self): + return "%s\n%s %s" % (self.name, self.keyid, self.date) + + +def _get_gpg_command(keyring=None): + """Return the gpg command""" + cmd = [apt_pkg.config.find_file("Dir::Bin::Gpg", "/usr/bin/gpg"), + "--ignore-time-conflict", + "--no-default-keyring", + "--trust-model", "always", + "--no-options", + "--secret-keyring", os.path.join(_TMPDIR, "secring.gpg")] + if keyring is None: + # Add the public keyring + cmd.extend(["--keyring", + apt_pkg.config.find_file("Dir::Etc::Trusted"), + "--primary-keyring", + apt_pkg.config.find_file("Dir::Etc::Trusted")]) + # Add the public keyring parts + trusted_parts_dir = apt_pkg.config.find_dir("Dir::Etc::TrustedParts") + for part_name in glob.glob(os.path.join(trusted_parts_dir, "*.gpg")): + part_path = os.path.join(trusted_parts_dir, part_name) + if os.access(part_path, os.R_OK): + cmd.extend(["--keyring", part_path]) + # TrustDB + trustdb_path = os.path.join(apt_pkg.config.find_dir("Dir::Etc"), + "trustdb.gpg") + cmd.extend(["--trustdb-name", trustdb_path]) + else: + cmd.extend(["--keyring", os.path.abspath(keyring), + "--primary-keyring", os.path.abspath(keyring), + "--trustdb-name", os.path.join(_TMPDIR, "trustdb.gpg")]) + return cmd + +def _wait_and_raise(proc): + """Wait until the given subprocess is completed and raise a + SystemError if it failed. + """ + if proc.wait() != 0: + output = proc.stdout.read() + raise SystemError("GnuPG command failed: %s" % output) + +def add_key_from_file(filename, wait=True): + """Import a GnuPG key file to trust repositores signed by it. + + Keyword arguments: + filename -- the absolute path to the public GnuPG key file + wait -- if the system should be blocked until the internal GnuPG call is + completed. Otherwise the subprocess.Popen() instance will be + returned. By default the call will be blocking. + """ + if not os.path.abspath(filename): + raise SystemError("An absolute path is required: %s" % filename) + if not os.access(os.R_OK): + raise SystemError("Key file cannot be accessed: %s" % filename) + cmd = _get_gpg_command() + cmd.extend(["--quiet", "--batch", "--import", filename]) + proc = subprocess.Popen(cmd, stdout=subprocess.PIPE, + stderr=subprocess.STDOUT, + universal_newlines=True) + if wait: + _wait_and_raise(proc) + else: + return proc + +def add_key_from_keyserver(keyid, keyserver, wait=True): + """Import a GnuPG key file to trust repositores signed by it. + + Keyword arguments: + keyid -- the identifier of the key, e.g. 0x0EB12DSA + keyserver -- the URL or hostname of the key server + wait -- if the system should be blocked until the internal GnuPG call is + completed. Otherwise the subprocess.Popen() instance will be + returned. By default the call will be blocking. + """ + cmd = _get_gpg_command() + cmd.extend(["--quiet", "--batch", + "--keyserver", keyserver, + "--recv", keyid]) + proc = subprocess.Popen(cmd, stdout=subprocess.PIPE, + stderr=subprocess.STDOUT, + universal_newlines=True) + if wait: + _wait_and_raise(proc) + else: + return proc + +def add_key(content, wait=True): + """Import a GnuPG key to trust repositores signed by it. + + Keyword arguments: + content -- the content of the GnuPG public key + wait -- if the system should be blocked until the internal GnuPG call is + completed. Otherwise the subprocess.Popen() instance will be + returned. By default the call will be blocking. + """ + cmd = _get_gpg_command() + cmd.extend(["--quiet", "--batch", "--import", "-"]) + proc = subprocess.Popen(cmd, stdin=subprocess.PIPE, + stdout=subprocess.PIPE, + stderr=subprocess.STDOUT, + universal_newlines=True) + proc = subprocess.Popen(cmd) + proc.stdin.write(content) + if wait: + _wait_and_raise(proc) + else: + return proc + +def remove_key(fingerprint, wait=True): + """Remove a GnuPG key to no longer trust repositores signed by it. + + Keyword arguments: + fingerprint -- the fingerprint identifying the key + wait -- if the system should be blocked until the internal GnuPG is + completed. Otherwise the subprocess.Popen() instance will be + returned. By default the call will be blocking. + """ + cmd = _get_gpg_command() + cmd.extend(["--quiet", "--batch", "--delete-key", "--yes", fingerprint]) + proc = subprocess.Popen(cmd, stdout=subprocess.PIPE, + stderr=subprocess.STDOUT, + universal_newlines=True) + if wait: + _wait_and_raise(proc) + else: + return proc + +def list_keys(): + """Returns a list of TrustedKey instances for each key which is + used to trust repositories. + """ + cmd = _get_gpg_command() + cmd.extend(["--with-colons", "--batch", "--list-keys"]) + res = [] + proc = subprocess.Popen(cmd, stdout=subprocess.PIPE, + stderr=subprocess.STDOUT, + universal_newlines=True) + _wait_and_raise(proc) + for line in proc.stdout.readlines(): + fields = line.split(":") + if fields[0] == "pub": + key = TrustedKey(fields[9], fields[4][-8:], fields[5]) + res.append(key) + return res + +if __name__ == "__main__": + # Add some known keys we would like to see translated so that they get + # picked up by gettext + lambda: _("Ubuntu Archive Automatic Signing Key ") + lambda: _("Ubuntu CD Image Automatic Signing Key ") + + apt_pkg.init() + for trusted_key in list_keys(): + print(trusted_key) -- cgit v1.2.3 From 1b18197f00a0f8b230c11dd90b6f9799ac114d55 Mon Sep 17 00:00:00 2001 From: Sebastian Heinlein Date: Thu, 31 May 2012 19:18:46 +0200 Subject: Don't overwrite proc twice --- apt/auth.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'apt') diff --git a/apt/auth.py b/apt/auth.py index 97f9d578..d6b1d97f 100644 --- a/apt/auth.py +++ b/apt/auth.py @@ -153,8 +153,8 @@ def add_key(content, wait=True): stdout=subprocess.PIPE, stderr=subprocess.STDOUT, universal_newlines=True) - proc = subprocess.Popen(cmd) proc.stdin.write(content) + proc.stdin.close() if wait: _wait_and_raise(proc) else: -- cgit v1.2.3 From fdae874a77c122a72d283fc13c5494b0359f6b67 Mon Sep 17 00:00:00 2001 From: Sebastian Heinlein Date: Fri, 1 Jun 2012 06:37:51 +0200 Subject: Add an export_key method --- apt/auth.py | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) (limited to 'apt') diff --git a/apt/auth.py b/apt/auth.py index d6b1d97f..3b15b256 100644 --- a/apt/auth.py +++ b/apt/auth.py @@ -179,6 +179,26 @@ def remove_key(fingerprint, wait=True): else: return proc +def export_key(fingerprint, wait=True): + """Return the GnuPG key in text format. + + Keyword arguments: + fingerprint -- the fingerprint identifying the key + wait -- if the system should be blocked until the internal GnuPG is + completed. Otherwise the subprocess.Popen() instance will be + returned. By default the call will be blocking. + """ + cmd = _get_gpg_command() + cmd.extend(["--armor", "--export", fingerprint]) + proc = subprocess.Popen(cmd, stdout=subprocess.PIPE, + stderr=subprocess.STDOUT, + universal_newlines=True) + if wait: + _wait_and_raise(proc) + return proc.stdout.read() + else: + return proc + def list_keys(): """Returns a list of TrustedKey instances for each key which is used to trust repositories. -- cgit v1.2.3 From 207b245b2ce5a9a04b187f5001b99093c660701d Mon Sep 17 00:00:00 2001 From: Sebastian Heinlein Date: Fri, 1 Jun 2012 06:38:33 +0200 Subject: Don't set the trust model to always --- apt/auth.py | 1 - 1 file changed, 1 deletion(-) (limited to 'apt') diff --git a/apt/auth.py b/apt/auth.py index 3b15b256..1d018f8b 100644 --- a/apt/auth.py +++ b/apt/auth.py @@ -60,7 +60,6 @@ def _get_gpg_command(keyring=None): cmd = [apt_pkg.config.find_file("Dir::Bin::Gpg", "/usr/bin/gpg"), "--ignore-time-conflict", "--no-default-keyring", - "--trust-model", "always", "--no-options", "--secret-keyring", os.path.join(_TMPDIR, "secring.gpg")] if keyring is None: -- cgit v1.2.3 From f968fba66c7b540bb34417e9675299e283b926d1 Mon Sep 17 00:00:00 2001 From: Sebastian Heinlein Date: Fri, 1 Jun 2012 07:23:30 +0200 Subject: Strip exported key --- apt/auth.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'apt') diff --git a/apt/auth.py b/apt/auth.py index 1d018f8b..8663a043 100644 --- a/apt/auth.py +++ b/apt/auth.py @@ -194,7 +194,7 @@ def export_key(fingerprint, wait=True): universal_newlines=True) if wait: _wait_and_raise(proc) - return proc.stdout.read() + return proc.stdout.read().strip() else: return proc -- cgit v1.2.3 From 18fa7a01ebb31f5bfa465bf191989eb060713248 Mon Sep 17 00:00:00 2001 From: Sebastian Heinlein Date: Fri, 1 Jun 2012 08:18:24 +0200 Subject: Small fix --- apt/auth.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'apt') diff --git a/apt/auth.py b/apt/auth.py index 8663a043..024f01da 100644 --- a/apt/auth.py +++ b/apt/auth.py @@ -103,7 +103,7 @@ def add_key_from_file(filename, wait=True): """ if not os.path.abspath(filename): raise SystemError("An absolute path is required: %s" % filename) - if not os.access(os.R_OK): + if not os.access(filename, os.R_OK): raise SystemError("Key file cannot be accessed: %s" % filename) cmd = _get_gpg_command() cmd.extend(["--quiet", "--batch", "--import", filename]) -- cgit v1.2.3 From 621372f543a4d0547f3637889da11665b628df14 Mon Sep 17 00:00:00 2001 From: Michael Vogt Date: Tue, 5 Jun 2012 11:39:40 +0200 Subject: pep8 fixes --- apt/auth.py | 14 +++++++++++--- tests/test_auth.py | 6 +++--- 2 files changed, 14 insertions(+), 6 deletions(-) (limited to 'apt') diff --git a/apt/auth.py b/apt/auth.py index 024f01da..04aae908 100644 --- a/apt/auth.py +++ b/apt/auth.py @@ -7,11 +7,11 @@ # Author: Michael Vogt # Sebastian Heinlein # -# This program is free software; you can redistribute it and/or -# modify it under the terms of the GNU General Public License as +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License as # published by the Free Software Foundation; either version 2 of the # License, or (at your option) any later version. -# +# # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the @@ -84,6 +84,7 @@ def _get_gpg_command(keyring=None): "--trustdb-name", os.path.join(_TMPDIR, "trustdb.gpg")]) return cmd + def _wait_and_raise(proc): """Wait until the given subprocess is completed and raise a SystemError if it failed. @@ -92,6 +93,7 @@ def _wait_and_raise(proc): output = proc.stdout.read() raise SystemError("GnuPG command failed: %s" % output) + def add_key_from_file(filename, wait=True): """Import a GnuPG key file to trust repositores signed by it. @@ -115,6 +117,7 @@ def add_key_from_file(filename, wait=True): else: return proc + def add_key_from_keyserver(keyid, keyserver, wait=True): """Import a GnuPG key file to trust repositores signed by it. @@ -137,6 +140,7 @@ def add_key_from_keyserver(keyid, keyserver, wait=True): else: return proc + def add_key(content, wait=True): """Import a GnuPG key to trust repositores signed by it. @@ -159,6 +163,7 @@ def add_key(content, wait=True): else: return proc + def remove_key(fingerprint, wait=True): """Remove a GnuPG key to no longer trust repositores signed by it. @@ -178,6 +183,7 @@ def remove_key(fingerprint, wait=True): else: return proc + def export_key(fingerprint, wait=True): """Return the GnuPG key in text format. @@ -198,6 +204,7 @@ def export_key(fingerprint, wait=True): else: return proc + def list_keys(): """Returns a list of TrustedKey instances for each key which is used to trust repositories. @@ -216,6 +223,7 @@ def list_keys(): res.append(key) return res + if __name__ == "__main__": # Add some known keys we would like to see translated so that they get # picked up by gettext diff --git a/tests/test_auth.py b/tests/test_auth.py index e9906f56..7ecebb7b 100644 --- a/tests/test_auth.py +++ b/tests/test_auth.py @@ -125,9 +125,9 @@ class TestAuthKeys(unittest.TestCase): os.makedirs(trustedparts_dir) def _restore_apt_config(self, cnf): - """Restore previous apt configuration.""" - for item in cnf: - apt_pkg.config.set(item, cnf[item]) + """Restore previous apt configuration.""" + for item in cnf: + apt_pkg.config.set(item, cnf[item]) def testAddAndExportKey(self): """Add an example key.""" -- cgit v1.2.3 From a8ec7c4c6430e69953ea8f8f3925d1b0fb4ac63a Mon Sep 17 00:00:00 2001 From: Michael Vogt Date: Tue, 5 Jun 2012 11:43:43 +0200 Subject: apt/auth.py: create _TMPDIR on demand --- apt/auth.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) (limited to 'apt') diff --git a/apt/auth.py b/apt/auth.py index 04aae908..d8c602fa 100644 --- a/apt/auth.py +++ b/apt/auth.py @@ -36,8 +36,7 @@ from apt_pkg import gettext as _ # Create a temporary dir to store secret keying and trust database. # APT doesn't use a secrect key ring but GnuPG fails without it. -_TMPDIR = tempfile.mkdtemp() -atexit.register(shutil.rmtree, _TMPDIR) +_TMPDIR = None class TrustedKey(object): @@ -57,6 +56,10 @@ class TrustedKey(object): def _get_gpg_command(keyring=None): """Return the gpg command""" + global _TMPDIR + if _TMPDIR is None: + _TMPDIR = tempfile.mkdtemp() + atexit.register(shutil.rmtree, _TMPDIR) cmd = [apt_pkg.config.find_file("Dir::Bin::Gpg", "/usr/bin/gpg"), "--ignore-time-conflict", "--no-default-keyring", -- cgit v1.2.3 From b6dbdb941109b5bbfbb5042ad49e1353602240d2 Mon Sep 17 00:00:00 2001 From: Sebastian Heinlein Date: Wed, 6 Jun 2012 12:18:16 +0200 Subject: Call apt-key instead of gnupg --- apt/auth.py | 160 ++++++++++++++++++------------------------------------------ 1 file changed, 48 insertions(+), 112 deletions(-) (limited to 'apt') diff --git a/apt/auth.py b/apt/auth.py index d8c602fa..3a1d132e 100644 --- a/apt/auth.py +++ b/apt/auth.py @@ -24,20 +24,14 @@ """Handle GnuPG keys used to trust signed repositories.""" import atexit -import glob import os import os.path -import shutil import subprocess import tempfile import apt_pkg from apt_pkg import gettext as _ -# Create a temporary dir to store secret keying and trust database. -# APT doesn't use a secrect key ring but GnuPG fails without it. -_TMPDIR = None - class TrustedKey(object): @@ -54,48 +48,42 @@ class TrustedKey(object): return "%s\n%s %s" % (self.name, self.keyid, self.date) -def _get_gpg_command(keyring=None): - """Return the gpg command""" - global _TMPDIR - if _TMPDIR is None: - _TMPDIR = tempfile.mkdtemp() - atexit.register(shutil.rmtree, _TMPDIR) - cmd = [apt_pkg.config.find_file("Dir::Bin::Gpg", "/usr/bin/gpg"), - "--ignore-time-conflict", - "--no-default-keyring", - "--no-options", - "--secret-keyring", os.path.join(_TMPDIR, "secring.gpg")] - if keyring is None: - # Add the public keyring - cmd.extend(["--keyring", - apt_pkg.config.find_file("Dir::Etc::Trusted"), - "--primary-keyring", - apt_pkg.config.find_file("Dir::Etc::Trusted")]) - # Add the public keyring parts - trusted_parts_dir = apt_pkg.config.find_dir("Dir::Etc::TrustedParts") - for part_name in glob.glob(os.path.join(trusted_parts_dir, "*.gpg")): - part_path = os.path.join(trusted_parts_dir, part_name) - if os.access(part_path, os.R_OK): - cmd.extend(["--keyring", part_path]) - # TrustDB - trustdb_path = os.path.join(apt_pkg.config.find_dir("Dir::Etc"), - "trustdb.gpg") - cmd.extend(["--trustdb-name", trustdb_path]) - else: - cmd.extend(["--keyring", os.path.abspath(keyring), - "--primary-keyring", os.path.abspath(keyring), - "--trustdb-name", os.path.join(_TMPDIR, "trustdb.gpg")]) - return cmd - - -def _wait_and_raise(proc): - """Wait until the given subprocess is completed and raise a - SystemError if it failed. - """ - if proc.wait() != 0: - output = proc.stdout.read() - raise SystemError("GnuPG command failed: %s" % output) - +def _call_apt_key_script(*args, **kwargs): + """Run the apt-key script with the given arguments.""" + cmd = [apt_pkg.config.find_file("Dir::Bin::Apt-Key", "/usr/bin/apt-key")] + cmd.extend(args) + if os.getuid() != 0: + cmd.insert(0, "fakeroot") + env = os.environ.copy() + env["LANG"] = "C" + if apt_pkg.config.find_dir("Dir") != "/": + # If the key is to be installed into a chroot we have to export the + # configuration from the chroot to the apt-key script by using + # a temporary APT_CONFIG file. The apt-key script uses apt-config shell + # internally + conf_fd, conf_name = tempfile.mkstemp(prefix="apt-key", suffix="conf") + atexit.register(os.remove, conf_name) + try: + os.write(conf_fd, apt_pkg.config.dump().encode("UTF-8")) + finally: + os.close(conf_fd) + env["APT_CONFIG"] = conf_name + proc = subprocess.Popen(cmd, env=env, universal_newlines=True, + stdin=subprocess.PIPE, + stdout=subprocess.PIPE, + stderr=subprocess.STDOUT) + try: + proc.stdin.write(kwargs["stdin"]) + except KeyError: + pass + finally: + proc.stdin.close() + return_code = proc.wait() + output = proc.stdout.read() + if return_code: + raise SystemError("The apt-key script failed with return code %s:\n" + "%s\n%s" % (return_code, " ".join(cmd), output)) + return output.strip() def add_key_from_file(filename, wait=True): """Import a GnuPG key file to trust repositores signed by it. @@ -110,16 +98,7 @@ def add_key_from_file(filename, wait=True): raise SystemError("An absolute path is required: %s" % filename) if not os.access(filename, os.R_OK): raise SystemError("Key file cannot be accessed: %s" % filename) - cmd = _get_gpg_command() - cmd.extend(["--quiet", "--batch", "--import", filename]) - proc = subprocess.Popen(cmd, stdout=subprocess.PIPE, - stderr=subprocess.STDOUT, - universal_newlines=True) - if wait: - _wait_and_raise(proc) - else: - return proc - + _call_apt_key_script("add", filename) def add_key_from_keyserver(keyid, keyserver, wait=True): """Import a GnuPG key file to trust repositores signed by it. @@ -131,18 +110,8 @@ def add_key_from_keyserver(keyid, keyserver, wait=True): completed. Otherwise the subprocess.Popen() instance will be returned. By default the call will be blocking. """ - cmd = _get_gpg_command() - cmd.extend(["--quiet", "--batch", - "--keyserver", keyserver, - "--recv", keyid]) - proc = subprocess.Popen(cmd, stdout=subprocess.PIPE, - stderr=subprocess.STDOUT, - universal_newlines=True) - if wait: - _wait_and_raise(proc) - else: - return proc - + _call_apt_key_script("adv", "--quiet", "--keyserver", keyserver, + "--recv", keyid) def add_key(content, wait=True): """Import a GnuPG key to trust repositores signed by it. @@ -153,19 +122,8 @@ def add_key(content, wait=True): completed. Otherwise the subprocess.Popen() instance will be returned. By default the call will be blocking. """ - cmd = _get_gpg_command() - cmd.extend(["--quiet", "--batch", "--import", "-"]) - proc = subprocess.Popen(cmd, stdin=subprocess.PIPE, - stdout=subprocess.PIPE, - stderr=subprocess.STDOUT, - universal_newlines=True) - proc.stdin.write(content) - proc.stdin.close() - if wait: - _wait_and_raise(proc) - else: - return proc - + _call_apt_key_script("adv", "--quiet", "--batch", + "--import", "-", stdin=content) def remove_key(fingerprint, wait=True): """Remove a GnuPG key to no longer trust repositores signed by it. @@ -176,16 +134,7 @@ def remove_key(fingerprint, wait=True): completed. Otherwise the subprocess.Popen() instance will be returned. By default the call will be blocking. """ - cmd = _get_gpg_command() - cmd.extend(["--quiet", "--batch", "--delete-key", "--yes", fingerprint]) - proc = subprocess.Popen(cmd, stdout=subprocess.PIPE, - stderr=subprocess.STDOUT, - universal_newlines=True) - if wait: - _wait_and_raise(proc) - else: - return proc - + _call_apt_key_script("rm", fingerprint) def export_key(fingerprint, wait=True): """Return the GnuPG key in text format. @@ -196,37 +145,24 @@ def export_key(fingerprint, wait=True): completed. Otherwise the subprocess.Popen() instance will be returned. By default the call will be blocking. """ - cmd = _get_gpg_command() - cmd.extend(["--armor", "--export", fingerprint]) - proc = subprocess.Popen(cmd, stdout=subprocess.PIPE, - stderr=subprocess.STDOUT, - universal_newlines=True) - if wait: - _wait_and_raise(proc) - return proc.stdout.read().strip() - else: - return proc - + return _call_apt_key_script("export", fingerprint) def list_keys(): """Returns a list of TrustedKey instances for each key which is used to trust repositories. """ - cmd = _get_gpg_command() - cmd.extend(["--with-colons", "--batch", "--list-keys"]) + # The output of `apt-key list` is difficult to parse since the + # --with-colons parameter isn't user + output = _call_apt_key_script("adv", "--with-colons", "--batch", + "--list-keys") res = [] - proc = subprocess.Popen(cmd, stdout=subprocess.PIPE, - stderr=subprocess.STDOUT, - universal_newlines=True) - _wait_and_raise(proc) - for line in proc.stdout.readlines(): + for line in output.split("\n"): fields = line.split(":") if fields[0] == "pub": key = TrustedKey(fields[9], fields[4][-8:], fields[5]) res.append(key) return res - if __name__ == "__main__": # Add some known keys we would like to see translated so that they get # picked up by gettext -- cgit v1.2.3 From 1b765003316443169b626874c50b98e0f91efb88 Mon Sep 17 00:00:00 2001 From: Sebastian Heinlein Date: Wed, 6 Jun 2012 12:19:45 +0200 Subject: Remove the wait statement to get a cleaner API --- apt/auth.py | 25 +++++-------------------- 1 file changed, 5 insertions(+), 20 deletions(-) (limited to 'apt') diff --git a/apt/auth.py b/apt/auth.py index 3a1d132e..27365678 100644 --- a/apt/auth.py +++ b/apt/auth.py @@ -85,14 +85,11 @@ def _call_apt_key_script(*args, **kwargs): "%s\n%s" % (return_code, " ".join(cmd), output)) return output.strip() -def add_key_from_file(filename, wait=True): +def add_key_from_file(filename): """Import a GnuPG key file to trust repositores signed by it. Keyword arguments: filename -- the absolute path to the public GnuPG key file - wait -- if the system should be blocked until the internal GnuPG call is - completed. Otherwise the subprocess.Popen() instance will be - returned. By default the call will be blocking. """ if not os.path.abspath(filename): raise SystemError("An absolute path is required: %s" % filename) @@ -100,50 +97,38 @@ def add_key_from_file(filename, wait=True): raise SystemError("Key file cannot be accessed: %s" % filename) _call_apt_key_script("add", filename) -def add_key_from_keyserver(keyid, keyserver, wait=True): +def add_key_from_keyserver(keyid, keyserver): """Import a GnuPG key file to trust repositores signed by it. Keyword arguments: keyid -- the identifier of the key, e.g. 0x0EB12DSA keyserver -- the URL or hostname of the key server - wait -- if the system should be blocked until the internal GnuPG call is - completed. Otherwise the subprocess.Popen() instance will be - returned. By default the call will be blocking. """ _call_apt_key_script("adv", "--quiet", "--keyserver", keyserver, "--recv", keyid) -def add_key(content, wait=True): +def add_key(content): """Import a GnuPG key to trust repositores signed by it. Keyword arguments: content -- the content of the GnuPG public key - wait -- if the system should be blocked until the internal GnuPG call is - completed. Otherwise the subprocess.Popen() instance will be - returned. By default the call will be blocking. """ _call_apt_key_script("adv", "--quiet", "--batch", "--import", "-", stdin=content) -def remove_key(fingerprint, wait=True): +def remove_key(fingerprint): """Remove a GnuPG key to no longer trust repositores signed by it. Keyword arguments: fingerprint -- the fingerprint identifying the key - wait -- if the system should be blocked until the internal GnuPG is - completed. Otherwise the subprocess.Popen() instance will be - returned. By default the call will be blocking. """ _call_apt_key_script("rm", fingerprint) -def export_key(fingerprint, wait=True): +def export_key(fingerprint): """Return the GnuPG key in text format. Keyword arguments: fingerprint -- the fingerprint identifying the key - wait -- if the system should be blocked until the internal GnuPG is - completed. Otherwise the subprocess.Popen() instance will be - returned. By default the call will be blocking. """ return _call_apt_key_script("export", fingerprint) -- cgit v1.2.3 From c1abd02805bf19dab6183b1f22900877fd62f1e8 Mon Sep 17 00:00:00 2001 From: Sebastian Heinlein Date: Wed, 6 Jun 2012 12:20:33 +0200 Subject: Don't call fakeroot by default - this should be done by an alternate Dir::Bin::Apt-key script --- apt/auth.py | 2 -- 1 file changed, 2 deletions(-) (limited to 'apt') diff --git a/apt/auth.py b/apt/auth.py index 27365678..255d7fd4 100644 --- a/apt/auth.py +++ b/apt/auth.py @@ -52,8 +52,6 @@ def _call_apt_key_script(*args, **kwargs): """Run the apt-key script with the given arguments.""" cmd = [apt_pkg.config.find_file("Dir::Bin::Apt-Key", "/usr/bin/apt-key")] cmd.extend(args) - if os.getuid() != 0: - cmd.insert(0, "fakeroot") env = os.environ.copy() env["LANG"] = "C" if apt_pkg.config.find_dir("Dir") != "/": -- cgit v1.2.3 From 8d592cbbc8cab3407aa628579ed002ef8d7b8c4a Mon Sep 17 00:00:00 2001 From: Sebastian Heinlein Date: Wed, 6 Jun 2012 12:26:44 +0200 Subject: Add support for update and net-update --- apt/auth.py | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) (limited to 'apt') diff --git a/apt/auth.py b/apt/auth.py index 255d7fd4..a999a7cf 100644 --- a/apt/auth.py +++ b/apt/auth.py @@ -130,6 +130,24 @@ def export_key(fingerprint): """ return _call_apt_key_script("export", fingerprint) +def update(): + """Update the local keyring with the archive keyring and remove from + the local keyring the archive keys which are no longer valid. The + archive keyring is shipped in the archive-keyring package of your + distribution, e.g. the debian-archive-keyring package in Debian. + """ + return _call_apt_key_script("update") + +def net_update(): + """Work similar to the update command above, but get the archive + keyring from an URI instead and validate it against a master key. + This requires an installed wget(1) and an APT build configured to + have a server to fetch from and a master keyring to validate. APT + in Debian does not support this command and relies on update + instead, but Ubuntu's APT does. + """ + return _call_apt_key_script("net-update") + def list_keys(): """Returns a list of TrustedKey instances for each key which is used to trust repositories. -- cgit v1.2.3 From b1d3ecfe422383a641512779fdb0084b397fd7e6 Mon Sep 17 00:00:00 2001 From: Sebastian Heinlein Date: Wed, 6 Jun 2012 12:27:18 +0200 Subject: Add to copyright --- apt/auth.py | 1 + 1 file changed, 1 insertion(+) (limited to 'apt') diff --git a/apt/auth.py b/apt/auth.py index a999a7cf..38c4bdc6 100644 --- a/apt/auth.py +++ b/apt/auth.py @@ -3,6 +3,7 @@ # auth - authentication key management # # Copyright (c) 2004 Canonical +# Copyright (c) 2012 Sebastian Heinlein # # Author: Michael Vogt # Sebastian Heinlein -- cgit v1.2.3 From 9ff1b964d3b4ccb041eddfda9ad930db736769a1 Mon Sep 17 00:00:00 2001 From: Michael Vogt Date: Mon, 11 Jun 2012 09:23:45 +0200 Subject: add test for utils.get_release_date_from_release_file and update code for more py3 friendliness --- apt/utils.py | 2 +- tests/data/misc/foo_Release | 492 ++++++++++++++++++++++++++++++++++++++++++++ tests/test_utils.py | 10 +- 3 files changed, 502 insertions(+), 2 deletions(-) create mode 100644 tests/data/misc/foo_Release (limited to 'apt') diff --git a/apt/utils.py b/apt/utils.py index 49e8bed5..08140258 100644 --- a/apt/utils.py +++ b/apt/utils.py @@ -48,7 +48,7 @@ def get_release_date_from_release_file(path): if not path or not os.path.exists(path): return None tag = apt_pkg.TagFile(open(path)) - section = tag.next() + section = next(tag) if not "Date" in section: return None date = section["Date"] diff --git a/tests/data/misc/foo_Release b/tests/data/misc/foo_Release new file mode 100644 index 00000000..0f42220c --- /dev/null +++ b/tests/data/misc/foo_Release @@ -0,0 +1,492 @@ +Origin: Ubuntu +Label: Ubuntu +Suite: precise +Version: 12.04 +Codename: precise +Date: Wed, 25 Apr 2012 22:49:23 UTC +Architectures: amd64 armel armhf i386 powerpc +Components: main restricted universe multiverse +Description: Ubuntu Precise 12.04 +MD5Sum: + 6a815674c5b3178152d449a9396cb2e5 1640344 main/binary-amd64/Packages.gz + 98140fa3444c9e945f6944acbb9ddcff 7818931 main/binary-amd64/Packages + 846e0e856bcbf9b64d9119c8727cda8c 97 main/binary-amd64/Release + cfd5f57de107941bbe661ffada4dce88 1272844 main/binary-amd64/Packages.bz2 + 5b220aea8056a900c8eaf28e79cdd64a 97 main/binary-armel/Release + 92e3160bbb664f8b5d7d4f2d161dd81c 1619078 main/binary-armel/Packages.gz + 28fbf69ee965639c92c8a54256cd6ba1 1257389 main/binary-armel/Packages.bz2 + 4e3ba67129f73f8afe28186b31eb3112 7743353 main/binary-armel/Packages + 81cfd32bf54d2b007542e14e09953bae 97 main/binary-armhf/Release + ed404b1123d1497cb09aceae8850fc06 7620333 main/binary-armhf/Packages + 64156d29df19fdaa36fa4eafd4c17dc1 1257653 main/binary-armhf/Packages.bz2 + 3d3238bd89cb1e8e23a1a4a5bf574739 1617483 main/binary-armhf/Packages.gz + dc2fe62f05e29f36ffe4e58499796ae6 1273857 main/binary-i386/Packages.bz2 + 7c678c50ce682e9f5cc5ba8f8eb2d6ad 1641082 main/binary-i386/Packages.gz + b6aa9a96c765bbd4202dae7dbaf18cc2 96 main/binary-i386/Release + eb3b4870b877863fecf1d3307c3beb54 7816415 main/binary-i386/Packages + 54658d89b612a38592431fe6b8a780a1 1627734 main/binary-powerpc/Packages.gz + 4185aca6984f00fd65aa0c0de12367d4 99 main/binary-powerpc/Release + fc9bf1959082733fc42c682285bf46e7 1263942 main/binary-powerpc/Packages.bz2 + c32afd79c9163cffe013f98669deb727 7661552 main/binary-powerpc/Packages + ff5434612595a569236f7ba4990a3b63 62166 main/debian-installer/binary-amd64/Packages.gz + 7dd470290bacbacc7bec24830100aade 234592 main/debian-installer/binary-amd64/Packages + 013d9839e9c8f8c102daf6eb77841bf7 48784 main/debian-installer/binary-amd64/Packages.bz2 + 08608ef832f2410970d07fdd802fc4e4 47964 main/debian-installer/binary-armel/Packages.bz2 + 0a0a559023aac4096a38e2fb51870ddf 230310 main/debian-installer/binary-armel/Packages + f79f29b841a05b971ac318a9e0a396e0 61118 main/debian-installer/binary-armel/Packages.gz + 20a72276b6607fa58f52fcc5b7742ff4 62777 main/debian-installer/binary-armhf/Packages.gz + 8da4a15400cec4b06e2aa1bba6cfc2c3 49051 main/debian-installer/binary-armhf/Packages.bz2 + 49f9f82a7c7e9e13c399d15df43148f1 238862 main/debian-installer/binary-armhf/Packages + 3dd181be504206f7abb41039dc508ed9 52279 main/debian-installer/binary-i386/Packages.bz2 + 5c2c6ca586bb6ffb2de414e2cd063c70 259996 main/debian-installer/binary-i386/Packages + ca1b7db323ebef593617ef0a7c833dfb 67180 main/debian-installer/binary-i386/Packages.gz + 876f115ee26518319458fc26ec785485 246636 main/debian-installer/binary-powerpc/Packages + 49402b5b12b10804abfae14629d23f1c 50309 main/debian-installer/binary-powerpc/Packages.bz2 + 29010a7ba3ac1e60a0efaef04f6f2e42 64468 main/debian-installer/binary-powerpc/Packages.gz + adf74189a01512a8f68d4bfc411dc692 3706 main/i18n/Index + 2f2ddab9be4ecc2c9e190bb639304943 4356187 main/source/Sources + 5c2893c352ebbf3ee380ebdab6b5e487 933770 main/source/Sources.bz2 + 323e5ca1ba86c7a503c4c7b0772749b1 1174967 main/source/Sources.gz + 3c3104465b2c7e54f4b8f566ac825339 98 main/source/Release + f75f8a98e9e952194046da388a11c42a 119109 multiverse/binary-amd64/Packages.bz2 + c2df9a9ff319486e3d6f46d9c35a8530 151924 multiverse/binary-amd64/Packages.gz + ece719c6a770134150f158cdb65ca6fc 581550 multiverse/binary-amd64/Packages + d3f34ec2fc86c95e4d3bc4606d751c66 103 multiverse/binary-amd64/Release + f296954282382f88b12dd8c64d813f5c 136295 multiverse/binary-armel/Packages.gz + d0da88f8a408fba12d44d5b6e107cfc0 519605 multiverse/binary-armel/Packages + 6d0c32b1cae4bdcfd6f18fd2d6fbf31f 103 multiverse/binary-armel/Release + 6744aaa8a3457d139348ea18379781ce 106873 multiverse/binary-armel/Packages.bz2 + 1cc2613becf46fd578aa6d52ab12db94 104529 multiverse/binary-armhf/Packages.bz2 + b0f8df0c9d700a1f7d295be4d4c03788 505901 multiverse/binary-armhf/Packages + 73cb5bc3d0c5021e5a4a413a73bcefce 103 multiverse/binary-armhf/Release + 00c07f1601226b8387fe4c9afaf2b044 133117 multiverse/binary-armhf/Packages.gz + bf0237c8c5d06a6df172db28710b8a36 121196 multiverse/binary-i386/Packages.bz2 + cf110f58668bf5731e593ed78af54c27 591662 multiverse/binary-i386/Packages + 8a915986a504c0f3eb95b61ae909c9a4 102 multiverse/binary-i386/Release + c6d89a2752d1154a219c295e6d70b697 154762 multiverse/binary-i386/Packages.gz + ae93681c6b316c95207091b3c91042a7 105 multiverse/binary-powerpc/Release + ec463c2070c515de099f8baa3a4b5993 107209 multiverse/binary-powerpc/Packages.bz2 + 675204a2b4aabeb9e99e406070b2af9b 520882 multiverse/binary-powerpc/Packages + c806ea7584c782d57363df12ddb28839 136930 multiverse/binary-powerpc/Packages.gz + d41d8cd98f00b204e9800998ecf8427e 0 multiverse/debian-installer/binary-amd64/Packages + 4a4dd3598707603b3f76a2378a4504aa 20 multiverse/debian-installer/binary-amd64/Packages.gz + 4059d198768f9f8dc9372dc1c54bc3c3 14 multiverse/debian-installer/binary-amd64/Packages.bz2 + 4059d198768f9f8dc9372dc1c54bc3c3 14 multiverse/debian-installer/binary-armel/Packages.bz2 + d41d8cd98f00b204e9800998ecf8427e 0 multiverse/debian-installer/binary-armel/Packages + 4a4dd3598707603b3f76a2378a4504aa 20 multiverse/debian-installer/binary-armel/Packages.gz + 4a4dd3598707603b3f76a2378a4504aa 20 multiverse/debian-installer/binary-armhf/Packages.gz + d41d8cd98f00b204e9800998ecf8427e 0 multiverse/debian-installer/binary-armhf/Packages + 4059d198768f9f8dc9372dc1c54bc3c3 14 multiverse/debian-installer/binary-armhf/Packages.bz2 + 4059d198768f9f8dc9372dc1c54bc3c3 14 multiverse/debian-installer/binary-i386/Packages.bz2 + 4a4dd3598707603b3f76a2378a4504aa 20 multiverse/debian-installer/binary-i386/Packages.gz + d41d8cd98f00b204e9800998ecf8427e 0 multiverse/debian-installer/binary-i386/Packages + 4a4dd3598707603b3f76a2378a4504aa 20 multiverse/debian-installer/binary-powerpc/Packages.gz + d41d8cd98f00b204e9800998ecf8427e 0 multiverse/debian-installer/binary-powerpc/Packages + 4059d198768f9f8dc9372dc1c54bc3c3 14 multiverse/debian-installer/binary-powerpc/Packages.bz2 + a2bcfa86da39328db94629011506a877 2676 multiverse/i18n/Index + c2ffda2a848000b71573dbc3dc7d4402 154990 multiverse/source/Sources.bz2 + f1d64bb88933686a71108de73b1f2262 628753 multiverse/source/Sources + 3d35747578528fa13640b98184120e51 188325 multiverse/source/Sources.gz + c9828946b46ac900fec682369c52bfa7 104 multiverse/source/Release + 5644835af0d1ed82cc7c14e34c2a543f 9098 restricted/binary-amd64/Packages.gz + d3058923c862e74c2c08b9a4ad7ec51e 134705 restricted/binary-amd64/Packages + 97ecff09a695f1460177843ff5d2b3e6 103 restricted/binary-amd64/Release + f8ed966e5400930411a32a7183357810 8452 restricted/binary-amd64/Packages.bz2 + 60e74c701a6e40b7f869dd83b335ec5c 103 restricted/binary-armel/Release + 4a4dd3598707603b3f76a2378a4504aa 20 restricted/binary-armel/Packages.gz + 4059d198768f9f8dc9372dc1c54bc3c3 14 restricted/binary-armel/Packages.bz2 + d41d8cd98f00b204e9800998ecf8427e 0 restricted/binary-armel/Packages + 55c08a48fc4b3370acc95a391bde1189 103 restricted/binary-armhf/Release + ce2da4621bbbaf55d858ae4243e17715 1103 restricted/binary-armhf/Packages.bz2 + b26814493282faf0c5b269c44b799653 2477 restricted/binary-armhf/Packages + 784050b4fd16ea71e10cf130e47132d9 941 restricted/binary-armhf/Packages.gz + c4280a67444afbb8e2564d6f2249d397 9108 restricted/binary-i386/Packages.gz + 6dfd90555b37a912f82894b4ec3b63a0 8431 restricted/binary-i386/Packages.bz2 + 48d3e36bf54be9b3fc7576cfcd0aac79 134582 restricted/binary-i386/Packages + e49b38cbf4dc409e8c25f6ab4b32fbe4 102 restricted/binary-i386/Release + 4a4dd3598707603b3f76a2378a4504aa 20 restricted/binary-powerpc/Packages.gz + 4059d198768f9f8dc9372dc1c54bc3c3 14 restricted/binary-powerpc/Packages.bz2 + d41d8cd98f00b204e9800998ecf8427e 0 restricted/binary-powerpc/Packages + edb68a453747a9faa9e98389787fb79d 105 restricted/binary-powerpc/Release + d41d8cd98f00b204e9800998ecf8427e 0 restricted/debian-installer/binary-amd64/Packages + 4059d198768f9f8dc9372dc1c54bc3c3 14 restricted/debian-installer/binary-amd64/Packages.bz2 + 4a4dd3598707603b3f76a2378a4504aa 20 restricted/debian-installer/binary-amd64/Packages.gz + 4a4dd3598707603b3f76a2378a4504aa 20 restricted/debian-installer/binary-armel/Packages.gz + 4059d198768f9f8dc9372dc1c54bc3c3 14 restricted/debian-installer/binary-armel/Packages.bz2 + d41d8cd98f00b204e9800998ecf8427e 0 restricted/debian-installer/binary-armel/Packages + 4059d198768f9f8dc9372dc1c54bc3c3 14 restricted/debian-installer/binary-armhf/Packages.bz2 + 4a4dd3598707603b3f76a2378a4504aa 20 restricted/debian-installer/binary-armhf/Packages.gz + d41d8cd98f00b204e9800998ecf8427e 0 restricted/debian-installer/binary-armhf/Packages + 4a4dd3598707603b3f76a2378a4504aa 20 restricted/debian-installer/binary-i386/Packages.gz + 4059d198768f9f8dc9372dc1c54bc3c3 14 restricted/debian-installer/binary-i386/Packages.bz2 + d41d8cd98f00b204e9800998ecf8427e 0 restricted/debian-installer/binary-i386/Packages + 4a4dd3598707603b3f76a2378a4504aa 20 restricted/debian-installer/binary-powerpc/Packages.gz + 4059d198768f9f8dc9372dc1c54bc3c3 14 restricted/debian-installer/binary-powerpc/Packages.bz2 + d41d8cd98f00b204e9800998ecf8427e 0 restricted/debian-installer/binary-powerpc/Packages + 39ef6c1d54f83252b07406f9cc3e9204 2596 restricted/i18n/Index + a76089a7a653d4f2196c38093058d1aa 19001 restricted/source/Sources + 3990b36e6ef2846251b58a58cde3768d 5470 restricted/source/Sources.bz2 + f0a32107aaf12daf3e64b4dc3ee11321 5306 restricted/source/Sources.gz + 7871a3f6f83e033ab93c06bf886e305d 104 restricted/source/Release + 3464b1b15950e714151f8bb43982951e 25546870 universe/binary-amd64/Packages + 9340d5c4051da92820bb5bd5ba05f7a7 4785960 universe/binary-amd64/Packages.bz2 + 1f6974aea9904921afdb4d1c5d0e8578 6166988 universe/binary-amd64/Packages.gz + 75ea366982b4862a41d9640687778dd2 101 universe/binary-amd64/Release + 5492f7f7183461d52f8d58871026c3e8 101 universe/binary-armel/Release + 420abe7ca02b5cb2abcd1b273efcfea9 4667308 universe/binary-armel/Packages.bz2 + 4da3448f54e710222b26646b0bee14e8 6009219 universe/binary-armel/Packages.gz + e99405db74ba425a3f898fdda0a42113 24901082 universe/binary-armel/Packages + d0545845bbcff572a5c852b80582b269 5948128 universe/binary-armhf/Packages.gz + 8f0d262f1eb03fc3523cd80b3112a7e0 101 universe/binary-armhf/Release + a10169d0cc23f526bcee5769d27778b0 4618508 universe/binary-armhf/Packages.bz2 + 952259d2cdb2c85df177702ac92075a0 24642528 universe/binary-armhf/Packages + df43c3b4ec37e79a00023475a09e2bfa 6179579 universe/binary-i386/Packages.gz + cc44a3e2ad759febb38ef2bec15ab29e 25568759 universe/binary-i386/Packages + 826b1d8609f0944a6d2ae95617e4d05a 100 universe/binary-i386/Release + 50690005918dd03ad9b71ffffa678d6f 4795820 universe/binary-i386/Packages.bz2 + 8a1f21204f4178574251a2238c00f317 25188905 universe/binary-powerpc/Packages + 7409876b9e1238d5a147720b41233a26 6080488 universe/binary-powerpc/Packages.gz + 9bcc15c59aa3fbbd15f0d187fe90b353 4716652 universe/binary-powerpc/Packages.bz2 + c3bdb05d2be1efa2dca9d2bac4e85b13 103 universe/binary-powerpc/Release + f8259410f0e1ad66324dbce895fdde2d 15255 universe/debian-installer/binary-amd64/Packages.bz2 + ce60affbf23a069735c29951bfb0b818 17243 universe/debian-installer/binary-amd64/Packages.gz + 13ce59ecd4540ddef3670dbbed73cdbc 61801 universe/debian-installer/binary-amd64/Packages + 988da583db40ce21d400926641fe6ed8 113584 universe/debian-installer/binary-armel/Packages + 75381e3ed15a3a2fa1480fdea72cfd24 23193 universe/debian-installer/binary-armel/Packages.bz2 + 4a8e6a98277f254660e8690fd050b232 27397 universe/debian-installer/binary-armel/Packages.gz + 02e7128ef42cd61e66c768520961fb11 20065 universe/debian-installer/binary-armhf/Packages.gz + 851afb686177a6819b291a714fa15813 17619 universe/debian-installer/binary-armhf/Packages.bz2 + ce3b6645e37226c4047546a40675ecdd 76034 universe/debian-installer/binary-armhf/Packages + 229235ad9979a343e3bea9aedb5af8da 17260 universe/debian-installer/binary-i386/Packages.gz + 8065c9994844e578af00ef8794709b18 15272 universe/debian-installer/binary-i386/Packages.bz2 + f28d6328611ab1e382fb0d0e798aca97 61718 universe/debian-installer/binary-i386/Packages + 60f54adbd38213dbbfe5d638f98a17e9 61121 universe/debian-installer/binary-powerpc/Packages + 1d48a07b3f4214200ff3eb1c5894e4a1 15024 universe/debian-installer/binary-powerpc/Packages.bz2 + 51eb13c4b9baf089e4e8b0e85556b90e 16860 universe/debian-installer/binary-powerpc/Packages.gz + 155e0b646671f37a7fe235c4579e59f2 2922 universe/i18n/Index + 2ef7ccbe106edb394dc69d8511775122 21256524 universe/source/Sources + e52b7cb491cc6a950cd11fa6263d7330 5019105 universe/source/Sources.bz2 + c722166709cfe9c398643b9c1a443610 102 universe/source/Release + 5ddd8bd0dda063b203d1a1da150983a0 6238766 universe/source/Sources.gz +SHA1: + 0b326daa3b2ac8748ca10942aaec15ebdcc78b36 1640344 main/binary-amd64/Packages.gz + 8a6068a75feb86e12b088dad2478600f6670f2d7 7818931 main/binary-amd64/Packages + 19655f20d48d9819ad95f2c9ecc59e5b1d94c3d0 97 main/binary-amd64/Release + f9761ecf8536859ef38b670a7f17d83febec4a37 1272844 main/binary-amd64/Packages.bz2 + a3a5faadbdf0a49d1587f07181b9eca870cb24ce 97 main/binary-armel/Release + 3c5d8f3a401427110adfd7f7d65513bfa47b933a 1619078 main/binary-armel/Packages.gz + 4d54e387978fdf829b4ff7336ed4d02e61c54d6a 1257389 main/binary-armel/Packages.bz2 + 969dfd243cc3f514eea9914de571db28621cd9bd 7743353 main/binary-armel/Packages + f8f33265eab2ff9fd8a6353e014e36a9d318a54d 97 main/binary-armhf/Release + 1d210f8ad3547b771bc8c8d2a9eb5ee99d437d81 7620333 main/binary-armhf/Packages + 5057a8b2d11d2325bb2546ad6613517b208fff18 1257653 main/binary-armhf/Packages.bz2 + 25fc010c2789028727308fbf6132a8da387423b8 1617483 main/binary-armhf/Packages.gz + 79369a31bc481f7f9f71f666906c3bdb356c44d8 1273857 main/binary-i386/Packages.bz2 + aaadaa6eaf0b7e73b0d371cdebae573f28833a43 1641082 main/binary-i386/Packages.gz + 6f8e5e9dd04a2379a7c6d8dc23b4ff8df5584741 96 main/binary-i386/Release + 8509eead0c5e9410e23d4226ee5d190220db1275 7816415 main/binary-i386/Packages + 7da3fd8ad7102912a4cc9882c66fbb1b65edd4c0 1627734 main/binary-powerpc/Packages.gz + 8a0c3b7757b738a89644e8c5a3b9afed806d2f83 99 main/binary-powerpc/Release + 4063338a28f6504f7bc2f9c00fc34ac26fc5a1fa 1263942 main/binary-powerpc/Packages.bz2 + 81844e44aaf0ed255ca200e7316bc9279ef238b6 7661552 main/binary-powerpc/Packages + d17439034551742aaa4762b4c174c1d72881f49f 62166 main/debian-installer/binary-amd64/Packages.gz + 05e024776b06a40253b9e252caad6dc4a13a90b9 234592 main/debian-installer/binary-amd64/Packages + fa692a307ecab69c106a5253107f980527cf58a9 48784 main/debian-installer/binary-amd64/Packages.bz2 + fb8be98ef08265d2f101cf265b9bf37654d750ac 47964 main/debian-installer/binary-armel/Packages.bz2 + 8953964fd2b54539e8276237cd5e4e2d4f9dc2ab 230310 main/debian-installer/binary-armel/Packages + cfdde60bbcbfb25c66fa474d28217e03a4c06789 61118 main/debian-installer/binary-armel/Packages.gz + 468306dc06acc828c50196427ba324c8ce225c79 62777 main/debian-installer/binary-armhf/Packages.gz + 17e7331a837675de31c365529ea9454cc48baf98 49051 main/debian-installer/binary-armhf/Packages.bz2 + 3404140163a5acece9a054912abf72d11a5591d0 238862 main/debian-installer/binary-armhf/Packages + 2bb7e052999eadc333a9f6481bdc20acb238d4c1 52279 main/debian-installer/binary-i386/Packages.bz2 + b2ad69cd6759724461c3566d91c1c5ec209eba07 259996 main/debian-installer/binary-i386/Packages + ebd0ab3923b3df4eca53eb863773bf0a9a4c3a67 67180 main/debian-installer/binary-i386/Packages.gz + eff551b62b72a1c0b183d4903a1452f2be08f3da 246636 main/debian-installer/binary-powerpc/Packages + 5c8aa09d48d7a792d555e7b16f4c89733e8441af 50309 main/debian-installer/binary-powerpc/Packages.bz2 + 582a86208e89e77a7f284e10a50b9cceaf4358ee 64468 main/debian-installer/binary-powerpc/Packages.gz + f426621b1015147e766dc003d1d2f140dc9c062d 3706 main/i18n/Index + b22a946cdae39d29535a63b020c0f2ad74c3c992 4356187 main/source/Sources + 711225fd252e77d85c7bb992aeb5aa5e45414ae1 933770 main/source/Sources.bz2 + a8807bf20dbaceb9d408f959840951131fd7d9f9 1174967 main/source/Sources.gz + cc2d2f6ce53597666df1a5ab216a08f08995f43f 98 main/source/Release + 06b070fc1ae771806c65e791742832320cc588e7 119109 multiverse/binary-amd64/Packages.bz2 + ae591617f4b6f988d6534270d4c32e3f0876166b 151924 multiverse/binary-amd64/Packages.gz + a28786a87a6136fb74a005c480c78a152123b9c0 581550 multiverse/binary-amd64/Packages + 3860a3ce6d87bb8a43803a1048cc96d4de3ae8a7 103 multiverse/binary-amd64/Release + 4bae80c3f5270e45f825141bcadb94d7dd82cd0e 136295 multiverse/binary-armel/Packages.gz + 9534dfc2e4a40386f48eff79062ee1742212b7d3 519605 multiverse/binary-armel/Packages + 92d1536ae99c958afd74299ffe30bdc6800cd8d0 103 multiverse/binary-armel/Release + 8e3ec557dc8f750e82e8ed5cbb0c509182feba79 106873 multiverse/binary-armel/Packages.bz2 + 009531e7cfb08701883a9e7a5f50235325e109cd 104529 multiverse/binary-armhf/Packages.bz2 + 596a666233560852574d9bca0109f4933a12c949 505901 multiverse/binary-armhf/Packages + eca4ab620c41d76c00a9615d8530db5a9c918fe8 103 multiverse/binary-armhf/Release + 53916a24cff0523e218bfee2c5464866d2099042 133117 multiverse/binary-armhf/Packages.gz + a7518b6b3e693d840a49b125020785d2049e75b9 121196 multiverse/binary-i386/Packages.bz2 + 84069f1643bd0092f4ec1b24eb921310532d72b2 591662 multiverse/binary-i386/Packages + 1d42b054d297b33cf69af011fa91f601c6b1a1b9 102 multiverse/binary-i386/Release + 8eae892b29234e9aac4c58a7098b097fe5ebde16 154762 multiverse/binary-i386/Packages.gz + 873bc8f864de428a4a47a3afaf53bc3a3a6e81c0 105 multiverse/binary-powerpc/Release + 665942774934afa56721d082a398735a067afe91 107209 multiverse/binary-powerpc/Packages.bz2 + c965416be0f7bf88c78394becf4a72aee342b829 520882 multiverse/binary-powerpc/Packages + e304951d28df0b75065fd3ef8166c65415a3cad2 136930 multiverse/binary-powerpc/Packages.gz + da39a3ee5e6b4b0d3255bfef95601890afd80709 0 multiverse/debian-installer/binary-amd64/Packages + a0fddd5458378c1bf3c10dd2f5c060d1347741ed 20 multiverse/debian-installer/binary-amd64/Packages.gz + 64a543afbb5f4bf728636bdcbbe7a2ed0804adc2 14 multiverse/debian-installer/binary-amd64/Packages.bz2 + 64a543afbb5f4bf728636bdcbbe7a2ed0804adc2 14 multiverse/debian-installer/binary-armel/Packages.bz2 + da39a3ee5e6b4b0d3255bfef95601890afd80709 0 multiverse/debian-installer/binary-armel/Packages + a0fddd5458378c1bf3c10dd2f5c060d1347741ed 20 multiverse/debian-installer/binary-armel/Packages.gz + a0fddd5458378c1bf3c10dd2f5c060d1347741ed 20 multiverse/debian-installer/binary-armhf/Packages.gz + da39a3ee5e6b4b0d3255bfef95601890afd80709 0 multiverse/debian-installer/binary-armhf/Packages + 64a543afbb5f4bf728636bdcbbe7a2ed0804adc2 14 multiverse/debian-installer/binary-armhf/Packages.bz2 + 64a543afbb5f4bf728636bdcbbe7a2ed0804adc2 14 multiverse/debian-installer/binary-i386/Packages.bz2 + a0fddd5458378c1bf3c10dd2f5c060d1347741ed 20 multiverse/debian-installer/binary-i386/Packages.gz + da39a3ee5e6b4b0d3255bfef95601890afd80709 0 multiverse/debian-installer/binary-i386/Packages + a0fddd5458378c1bf3c10dd2f5c060d1347741ed 20 multiverse/debian-installer/binary-powerpc/Packages.gz + da39a3ee5e6b4b0d3255bfef95601890afd80709 0 multiverse/debian-installer/binary-powerpc/Packages + 64a543afbb5f4bf728636bdcbbe7a2ed0804adc2 14 multiverse/debian-installer/binary-powerpc/Packages.bz2 + 09a8aff01c419e0eae19749acf3b223e1f95eccc 2676 multiverse/i18n/Index + a088c61bca210afebf4c53904049e2a969d8ece3 154990 multiverse/source/Sources.bz2 + df4078a91645024ce93b1ee363003c83ffa2aa84 628753 multiverse/source/Sources + e6ec0430d594fa7fa2c98cb9a5fa8ac7ed0d506d 188325 multiverse/source/Sources.gz + 0750588f28b1c2cf6a005501c7d027cfa663cce1 104 multiverse/source/Release + 200fdeee1984ac78b6fdabfad34d2b485512ca2d 9098 restricted/binary-amd64/Packages.gz + 0e7ebd3d2690c7f89d19f8b337cf292cac913c18 134705 restricted/binary-amd64/Packages + c968d68baa554ffc7009688ee7d0d3e70663243c 103 restricted/binary-amd64/Release + 4185a5b6c2e702ea4754437ebfef23d828ec67a0 8452 restricted/binary-amd64/Packages.bz2 + 27886dcc6c7d08369cc65d4c35f26b806f57be56 103 restricted/binary-armel/Release + a0fddd5458378c1bf3c10dd2f5c060d1347741ed 20 restricted/binary-armel/Packages.gz + 64a543afbb5f4bf728636bdcbbe7a2ed0804adc2 14 restricted/binary-armel/Packages.bz2 + da39a3ee5e6b4b0d3255bfef95601890afd80709 0 restricted/binary-armel/Packages + d83803a5f6c2f5c7321d68d126733b6015c8e2a9 103 restricted/binary-armhf/Release + 6607111b78dcd3de05cb88692c1e2142abf9a4c1 1103 restricted/binary-armhf/Packages.bz2 + b9f55d161632f89b5125a638ca6ad4fe5e9d11fe 2477 restricted/binary-armhf/Packages + d0c0a616aeac580d13256693cfbd507ae7c9b280 941 restricted/binary-armhf/Packages.gz + f3e483bbe77cbf0f64accbd0291df19b4e4d694b 9108 restricted/binary-i386/Packages.gz + 48d76b03a19e4d66d6f7a20339dab91acebaba99 8431 restricted/binary-i386/Packages.bz2 + 3ca46ea8f32b7cbc49ce76dd1c1ab91589900fd7 134582 restricted/binary-i386/Packages + 2aad61f5084ecdd64ff520520d77e980e8b21f81 102 restricted/binary-i386/Release + a0fddd5458378c1bf3c10dd2f5c060d1347741ed 20 restricted/binary-powerpc/Packages.gz + 64a543afbb5f4bf728636bdcbbe7a2ed0804adc2 14 restricted/binary-powerpc/Packages.bz2 + da39a3ee5e6b4b0d3255bfef95601890afd80709 0 restricted/binary-powerpc/Packages + 6b0a230bbf47463e3d7e88f535e210aaa96a1251 105 restricted/binary-powerpc/Release + da39a3ee5e6b4b0d3255bfef95601890afd80709 0 restricted/debian-installer/binary-amd64/Packages + 64a543afbb5f4bf728636bdcbbe7a2ed0804adc2 14 restricted/debian-installer/binary-amd64/Packages.bz2 + a0fddd5458378c1bf3c10dd2f5c060d1347741ed 20 restricted/debian-installer/binary-amd64/Packages.gz + a0fddd5458378c1bf3c10dd2f5c060d1347741ed 20 restricted/debian-installer/binary-armel/Packages.gz + 64a543afbb5f4bf728636bdcbbe7a2ed0804adc2 14 restricted/debian-installer/binary-armel/Packages.bz2 + da39a3ee5e6b4b0d3255bfef95601890afd80709 0 restricted/debian-installer/binary-armel/Packages + 64a543afbb5f4bf728636bdcbbe7a2ed0804adc2 14 restricted/debian-installer/binary-armhf/Packages.bz2 + a0fddd5458378c1bf3c10dd2f5c060d1347741ed 20 restricted/debian-installer/binary-armhf/Packages.gz + da39a3ee5e6b4b0d3255bfef95601890afd80709 0 restricted/debian-installer/binary-armhf/Packages + a0fddd5458378c1bf3c10dd2f5c060d1347741ed 20 restricted/debian-installer/binary-i386/Packages.gz + 64a543afbb5f4bf728636bdcbbe7a2ed0804adc2 14 restricted/debian-installer/binary-i386/Packages.bz2 + da39a3ee5e6b4b0d3255bfef95601890afd80709 0 restricted/debian-installer/binary-i386/Packages + a0fddd5458378c1bf3c10dd2f5c060d1347741ed 20 restricted/debian-installer/binary-powerpc/Packages.gz + 64a543afbb5f4bf728636bdcbbe7a2ed0804adc2 14 restricted/debian-installer/binary-powerpc/Packages.bz2 + da39a3ee5e6b4b0d3255bfef95601890afd80709 0 restricted/debian-installer/binary-powerpc/Packages + d59f5ac2532dcc7cb2f1e5b788c700bf8ab13b66 2596 restricted/i18n/Index + 8a80cdcf50cdcabcf4f47c772d7b252587cc9dc1 19001 restricted/source/Sources + ff7d82cf1d965953c745d224a1d4adc67b586528 5470 restricted/source/Sources.bz2 + f64da9da2038712c5d73ce3337e91d92ee39cd30 5306 restricted/source/Sources.gz + 5c963f5f4d4720afa5fbb914375d0033bcd50078 104 restricted/source/Release + 10c6989f4a241aabe00146905e776391fc4d9ac0 25546870 universe/binary-amd64/Packages + f6900616102430e0eafa8ac89795efff7edc0710 4785960 universe/binary-amd64/Packages.bz2 + 4f938bde9dff32a49bccd917613666017185882d 6166988 universe/binary-amd64/Packages.gz + 9d6cba1ed46b5eee1f6c065934e5f4854b3efee5 101 universe/binary-amd64/Release + 3573e863c714c0a083f9c962ea9136916f796e92 101 universe/binary-armel/Release + bb8f53ead3723737c13950e5343327884f737da9 4667308 universe/binary-armel/Packages.bz2 + 43d9a98706b6e50c21092ce35313b5b034f30d01 6009219 universe/binary-armel/Packages.gz + 7f97dcbe710882d281efcfd2f8d70ca7d4e47265 24901082 universe/binary-armel/Packages + 8f6d42d7f8178a51a7065d7bf3234eecbca12810 5948128 universe/binary-armhf/Packages.gz + ea648f433d0edd47de820582fa6a1cd89ef66681 101 universe/binary-armhf/Release + e6ad9bdb18ce9e09e262b9e6f85b0b307a3456f1 4618508 universe/binary-armhf/Packages.bz2 + ba55530da6c3a9604977ea4a37b4b4d8943ff994 24642528 universe/binary-armhf/Packages + 05de59263866a33c104787943347164e7b124aba 6179579 universe/binary-i386/Packages.gz + 286a3dd2fda0d98d2ff14eafee0bba5c912d6df2 25568759 universe/binary-i386/Packages + 839529b6f6e2a64465e4a825fa5ad46a36f1c73d 100 universe/binary-i386/Release + 70c27be4d8bc87dc26bbe6f21fbb7328dc1c4d01 4795820 universe/binary-i386/Packages.bz2 + 06af7fdef1a54920a7667728a7b810553d00a9c8 25188905 universe/binary-powerpc/Packages + 4df1b07075b25ccae6e60d26d5a5761444ece689 6080488 universe/binary-powerpc/Packages.gz + 15d041ad1284527b07f75218cf6eb32322092f84 4716652 universe/binary-powerpc/Packages.bz2 + 42d6a46ed2e525b219f8f6dc076dadbd06fc7f1b 103 universe/binary-powerpc/Release + 92c3bef6ad40051021a4e9dadb16e5edc7410b57 15255 universe/debian-installer/binary-amd64/Packages.bz2 + 31d8725b0d238c282d9b572bb56b7e45c0ff53f8 17243 universe/debian-installer/binary-amd64/Packages.gz + e334a7c80e14dae3055950d9e45213db65d4087b 61801 universe/debian-installer/binary-amd64/Packages + 1fd1ae2b87eb85525b173ea982d15f3c98e6e33d 113584 universe/debian-installer/binary-armel/Packages + c56579feb77b5aac2d261abbbb6c89a1458ca4d9 23193 universe/debian-installer/binary-armel/Packages.bz2 + e4b08e57397f3ab0599e0bf6a2fbcba3aed438b5 27397 universe/debian-installer/binary-armel/Packages.gz + 5a9608213061eab983392258288e8aec36d006f0 20065 universe/debian-installer/binary-armhf/Packages.gz + dc558d56aef72991a3188909a14f752aabdee325 17619 universe/debian-installer/binary-armhf/Packages.bz2 + c6afa0e14c706aceea60aad033b6a067320fc165 76034 universe/debian-installer/binary-armhf/Packages + f359cf916a19055133546a7b7f3e35c7c260488e 17260 universe/debian-installer/binary-i386/Packages.gz + 23920ee4974d88ba824b0e884f8df6e2711a20db 15272 universe/debian-installer/binary-i386/Packages.bz2 + 21cfd020eefc848307fac14b8f0efe0d6cd9c6ea 61718 universe/debian-installer/binary-i386/Packages + f2ff72c8f1fe4ce40ccf44f8ccd6623cedf4e6f0 61121 universe/debian-installer/binary-powerpc/Packages + bd9c731941b261b22c022d97ca139bfeb1ad70ba 15024 universe/debian-installer/binary-powerpc/Packages.bz2 + e173071bda799068783c9192bff6536db9790a27 16860 universe/debian-installer/binary-powerpc/Packages.gz + 59a86abaed7cab292600b6766b18752b7e7c3d49 2922 universe/i18n/Index + 4b0ed5f327b0fa9b3f9d9410933a3d2afe467a7e 21256524 universe/source/Sources + d0525203f9ad5ec9183996e6765d0ef9a024691f 5019105 universe/source/Sources.bz2 + 00847d46051ba44d436000b0394b218503de125b 102 universe/source/Release + d9706a8ab2ffeadb51b50d042712536a95dc4343 6238766 universe/source/Sources.gz +SHA256: + 0d61aacd269015c0abfe01fe7f90a4f534c368e9c513f7e90d3111af82656b3d 1640344 main/binary-amd64/Packages.gz + a1bc8d839ca9966a0b924e4a4c60f1c23b4d431deb81e1bc529edf95f30fc29d 7818931 main/binary-amd64/Packages + a55d3b2e6e2a175529d73e6ca92989018cf57745e705f7ff675b05f80e5141f7 97 main/binary-amd64/Release + cc0d3a19c51188b4b4acb80e3013264462c6e0f60759bfd46206c60681bd4ba9 1272844 main/binary-amd64/Packages.bz2 + a841750f49bd11f99b9dae6941d2fa6ec1fd87906139d0ceeacf0d4df57a87cf 97 main/binary-armel/Release + e80eaf12c1aa520b353de8ad97e79364779e82ef011cb93db372edc900eb7be9 1619078 main/binary-armel/Packages.gz + 0ab0929c3c44837886e532f8ec4bee77f3664bdcc2cc3192a02b991c52b156ce 1257389 main/binary-armel/Packages.bz2 + ea400cf67f84c12265e4bf419de442a38880fab37d76999985972fc6df3e13b3 7743353 main/binary-armel/Packages + f3c40f057bb085f28ef2ed950f62366483d2a418571435c355dc27c0912dccff 97 main/binary-armhf/Release + cde037224f43e4619213c5195f2ea5c2f91d078f449579652dbd4128793d5062 7620333 main/binary-armhf/Packages + 7c6ea67e609b96dec6f2185df4cd81160e37ba467f9132a9bfb101da3f9a0468 1257653 main/binary-armhf/Packages.bz2 + 2b83bf5501ecae3347e5c96658edae7d48eef42108b4786471a3de241e75e7d3 1617483 main/binary-armhf/Packages.gz + 4d74c53917b84d37cb3277e2b755672a8733e2cfaf949f6e644e6e88094cdaa2 1273857 main/binary-i386/Packages.bz2 + 07f33bddfefdb4a0c44ddee59fd3eca497df2ad0456e0eeade136e4f0302ee3c 1641082 main/binary-i386/Packages.gz + 5182e22f799fe66c8db6dfb073fb040e9e583d88bb9d4d77e058b2afb87e9479 96 main/binary-i386/Release + 4cecfc8c0d2113d51b03afa8fdcdcc963d9ad74474696472ec1cdbdb38b856e2 7816415 main/binary-i386/Packages + 2795904625f466b4a2fb96d41c00b000ab7f2bdc7f288b0c9ef1283d7e110f87 1627734 main/binary-powerpc/Packages.gz + a9247e6d8b0c5977bc1e72be09b1f42a83b5f5a6a70b17f4fef35a0657e3c206 99 main/binary-powerpc/Release + 6e745b7edcd67755fa09f54cc3afdd0ffbd0475302a74293472e97e46ba75ddc 1263942 main/binary-powerpc/Packages.bz2 + 8b953dbae4a14e7ab47151044a47c7b0f0e1dd2a6480170b8172e00d9dad7a2a 7661552 main/binary-powerpc/Packages + a5bdf4116ecfeda052d5b3751138c6153e814ac58b2f551503f3ed90e6c3510c 62166 main/debian-installer/binary-amd64/Packages.gz + 6a9a4837a4a7df3e7e0566b354b7f1dd2dcf46254335ae3d06a72538f85e410a 234592 main/debian-installer/binary-amd64/Packages + 0da4f8190eebfef22103f1f6f7051adbe9489d454ab7224f09d05646407881e8 48784 main/debian-installer/binary-amd64/Packages.bz2 + ea01244357deb22c2a4bd7eaa34a1635c0915b89ce174f312c0e0a4b081eaf30 47964 main/debian-installer/binary-armel/Packages.bz2 + 4177b0519c75f7f950e5a0f0d72d40cc0c4ccf29ebe89fbb9bc1f11a80874526 230310 main/debian-installer/binary-armel/Packages + 2b6f81ef9fa687bfb2eb56bb3e90faef0c012351d096b141caa710fd50846043 61118 main/debian-installer/binary-armel/Packages.gz + 52c834247ff3a5475466e647802f6eff393f85589e5da5fd3e5b497669b8b49c 62777 main/debian-installer/binary-armhf/Packages.gz + 7090f1ad1307a012fbfff883885f16099ed66ebffdeece356f837e632b177a4f 49051 main/debian-installer/binary-armhf/Packages.bz2 + 4bae13f507993e977c279937406fc03e37fede7805a92508f6d3cba76f1aaf95 238862 main/debian-installer/binary-armhf/Packages + d1d23926ff15cfaa6160c5fd0327d181721093fbd2f2e8125be5559a991a81a8 52279 main/debian-installer/binary-i386/Packages.bz2 + 30e0ec7a2c3d47d5501de8414b436482ff523e9c4983b536b2c9911a30618b98 259996 main/debian-installer/binary-i386/Packages + 7a4dcb001ed4bb5fa2458af901de312e11a745bc86a0f877e47567d0f911bc0d 67180 main/debian-installer/binary-i386/Packages.gz + 30c2f590c2dedc9f78dfc7f0026b51bc9baa712ac8c9310404d9d6577af77d90 246636 main/debian-installer/binary-powerpc/Packages + 6c53fab780cf774c5cabb1788eea1e3446c528cde4352d106907f4ec22449370 50309 main/debian-installer/binary-powerpc/Packages.bz2 + b003f3fbf2fa6dbf7d47cf3fbd029ecd86b316ec497a9ac4eea3614cf4ec76af 64468 main/debian-installer/binary-powerpc/Packages.gz + fefed230e286d832ab6eb0fb7b72442165b50df23a68402ae6e9d265a31920a2 3706 main/i18n/Index + bb618cebb361a2a7148be0bad9af012c8d9de23dbc32d6d9ba035fa6ee0078ab 4356187 main/source/Sources + 0aeef2c2258136f9f774c36a158cf759389acf6a35a3153a03d3fa41d4f346d5 933770 main/source/Sources.bz2 + 4a058ba65244e8eaf17d159b72edebe4e621d54c274a82d4a973b358b4af9a28 1174967 main/source/Sources.gz + 864ba9a26e348c6297c08c047d8c228e5ed031ec3d46ef7aad93c3fa550395a8 98 main/source/Release + 85477d2b2e7ea2f46b6a059f7526cf52d75fea1a5120aa3b256c576e904d40ff 119109 multiverse/binary-amd64/Packages.bz2 + 2967ae6c1cc065bec03225d808b4511b138cc13b8de801a0562fec6e30710f36 151924 multiverse/binary-amd64/Packages.gz + 18fcf61bb74ef2a01c3d4a8d4646a75836f43244168b43d6ae202f368167b224 581550 multiverse/binary-amd64/Packages + fbc4931ef84d50a39da65d110f787aee274df8819a758a3c0aa1ff13f0ba6ee0 103 multiverse/binary-amd64/Release + 49f48a34696d08a13a0fdc19a0f6896af2cb477e72860a8880954926c7d45e60 136295 multiverse/binary-armel/Packages.gz + f20d7f0bc32b5b2fbcb442f7c128aaad7e18aece3781d53f560932bb191d6830 519605 multiverse/binary-armel/Packages + f7ea72b2c07af81f2e342414025dce7a658a6a9915c4d8adc13b992cb3b9fd2f 103 multiverse/binary-armel/Release + dd3d4e8a6ec9055d5b553af49822de74648f071ceb0fd314d6cd1aaf7ad6882b 106873 multiverse/binary-armel/Packages.bz2 + 567c1f9d30a4d6650552d66c5fb43d2d8910d3fed69793daed622d2c699f4bc8 104529 multiverse/binary-armhf/Packages.bz2 + a47ef2c0a68adeb70a0bc6b22c94b08402396ff6f5c77664e06c2fb7ee0e7ab0 505901 multiverse/binary-armhf/Packages + 6b95e8edaa2bb799f6e15a4a6aaf223da0faea670cd03340395bdcea90205afc 103 multiverse/binary-armhf/Release + 14721b333f19a6344addb185f161d1cd14e04ac284c8fa9d726064ec228269a8 133117 multiverse/binary-armhf/Packages.gz + 454436f374186007075445c1f206ba5c926f30609baa732c495f1ba456d71e59 121196 multiverse/binary-i386/Packages.bz2 + 9fabd7bfdbfd216968f7a17265e5609cdd72f1ea7c8f50941e294694e76b180d 591662 multiverse/binary-i386/Packages + 7141881b898ac6a78f1ca6f3e81481ee6657f6762fa22768816ab39f6b17e695 102 multiverse/binary-i386/Release + 3f4cae31df741f55d523ecea758d05a7e012a205bb03974ee20eb09e3f4fa63b 154762 multiverse/binary-i386/Packages.gz + 332dde644a8467496eb5f45ffd2d735ca61ea781da21cd205b3267cd83fa0563 105 multiverse/binary-powerpc/Release + 99ef0a611aa32ffa4f16a006d641fbd8dd9e3e73bde3c93b831cd6583746e64b 107209 multiverse/binary-powerpc/Packages.bz2 + 60f2431dab7bd02fe2c2428bf400c3535be49641cc9d5645a8f1b4fd44f5086f 520882 multiverse/binary-powerpc/Packages + cacfd10b40992a617ce32c479f9505531c8cc57e4cf964687d663a5f41f8dcbd 136930 multiverse/binary-powerpc/Packages.gz + e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 0 multiverse/debian-installer/binary-amd64/Packages + f61f27bd17de546264aa58f40f3aafaac7021e0ef69c17f6b1b4cd7664a037ec 20 multiverse/debian-installer/binary-amd64/Packages.gz + d3dda84eb03b9738d118eb2be78e246106900493c0ae07819ad60815134a8058 14 multiverse/debian-installer/binary-amd64/Packages.bz2 + d3dda84eb03b9738d118eb2be78e246106900493c0ae07819ad60815134a8058 14 multiverse/debian-installer/binary-armel/Packages.bz2 + e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 0 multiverse/debian-installer/binary-armel/Packages + f61f27bd17de546264aa58f40f3aafaac7021e0ef69c17f6b1b4cd7664a037ec 20 multiverse/debian-installer/binary-armel/Packages.gz + f61f27bd17de546264aa58f40f3aafaac7021e0ef69c17f6b1b4cd7664a037ec 20 multiverse/debian-installer/binary-armhf/Packages.gz + e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 0 multiverse/debian-installer/binary-armhf/Packages + d3dda84eb03b9738d118eb2be78e246106900493c0ae07819ad60815134a8058 14 multiverse/debian-installer/binary-armhf/Packages.bz2 + d3dda84eb03b9738d118eb2be78e246106900493c0ae07819ad60815134a8058 14 multiverse/debian-installer/binary-i386/Packages.bz2 + f61f27bd17de546264aa58f40f3aafaac7021e0ef69c17f6b1b4cd7664a037ec 20 multiverse/debian-installer/binary-i386/Packages.gz + e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 0 multiverse/debian-installer/binary-i386/Packages + f61f27bd17de546264aa58f40f3aafaac7021e0ef69c17f6b1b4cd7664a037ec 20 multiverse/debian-installer/binary-powerpc/Packages.gz + e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 0 multiverse/debian-installer/binary-powerpc/Packages + d3dda84eb03b9738d118eb2be78e246106900493c0ae07819ad60815134a8058 14 multiverse/debian-installer/binary-powerpc/Packages.bz2 + f0b16a5cfd2d633c9ddecfadfa6742544b18c23ed30023286e2b20ef29f33c73 2676 multiverse/i18n/Index + faa0360612fc00453dfdd55b6a1bb20e4f876e041ad6fca410d5da65608ab31e 154990 multiverse/source/Sources.bz2 + 2f0deae62e2cf7e5257bbd858cb0bf2a94122c4eb82be13e13768d0b9ce84c9e 628753 multiverse/source/Sources + 28f6d95fcba03e442cf24dc547653d5ec60177a29d7cfea771efcc5501077747 188325 multiverse/source/Sources.gz + f35f721bf16691842cc916c3563fab535f6bb83329f40c33ac02f4ba637707d3 104 multiverse/source/Release + 3e872fa356cbce4dfd75a88caa4fc6b47616e1fc7d224f4fc2123650fd7f4be3 9098 restricted/binary-amd64/Packages.gz + 459a26c3ef3cb5db8c8355ea6abfa8cfe0a7a266a197929d86d37686daf8a337 134705 restricted/binary-amd64/Packages + ea47572182da041b46543e471cb7a6fcc4e001fbe19a27740085ebf5d77252a9 103 restricted/binary-amd64/Release + adb08d7f0fa444f2869e8d932db7adb1515839f11af6032284cf1e20060e2dd6 8452 restricted/binary-amd64/Packages.bz2 + 65a5ac0820d61383f7dcf33699aa029b5965b7906bc8341f94f8f7f354cdcd83 103 restricted/binary-armel/Release + f61f27bd17de546264aa58f40f3aafaac7021e0ef69c17f6b1b4cd7664a037ec 20 restricted/binary-armel/Packages.gz + d3dda84eb03b9738d118eb2be78e246106900493c0ae07819ad60815134a8058 14 restricted/binary-armel/Packages.bz2 + e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 0 restricted/binary-armel/Packages + ee447ce81793bd3bc8c127d4e065c6ec24e5901573dffb7cc5abedfbcb86592c 103 restricted/binary-armhf/Release + d88ed7df97cd60cdce35c3ca81de66e2bedf0f22e67ec8922dbd5eca545b5e50 1103 restricted/binary-armhf/Packages.bz2 + 9ade66f4a49598fb371705a79244e5f3abb74c04467f9f9954641ae5acec6766 2477 restricted/binary-armhf/Packages + 03d8b64c445f327ce9e369bca815652844bd6aafb344d0287fb4e71f321d0414 941 restricted/binary-armhf/Packages.gz + 07e344ed07234876c3fddd9aa763e04bfc2e013fc18428738be71abfb9e1ca77 9108 restricted/binary-i386/Packages.gz + 8061335b923c49e72a2b60b437d5bbad1b98a45ac178a68fd8359cec9fad27ec 8431 restricted/binary-i386/Packages.bz2 + 122336146860047af3d5817dbc423f01d57a90cbf41db1ee0ad9235c0559a43e 134582 restricted/binary-i386/Packages + 58634ed42b6fadb280d48f419b960e28151320a62b4486e520ca327719db554a 102 restricted/binary-i386/Release + f61f27bd17de546264aa58f40f3aafaac7021e0ef69c17f6b1b4cd7664a037ec 20 restricted/binary-powerpc/Packages.gz + d3dda84eb03b9738d118eb2be78e246106900493c0ae07819ad60815134a8058 14 restricted/binary-powerpc/Packages.bz2 + e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 0 restricted/binary-powerpc/Packages + d9bce398e46f0eac57d1d33fd8a6caa0bd7ab6334508c0640956cb7adbe1eba1 105 restricted/binary-powerpc/Release + e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 0 restricted/debian-installer/binary-amd64/Packages + d3dda84eb03b9738d118eb2be78e246106900493c0ae07819ad60815134a8058 14 restricted/debian-installer/binary-amd64/Packages.bz2 + f61f27bd17de546264aa58f40f3aafaac7021e0ef69c17f6b1b4cd7664a037ec 20 restricted/debian-installer/binary-amd64/Packages.gz + f61f27bd17de546264aa58f40f3aafaac7021e0ef69c17f6b1b4cd7664a037ec 20 restricted/debian-installer/binary-armel/Packages.gz + d3dda84eb03b9738d118eb2be78e246106900493c0ae07819ad60815134a8058 14 restricted/debian-installer/binary-armel/Packages.bz2 + e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 0 restricted/debian-installer/binary-armel/Packages + d3dda84eb03b9738d118eb2be78e246106900493c0ae07819ad60815134a8058 14 restricted/debian-installer/binary-armhf/Packages.bz2 + f61f27bd17de546264aa58f40f3aafaac7021e0ef69c17f6b1b4cd7664a037ec 20 restricted/debian-installer/binary-armhf/Packages.gz + e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 0 restricted/debian-installer/binary-armhf/Packages + f61f27bd17de546264aa58f40f3aafaac7021e0ef69c17f6b1b4cd7664a037ec 20 restricted/debian-installer/binary-i386/Packages.gz + d3dda84eb03b9738d118eb2be78e246106900493c0ae07819ad60815134a8058 14 restricted/debian-installer/binary-i386/Packages.bz2 + e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 0 restricted/debian-installer/binary-i386/Packages + f61f27bd17de546264aa58f40f3aafaac7021e0ef69c17f6b1b4cd7664a037ec 20 restricted/debian-installer/binary-powerpc/Packages.gz + d3dda84eb03b9738d118eb2be78e246106900493c0ae07819ad60815134a8058 14 restricted/debian-installer/binary-powerpc/Packages.bz2 + e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 0 restricted/debian-installer/binary-powerpc/Packages + 17dde58abfdb4dfdad9c8a82db09c9dbc3d8a7cd84b51dd9167579d6899e9ff5 2596 restricted/i18n/Index + ee3655459e45778fdfa06fb649565e66b25d2dd0870c75890005fb3597bb71d7 19001 restricted/source/Sources + cff18d2ad74ead8712f1b77a23b32e84e54269b03ba2a409ae4227860d1181f5 5470 restricted/source/Sources.bz2 + cf085bdcb323dd2c2a599ddb7a9b3ae7bd37121f42024d68b367a4f735df900f 5306 restricted/source/Sources.gz + 9137393fc24cf64808d55ca7665bc5a7bd46b48918e6720a95ba239a8fab092e 104 restricted/source/Release + 901469729d2354891be94c192dabd8c1d0bc31e1497ea8360b70d2e847c1f3c1 25546870 universe/binary-amd64/Packages + 799347395d4e011a215aa5ce0c9006449d8af884795ffbce7a35767a55f99074 4785960 universe/binary-amd64/Packages.bz2 + 68b08847604c4efe7d6f56ba79f923ce0ab82127dfcc6e8cffaf12af25d7adba 6166988 universe/binary-amd64/Packages.gz + 52ffdd1777a886edc5e1e1ef430b03a72937920f9722fd453ee8243cb0aac860 101 universe/binary-amd64/Release + a78a1304e105b2fe4c950c77c1794f715c1256d14d8541cca8f5cd13db48119e 101 universe/binary-armel/Release + d6d4bfa5d0891086f5a4f2aabfaecd7a1e0c0d8b46aef33b3470e349e7a9210e 4667308 universe/binary-armel/Packages.bz2 + 374d50d0335c655da46f9cd54cd00d9a20058d2fe7c56989aa121b49883cfb88 6009219 universe/binary-armel/Packages.gz + ab5073e90417b729d1fe3b68052e6a8e66e48986c35470944f6a58676e967450 24901082 universe/binary-armel/Packages + af74034d1a3e1f90745dc48b996a98c471d997b12a1d810eb8754088540591d7 5948128 universe/binary-armhf/Packages.gz + 6697d196b35850817476e884fdc013d9670b4bac73310c54a4d62cd810f02c70 101 universe/binary-armhf/Release + 1ca17d3aecec2325cba53e1c299aeb6a1fed01d7acbc40163595de9e651abdeb 4618508 universe/binary-armhf/Packages.bz2 + 2b422ffa77d4374650d4cc543c5a1123b2535effb2c8cdaf25fd77d1dde632c4 24642528 universe/binary-armhf/Packages + cd6b5cb8165553482abee1bf85e5cd3288abadccb6acf34239ec45f79a090784 6179579 universe/binary-i386/Packages.gz + 8ef7db20ba08cf1b4d98a618189c615c69865f4da025ac654e3e6b8a4382a3ae 25568759 universe/binary-i386/Packages + 06af492500145bd64762d885417d167269db6ea03022c6968f1a5d0515ac55dd 100 universe/binary-i386/Release + 530a2efb8051a63ed17431ae0c7243df79ecb418acf1dadc2487cd6fd79fb420 4795820 universe/binary-i386/Packages.bz2 + 1e8fa52a64292d2c73cee0645d0eed5583ea7cc1138af4744838c6833716d638 25188905 universe/binary-powerpc/Packages + 5d2b8e23e0a16f13e25595b63807fb64afd9074aadf7a37b8e238b2011e894b8 6080488 universe/binary-powerpc/Packages.gz + eb482b008c8c882b349230abaa812ed6e545a2ef9132bb0d3d3bffa74da0c6c7 4716652 universe/binary-powerpc/Packages.bz2 + 98d44cc7544f79c18b8e8ea697d476e9b85d91088385b643c82d4064b21f4657 103 universe/binary-powerpc/Release + 3da2d1e57aaca628148e2688a951cbb784a9a54b7f6b1f84d530add1b66fcceb 15255 universe/debian-installer/binary-amd64/Packages.bz2 + 43f891ac590f44fde5854de9ba15222c088b70562f5dc4ff26064909e60cf62e 17243 universe/debian-installer/binary-amd64/Packages.gz + 3084a8a441e961eeb3865ff411557166ec105be86a55df268cdb6725f49e1f67 61801 universe/debian-installer/binary-amd64/Packages + a1ff01f18766744f36d0774a68d8a89355246c585c4b28ee18e5e139fafae530 113584 universe/debian-installer/binary-armel/Packages + e0713f86f5f5121deb60ce61d774951468625184a7ae9576f81d70202ef585b7 23193 universe/debian-installer/binary-armel/Packages.bz2 + 5a8411e2b0648e553fa25ac82ea83fb17dd2d2a77bd10cec14cab12f5582d4c4 27397 universe/debian-installer/binary-armel/Packages.gz + b79c86d926c3129f5c27e50185157a78d85abde8ada90a9910338e660c4318be 20065 universe/debian-installer/binary-armhf/Packages.gz + b2113b25380423be8f6202a4860479e44a00072e46fa035f0da2f3a5a280de20 17619 universe/debian-installer/binary-armhf/Packages.bz2 + 3f023d2cc55d6ebab883f6f2d7305a4e3564f918f63ca4f745d6fd1318e67ab7 76034 universe/debian-installer/binary-armhf/Packages + 5ea61a62a3e8fc755c22e23c9d87b20924707c0986a490458472a3d7e9cbe117 17260 universe/debian-installer/binary-i386/Packages.gz + 7a90b014c655311e92de1ea4cf24e100665c564a2ed699df63d17c82ad9e1349 15272 universe/debian-installer/binary-i386/Packages.bz2 + 7e39417ce073e3a35d048847a29a0414af69c4e923c018dc22438319c79adea5 61718 universe/debian-installer/binary-i386/Packages + cdf17a791544d0c522fa853a23b317deffa76ac643e88bec0b84b0aa5afe957b 61121 universe/debian-installer/binary-powerpc/Packages + b470146da791dc4f4593d2bb00ea4e305d6f55f346a5f3ac6755d890a3318080 15024 universe/debian-installer/binary-powerpc/Packages.bz2 + 810d1590d1cd7298e1fd5465f85ba49b6ae79780b42d8e8b68aebb42283785ea 16860 universe/debian-installer/binary-powerpc/Packages.gz + 563a55a892e1ec8bf565e3294c033b4e8dbbbe4651e73733eac7338db77282f7 2922 universe/i18n/Index + 7bc01d4f10bbcf882ce6931aa9371b2de6b35277efc2ae52e233280dcd12a18d 21256524 universe/source/Sources + 95135631873f4dce05ba657478475033d02462bbb8f7263832585d1decb5c9b8 5019105 universe/source/Sources.bz2 + 0fd2ae580be352cb8ab4bb87e5504b78f78bcb7249b644719b3c2db3b5d3ca8c 102 universe/source/Release + d1dd96015e24dd369ea22413a2b876686a60c5d9d91958a5df3745a66289910f 6238766 universe/source/Sources.gz diff --git a/tests/test_utils.py b/tests/test_utils.py index 26ee0bff..dc54c1ba 100644 --- a/tests/test_utils.py +++ b/tests/test_utils.py @@ -12,11 +12,19 @@ import apt.utils import datetime import unittest +from apt.utils import ( + get_maintenance_end_date, + get_release_date_from_release_file, + ) + class TestUtils(unittest.TestCase): + def test_get_release_date_from_release_file(self): + t = get_release_date_from_release_file("./tests/data/misc/foo_Release") + self.assertEqual(str(datetime.datetime.utcfromtimestamp(t)), + "2012-04-25 22:49:23") def test_maintenance_time(self): - from apt.utils import get_maintenance_end_date months_of_support = 18 # test historic releases, jaunty release_date = datetime.datetime(2009, 4, 23) -- cgit v1.2.3 From 32eb1cd06404fdcb0d4507ea77e1685a19d185c1 Mon Sep 17 00:00:00 2001 From: Michael Vogt Date: Tue, 12 Jun 2012 09:00:23 +0200 Subject: merge fix from lp:~ev/python-apt/dont-leak-fds, many thanks --- apt/progress/base.py | 4 ++++ debian/changelog | 6 ++++++ 2 files changed, 10 insertions(+) (limited to 'apt') diff --git a/apt/progress/base.py b/apt/progress/base.py index 4943978c..88b1ad21 100644 --- a/apt/progress/base.py +++ b/apt/progress/base.py @@ -146,6 +146,10 @@ class InstallProgress(object): self.status_stream = os.fdopen(self.statusfd, "r") fcntl.fcntl(self.statusfd, fcntl.F_SETFL, os.O_NONBLOCK) + def __del__(self): + self.write_stream.close() + self.status_stream.close() + def start_update(self): """(Abstract) Start update.""" diff --git a/debian/changelog b/debian/changelog index 63f72fa5..8d8be6a6 100644 --- a/debian/changelog +++ b/debian/changelog @@ -43,6 +43,12 @@ python-apt (0.8.4~exp1) experimental; urgency=low -- Michael Vogt Tue, 24 Jan 2012 14:02:46 +0100 +python-apt (0.8.3ubuntu9) UNRELEASED; urgency=low + + * Don't leak file descriptors. + + -- Evan Dandrea Mon, 11 Jun 2012 17:00:37 +0100 + python-apt (0.8.3) unstable; urgency=low [ Alexey Feldgendler ] -- cgit v1.2.3 From 5cdd74bc9e017f8802f96d915131c8cf525d4146 Mon Sep 17 00:00:00 2001 From: Evan Dandrea Date: Tue, 12 Jun 2012 16:06:16 +0100 Subject: Drop __del__ statement, which is unsafe: http://www.algorithm.co.il/blogs/programming/python-gotchas-1-__del__-is-not-the-opposite-of-__init__/ --- apt/progress/base.py | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) (limited to 'apt') diff --git a/apt/progress/base.py b/apt/progress/base.py index 88b1ad21..ab57dd82 100644 --- a/apt/progress/base.py +++ b/apt/progress/base.py @@ -142,14 +142,11 @@ class InstallProgress(object): def __init__(self): (self.statusfd, self.writefd) = os.pipe() + # These will leak fds, but fixing this safely requires API changes. self.write_stream = os.fdopen(self.writefd, "w") self.status_stream = os.fdopen(self.statusfd, "r") fcntl.fcntl(self.statusfd, fcntl.F_SETFL, os.O_NONBLOCK) - def __del__(self): - self.write_stream.close() - self.status_stream.close() - def start_update(self): """(Abstract) Start update.""" -- cgit v1.2.3 From b50494275b8235e5c8a53e13fd56e3cea1c8d5b3 Mon Sep 17 00:00:00 2001 From: Julian Andres Klode Date: Mon, 25 Jun 2012 14:23:10 +0200 Subject: * apt/auth.py: - Use tempfile.NamedTemporaryFile to create temporary file --- apt/auth.py | 52 +++++++++++++++++++++++++++------------------------- debian/changelog | 4 ++++ 2 files changed, 31 insertions(+), 25 deletions(-) (limited to 'apt') diff --git a/apt/auth.py b/apt/auth.py index 38c4bdc6..1a81d3b0 100644 --- a/apt/auth.py +++ b/apt/auth.py @@ -51,38 +51,40 @@ class TrustedKey(object): def _call_apt_key_script(*args, **kwargs): """Run the apt-key script with the given arguments.""" + conf = None cmd = [apt_pkg.config.find_file("Dir::Bin::Apt-Key", "/usr/bin/apt-key")] cmd.extend(args) env = os.environ.copy() env["LANG"] = "C" - if apt_pkg.config.find_dir("Dir") != "/": - # If the key is to be installed into a chroot we have to export the - # configuration from the chroot to the apt-key script by using - # a temporary APT_CONFIG file. The apt-key script uses apt-config shell - # internally - conf_fd, conf_name = tempfile.mkstemp(prefix="apt-key", suffix="conf") - atexit.register(os.remove, conf_name) + try: + if apt_pkg.config.find_dir("Dir") != "/": + # If the key is to be installed into a chroot we have to export the + # configuration from the chroot to the apt-key script by using + # a temporary APT_CONFIG file. The apt-key script uses apt-config + # shell internally + conf = tempfile.NamedTemporaryFile(prefix="apt-key", suffix=".conf") + conf.write(apt_pkg.config.dump().encode("UTF-8")) + conf.flush() + env["APT_CONFIG"] = conf.name + proc = subprocess.Popen(cmd, env=env, universal_newlines=True, + stdin=subprocess.PIPE, + stdout=subprocess.PIPE, + stderr=subprocess.STDOUT) try: - os.write(conf_fd, apt_pkg.config.dump().encode("UTF-8")) + proc.stdin.write(kwargs["stdin"]) + except KeyError: + pass finally: - os.close(conf_fd) - env["APT_CONFIG"] = conf_name - proc = subprocess.Popen(cmd, env=env, universal_newlines=True, - stdin=subprocess.PIPE, - stdout=subprocess.PIPE, - stderr=subprocess.STDOUT) - try: - proc.stdin.write(kwargs["stdin"]) - except KeyError: - pass + proc.stdin.close() + return_code = proc.wait() + output = proc.stdout.read() + if return_code: + raise SystemError("The apt-key script failed with return code %s:\n" + "%s\n%s" % (return_code, " ".join(cmd), output)) + return output.strip() finally: - proc.stdin.close() - return_code = proc.wait() - output = proc.stdout.read() - if return_code: - raise SystemError("The apt-key script failed with return code %s:\n" - "%s\n%s" % (return_code, " ".join(cmd), output)) - return output.strip() + if conf is not None: + conf.close() def add_key_from_file(filename): """Import a GnuPG key file to trust repositores signed by it. diff --git a/debian/changelog b/debian/changelog index ffb6a2b4..e6d6d648 100644 --- a/debian/changelog +++ b/debian/changelog @@ -5,6 +5,10 @@ python-apt (0.8.6) UNRELEASED; urgency=low - add build-dep for apt (>= 0.9.6) to make test_auth.py test work reliable + [ Julian Andres Klode ] + * apt/auth.py: + - Use tempfile.NamedTemporaryFile to create temporary file + -- Michael Vogt Mon, 25 Jun 2012 13:41:02 +0200 python-apt (0.8.5) unstable; urgency=low -- cgit v1.2.3 From f513760769e5e0c7ba08f171e4d5399000b8ae51 Mon Sep 17 00:00:00 2001 From: Julian Andres Klode Date: Mon, 25 Jun 2012 14:26:43 +0200 Subject: Use Popen.communicate() instead of stdin, stdout --- apt/auth.py | 22 ++++++++++++---------- debian/changelog | 1 + 2 files changed, 13 insertions(+), 10 deletions(-) (limited to 'apt') diff --git a/apt/auth.py b/apt/auth.py index 1a81d3b0..5d4b1cd6 100644 --- a/apt/auth.py +++ b/apt/auth.py @@ -70,17 +70,19 @@ def _call_apt_key_script(*args, **kwargs): stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) - try: - proc.stdin.write(kwargs["stdin"]) - except KeyError: - pass - finally: - proc.stdin.close() - return_code = proc.wait() - output = proc.stdout.read() - if return_code: + + content = kwargs.get("stdin", None) + if isinstance(content, unicode): + content = content.encode("utf-8") + + output, stderr = proc.communicate(content) + + assert stderr == None + + if proc.returncode: raise SystemError("The apt-key script failed with return code %s:\n" - "%s\n%s" % (return_code, " ".join(cmd), output)) + "%s\n%s" % (proc.returncode, " ".join(cmd), + output)) return output.strip() finally: if conf is not None: diff --git a/debian/changelog b/debian/changelog index e6d6d648..627ef655 100644 --- a/debian/changelog +++ b/debian/changelog @@ -8,6 +8,7 @@ python-apt (0.8.6) UNRELEASED; urgency=low [ Julian Andres Klode ] * apt/auth.py: - Use tempfile.NamedTemporaryFile to create temporary file + - Use Popen.communicate() instead of stdin, stdout -- Michael Vogt Mon, 25 Jun 2012 13:41:02 +0200 -- cgit v1.2.3