From 3b086277b5e7577e46f26b7e0ba669ef12fd24ff Mon Sep 17 00:00:00 2001 From: Panu Matilainen Date: Wed, 18 Dec 2013 09:01:12 +0200 Subject: [PATCH] New rpmfile[s] python bindings - rpmfiles is represented as a sequence of individual files, thus its iterable as-is - Individual files are handled internally as index on rpmfiles handle, all data made available as read-only properties - We can now consider the old, bizarre rpm.fi bindings as deprecated --- python/Makefile.am | 1 + python/rpmfiles-py.c | 517 +++++++++++++++++++++++++++++++++++++++++++ python/rpmfiles-py.h | 18 ++ python/rpmmodule.c | 9 + python/setup.py.in | 2 +- 5 files changed, 546 insertions(+), 1 deletion(-) create mode 100644 python/rpmfiles-py.c create mode 100644 python/rpmfiles-py.h diff --git a/python/Makefile.am b/python/Makefile.am index 46e217444..d2a2d0193 100644 --- a/python/Makefile.am +++ b/python/Makefile.am @@ -20,6 +20,7 @@ _rpmmodule_la_SOURCES = rpmmodule.c rpmsystem-py.h \ rpmds-py.c rpmds-py.h \ rpmfd-py.c rpmfd-py.h \ rpmfi-py.c rpmfi-py.h \ + rpmfiles-py.c rpmfiles-py.h \ rpmkeyring-py.c rpmkeyring-py.h \ rpmmi-py.c rpmmi-py.h \ rpmii-py.c rpmii-py.h \ diff --git a/python/rpmfiles-py.c b/python/rpmfiles-py.c new file mode 100644 index 000000000..2584d1acd --- /dev/null +++ b/python/rpmfiles-py.c @@ -0,0 +1,517 @@ +#include "rpmsystem-py.h" + +#include +#include + +#include "header-py.h" +#include "rpmfi-py.h" +#include "rpmfiles-py.h" +#include "rpmstrpool-py.h" + +/* A single file from rpmfiles set, can't be independently instanciated */ +struct rpmfileObject_s { + PyObject_HEAD + PyObject *md_dict; + rpmfiles files; /* reference to rpmfiles */ + int ix; /* index to rpmfiles */ +}; + +static void rpmfile_dealloc(rpmfileObject * s) +{ + s->files = rpmfilesFree(s->files); + Py_TYPE(s)->tp_free((PyObject *)s); +} + +static char rpmfile_doc[] = +""; + +static PyObject *rpmfile_fx(rpmfileObject *s) +{ + return Py_BuildValue("i", s->ix); +} + +static PyObject *rpmfile_dx(rpmfileObject *s) +{ + return Py_BuildValue("i", rpmfilesDI(s->files, s->ix)); +} + +static PyObject *rpmfile_name(rpmfileObject *s) +{ + char * fn = rpmfilesFN(s->files, s->ix); + PyObject *o = Py_BuildValue("s", fn); + free(fn); + return o; +} + +static PyObject *rpmfile_basename(rpmfileObject *s) +{ + return Py_BuildValue("s", rpmfilesBN(s->files, s->ix)); +} + +static PyObject *rpmfile_dirname(rpmfileObject *s) +{ + return Py_BuildValue("s", rpmfilesDN(s->files, rpmfilesDI(s->files, s->ix))); +} + +static PyObject *rpmfile_orig_name(rpmfileObject *s) +{ + char * fn = rpmfilesOFN(s->files, s->ix); + PyObject *o = Py_BuildValue("s", fn); + free(fn); + return o; +} + +static PyObject *rpmfile_orig_basename(rpmfileObject *s) +{ + return Py_BuildValue("s", rpmfilesOBN(s->files, s->ix)); +} + +static PyObject *rpmfile_orig_dirname(rpmfileObject *s) +{ + return Py_BuildValue("s", rpmfilesODN(s->files, rpmfilesODI(s->files, s->ix))); +} +static PyObject *rpmfile_mode(rpmfileObject *s) +{ + return Py_BuildValue("i", rpmfilesFMode(s->files, s->ix)); +} + +static PyObject *rpmfile_size(rpmfileObject *s) +{ + return Py_BuildValue("L", rpmfilesFSize(s->files, s->ix)); +} + +static PyObject *rpmfile_mtime(rpmfileObject *s) +{ + return Py_BuildValue("i", rpmfilesFMtime(s->files, s->ix)); +} + +static PyObject *rpmfile_rdev(rpmfileObject *s) +{ + return Py_BuildValue("i", rpmfilesFRdev(s->files, s->ix)); +} + +static PyObject *rpmfile_inode(rpmfileObject *s) +{ + return Py_BuildValue("i", rpmfilesFInode(s->files, s->ix)); +} + +static PyObject *rpmfile_nlink(rpmfileObject *s) +{ + return Py_BuildValue("i", rpmfilesFNlink(s->files, s->ix)); +} + +static PyObject *rpmfile_linkto(rpmfileObject *s) +{ + return Py_BuildValue("s", rpmfilesFLink(s->files, s->ix)); +} + +static PyObject *rpmfile_user(rpmfileObject *s) +{ + return Py_BuildValue("s", rpmfilesFUser(s->files, s->ix)); +} + +static PyObject *rpmfile_group(rpmfileObject *s) +{ + return Py_BuildValue("s", rpmfilesFGroup(s->files, s->ix)); +} + +static PyObject *rpmfile_fflags(rpmfileObject *s) +{ + return Py_BuildValue("i", rpmfilesFFlags(s->files, s->ix)); +} + +static PyObject *rpmfile_vflags(rpmfileObject *s) +{ + return Py_BuildValue("i", rpmfilesVFlags(s->files, s->ix)); +} + +static PyObject *rpmfile_color(rpmfileObject *s) +{ + return Py_BuildValue("i", rpmfilesFColor(s->files, s->ix)); +} + +static PyObject *rpmfile_state(rpmfileObject *s) +{ + return Py_BuildValue("i", rpmfilesFState(s->files, s->ix)); +} + +static PyObject *rpmfile_digest(rpmfileObject *s) +{ + size_t diglen = 0; + const unsigned char *digest = rpmfilesFDigest(s->files, s->ix, + NULL, &diglen); + if (digest) { + char * hex = pgpHexStr(digest, diglen); + PyObject *o = Py_BuildValue("s", hex); + free(hex); + return o; + } + Py_RETURN_NONE; +} + +static PyObject *rpmfile_class(rpmfileObject *s) +{ + return Py_BuildValue("s", rpmfilesFClass(s->files, s->ix)); +} + +static PyObject *rpmfile_caps(rpmfileObject *s) +{ + return Py_BuildValue("s", rpmfilesFCaps(s->files, s->ix)); +} + +static PyObject *rpmfile_langs(rpmfileObject *s) +{ + return Py_BuildValue("s", rpmfilesFLangs(s->files, s->ix)); +} + +static PyObject *rpmfile_links(rpmfileObject *s) +{ + PyObject *result = NULL; + const int * links = NULL; + uint32_t nlinks = rpmfilesFLinks(s->files, s->ix, &links); + + if (nlinks == 0) + Py_RETURN_NONE; + else if (nlinks == 1) + links = &s->ix; /* file itself */ + + result = PyTuple_New(nlinks); + if (result) { + for (uint32_t i = 0; i < nlinks; i++) { + int lix = links[i]; + PyObject * o; + + if (lix == s->ix) { + /* file itself, return a reference instead of new object */ + Py_INCREF(s); + o = (PyObject *) s; + } else { + o = rpmfile_Wrap(s->files, lix); + } + + PyTuple_SET_ITEM(result, i, o); + } + } + return result; +} + +/* + * Exported as "matches" instead of a rich comparison operator or such + * as this cannot be used for comparing file *object* equality, + * rpmfilesCompare() determines whether the file *contents* match. + */ +static PyObject *rpmfile_matches(rpmfileObject *s, PyObject *o) +{ + PyObject *result = NULL; + if (rpmfileObject_Check(o)) { + rpmfileObject *of = (rpmfileObject *)o; + int rc = rpmfilesCompare(s->files, s->ix, of->files, of->ix); + result = PyBool_FromLong(rc == 0); + } else { + PyErr_SetObject(PyExc_TypeError, o); + } + return result; +} + +static PyGetSetDef rpmfile_getseters[] = { + { "fx", (getter) rpmfile_fx, NULL, NULL }, + { "dx", (getter) rpmfile_dx, NULL, NULL }, + { "name", (getter) rpmfile_name, NULL, NULL }, + { "basename", (getter) rpmfile_basename, NULL, NULL }, + { "dirname", (getter) rpmfile_dirname, NULL, NULL }, + { "orig_name", (getter) rpmfile_orig_name, NULL, NULL }, + { "orig_basename", (getter) rpmfile_orig_basename, NULL, NULL }, + { "orig_dirname", (getter) rpmfile_orig_dirname, NULL, NULL }, + { "mode", (getter) rpmfile_mode, NULL, NULL }, + { "mtime", (getter) rpmfile_mtime, NULL, NULL }, + { "size", (getter) rpmfile_size, NULL, NULL }, + { "rdev", (getter) rpmfile_rdev, NULL, NULL }, + { "inode", (getter) rpmfile_inode, NULL, NULL }, + { "fflags", (getter) rpmfile_fflags, NULL, NULL }, + { "vflags", (getter) rpmfile_vflags, NULL, NULL }, + { "linkto", (getter) rpmfile_linkto, NULL, NULL }, + { "color", (getter) rpmfile_color, NULL, NULL }, + { "nlink", (getter) rpmfile_nlink, NULL, NULL }, + { "links", (getter) rpmfile_links, NULL, NULL }, + { "user", (getter) rpmfile_user, NULL, NULL }, + { "group", (getter) rpmfile_group, NULL, NULL }, + { "digest", (getter) rpmfile_digest, NULL, NULL }, + { "class", (getter) rpmfile_class, NULL, NULL }, + { "state", (getter) rpmfile_state, NULL, NULL }, + { "langs", (getter) rpmfile_langs, NULL, NULL }, + { "caps", (getter) rpmfile_caps, NULL, NULL }, + { NULL, NULL, NULL, NULL } +}; + +static struct PyMethodDef rpmfile_methods[] = { + { "matches", (PyCFunction) rpmfile_matches, METH_O, + NULL }, + { NULL, NULL, 0, NULL } +}; + +PyTypeObject rpmfile_Type = { + PyVarObject_HEAD_INIT(&PyType_Type, 0) + "rpm.file", /* tp_name */ + sizeof(rpmfileObject), /* tp_basicsize */ + 0, /* tp_itemsize */ + /* methods */ + (destructor) rpmfile_dealloc, /* 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 */ + (reprfunc)rpmfile_name, /* tp_str */ + PyObject_GenericGetAttr, /* tp_getattro */ + PyObject_GenericSetAttr, /* tp_setattro */ + 0, /* tp_as_buffer */ + Py_TPFLAGS_DEFAULT|Py_TPFLAGS_BASETYPE, /* tp_flags */ + rpmfile_doc, /* tp_doc */ + 0, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + 0, /* tp_iter */ + 0, /* tp_iternext */ + rpmfile_methods, /* tp_methods */ + 0, /* tp_members */ + rpmfile_getseters, /* 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 */ + 0, /* tp_new */ + 0, /* tp_free */ + 0, /* tp_is_gc */ +}; + +PyObject * rpmfile_Wrap(rpmfiles files, int ix) +{ + rpmfileObject *s = PyObject_New(rpmfileObject, &rpmfile_Type); + if (s == NULL) return NULL; + + s->files = rpmfilesLink(files); + s->ix = ix; + return (PyObject *) s; +} + +/* The actual rpmfiles info set */ +struct rpmfilesObject_s { + PyObject_HEAD + PyObject *md_dict; /*!< to look like PyModuleObject */ + rpmfiles files; +}; + +static void rpmfiles_dealloc(rpmfilesObject * s) +{ + s->files = rpmfilesFree(s->files); + Py_TYPE(s)->tp_free((PyObject *)s); +} + +static PyObject * rpmfiles_new(PyTypeObject * subtype, PyObject *args, PyObject *kwds) +{ + PyObject * to = NULL; + Header h = NULL; + rpmfiles files = NULL; + rpmTagVal tagN = RPMTAG_BASENAMES; + int flags = 0; + rpmstrPool pool = NULL; + char * kwlist[] = {"header", "tag", "flags", "pool", NULL}; + + if (!PyArg_ParseTupleAndKeywords(args, kwds, "O&|OiO&:rpmfiles_new", kwlist, + hdrFromPyObject, &h, &to, &flags, + poolFromPyObject, &pool)) + return NULL; + + files = rpmfilesNew(pool, h, tagN, flags); + + if (files == NULL) { + PyErr_SetString(PyExc_ValueError, "invalid file data in header"); + return NULL; + } + + return rpmfiles_Wrap(subtype, files); +} + +static Py_ssize_t rpmfiles_length(rpmfilesObject *s) +{ + return rpmfilesFC(s->files); +} + +static PyObject * rpmfiles_getitem(rpmfilesObject *s, Py_ssize_t ix) +{ + if (ix >= 0 && ix < rpmfilesFC(s->files)) + return rpmfile_Wrap(s->files, ix); + + PyErr_SetObject(PyExc_IndexError, Py_BuildValue("i", ix)); + return NULL; +} + +static int rpmfiles_contains(rpmfilesObject *s, PyObject *value) +{ + const char *fn = NULL; + + if (!PyArg_Parse(value, "s", &fn)) + return -1; + + return (rpmfilesFindFN(s->files, fn) >= 0) ? 1 : 0; +} + +static PySequenceMethods rpmfiles_as_sequence = { + (lenfunc)rpmfiles_length, /* sq_length */ + 0, /* sq_concat */ + 0, /* sq_repeat */ + (ssizeargfunc) rpmfiles_getitem, /* sq_item */ + 0, /* sq_slice */ + 0, /* sq_ass_item */ + 0, /* sq_ass_slice */ + (objobjproc)rpmfiles_contains, /* sq_contains */ + 0, /* sq_inplace_concat */ + 0, /* sq_inplace_repeat */ +}; + +static PyObject * rpmfiles_find(rpmfileObject *s, + PyObject *args, PyObject *kwds) +{ + const char *fn = NULL; + int fx, orig = 0; + char * kwlist[] = {"filename", "orig", NULL}; + + if (!PyArg_ParseTupleAndKeywords(args, kwds, "s|i", kwlist, &fn, &orig)) + return NULL; + + if (orig) + fx = rpmfilesFindOFN(s->files, fn); + else + fx = rpmfilesFindFN(s->files, fn); + + if (fx >= 0) + return rpmfile_Wrap(s->files, fx); + + Py_RETURN_NONE; +} + +static PyObject *rpmfiles_subscript(rpmfilesObject *s, PyObject *item) +{ + PyObject *str = NULL; + + /* treat numbers as sequence accesses */ + if (PyInt_Check(item)) { + return rpmfiles_getitem(s, PyInt_AsSsize_t(item)); + } else if (PyLong_Check(item)) { + return rpmfiles_getitem(s, PyLong_AsSsize_t(item)); + } + + /* handle slices by returning tuples of rpm.file items */ + if (PySlice_Check(item)) { + Py_ssize_t start, stop, step, slicelength, i, cur; + PyObject * result; + + if (PySlice_GetIndicesEx((PySliceObject*)item, rpmfiles_length(s), + &start, &stop, &step, &slicelength) < 0) { + return NULL; + } + + result = PyTuple_New(slicelength); + if (result) { + for (cur = start, i = 0; i < slicelength; cur += step, i++) { + PyTuple_SET_ITEM(result, i, rpmfiles_getitem(s, cur)); + } + } + return result; + } + + /* ... and strings as mapping access */ + if (utf8FromPyObject(item, &str)) { + int fx = rpmfilesFindFN(s->files, PyBytes_AsString(str)); + Py_DECREF(str); + + if (fx >= 0) { + return rpmfile_Wrap(s->files, fx); + } else { + PyErr_SetObject(PyExc_KeyError, item); + } + } else { + PyErr_SetObject(PyExc_TypeError, item); + } + + return NULL; +} + +static PyMappingMethods rpmfiles_as_mapping = { + (lenfunc) rpmfiles_length, /* mp_length */ + (binaryfunc) rpmfiles_subscript, /* mp_subscript */ + 0, /* mp_ass_subscript */ +}; + +static struct PyMethodDef rpmfiles_methods[] = { + { "find", (PyCFunction) rpmfiles_find, METH_VARARGS|METH_KEYWORDS, + NULL }, + { NULL, NULL, 0, NULL } +}; + +static char rpmfiles_doc[] = +""; + + +PyTypeObject rpmfiles_Type = { + PyVarObject_HEAD_INIT(&PyType_Type, 0) + "rpm.files", /* tp_name */ + sizeof(rpmfilesObject), /* tp_basicsize */ + 0, /* tp_itemsize */ + /* methods */ + (destructor) rpmfiles_dealloc, /* tp_dealloc */ + 0, /* tp_print */ + 0, /* tp_getattr */ + 0, /* tp_setattr */ + 0, /* tp_compare */ + 0, /* tp_repr */ + 0, /* tp_as_number */ + &rpmfiles_as_sequence, /* tp_as_sequence */ + &rpmfiles_as_mapping, /* tp_as_mapping */ + 0, /* tp_hash */ + 0, /* tp_call */ + 0, /* tp_str */ + PyObject_GenericGetAttr, /* tp_getattro */ + PyObject_GenericSetAttr, /* tp_setattro */ + 0, /* tp_as_buffer */ + Py_TPFLAGS_DEFAULT|Py_TPFLAGS_BASETYPE, /* tp_flags */ + rpmfiles_doc, /* tp_doc */ + 0, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + 0, /* tp_iter */ + 0, /* tp_iternext */ + rpmfiles_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 */ + (newfunc) rpmfiles_new, /* tp_new */ + 0, /* tp_free */ + 0, /* tp_is_gc */ +}; + +PyObject * rpmfiles_Wrap(PyTypeObject *subtype, rpmfiles files) +{ + rpmfilesObject *s = (rpmfilesObject *)subtype->tp_alloc(subtype, 0); + if (s == NULL) return NULL; + + s->files = files; + return (PyObject *) s; +} + diff --git a/python/rpmfiles-py.h b/python/rpmfiles-py.h new file mode 100644 index 000000000..2e27d3db4 --- /dev/null +++ b/python/rpmfiles-py.h @@ -0,0 +1,18 @@ +#ifndef H_RPMFILES_PY +#define H_RPMFILES_PY + +#include + +typedef struct rpmfileObject_s rpmfileObject; +typedef struct rpmfilesObject_s rpmfilesObject; + +extern PyTypeObject rpmfile_Type; +extern PyTypeObject rpmfiles_Type; + +#define rpmfileObject_Check(v) ((v)->ob_type == &rpmfile_Type) +#define rpmfilesObject_Check(v) ((v)->ob_type == &rpmfiles_Type) + +PyObject * rpmfile_Wrap(rpmfiles files, int ix); +PyObject * rpmfiles_Wrap(PyTypeObject *subtype, rpmfiles files); + +#endif diff --git a/python/rpmmodule.c b/python/rpmmodule.c index 74fed65fb..2e5ea87e9 100644 --- a/python/rpmmodule.c +++ b/python/rpmmodule.c @@ -11,6 +11,7 @@ #include "rpmds-py.h" #include "rpmfd-py.h" #include "rpmfi-py.h" +#include "rpmfiles-py.h" #include "rpmkeyring-py.h" #include "rpmmi-py.h" #include "rpmii-py.h" @@ -219,6 +220,8 @@ static int prepareInitModule(void) if (PyType_Ready(&rpmds_Type) < 0) return 0; if (PyType_Ready(&rpmfd_Type) < 0) return 0; if (PyType_Ready(&rpmfi_Type) < 0) return 0; + if (PyType_Ready(&rpmfile_Type) < 0) return 0; + if (PyType_Ready(&rpmfiles_Type) < 0) return 0; if (PyType_Ready(&rpmKeyring_Type) < 0) return 0; if (PyType_Ready(&rpmmi_Type) < 0) return 0; if (PyType_Ready(&rpmii_Type) < 0) return 0; @@ -318,6 +321,12 @@ static int initModule(PyObject *m) Py_INCREF(&rpmfi_Type); PyModule_AddObject(m, "fi", (PyObject *) &rpmfi_Type); + Py_INCREF(&rpmfile_Type); + PyModule_AddObject(m, "file", (PyObject *) &rpmfile_Type); + + Py_INCREF(&rpmfiles_Type); + PyModule_AddObject(m, "files", (PyObject *) &rpmfiles_Type); + Py_INCREF(&rpmKeyring_Type); PyModule_AddObject(m, "keyring", (PyObject *) &rpmKeyring_Type); diff --git a/python/setup.py.in b/python/setup.py.in index 6b6c4241d..7548a2fab 100644 --- a/python/setup.py.in +++ b/python/setup.py.in @@ -24,7 +24,7 @@ rpmmod = Extension('rpm._rpm', sources = mksources([ 'header', 'rpmds', 'rpmfd', 'rpmfi', 'rpmii', 'rpmkeyring', 'rpmmacro', 'rpmmi', 'rpmps', - 'rpmstrpool', + 'rpmstrpool', 'rpmfiles' 'rpmtd', 'rpmte', 'rpmts', 'rpmmodule', ]), include_dirs = pkgconfig('--cflags'),