summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJulian Andres Klode <jak@debian.org>2009-03-08 16:46:39 +0100
committerJulian Andres Klode <jak@debian.org>2009-03-08 16:46:39 +0100
commita81e4f2f93808aeaeac1887aba9b41c1ee1c9b11 (patch)
tree6a0a7f10ce059ff04c60813cb7c5bc24e4e867ad
parentdc7440e55ea06cc4e0a6e53cd7bb6e3eee3af452 (diff)
downloadpython-apt-a81e4f2f93808aeaeac1887aba9b41c1ee1c9b11.tar.gz
* apt/package.py: Introduce the Version class
- Deprecate Package.candidate*() and Package.installed*(), except for installedFiles. - Provide Version.get_source() (LP: #118788)
-rw-r--r--apt/package.py550
-rw-r--r--debian/changelog4
-rw-r--r--doc/source/apt/package.rst5
3 files changed, 337 insertions, 222 deletions
diff --git a/apt/package.py b/apt/package.py
index ecfd8b61..a44eb2a6 100644
--- a/apt/package.py
+++ b/apt/package.py
@@ -21,15 +21,19 @@
"""Functionality related to packages."""
import gettext
import httplib
+import os
import sys
import re
import socket
+import subprocess
import urllib2
+import warnings
import apt_pkg
+import apt.progress
-
-__all__ = 'BaseDependency', 'Dependency', 'Origin', 'Package', 'Record'
+__all__ = ('BaseDependency', 'Dependency', 'Origin', 'Package', 'Record',
+ 'Version')
# Set a timeout for the changelog download
@@ -57,6 +61,10 @@ class BaseDependency(object):
self.version = ver
self.preDepend = pre
+ def __repr__(self):
+ return ('<BaseDependency: name:%r relation:%r version:%r preDepend:%r>'
+ % (self.name, self.relation, self.version, self.preDepend))
+
class Dependency(object):
"""Represent an Or-group of dependencies.
@@ -68,6 +76,26 @@ class Dependency(object):
def __init__(self, alternatives):
self.or_dependencies = alternatives
+ def __repr__(self):
+ return repr(self.or_dependencies)
+
+class DeprecatedProperty(property):
+ """A property which gives DeprecationWarning on access.
+
+ This is only used for providing the properties in Package, which have been
+ replaced by the ones in Version.
+ """
+
+ def __init__(self, fget=None, fset=None, fdel=None, doc=None):
+ property.__init__(self, fget, fset, fdel, doc)
+ self.__doc__ = ':Deprecated: ' + (doc or fget.__doc__ or '')
+
+ def __get__(self, obj, type=None):
+ warnings.warn("Accessed deprecated property %s.%s, please see the "
+ "Version class for alternatives." %
+ ((obj.__class__.__name__ or type.__name__),
+ self.fget.func_name), DeprecationWarning, 2)
+ return property.__get__(self, obj, type)
class Origin(object):
"""The origin of a version.
@@ -95,10 +123,10 @@ class Origin(object):
self.trusted = False
def __repr__(self):
- return ("<Origin component:'%s' archive:'%s' origin:'%s' label:'%s'"
- "site:'%s' isTrusted:'%s'>") % (self.component, self.archive,
- self.origin, self.label,
- self.site, self.trusted)
+ return ("<Origin component:%r archive:%r origin:%r label:%r "
+ "site:%r isTrusted:%r>") % (self.component, self.archive,
+ self.origin, self.label,
+ self.site, self.trusted)
class Record(object):
@@ -139,6 +167,208 @@ class Record(object):
"""deprecated form of 'key in x'."""
return self._rec.has_key(key)
+class Version(object):
+ """Representation of a package version.
+
+ :since: 0.7.9
+ """
+
+ def __init__(self, package, cand):
+ self.package = package
+ self._cand = cand
+
+ def __eq__(self, other):
+ return self._cand.ID == other._cand.ID
+
+ def __gt__(self, other):
+ return apt_pkg.VersionCompare(self.version, other.version) > 0
+
+ def __lt__(self, other):
+ return apt_pkg.VersionCompare(self.version, other.version) < 0
+
+ def __ne__(self, other):
+ return not self.__eq__(other)
+
+ def __hash__(self):
+ return self._cand.Hash
+
+ def __repr__(self):
+ return '<Version: package:%r version:%r>' % (self.package.name,
+ self.version)
+
+ @property
+ def _records(self):
+ """Internal helper that moves the Records to the right position."""
+ if self.package._records.Lookup(self._cand.FileList[0]):
+ return self.package._records
+
+ @property
+ def installed_size(self):
+ """Return the size of the package when installed."""
+ return self._cand.InstalledSize
+
+ @property
+ def homepage(self):
+ """Return the homepage for the package."""
+ return self._records.Homepage
+
+ @property
+ def size(self):
+ """Return the size of the package."""
+ return self._cand.Size
+
+ @property
+ def architecture(self):
+ """Return the architecture of the package version."""
+ return self._cand.Arch
+
+ @property
+ def downloadable(self):
+ """Return whether the version of the package is downloadable."""
+ return bool(self._cand.Downloadable)
+
+ @property
+ def version(self):
+ """Return the version as a string."""
+ return self._cand.VerStr
+
+ @property
+ def summary(self):
+ """Return the short description (one line summary)."""
+ desc_iter = self._cand.TranslatedDescription
+ self.package._records.Lookup(desc_iter.FileList.pop(0))
+ return self.package._records.ShortDesc
+
+ @property
+ def raw_description(self):
+ """return the long description (raw)."""
+ return self._records.LongDesc
+
+ @property
+ def section(self):
+ """Return the section of the package."""
+ return self._cand.Section
+
+ @property
+ def description(self, format=True, useDots=False):
+ """Return the formatted long description.
+
+ Return the formated long description according to the Debian policy
+ (Chapter 5.6.13).
+ See http://www.debian.org/doc/debian-policy/ch-controlfields.html
+ for more information.
+ """
+ self.summary # This does the lookup for us.
+ desc = ''
+ try:
+ dsc = unicode(self.package._records.LongDesc, "utf-8")
+ except UnicodeDecodeError, err:
+ return _("Invalid unicode in description for '%s' (%s). "
+ "Please report.") % (self.name, err)
+
+ lines = iter(dsc.split("\n"))
+ # Skip the first line, since its a duplication of the summary
+ lines.next()
+ for raw_line in lines:
+ if raw_line.strip() == ".":
+ # The line is just line break
+ if not desc.endswith("\n"):
+ desc += "\n\n"
+ continue
+ if raw_line.startswith(" "):
+ # The line should be displayed verbatim without word wrapping
+ if not desc.endswith("\n"):
+ line = "\n%s\n" % raw_line[2:]
+ else:
+ line = "%s\n" % raw_line[2:]
+ elif raw_line.startswith(" "):
+ # The line is part of a paragraph.
+ if desc.endswith("\n") or desc == "":
+ # Skip the leading white space
+ line = raw_line[1:]
+ else:
+ line = raw_line
+ else:
+ line = raw_line
+ # Add current line to the description
+ desc += line
+ return desc
+
+ @property
+ def source_name(self):
+ """Return the name of the source package."""
+ try:
+ return self._records.SourcePkg or self.package.name
+ except IndexError:
+ return self.package.name
+
+ @property
+ def priority(self):
+ """Return the priority of the package, as string."""
+ return self._cand.PriorityStr
+
+ @property
+ def record(self):
+ """Return a Record() object for this version."""
+ return Record(self._records.Record)
+
+ @property
+ def dependencies(self):
+ """Return the dependencies of the package version."""
+ depends_list = []
+ depends = self._cand.DependsList
+ for t in ["PreDepends", "Depends"]:
+ try:
+ for depVerList in depends[t]:
+ base_deps = []
+ for depOr in depVerList:
+ base_deps.append(BaseDependency(depOr.TargetPkg.Name,
+ depOr.CompType, depOr.TargetVer,
+ (t == "PreDepends")))
+ depends_list.append(Dependency(base_deps))
+ except KeyError:
+ pass
+ return depends_list
+
+ @property
+ def origins(self):
+ """Return a list of origins for the package version."""
+ origins = []
+ for (verFileIter, index) in self._cand.FileList:
+ origins.append(Origin(self.package, verFileIter))
+ return origins
+
+ def fetch_source(self):
+ """Get the source code of a package"""
+ src = apt_pkg.GetPkgSrcRecords()
+ acq = apt_pkg.GetAcquire(apt.progress.TextFetchProgress())
+ dsc = None
+ src.Lookup(self.package.name)
+ try:
+ while self.version != src.Version:
+ src.Lookup(self.package.name)
+ except AttributeError:
+ raise ValueError("No source for %r" % self)
+ for md5, size, path, type in src.Files:
+ base = os.path.basename(path)
+ if type == 'dsc':
+ dsc = base
+ if os.path.exists(base) and os.path.getsize(base) == size:
+ fobj = open(base)
+ try:
+ if apt_pkg.md5sum(fobj) == md5:
+ print 'Ignoring already existing file', base
+ continue
+ finally:
+ fobj.close()
+ apt_pkg.GetPkgAcqFile(acq, src.Index.ArchiveURI(path), md5, size,
+ base)
+ acq.Run()
+
+ outdir = src.Package + '-' + apt_pkg.UpstreamVersion(src.Version)
+ subprocess.check_call(["dpkg-source", "-x", dsc, outdir])
+ return os.path.abspath(outdir)
+
class Package(object):
"""Representation of a package in a cache.
@@ -158,28 +388,23 @@ class Package(object):
self._pcache = pcache # python cache in cache.py
self._changelog = "" # Cached changelog
- def _lookupRecord(self, UseCandidate=True):
- """Internal helper that moves the Records to the right position.
+ def __repr__(self):
+ return '<Package: name:%r id:%r>' % (self._pkg.Name, self._pkg.ID)
- Must be called before _records is accessed.
- """
- if UseCandidate:
- ver = self._depcache.GetCandidateVer(self._pkg)
- else:
- ver = self._pkg.CurrentVer
+ @property
+ def candidate(self):
+ """Return the candidate version of the package.
- # check if we found a version
- if ver is None:
- #print "No version for: %s (Candidate: %s)" % (self._pkg.Name,
- # UseCandidate)
- return False
+ :since: 0.7.9"""
+ return Version(self, self._depcache.GetCandidateVer(self._pkg))
- if ver.FileList is None:
- print "No FileList for: %s " % self._pkg.Name()
- return False
- f, index = ver.FileList.pop(0)
- self._records.Lookup((f, index))
- return True
+ @property
+ def installed(self):
+ """Return the currently installed version of the package.
+
+ :since: 0.7.9"""
+ if self._pkg.CurrentVer is not None:
+ return Version(self, self._pkg.CurrentVer)
@property
def name(self):
@@ -199,142 +424,81 @@ class Package(object):
This returns the same value as ID, which is unique."""
return self._pkg.ID
- @property
+ @DeprecatedProperty
def installedVersion(self):
- """Return the installed version as string."""
- ver = self._pkg.CurrentVer
- if ver is not None:
- return ver.VerStr
- else:
- return None
+ """Return the installed version as string.
- @property
+ Deprecated, please use installed.version instead."""
+ return getattr(self.installed, 'version', None)
+
+ @DeprecatedProperty
def candidateVersion(self):
"""Return the candidate version as string."""
- ver = self._depcache.GetCandidateVer(self._pkg)
- if ver is not None:
- return ver.VerStr
- else:
- return None
-
- def _getDependencies(self, ver):
- """Get the dependencies for a given version of a package."""
- depends_list = []
- depends = ver.DependsList
- for t in ["PreDepends", "Depends"]:
- try:
- for depVerList in depends[t]:
- base_deps = []
- for depOr in depVerList:
- base_deps.append(BaseDependency(depOr.TargetPkg.Name,
- depOr.CompType, depOr.TargetVer,
- (t == "PreDepends")))
- depends_list.append(Dependency(base_deps))
- except KeyError:
- pass
- return depends_list
+ return self.candidate.version
- @property
+ @DeprecatedProperty
def candidateDependencies(self):
"""Return a list of candidate dependencies."""
- candver = self._depcache.GetCandidateVer(self._pkg)
- if candver is None:
- return []
- return self._getDependencies(candver)
+ return self.candidate.dependencies
- @property
+ @DeprecatedProperty
def installedDependencies(self):
"""Return a list of installed dependencies."""
- ver = self._pkg.CurrentVer
- if ver is None:
- return []
- return self._getDependencies(ver)
+ return getattr(self.installed, 'dependencies', [])
- @property
+ @DeprecatedProperty
def architecture(self):
"""Return the Architecture of the package"""
- if not self._lookupRecord():
- return None
- sec = apt_pkg.ParseSection(self._records.Record)
- try:
- return sec["Architecture"]
- except KeyError:
- return None
-
- def _downloadable(self, useCandidate=True):
- """Return True if the package is downloadable."""
- if useCandidate:
- ver = self._depcache.GetCandidateVer(self._pkg)
- else:
- ver = self._pkg.CurrentVer
- if ver is None:
- return False
- return ver.Downloadable
+ return self.candidate.architecture
- @property
+ @DeprecatedProperty
def candidateDownloadable(self):
"""Return True if the candidate is downloadable."""
- return self._downloadable(True)
+ return self.candidate.downloadable
- @property
+ @DeprecatedProperty
def installedDownloadable(self):
"""Return True if the installed version is downloadable."""
- return self._downloadable(False)
+ return getattr(self.installed, 'downloadable', False)
- @property
+ @DeprecatedProperty
def sourcePackageName(self):
"""Return the source package name as string."""
- if not self._lookupRecord():
- if not self._lookupRecord(False):
+ try:
+ return self.candidate._records.SourcePkg or self._pkg.Name
+ except AttributeError:
+ try:
+ return self.installed._records.SourcePkg or self._pkg.Name
+ except AttributeError:
return self._pkg.Name
- src = self._records.SourcePkg
- if src != "":
- return src
- else:
- return self._pkg.Name
- @property
+ @DeprecatedProperty
def homepage(self):
"""Return the homepage field as string."""
- if not self._lookupRecord():
- return None
- return self._records.Homepage
+ return self.candidate.homepage
@property
def section(self):
"""Return the section of the package."""
return self._pkg.Section
- @property
+ @DeprecatedProperty
def priority(self):
"""Return the priority (of the candidate version)."""
- ver = self._depcache.GetCandidateVer(self._pkg)
- if ver:
- return ver.PriorityStr
- else:
- return None
+ return self.candidate.priority
- @property
+ @DeprecatedProperty
def installedPriority(self):
"""Return the priority (of the installed version)."""
- ver = self._depcache.GetCandidateVer(self._pkg)
- if ver:
- return ver.PriorityStr
- else:
- return None
+ return getattr(self.installed, 'priority', None)
- @property
+ @DeprecatedProperty
def summary(self):
"""Return the short description (one line summary)."""
- if not self._lookupRecord():
- return ""
- ver = self._depcache.GetCandidateVer(self._pkg)
- desc_iter = ver.TranslatedDescription
- self._records.Lookup(desc_iter.FileList.pop(0))
- return self._records.ShortDesc
+ return self.candidate.summary
- @property
- def description(self, format=True, useDots=False):
+ @DeprecatedProperty
+ def description(self):
"""Return the formatted long description.
Return the formated long description according to the Debian policy
@@ -342,73 +506,22 @@ class Package(object):
See http://www.debian.org/doc/debian-policy/ch-controlfields.html
for more information.
"""
- if not format:
- return self.rawDescription
- if not self._lookupRecord():
- return ""
- # get the translated description
- ver = self._depcache.GetCandidateVer(self._pkg)
- desc_iter = ver.TranslatedDescription
- self._records.Lookup(desc_iter.FileList.pop(0))
- desc = ""
- try:
- dsc = unicode(self._records.LongDesc, "utf-8")
- except UnicodeDecodeError, err:
- dsc = _("Invalid unicode in description for '%s' (%s). "
- "Please report.") % (self.name, err)
- lines = dsc.split("\n")
- for i in range(len(lines)):
- # Skip the first line, since its a duplication of the summary
- if i == 0:
- continue
- raw_line = lines[i]
- if raw_line.strip() == ".":
- # The line is just line break
- if not desc.endswith("\n"):
- desc += "\n"
- continue
- elif raw_line.startswith(" "):
- # The line should be displayed verbatim without word wrapping
- if not desc.endswith("\n"):
- line = "\n%s\n" % raw_line[2:]
- else:
- line = "%s\n" % raw_line[2:]
- elif raw_line.startswith(" "):
- # The line is part of a paragraph.
- if desc.endswith("\n") or desc == "":
- # Skip the leading white space
- line = raw_line[1:]
- else:
- line = raw_line
- else:
- line = raw_line
- # Use dots for lists
- if useDots:
- line = re.sub(r"^(\s*)(\*|0|o|-) ", ur"\1\u2022 ", line, 1)
- # Add current line to the description
- desc += line
- return desc
+ return self.candidate.description
- @property
+ @DeprecatedProperty
def rawDescription(self):
"""return the long description (raw)."""
- if not self._lookupRecord():
- return ""
- return self._records.LongDesc
+ return self.candidate.raw_description
- @property
+ @DeprecatedProperty
def candidateRecord(self):
"""Return the Record of the candidate version of the package."""
- if not self._lookupRecord(True):
- return None
- return Record(self._records.Record)
+ return self.candidate.record
- @property
+ @DeprecatedProperty
def installedRecord(self):
"""Return the Record of the candidate version of the package."""
- if not self._lookupRecord(False):
- return None
- return Record(self._records.Record)
+ return getattr(self.installed, 'record', '')
# depcache states
@@ -464,34 +577,25 @@ class Package(object):
# sizes
- @property
+ @DeprecatedProperty
def packageSize(self):
"""Return the size of the candidate deb package."""
- ver = self._depcache.GetCandidateVer(self._pkg)
- return ver.Size
+ return self.candidate.size
- @property
+ @DeprecatedProperty
def installedPackageSize(self):
"""Return the size of the installed deb package."""
- ver = self._pkg.CurrentVer
- return ver.Size
+ return getattr(self.installed, 'size', 0)
- @property
- def candidateInstalledSize(self, UseCandidate=True):
+ @DeprecatedProperty
+ def candidateInstalledSize(self):
"""Return the size of the candidate installed package."""
- ver = self._depcache.GetCandidateVer(self._pkg)
- if ver:
- return ver.Size
- else:
- return None
+ return self.candidate.installed_size
- @property
+ @DeprecatedProperty
def installedSize(self):
"""Return the size of the currently installed package."""
- ver = self._pkg.CurrentVer
- if ver is None:
- return 0
- return ver.InstalledSize
+ return getattr(self.installed, 'installed_size', 0)
@property
def installedFiles(self):
@@ -651,16 +755,18 @@ class Package(object):
"check your Internet connection.")
return self._changelog
- @property
+ @DeprecatedProperty
def candidateOrigin(self):
- """Return the Origin() of the candidate version."""
- ver = self._depcache.GetCandidateVer(self._pkg)
- if not ver:
- return None
- origins = []
- for (verFileIter, index) in ver.FileList:
- origins.append(Origin(self, verFileIter))
- return origins
+ """Return a list of Origin() objects for the candidate version."""
+ return self.candidate.origins
+
+ @property
+ def versions(self):
+ """Return a list of versions.
+
+ :since: 0.7.9
+ """
+ return [Version(self, ver) for ver in self._pkg.VersionList]
# depcache actions
@@ -751,26 +857,26 @@ def _test():
pkg = Package(cache, depcache, records, sourcelist, None, pkgiter)
print "Name: %s " % pkg.name
print "ID: %s " % pkg.id
- print "Priority (Candidate): %s " % pkg.priority
- print "Priority (Installed): %s " % pkg.installedPriority
- print "Installed: %s " % pkg.installedVersion
- print "Candidate: %s " % pkg.candidateVersion
- print "CandidateDownloadable: %s" % pkg.candidateDownloadable
- print "CandidateOrigins: %s" % pkg.candidateOrigin
- print "SourcePkg: %s " % pkg.sourcePackageName
+ print "Priority (Candidate): %s " % pkg.candidate.priority
+ print "Priority (Installed): %s " % pkg.installed.priority
+ print "Installed: %s " % pkg.installed.version
+ print "Candidate: %s " % pkg.candidate.version
+ print "CandidateDownloadable: %s" % pkg.candidate.downloadable
+ print "CandidateOrigins: %s" % pkg.candidate.origins
+ print "SourcePkg: %s " % pkg.candidate.source_name
print "Section: %s " % pkg.section
- print "Summary: %s" % pkg.summary
- print "Description (formated) :\n%s" % pkg.description
- print "Description (unformated):\n%s" % pkg.rawDescription
- print "InstalledSize: %s " % pkg.installedSize
- print "PackageSize: %s " % pkg.packageSize
- print "Dependencies: %s" % pkg.installedDependencies
- for dep in pkg.candidateDependencies:
+ print "Summary: %s" % pkg.candidate.summary
+ print "Description (formated) :\n%s" % pkg.candidate.description
+ print "Description (unformated):\n%s" % pkg.candidate.raw_description
+ print "InstalledSize: %s " % pkg.candidate.installed_size
+ print "PackageSize: %s " % pkg.candidate.size
+ print "Dependencies: %s" % pkg.installed.dependencies
+ for dep in pkg.candidate.dependencies:
print ",".join("%s (%s) (%s) (%s)" % (o.name, o.version, o.relation,
o.preDepend) for o in dep.or_dependencies)
- print "arch: %s" % pkg.architecture
- print "homepage: %s" % pkg.homepage
- print "rec: ", pkg.candidateRecord
+ print "arch: %s" % pkg.candidate.architecture
+ print "homepage: %s" % pkg.candidate.homepage
+ print "rec: ", pkg.candidate.record
# now test install/remove
progress = apt.progress.OpTextProgress()
diff --git a/debian/changelog b/debian/changelog
index 81e25b17..1ed96cc3 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -3,6 +3,10 @@ python-apt (0.7.9~exp3) experimental; urgency=low
[ Julian Andres Klode ]
* apt/gtk/widgets.py:
- Handle older versions of python-gobject which do not ship glib
+ * apt/package.py: Introduce the Version class
+ - Deprecate Package.candidate*() and Package.installed*(), except for
+ installedFiles.
+ - Provide Version.get_source() (LP: #118788)
[ Michael Vogt ]
* aptsources/distro.py:
diff --git a/doc/source/apt/package.rst b/doc/source/apt/package.rst
index 1bd032ab..6781717c 100644
--- a/doc/source/apt/package.rst
+++ b/doc/source/apt/package.rst
@@ -10,6 +10,11 @@ The Package class
.. autoclass:: Package
:members:
+The Version class
+-----------------
+.. autoclass:: Version
+ :members:
+
Dependency Information
----------------------