~doctormo/apt-cat/trunk

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
#
# Copyright 2010 Martin Owens
#
# 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 3 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, see <http://www.gnu.org/licenses/>
#
"""
Finding packages in Apt by category.

To make a package available simply tell apt that it enhances one of the apt-category-* packages.
"""

import os
import apt.cache
import logging

__appname__ = "apt-cat"
__version__ = "0.2"

PKG_TEMP  = "apt-category-%s"
CACHES    = apt.cache.Cache()

class NoPackageError(KeyError):
    """Specific package couldn't be found"""
    pass

class NoCategoriesError(ValueError):
    """No categories can be found, non installed."""
    pass

class NoPackagesError(ValueError):
    """No packages can be found in this category."""
    pass

class NoCategoryError(KeyError):
    """The specified category can't be found. Not installed."""
    pass


class Package(object):
    """Cover an installable package."""
    def __init__(self, apt_package):
        self._pkg  = apt_package
        self._cand = self._pkg.candidate

    @property
    def name(self):
        """Return the name of the package"""
        return self._pkg.name.replace('-', ' ').title()

    @property
    def pid(self):
        """Return the package identifier"""
        return self._pkg.name

    @property
    def summary(self):
        """Return the short one line summary"""
        return self._cand.summary

    @property
    def description(self):
        """Return the long description"""
        return self._cand.description

    def is_installed(self):
        """Return true is the package is installed."""
        return self._pkg.is_installed


class Category(Package):
    """A package category"""
    @property
    def name(self):
        """Return the summary as the name"""
        return self._cand.summary.title()

    @property
    def cid(self):
        """Return the part of the package name as the category id"""
        return self.pid.split('-')[-1]


def _it_rev_dep(package_name, relationship, installed=None):
    """Iterate over reverse dependancies"""
    already = []
    package = CACHES[package_name]
    for dep in package._pkg.rev_depends_list:
        # We like the wrapper classes from python-apt
        pkg = apt.package.Package(CACHES, dep.parent_pkg)
        # Only return unique items, suspect versions from locations.
        is_unique = not pkg.name in already
        if dep.dep_type == relationship and is_unique:
            if installed == None or pkg.is_installed == installed:
                already.append(pkg.name)
                yield pkg


def iterate_categories(installed=None, category='apt-cat'):
    """Iterate over available/installed package categories"""
    pkg = None
    try:
        # apt-cat is the root category
        for pkg in _it_rev_dep(category, 'Depends', installed):
            yield Category(pkg)
    except KeyError:
        raise NoPackageError("Apt-cat isn't installed. Please install first.")
    if not pkg:
        raise NoCategoriesError("No categories found")


def iterate_packages(categories, installed=None):
    """Iterate over packages available in the given category."""
    if not isinstance(categories, list):
        categories = [ categories ]
    dep = None
    category = None
    for category in categories:
        if isinstance(category, Category):
            pkg = category.pid
        else:
            pkg = PKG_TEMP % category
        try:
            for dep in _it_rev_dep(pkg, 'Enhances', installed):
                yield Package(dep)
        except KeyError:
            raise NoCategoryError("Category %s not found." % pkg)
    if not dep:
        raise NoPackagesError("No packages found in %s." % categories)