Commit 4548cac3 authored by Julian Andres Klode's avatar Julian Andres Klode

Add apt_pkg.Group class, wrapping pkgCache::GrpIterator

parent f7adc2d7
python-apt (0.8.0~exp1) UNRELEASED; urgency=low
* Disable the old-style API, and break all packages using it
* Add an 'is_multi_arch' attribute to apt_pkg.Cache
* Add an 'is_multi_arch' attribute to apt_pkg.Cache
* Add apt_pkg.Group class, wrapping pkgCache::GrpIterator
-- Julian Andres Klode <jak@debian.org> Tue, 05 Apr 2011 10:33:54 +0200
......
......@@ -389,6 +389,36 @@ Description (pkgCache::DescIterator)
Return the :ctype:`pkgCache::DescIterator` reference contained in the
Python object *object*.
Group (pkgCache::GrpIterator)
----------------------------------
.. 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
......
......@@ -436,6 +436,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
^^^^^^^^^^^^^^^^^^^^^^^^^^^^
......
......@@ -732,6 +732,9 @@ 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
};
......@@ -811,6 +814,7 @@ 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);
/* ============================ cdrom.cc ============================ */
ADDTYPE(Module,"Cdrom",&PyCdrom_Type);
/* ========================= configuration.cc ========================= */
......
......@@ -70,6 +70,7 @@ extern PyTypeObject PyCache_Type;
extern PyTypeObject PyCacheFile_Type;
extern PyTypeObject PyPackageList_Type;
extern PyTypeObject PyDescription_Type;
extern PyTypeObject PyGroup_Type;
extern PyTypeObject PyPackage_Type;
extern PyTypeObject PyPackageFile_Type;
extern PyTypeObject PyDependency_Type;
......@@ -149,6 +150,7 @@ extern PyTypeObject PyFileLock_Type;
# define PyDependency_ToCpp GetCpp<pkgCache::DepIterator>
# define PyDependencyList_ToCpp GetCpp<RDepListStruct> // TODO
# define PyDescription_ToCpp GetCpp<pkgCache::DescIterator>
# define PyGroup_ToCpp GetCpp<pkgCache::GrpIterator>
# define PyHashes_ToCpp GetCpp<Hashes>
# define PyHashString_ToCpp GetCpp<HashString*>
# define PyIndexRecords_ToCpp GetCpp<indexRecords*>
......@@ -186,6 +188,7 @@ 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* PyPackageFile_FromCpp(pkgCache::PkgFileIterator const &obj, bool Delete, PyObject *Owner);
//PyObject* PyPackageList_FromCpp(PkgListStruct const &obj, bool Delete, PyObject *Owner);
......
/*
* cachegroup.cc - Wrapper around pkgCache::GrpIterator
*
* Copyright 2011 Julian Andres Klode <jak@debian.org>
*
* 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 <Python.h>
#include "apt_pkgmodule.h"
#include "generic.h"
#include <apt-pkg/pkgcache.h>
struct PyGroup : CppPyObject<pkgCache::GrpIterator> {
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<pkgCache *>(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<pkgCache::GrpIterator>(self);
PyObject *owner = GetOwner<pkgCache::GrpIterator>(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<pkgCache::GrpIterator>(self);
PyObject *owner = GetOwner<pkgCache::GrpIterator>(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<PyGroup *>(pySelf);
pkgCache::GrpIterator grp = GetCpp<pkgCache::GrpIterator>(self);
PyObject *owner = GetOwner<pkgCache::GrpIterator>(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<pkgCache::GrpIterator>, // 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
};
......@@ -52,6 +52,7 @@ 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(PyPackageFile_FromCpp,&PyPackageFile_Type,pkgCache::PkgFileIterator)
//NEW_FROM(PyPackageList_FromCpp,&PyPackageList_Type,PkgListStruct)
......
......@@ -167,6 +167,9 @@ 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);
};
// Checking macros.
......@@ -184,6 +187,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,6 +221,7 @@ 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)
......@@ -260,6 +265,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)
......@@ -292,6 +298,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
......@@ -324,6 +331,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
......
......@@ -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']
files = sorted(['python/' + fname for fname in files], key=lambda s: s[:-3])
apt_pkg = Extension("apt_pkg", files, libraries=["apt-pkg"])
......
import unittest
import apt_pkg
class TestGroup(unittest.TestCase):
def setUp(self):
apt_pkg.init()
self.cache = apt_pkg.Cache()
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)
if __name__ == "__main__":
unittest.main()
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment