ADD_SUBDIRECTORY (src)
ADD_SUBDIRECTORY (doc)
+ENABLE_TESTING()
ADD_SUBDIRECTORY (tests EXCLUDE_FROM_ALL)
utils/make_rpm.sh .
+## Testing
+
+All unit tests run from librepo checkout dir
+
+### Build C tests && run c and python tests
+
+ make tests && make test
+
+### Run (from your checkout dir) - C unittests:
+
+ build/tests/run_gtester.sh
+
+### Run (from your checkout dir) - Python unittests:
+
+ PYTHONPATH=`readlink -f ./build/src/python/` nosetests -s tests/python/tests/
+
---------------------------------------------------
# Differences in behavior between createrepo_c and createrepo
INSTALL(TARGETS libcreaterepo_c LIBRARY DESTINATION ${LIB_INSTALL_DIR})
INSTALL(TARGETS createrepo_c DESTINATION bin/)
INSTALL(TARGETS mergerepo_c DESTINATION bin/)
+
+ADD_SUBDIRECTORY(python)
--- /dev/null
+FIND_PACKAGE (PythonLibs)
+FIND_PACKAGE (PythonInterp REQUIRED)
+EXECUTE_PROCESS(COMMAND ${PYTHON_EXECUTABLE} -c "from sys import stdout; from distutils import sysconfig; stdout.write(sysconfig.get_python_lib(True))" OUTPUT_VARIABLE PYTHON_INSTALL_DIR)
+INCLUDE_DIRECTORIES (${PYTHON_INCLUDE_PATH})
+
+MESSAGE(STATUS "Python install dir is ${PYTHON_INSTALL_DIR}")
+
+set (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fno-strict-aliasing")
+set (CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -fno-strict-aliasing")
+set (CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} -fno-strict-aliasing")
+
+SET (createrepo_c_COPIES __init__.py)
+FILE(COPY ${createrepo_c_COPIES} DESTINATION ./createrepo_c/)
+
+SET (craeterepo_cmodule_SRCS
+ createrepo_cmodule.c
+ exception-py.c
+ load_metadata-py.c
+ locate_metadata-py.c
+ #metadata_hashtable.c
+ package-py.c
+ parsepkg-py.c
+ repomd-py.c
+ repomdrecord-py.c
+ sqlite-py.c
+ typeconversion.c
+ xml_dump-py.c
+ )
+
+ADD_LIBRARY(_createrepo_cmodule SHARED ${craeterepo_cmodule_SRCS})
+SET_TARGET_PROPERTIES(_createrepo_cmodule PROPERTIES PREFIX "")
+SET_TARGET_PROPERTIES(_createrepo_cmodule PROPERTIES LIBRARY_OUTPUT_DIRECTORY "./createrepo_c")
+TARGET_LINK_LIBRARIES(_createrepo_cmodule libcreaterepo_c)
+TARGET_LINK_LIBRARIES(_createrepo_cmodule
+ ${EXPAT_LIBRARIES}
+ ${CURL_LIBRARY}
+ ${PYTHON_LIBRARY}
+ )
+
+INSTALL(FILES __init__.py DESTINATION ${PYTHON_INSTALL_DIR}/createrepo_c)
+INSTALL(TARGETS _createrepo_cmodule LIBRARY DESTINATION ${PYTHON_INSTALL_DIR}/createrepo_c)
--- /dev/null
+"""
+"""
+
+import _createrepo_c
+
+VERSION_MAJOR = _createrepo_c.VERSION_MAJOR
+VERSION_MINOR = _createrepo_c.VERSION_MINOR
+VERSION_PATCH = _createrepo_c.VERSION_PATCH
+VERSION = u"%d.%d.%d" % (VERSION_MAJOR, VERSION_MINOR, VERSION_PATCH)
+
+MD5 = _createrepo_c.MD5
+SHA1 = _createrepo_c.SHA1
+SHA256 = _createrepo_c.SHA256
+
+AUTO_DETECT_COMPRESSION = _createrepo_c.AUTO_DETECT_COMPRESSION
+UNKNOWN_COMPRESSION = _createrepo_c.UNKNOWN_COMPRESSION
+NO_COMPRESSION = _createrepo_c.NO_COMPRESSION
+GZ_COMPRESSION = _createrepo_c.GZ_COMPRESSION
+BZ2_COMPRESSION = _createrepo_c.BZ2_COMPRESSION
+XZ_COMPRESSION = _createrepo_c.XZ_COMPRESSION
+
+HT_KEY_DEFAULT = _createrepo_c.HT_KEY_DEFAULT
+HT_KEY_HASH = _createrepo_c.HT_KEY_HASH
+HT_KEY_NAME = _createrepo_c.HT_KEY_NAME
+HT_KEY_FILENAME = _createrepo_c.HT_KEY_FILENAME
+
+DB_PRIMARY = _createrepo_c.DB_PRIMARY
+DB_FILELISTS = _createrepo_c.DB_FILELISTS
+DB_OTHER = _createrepo_c.DB_OTHER
+
+CreaterepoCException = _createrepo_c.CreaterepoCException
+
+# Metadata class
+
+Metadata = _createrepo_c.Metadata
+
+# MetadataLocation class
+
+MetadataLocation = _createrepo_c.MetadataLocation
+
+# Package class
+
+class Package(_createrepo_c.Package):
+ def __copy__(self):
+ return self.copy()
+ def __deepcopy__(self, _):
+ return self.copy()
+
+# Repomd class
+
+Repomd = _createrepo_c.Repomd
+
+# RepomdRecord class
+
+class RepomdRecord(_createrepo_c.RepomdRecord):
+ def compress_and_fill(self, hashtype, compresstype):
+ rec = RepomdRecord("")
+ _createrepo_c.RepomdRecord.compress_and_fill(self, rec, hashtype, compresstype)
+ return rec
+
+
+# Sqlite class
+
+Sqlite = _createrepo_c.Sqlite
+
+class PrimarySqlite(Sqlite):
+ def __init__(self, filename):
+ Sqlite.__init__(self, filename, DB_PRIMARY)
+
+class FilelistsSqlite(Sqlite):
+ def __init__(self, filename):
+ Sqlite.__init__(self, filename, DB_FILELISTS)
+
+class OtherSqlite(Sqlite):
+ def __init__(self, filename):
+ Sqlite.__init__(self, filename, DB_OTHER)
+
+# Methods
+
+xml_dump_primary = _createrepo_c.xml_dump_primary
+xml_dump_filelists = _createrepo_c.xml_dump_filelists
+xml_dump_other = _createrepo_c.xml_dump_other
+xml_dump = _createrepo_c.xml_dump
+
+def package_from_rpm(filename, checksum_type=SHA256, location_href=None,
+ location_base=None, changelog_limit=10):
+ return _createrepo_c.package_from_rpm(filename, checksum_type,
+ location_href, location_base, changelog_limit)
+
+def xml_from_rpm(filename, checksum_type=SHA256, location_href=None,
+ location_base=None, changelog_limit=10):
+ return _createrepo_c.xml_from_rpm(filename, checksum_type,
+ location_href, location_base, changelog_limit)
--- /dev/null
+/* createrepo_c - Library of routines for manipulation with repodata
+ * Copyright (C) 2012-2013 Tomas Mlcoch
+ *
+ * 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 "src/createrepo_c.h"
+
+#include "exception-py.h"
+#include "load_metadata-py.h"
+#include "locate_metadata-py.h"
+#include "package-py.h"
+#include "parsepkg-py.h"
+#include "repomd-py.h"
+#include "repomdrecord-py.h"
+#include "sqlite-py.h"
+#include "xml_dump-py.h"
+
+static struct PyMethodDef createrepo_c_methods[] = {
+ {"package_from_rpm", (PyCFunction)py_package_from_rpm,
+ METH_VARARGS | METH_KEYWORDS, NULL},
+ {"xml_from_rpm", (PyCFunction)py_xml_from_rpm,
+ METH_VARARGS | METH_KEYWORDS, NULL},
+ {"xml_dump_primary", (PyCFunction)py_xml_dump_primary,
+ METH_VARARGS, NULL},
+ {"xml_dump_filelists", (PyCFunction)py_xml_dump_filelists,
+ METH_VARARGS, NULL},
+ {"xml_dump_other", (PyCFunction)py_xml_dump_other,
+ METH_VARARGS, NULL},
+ {"xml_dump", (PyCFunction)py_xml_dump,
+ METH_VARARGS, NULL},
+ { NULL }
+};
+
+PyMODINIT_FUNC
+init_createrepo_c(void)
+{
+ PyObject *m = Py_InitModule("_createrepo_c", createrepo_c_methods);
+ if (!m)
+ return;
+
+ /* Exceptions */
+ if (!init_exceptions())
+ return;
+ PyModule_AddObject(m, "CreaterepoCException", CrErr_Exception);
+
+ /* Objects */
+
+ /* _createrepo_c.Package */
+ if (PyType_Ready(&Package_Type) < 0)
+ return;
+ Py_INCREF(&Package_Type);
+ PyModule_AddObject(m, "Package", (PyObject *)&Package_Type);
+
+ /* _createrepo_c.Metadata */
+ if (PyType_Ready(&Metadata_Type) < 0)
+ return;
+ Py_INCREF(&Metadata_Type);
+ PyModule_AddObject(m, "Metadata", (PyObject *)&Metadata_Type);
+
+ /* _createrepo_c.MetadataLocation */
+ if (PyType_Ready(&MetadataLocation_Type) < 0)
+ return;
+ Py_INCREF(&MetadataLocation_Type);
+ PyModule_AddObject(m, "MetadataLocation", (PyObject *)&MetadataLocation_Type);
+
+ /* _createrepo_c.Repomd */
+ if (PyType_Ready(&Repomd_Type) < 0)
+ return;
+ Py_INCREF(&Repomd_Type);
+ PyModule_AddObject(m, "Repomd", (PyObject *)&Repomd_Type);
+
+ /* _createrepo_c.RepomdRecord */
+ if (PyType_Ready(&RepomdRecord_Type) < 0)
+ return;
+ Py_INCREF(&RepomdRecord_Type);
+ PyModule_AddObject(m, "RepomdRecord", (PyObject *)&RepomdRecord_Type);
+
+ /* _createrepo_c.Sqlite */
+ if (PyType_Ready(&Sqlite_Type) < 0)
+ return;
+ Py_INCREF(&Sqlite_Type);
+ PyModule_AddObject(m, "Sqlite", (PyObject *)&Sqlite_Type);
+
+ /* Createrepo init */
+
+ cr_xml_dump_init();
+ cr_package_parser_init();
+
+ /* Module constants */
+
+ /* Version */
+ PyModule_AddIntConstant(m, "VERSION_MAJOR", CR_VERSION_MAJOR);
+ PyModule_AddIntConstant(m, "VERSION_MINOR", CR_VERSION_MINOR);
+ PyModule_AddIntConstant(m, "VERSION_PATCH", CR_VERSION_PATCH);
+
+ /* Checksum types */
+ PyModule_AddIntConstant(m, "MD5", CR_CHECKSUM_MD5);
+ PyModule_AddIntConstant(m, "SHA1", CR_CHECKSUM_SHA1);
+ PyModule_AddIntConstant(m, "SHA256", CR_CHECKSUM_SHA256);
+
+ /* Compression types */
+ PyModule_AddIntConstant(m, "AUTO_DETECT_COMPRESSION", CR_CW_AUTO_DETECT_COMPRESSION);
+ PyModule_AddIntConstant(m, "UNKNOWN_COMPRESSION", CR_CW_UNKNOWN_COMPRESSION);
+ PyModule_AddIntConstant(m, "NO_COMPRESSION", CR_CW_NO_COMPRESSION);
+ PyModule_AddIntConstant(m, "GZ_COMPRESSION", CR_CW_GZ_COMPRESSION);
+ PyModule_AddIntConstant(m, "BZ2_COMPRESSION", CR_CW_BZ2_COMPRESSION);
+ PyModule_AddIntConstant(m, "XZ_COMPRESSION", CR_CW_XZ_COMPRESSION);
+
+ /* Load Metadata key values */
+ PyModule_AddIntConstant(m, "HT_KEY_DEFAULT", CR_HT_KEY_DEFAULT);
+ PyModule_AddIntConstant(m, "HT_KEY_HASH", CR_HT_KEY_HASH);
+ PyModule_AddIntConstant(m, "HT_KEY_NAME", CR_HT_KEY_NAME);
+ PyModule_AddIntConstant(m, "HT_KEY_FILENAME", CR_HT_KEY_FILENAME);
+
+ /* Sqlite DB types */
+ PyModule_AddIntConstant(m, "DB_PRIMARY", CR_DB_PRIMARY);
+ PyModule_AddIntConstant(m, "DB_FILELISTS", CR_DB_FILELISTS);
+ PyModule_AddIntConstant(m, "DB_OTHER", CR_DB_OTHER);
+}
--- /dev/null
+/* createrepo_c - Library of routines for manipulation with repodata
+ * Copyright (C) 2012-2013 Tomas Mlcoch
+ *
+ * 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 "exception-py.h"
+
+PyObject *CrErr_Exception = NULL;
+
+int
+init_exceptions()
+{
+ CrErr_Exception = PyErr_NewException("_createrepo_c.Exception", NULL, NULL);
+ if (!CrErr_Exception)
+ return 0;
+ Py_INCREF(CrErr_Exception);
+
+ return 1;
+}
--- /dev/null
+/* createrepo_c - Library of routines for manipulation with repodata
+ * Copyright (C) 2012-2013 Tomas Mlcoch
+ *
+ * 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.
+ */
+
+#ifndef CR_EXCEPTION_PY_H
+#define CR_EXCEPTION_PY_H
+
+#include "src/createrepo_c.h"
+
+extern PyObject *CrErr_Exception;
+
+int init_exceptions();
+
+#endif
--- /dev/null
+/* createrepo_c - Library of routines for manipulation with repodata
+ * Copyright (C) 2013 Tomas Mlcoch
+ *
+ * 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 <assert.h>
+#include <stddef.h>
+
+#include "load_metadata-py.h"
+#include "locate_metadata-py.h"
+#include "package-py.h"
+#include "exception-py.h"
+#include "typeconversion.h"
+#include "metadata_hashtable.h"
+
+/* TODO:
+ * keys() and records() method (same method - alias only)
+ **/
+
+typedef struct {
+ PyObject_HEAD
+ cr_Metadata md;
+} _MetadataObject;
+
+static int
+check_MetadataStatus(const _MetadataObject *self)
+{
+ assert(self != NULL);
+ assert(MetadataObject_Check(self));
+ if (self->md == NULL) {
+ PyErr_SetString(CrErr_Exception, "Improper createrepo_c Metadata object.");
+ return -1;
+ }
+ return 0;
+}
+
+/* Function on the type */
+
+static PyObject *
+metadata_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
+{
+ CR_UNUSED(args);
+ CR_UNUSED(kwds);
+
+ _MetadataObject *self = (_MetadataObject *)type->tp_alloc(type, 0);
+ if (self)
+ self->md = NULL;
+ return (PyObject *)self;
+}
+
+static int
+metadata_init(_MetadataObject *self, PyObject *args, PyObject *kwds)
+{
+ static char *kwlist[] = { "key", "use_single_chunk", "pkglist", NULL };
+ int key = CR_HT_KEY_DEFAULT;
+ int use_single_chunk = 0;
+ PyObject *py_pkglist = NULL;
+ GSList *pkglist = NULL;
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, "|iiO!:metadata_init", kwlist,
+ &key, &use_single_chunk, &PyList_Type, &py_pkglist))
+ return -1;
+
+ /* Free all previous resources when reinitialization */
+ if (self->md) {
+ cr_metadata_free(self->md);
+ }
+
+ /* Init */
+ pkglist = GSList_FromPyList_Str(py_pkglist);
+ self->md = cr_metadata_new(key, use_single_chunk, pkglist);
+ g_slist_free(pkglist);
+ if (self->md == NULL) {
+ PyErr_SetString(CrErr_Exception, "Metadata initialization failed");
+ return -1;
+ }
+ return 0;
+}
+
+static void
+metadata_dealloc(_MetadataObject *self)
+{
+ if (self->md)
+ cr_metadata_free(self->md);
+ Py_TYPE(self)->tp_free(self);
+}
+
+static PyObject *
+metadata_repr(_MetadataObject *self)
+{
+ CR_UNUSED(self);
+ return PyString_FromFormat("<createrepo_c.Metadata object>");
+}
+
+/* Getters */
+
+static PyObject *
+get_key(_MetadataObject *self, void *nothing)
+{
+ CR_UNUSED(nothing);
+ if (check_MetadataStatus(self))
+ return NULL;
+ cr_HashTableKey val = self->md->key;
+ return PyLong_FromLong((long) val);
+}
+
+/*
+static PyObject *
+get_ht(_MetadataObject *self, void *nothing)
+{
+ CR_UNUSED(nothing);
+ if (check_MetadataStatus(self))
+ return NULL;
+ return Object_FromGHashtable((PyObject *) self, self->md->ht);
+}
+*/
+
+static PyGetSetDef metadata_getsetters[] = {
+ {"key", (getter)get_key, NULL, NULL, NULL},
+// {"ht", (getter)get_ht, NULL, NULL, NULL},
+ {NULL, NULL, NULL, NULL, NULL} /* sentinel */
+};
+
+/* Metadata methods */
+
+static PyObject *
+load_xml(_MetadataObject *self, PyObject *args)
+{
+ int rc;
+ PyObject *ml;
+
+ if (!PyArg_ParseTuple(args, "O!:load_xml", &MetadataLocation_Type, &ml))
+ return NULL;
+ if (check_MetadataStatus(self))
+ return NULL;
+
+ rc = cr_metadata_load_xml(self->md, MetadataLocation_FromPyObject(ml));
+ if (rc != CR_LOAD_METADATA_OK) {
+ PyErr_SetString(CrErr_Exception, "Cannot load metadata");
+ return NULL;
+ }
+ Py_RETURN_NONE;
+}
+
+static PyObject *
+locate_and_load_xml(_MetadataObject *self, PyObject *args)
+{
+ int rc;
+ char *path;
+
+ if (!PyArg_ParseTuple(args, "s:locate_and_load_xml", &path))
+ return NULL;
+ if (check_MetadataStatus(self))
+ return NULL;
+
+ rc = cr_metadata_locate_and_load_xml(self->md, path);
+ if (rc != CR_LOAD_METADATA_OK) {
+ PyErr_SetString(CrErr_Exception, "Cannot load metadata");
+ return NULL;
+ }
+ Py_RETURN_NONE;
+}
+
+/* Hashtable methods */
+
+static PyObject *
+ht_len(_MetadataObject *self, PyObject *noarg)
+{
+ CR_UNUSED(noarg);
+ unsigned long len = 0;
+ if (check_MetadataStatus(self))
+ return NULL;
+ if (self->md->ht)
+ len = (unsigned long) g_hash_table_size(self->md->ht);
+ return PyLong_FromUnsignedLong(len);
+}
+
+/*
+static PyObject *
+ht_add(_MetadataObject *self, PyObject *args)
+{
+ char *key;
+ PyObject *py_pkg;
+ cr_Package *pkg;
+
+ if (!PyArg_ParseTuple(args, "sO!:add", &key, &Package_Type, &pkg))
+ return NULL;
+ if (check_MetadataHashtableStatus(self))
+ return NULL;
+
+ pkg = Package_FromPyObject(pkg);
+ if (!pkg)
+ Py_RETURN_NONE;
+
+ Py_XINCREF(py_pkg);
+ // XXX: Store referenced object for Py_XDECREF!!!!!
+ g_hash_table_replace(self->md->ht, key, pkg);
+ Py_RETURN_NONE;
+}
+*/
+
+static PyObject *
+ht_has_key(_MetadataObject *self, PyObject *args)
+{
+ char *key;
+
+ if (!PyArg_ParseTuple(args, "s:has_key", &key))
+ return NULL;
+ if (check_MetadataStatus(self))
+ return NULL;
+
+ if (g_hash_table_lookup(self->md->ht, key))
+ Py_RETURN_TRUE;
+ Py_RETURN_FALSE;
+}
+
+static PyObject *
+ht_keys(_MetadataObject *self, PyObject *args)
+{
+ CR_UNUSED(args);
+
+ if (check_MetadataStatus(self))
+ return NULL;
+ GList *keys = g_hash_table_get_keys(self->md->ht);
+ PyObject *list = PyList_New(0);
+ for (GList *elem = keys; elem; elem = g_list_next(elem)) {
+ PyObject *py_str = PyString_FromString(elem->data);
+ assert(py_str);
+ if (PyList_Append(list, py_str) == -1) {
+ Py_XDECREF(list);
+ return NULL;
+ }
+ }
+ return list;
+}
+
+static PyObject *
+ht_remove(_MetadataObject *self, PyObject *args)
+{
+ char *key;
+
+ if (!PyArg_ParseTuple(args, "s:del", &key))
+ return NULL;
+ if (check_MetadataStatus(self))
+ return NULL;
+
+ if (g_hash_table_remove(self->md->ht, key))
+ Py_RETURN_TRUE;
+ Py_RETURN_FALSE;
+}
+
+static PyObject *
+ht_get(_MetadataObject *self, PyObject *args)
+{
+ char *key;
+
+ if (!PyArg_ParseTuple(args, "s:get", &key))
+ return NULL;
+ if (check_MetadataStatus(self))
+ return NULL;
+
+ cr_Package *pkg = g_hash_table_lookup(self->md->ht, key);
+ if (!pkg)
+ Py_RETURN_NONE;
+ return (Object_FromPackage_WithParent(pkg, 0, (PyObject *) self));
+}
+
+static struct PyMethodDef metadata_methods[] = {
+ {"load_xml", (PyCFunction)load_xml, METH_VARARGS, NULL},
+ {"locate_and_load_xml", (PyCFunction)locate_and_load_xml, METH_VARARGS, NULL},
+ {"len", (PyCFunction)ht_len, METH_NOARGS, NULL},
+// {"add", (PyCFunction)ht_add, METH_VARARGS, NULL},
+ {"has_key", (PyCFunction)ht_has_key, METH_VARARGS, NULL},
+ {"keys", (PyCFunction)ht_keys, METH_NOARGS, NULL},
+ {"remove", (PyCFunction)ht_remove, METH_VARARGS, NULL},
+ {"get", (PyCFunction)ht_get, METH_VARARGS, NULL},
+ {NULL} /* sentinel */
+};
+
+/* Object */
+
+PyTypeObject Metadata_Type = {
+ PyObject_HEAD_INIT(NULL)
+ 0, /* ob_size */
+ "_librepo.Metadata", /* tp_name */
+ sizeof(_MetadataObject), /* tp_basicsize */
+ 0, /* tp_itemsize */
+ (destructor)metadata_dealloc, /* tp_dealloc */
+ 0, /* tp_print */
+ 0, /* tp_getattr */
+ 0, /* tp_setattr */
+ 0, /* tp_compare */
+ (reprfunc)metadata_repr, /* 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|Py_TPFLAGS_BASETYPE, /* tp_flags */
+ "Metadata object", /* tp_doc */
+ 0, /* tp_traverse */
+ 0, /* tp_clear */
+ 0, /* tp_richcompare */
+ 0, /* tp_weaklistoffset */
+ PyObject_SelfIter, /* tp_iter */
+ 0, /* tp_iternext */
+ metadata_methods, /* tp_methods */
+ 0, /* tp_members */
+ metadata_getsetters, /* tp_getset */
+ 0, /* tp_base */
+ 0, /* tp_dict */
+ 0, /* tp_descr_get */
+ 0, /* tp_descr_set */
+ 0, /* tp_dictoffset */
+ (initproc)metadata_init, /* tp_init */
+ 0, /* tp_alloc */
+ metadata_new, /* tp_new */
+ 0, /* tp_free */
+ 0, /* tp_is_gc */
+};
--- /dev/null
+/* createrepo_c - Library of routines for manipulation with repodata
+ * Copyright (C) 2013 Tomas Mlcoch
+ *
+ * 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.
+ */
+
+#ifndef CR_LOAD_METADATA_PY_H
+#define CR_LOAD_METADATA_PY_H
+
+#include "src/createrepo_c.h"
+
+extern PyTypeObject Metadata_Type;
+
+#define MetadataObject_Check(o) PyObject_TypeCheck(o, &Metadata_Type)
+
+#endif
--- /dev/null
+/* createrepo_c - Library of routines for manipulation with repodata
+ * Copyright (C) 2013 Tomas Mlcoch
+ *
+ * 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 <assert.h>
+#include <stddef.h>
+
+#include "locate_metadata-py.h"
+#include "exception-py.h"
+#include "typeconversion.h"
+
+typedef struct {
+ PyObject_HEAD
+ struct cr_MetadataLocation *ml;
+} _MetadataLocationObject;
+
+struct cr_MetadataLocation *
+MetadataLocation_FromPyObject(PyObject *o)
+{
+ if (!MetadataLocationObject_Check(o)) {
+ PyErr_SetString(PyExc_TypeError, "Expected a createrepo_c.MetadataLocation object.");
+ return NULL;
+ }
+ return ((_MetadataLocationObject *) o)->ml;
+}
+
+static int
+check_MetadataLocationStatus(const _MetadataLocationObject *self)
+{
+ assert(self != NULL);
+ assert(MetadataLocationObject_Check(self));
+ if (self->ml == NULL) {
+ PyErr_SetString(CrErr_Exception, "Improper createrepo_c MetadataLocation object.");
+ return -1;
+ }
+ return 0;
+}
+
+/* Function on the type */
+
+static PyObject *
+metadatalocation_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
+{
+ CR_UNUSED(args);
+ CR_UNUSED(kwds);
+
+ _MetadataLocationObject *self = (_MetadataLocationObject *)type->tp_alloc(type, 0);
+ if (self)
+ self->ml = NULL;
+ return (PyObject *)self;
+}
+
+static int
+metadatalocation_init(_MetadataLocationObject *self, PyObject *args, PyObject *kwds)
+{
+ CR_UNUSED(kwds);
+ char *repopath;
+ int ignore_db;
+
+ if (!PyArg_ParseTuple(args, "si|:metadatalocation_init", &repopath, &ignore_db))
+ return -1;
+
+ /* Free all previous resources when reinitialization */
+ if (self->ml) {
+ cr_metadatalocation_free(self->ml);
+ }
+
+ /* Init */
+ self->ml = cr_locate_metadata(repopath, ignore_db);
+ if (self->ml == NULL) {
+ PyErr_SetString(CrErr_Exception, "MetadataLocation initialization failed");
+ return -1;
+ }
+ return 0;
+}
+
+static void
+metadatalocation_dealloc(_MetadataLocationObject *self)
+{
+ if (self->ml)
+ cr_metadatalocation_free(self->ml);
+ Py_TYPE(self)->tp_free(self);
+}
+
+static PyObject *
+metadatalocation_repr(_MetadataLocationObject *self)
+{
+ CR_UNUSED(self);
+ return PyString_FromFormat("<createrepo_c.MetadataLocation object>");
+}
+
+/* MetadataLocation methods */
+
+static struct PyMethodDef metadatalocation_methods[] = {
+ {NULL} /* sentinel */
+};
+
+/* Mapping interface */
+
+Py_ssize_t
+length(_MetadataLocationObject *self)
+{
+ if (self->ml)
+ return 9;
+ return 0;
+}
+
+PyObject *
+getitem(_MetadataLocationObject *self, PyObject *pykey)
+{
+ char *key, *value;
+
+ if (check_MetadataLocationStatus(self))
+ return NULL;
+
+ if (!PyString_Check(pykey)) {
+ PyErr_SetString(PyExc_ValueError, "String expected!");
+ return NULL;
+ }
+
+ key = PyString_AsString(pykey);
+ value = NULL;
+
+ if (!strcmp(key, "primary")) {
+ value = self->ml->pri_xml_href;
+ } else if (!strcmp(key, "filelists")) {
+ value = self->ml->fil_xml_href;
+ } else if (!strcmp(key, "other")) {
+ value = self->ml->oth_xml_href;
+ } else if (!strcmp(key, "primary_db")) {
+ value = self->ml->pri_sqlite_href;
+ } else if (!strcmp(key, "filelists_db")) {
+ value = self->ml->fil_sqlite_href;
+ } else if (!strcmp(key, "other_db")) {
+ value = self->ml->oth_sqlite_href;
+ } else if (!strcmp(key, "group")) {
+ value = self->ml->groupfile_href;
+ } else if (!strcmp(key, "group_gz")) {
+ value = self->ml->cgroupfile_href;
+ } else if (!strcmp(key, "updateinfo")) {
+ value = self->ml->updateinfo_href;
+ }
+
+ if (value)
+ return PyString_FromString(value);
+ else
+ Py_RETURN_NONE;
+}
+
+static PyMappingMethods mapping_methods = {
+ .mp_length = (lenfunc) length,
+ .mp_subscript = (binaryfunc) getitem,
+ .mp_ass_subscript = NULL,
+};
+
+/* Object */
+
+PyTypeObject MetadataLocation_Type = {
+ PyObject_HEAD_INIT(NULL)
+ 0, /* ob_size */
+ "_librepo.MetadataLocation", /* tp_name */
+ sizeof(_MetadataLocationObject),/* tp_basicsize */
+ 0, /* tp_itemsize */
+ (destructor)metadatalocation_dealloc,/* tp_dealloc */
+ 0, /* tp_print */
+ 0, /* tp_getattr */
+ 0, /* tp_setattr */
+ 0, /* tp_compare */
+ (reprfunc)metadatalocation_repr,/* tp_repr */
+ 0, /* tp_as_number */
+ 0, /* tp_as_sequence */
+ &mapping_methods, /* 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_BASETYPE, /* tp_flags */
+ "MetadataLocation object", /* tp_doc */
+ 0, /* tp_traverse */
+ 0, /* tp_clear */
+ 0, /* tp_richcompare */
+ 0, /* tp_weaklistoffset */
+ PyObject_SelfIter, /* tp_iter */
+ 0, /* tp_iternext */
+ metadatalocation_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 */
+ (initproc)metadatalocation_init,/* tp_init */
+ 0, /* tp_alloc */
+ metadatalocation_new, /* tp_new */
+ 0, /* tp_free */
+ 0, /* tp_is_gc */
+};
--- /dev/null
+/* createrepo_c - Library of routines for manipulation with repodata
+ * Copyright (C) 2013 Tomas Mlcoch
+ *
+ * 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.
+ */
+
+#ifndef CR_LOCATE_METADATA_PY_H
+#define CR_LOCATE_METADATA_PY_H
+
+#include "src/createrepo_c.h"
+
+extern PyTypeObject MetadataLocation_Type;
+
+#define MetadataLocationObject_Check(o) PyObject_TypeCheck(o, &MetadataLocation_Type)
+struct cr_MetadataLocation *MetadataLocation_FromPyObject(PyObject *o);
+
+#endif
--- /dev/null
+/* createrepo_c - Library of routines for manipulation with repodata
+ * Copyright (C) 2013 Tomas Mlcoch
+ *
+ * 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 <glib.h>
+#include <assert.h>
+#include <stddef.h>
+
+#include "metadata_hashtable.h"
+#include "package-py.h"
+#include "exception-py.h"
+#include "typeconversion.h"
+
+/* TODO:
+ * Add support for iteration
+ **/
+
+typedef struct {
+ PyObject_HEAD
+ PyObject *owner;
+ GHashTable *ht;
+} _MetadataHashtableObject;
+
+PyObject *
+Object_FromGHashtable(PyObject *owner, GHashTable *ht)
+{
+ PyObject *pyht;
+
+ if (!ht) {
+ PyErr_SetString(PyExc_TypeError, "Expected a hash table pointer not NULL.");
+ return NULL;
+ }
+
+ Py_XINCREF(owner);
+ ((_MetadataHashtableObject *) pyht)->owner = owner;
+ pyht = PyObject_CallObject((PyObject*)&Package_Type, NULL);
+ ((_MetadataHashtableObject *) pyht)->ht = ht;
+ return pyht;
+}
+
+static int
+check_MetadataHashtableStatus(const _MetadataHashtableObject *self)
+{
+ assert(self != NULL);
+ assert(MetadataHashtableObject_Check(self));
+ if (self->ht == NULL) {
+ PyErr_SetString(CrErr_Exception, "Improper createrepo_c MetadataHashtable object.");
+ return -1;
+ }
+ return 0;
+}
+
+/* Function on the type */
+
+static PyObject *
+metadata_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
+{
+ CR_UNUSED(args);
+ CR_UNUSED(kwds);
+
+ _MetadataHashtableObject *self = (_MetadataHashtableObject *)type->tp_alloc(type, 0);
+ if (self) {
+ self->ht = NULL;
+ }
+ return (PyObject *)self;
+}
+
+static int
+metadata_init(_MetadataHashtableObject *self, PyObject *args, PyObject *kwds)
+{
+ char *kwlist[] = {NULL};
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, "|:metadata_init", kwlist))
+ return -1;
+
+ /* Free all previous resources when reinitialization */
+ if (self->ht)
+ g_hash_table_destroy(self->ht);
+ Py_XDECREF(self->owner);
+ self->owner = NULL;
+ self->ht = NULL;
+ return 0;
+}
+
+static void
+metadata_dealloc(_MetadataHashtableObject *self)
+{
+ if (self->ht)
+ g_hash_table_destroy(self->ht);
+ Py_XDECREF(self->owner);
+ Py_TYPE(self)->tp_free(self);
+}
+
+static PyObject *
+metadata_repr(_MetadataHashtableObject *self)
+{
+ CR_UNUSED(self);
+ return PyString_FromFormat("<createrepo_c.MetadataHashtable object>");
+}
+
+/* MetadataHashtable methods */
+
+static PyObject *
+len(_MetadataHashtableObject *self, PyObject *noarg)
+{
+ CR_UNUSED(noarg);
+ unsigned long len = 0;
+ if (self->ht)
+ len = (unsigned long) g_hash_table_size(self->ht);
+ return PyLong_FromUnsignedLong(len);
+}
+
+/*
+static PyObject *
+add(_MetadataHashtableObject *self, PyObject *args)
+{
+ char *key;
+ PyObject *py_pkg;
+ cr_Package *pkg;
+
+ if (!PyArg_ParseTuple(args, "sO!:add", &key, &Package_Type, &pkg))
+ return NULL;
+ if (check_MetadataHashtableStatus(self))
+ return NULL;
+
+ pkg = Package_FromPyObject(pkg);
+ if (!pkg)
+ Py_RETURN_NONE;
+
+ Py_XINCREF(py_pkg);
+ // XXX: Store referenced object for Py_XDECREF!!!!!
+ g_hash_table_replace(self->ht, key, pkg);
+ Py_RETURN_NONE;
+}
+*/
+
+static PyObject *
+has_key(_MetadataHashtableObject *self, PyObject *args)
+{
+ char *key;
+
+ if (!PyArg_ParseTuple(args, "s:has_key", &key))
+ return NULL;
+ if (check_MetadataHashtableStatus(self))
+ return NULL;
+
+ if (g_hash_table_lookup(self->ht, key))
+ Py_RETURN_TRUE;
+ Py_RETURN_FALSE;
+}
+
+static PyObject *
+keys(_MetadataHashtableObject *self, PyObject *args)
+{
+ CR_UNUSED(args);
+
+ GList *keys = g_hash_table_get_values(self->ht);
+ PyObject *list = PyList_New(0);
+ for (GList *elem = keys; elem; elem = g_list_next(elem)) {
+ PyObject *py_str = PyString_FromString(elem->data);
+ assert(py_str);
+ if (PyList_Append(list, py_str) == -1) {
+ Py_XDECREF(list);
+ return NULL;
+ }
+ }
+ return list;
+}
+
+static PyObject *
+del(_MetadataHashtableObject *self, PyObject *args)
+{
+ char *key;
+
+ if (!PyArg_ParseTuple(args, "s:del", &key))
+ return NULL;
+ if (check_MetadataHashtableStatus(self))
+ return NULL;
+
+ if (g_hash_table_remove(self->ht, key))
+ Py_RETURN_TRUE;
+ Py_RETURN_FALSE;
+}
+
+static struct PyMethodDef metadata_methods[] = {
+ {"len", (PyCFunction)len, METH_NOARGS, NULL},
+// {"add", (PyCFunction)add, METH_VARARGS, NULL},
+ {"has_key", (PyCFunction)has_key, METH_VARARGS, NULL},
+ {"keys", (PyCFunction)keys, METH_NOARGS, NULL},
+ {"del", (PyCFunction)del, METH_VARARGS, NULL},
+ {NULL} /* sentinel */
+};
+
+/* Object */
+
+PyTypeObject MetadataHashtable_Type = {
+ PyObject_HEAD_INIT(NULL)
+ 0, /* ob_size */
+ "_librepo.MetadataHashtable", /* tp_name */
+ sizeof(_MetadataHashtableObject),/* tp_basicsize */
+ 0, /* tp_itemsize */
+ (destructor)metadata_dealloc, /* tp_dealloc */
+ 0, /* tp_print */
+ 0, /* tp_getattr */
+ 0, /* tp_setattr */
+ 0, /* tp_compare */
+ (reprfunc)metadata_repr, /* 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|Py_TPFLAGS_BASETYPE, /* tp_flags */
+ "MetadataHashtable object", /* tp_doc */
+ 0, /* tp_traverse */
+ 0, /* tp_clear */
+ 0, /* tp_richcompare */
+ 0, /* tp_weaklistoffset */
+ PyObject_SelfIter, /* tp_iter */
+ 0, /* tp_iternext */
+ metadata_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 */
+ (initproc)metadata_init, /* tp_init */
+ 0, /* tp_alloc */
+ metadata_new, /* tp_new */
+ 0, /* tp_free */
+ 0, /* tp_is_gc */
+};
--- /dev/null
+/* createrepo_c - Library of routines for manipulation with repodata
+ * Copyright (C) 2013 Tomas Mlcoch
+ *
+ * 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.
+ */
+
+#ifndef CR_METADATA_HASHTABLE_PY_H
+#define CR_METADATA_HASHTABLE_PY_H
+
+#include "src/createrepo_c.h"
+
+extern PyTypeObject MetadataHashtable_Type;
+
+#define MetadataHashtableObject_Check(o) PyObject_TypeCheck(o, &MetadataHashtable_Type)
+
+PyObject *Object_FromGHashtable(PyObject self, GHashTable *ht);
+
+#endif
--- /dev/null
+/* createrepo_c - Library of routines for manipulation with repodata
+ * Copyright (C) 2013 Tomas Mlcoch
+ *
+ * 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 <assert.h>
+#include <stddef.h>
+
+#include "package-py.h"
+#include "exception-py.h"
+#include "typeconversion.h"
+
+typedef struct {
+ PyObject_HEAD
+ cr_Package *package;
+ int free_on_destroy;
+ PyObject *parent;
+} _PackageObject;
+
+cr_Package *
+Package_FromPyObject(PyObject *o)
+{
+ if (!PackageObject_Check(o)) {
+ PyErr_SetString(PyExc_TypeError, "Expected a createrepo_c.Package object.");
+ return NULL;
+ }
+ return ((_PackageObject *)o)->package;
+}
+
+PyObject *
+Object_FromPackage(cr_Package *pkg, int free_on_destroy)
+{
+ PyObject *pypkg;
+
+ if (!pkg) {
+ PyErr_SetString(PyExc_TypeError, "Expected a cr_Package pointer not NULL.");
+ return NULL;
+ }
+
+ pypkg = PyObject_CallObject((PyObject*)&Package_Type, NULL);
+ // XXX: Remove empty package in pypkg and replace it with pkg
+ cr_package_free(((_PackageObject *)pypkg)->package);
+ ((_PackageObject *)pypkg)->package = pkg;
+ ((_PackageObject *)pypkg)->free_on_destroy = free_on_destroy;
+ ((_PackageObject *)pypkg)->parent = NULL;
+
+ return pypkg;
+}
+
+PyObject *
+Object_FromPackage_WithParent(cr_Package *pkg, int free_on_destroy, PyObject *parent)
+{
+ PyObject *pypkg;
+ pypkg = Object_FromPackage(pkg, free_on_destroy);
+ if (pypkg) {
+ ((_PackageObject *)pypkg)->parent = parent;
+ Py_XINCREF(parent);
+ }
+ return pypkg;
+}
+
+static int
+check_PackageStatus(const _PackageObject *self)
+{
+ assert(self != NULL);
+ assert(PackageObject_Check(self));
+ if (self->package == NULL) {
+ PyErr_SetString(CrErr_Exception, "Improper createrepo_c Package object.");
+ return -1;
+ }
+ return 0;
+}
+
+/* Function on the type */
+
+static PyObject *
+package_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
+{
+ CR_UNUSED(args);
+ CR_UNUSED(kwds);
+ _PackageObject *self = (_PackageObject *)type->tp_alloc(type, 0);
+ if (self) {
+ self->package = NULL;
+ self->free_on_destroy = 1;
+ self->parent = NULL;
+ }
+ return (PyObject *)self;
+}
+
+static int
+package_init(_PackageObject *self, PyObject *args, PyObject *kwds)
+{
+ char *kwlist[] = {NULL};
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, "|:package_init", kwlist))
+ return -1;
+
+ if (self->package && self->free_on_destroy) // reinitialization by __init__()
+ cr_package_free(self->package);
+ if (self->parent) {
+ Py_DECREF(self->parent);
+ self->parent = NULL;
+ }
+
+ self->package = cr_package_new();
+ if (self->package == NULL) {
+ PyErr_SetString(CrErr_Exception, "Package initialization failed");
+ return -1;
+ }
+ return 0;
+}
+
+static void
+package_dealloc(_PackageObject *self)
+{
+ if (self->package && self->free_on_destroy)
+ cr_package_free(self->package);
+ if (self->parent) {
+ Py_DECREF(self->parent);
+ self->parent = NULL;
+ }
+ Py_TYPE(self)->tp_free(self);
+}
+
+static PyObject *
+package_repr(_PackageObject *self)
+{
+ cr_Package *pkg = self->package;
+ PyObject *repr;
+ if (pkg) {
+ repr = PyString_FromFormat("<createrepo_c.Package object id %s, %s>",
+ (pkg->pkgId ? pkg->pkgId : "-"),
+ (pkg->name ? pkg->name : "-"));
+ } else {
+ repr = PyString_FromFormat("<createrepo_c.Package object id -, ->");
+ }
+ return repr;
+}
+
+static PyObject *
+package_str(_PackageObject *self)
+{
+ PyObject *ret;
+ if (check_PackageStatus(self))
+ return NULL;
+ if (self->package) {
+ char *nevra = cr_package_nvra(self->package);
+ ret = PyString_FromString(nevra);
+ free(nevra);
+ } else {
+ ret = PyString_FromString("-");
+ }
+ return ret;
+}
+
+/* Package methods */
+
+static PyObject *
+nvra(_PackageObject *self, void *nothing)
+{
+ CR_UNUSED(nothing);
+ PyObject *pystr;
+ if (check_PackageStatus(self))
+ return NULL;
+ char *nvra = cr_package_nvra(self->package);
+ pystr = PyStringOrNone_FromString(nvra);
+ free(nvra);
+ return pystr;
+}
+
+static PyObject *
+nevra(_PackageObject *self, void *nothing)
+{
+ CR_UNUSED(nothing);
+ PyObject *pystr;
+ if (check_PackageStatus(self))
+ return NULL;
+ char *nevra = cr_package_nevra(self->package);
+ pystr = PyStringOrNone_FromString(nevra);
+ free(nevra);
+ return pystr;
+}
+
+static PyObject *
+copy_pkg(_PackageObject *self, void *nothing)
+{
+ CR_UNUSED(nothing);
+ if (check_PackageStatus(self))
+ return NULL;
+ return Object_FromPackage(cr_package_copy(self->package), 1);
+}
+
+static struct PyMethodDef package_methods[] = {
+ {"nvra", (PyCFunction)nvra, METH_NOARGS, NULL},
+ {"nevra", (PyCFunction)nevra, METH_NOARGS, NULL},
+ {"copy", (PyCFunction)copy_pkg, METH_NOARGS, NULL},
+ {NULL} /* sentinel */
+};
+
+/* Getters */
+
+static PyObject *
+get_num(_PackageObject *self, void *member_offset)
+{
+ if (check_PackageStatus(self))
+ return NULL;
+ cr_Package *pkg = self->package;
+ gint64 val = *((gint64 *) ((size_t)pkg + (size_t) member_offset));
+ return PyLong_FromLongLong((long long) val);
+}
+
+static PyObject *
+get_str(_PackageObject *self, void *member_offset)
+{
+ if (check_PackageStatus(self))
+ return NULL;
+ cr_Package *pkg = self->package;
+ char *str = *((char **) ((size_t) pkg + (size_t) member_offset));
+ if (str == NULL)
+ Py_RETURN_NONE;
+ return PyString_FromString(str);
+}
+
+/** Return offset of a selected member of cr_Package structure. */
+#define OFFSET(member) (void *) offsetof(cr_Package, member)
+
+/** Convert C object to PyObject.
+ * @param C object
+ * @return PyObject representation
+ */
+typedef PyObject *(*ConversionFromFunc)(void *);
+
+/** Check an element from a list if has a valid format.
+ * @param a single list element
+ * @return 0 if ok, 1 otherwise
+ */
+typedef int *(*ConversionToCheckFunc)(void *);
+
+/** Convert PyObject to C representation.
+ * @param PyObject
+ * @return C representation
+ */
+typedef void *(*ConversionToFunc)(void *, void *);
+
+/* Pre-Declaration for check functions */
+static int CheckPyDependency(PyObject *dep);
+static int CheckPyPackageFile(PyObject *dep);
+static int CheckPyChangelogEntry(PyObject *dep);
+
+typedef struct {
+ size_t offset; /*!< Ofset of the list in cr_Package */
+ ConversionFromFunc f; /*!< Conversion func to PyObject from a C object */
+ ConversionToCheckFunc t_check; /*!< Check func for a single element of list */
+ ConversionToFunc t; /*!< Conversion func to C object from PyObject */
+} ListConvertor;
+
+/** List of convertors for converting a lists in cr_Package. */
+static ListConvertor list_convertors[] = {
+ { offsetof(cr_Package, requires), PyObject_FromDependency,
+ CheckPyDependency, PyObject_ToDependency },
+ { offsetof(cr_Package, provides), PyObject_FromDependency,
+ CheckPyDependency, PyObject_ToDependency },
+ { offsetof(cr_Package, conflicts), PyObject_FromDependency,
+ CheckPyDependency, PyObject_ToDependency },
+ { offsetof(cr_Package, obsoletes), PyObject_FromDependency,
+ CheckPyDependency, PyObject_ToDependency },
+ { offsetof(cr_Package, files), PyObject_FromPackageFile,
+ CheckPyPackageFile, PyObject_ToPackageFile },
+ { offsetof(cr_Package, changelogs), PyObject_FromChangelogEntry,
+ CheckPyChangelogEntry, PyObject_ToChangelogEntry },
+};
+
+static PyObject *
+get_list(_PackageObject *self, void *conv)
+{
+ ListConvertor *convertor = conv;
+ PyObject *list;
+ cr_Package *pkg = self->package;
+ GSList *glist = *((GSList **) ((size_t) pkg + (size_t) convertor->offset));
+
+ if (check_PackageStatus(self))
+ return NULL;
+
+ if ((list = PyList_New(0)) == NULL)
+ return NULL;
+
+ for (GSList *elem = glist; elem; elem = g_slist_next(elem))
+ PyList_Append(list, convertor->f(elem->data));
+
+ return list;
+}
+
+/* Setters */
+
+static int
+set_num(_PackageObject *self, PyObject *value, void *member_offset)
+{
+ long val;
+ if (check_PackageStatus(self))
+ return -1;
+ if (PyLong_Check(value)) {
+ val = PyLong_AsLong(value);
+ } else if (PyInt_Check(value)) {
+ val = PyInt_AS_LONG(value);
+ } else {
+ PyErr_SetString(PyExc_ValueError, "Number expected!");
+ return -1;
+ }
+ cr_Package *pkg = self->package;
+ *((long *) ((size_t) pkg + (size_t) member_offset)) = val;
+ return 0;
+}
+
+static int
+set_str(_PackageObject *self, PyObject *value, void *member_offset)
+{
+ if (check_PackageStatus(self))
+ return -1;
+ if (!PyString_Check(value)) {
+ PyErr_SetString(PyExc_ValueError, "String expected!");
+ return -1;
+ }
+ cr_Package *pkg = self->package;
+
+ // Check if chunk exits
+ // If it doesn't - this is package from loaded metadata and all its
+ // strings are in a metadata common chunk (cr_Metadata->chunk).
+ // In this case, we have to create a chunk for this package before
+ // inserting a new string.
+ if (!pkg->chunk)
+ pkg->chunk = g_string_chunk_new(0);
+
+ char *str = g_string_chunk_insert(pkg->chunk, PyString_AsString(value));
+ *((char **) ((size_t) pkg + (size_t) member_offset)) = str;
+ return 0;
+}
+
+
+static int
+CheckPyDependency(PyObject *dep)
+{
+ if (!PyTuple_Check(dep) || PyTuple_Size(dep) != 6) {
+ PyErr_SetString(PyExc_ValueError, "Element of list has to be a tuple with 6 items.");
+ return 1;
+ }
+ return 0;
+}
+
+static int
+CheckPyPackageFile(PyObject *dep)
+{
+ if (!PyTuple_Check(dep) || PyTuple_Size(dep) != 3) {
+ PyErr_SetString(PyExc_ValueError, "Element of list has to be a tuple with 3 items.");
+ return 1;
+ }
+ return 0;
+}
+
+static int
+CheckPyChangelogEntry(PyObject *dep)
+{
+ if (!PyTuple_Check(dep) || PyTuple_Size(dep) != 3) {
+ PyErr_SetString(PyExc_ValueError, "Element of list has to be a tuple with 3 items.");
+ return 1;
+ }
+ return 0;
+}
+
+static int
+set_list(_PackageObject *self, PyObject *list, void *conv)
+{
+ ListConvertor *convertor = conv;
+ cr_Package *pkg = self->package;
+ GSList *glist = NULL;
+
+ if (check_PackageStatus(self))
+ return -1;
+
+ if (!PyList_Check(list)) {
+ PyErr_SetString(PyExc_ValueError, "List expected!");
+ return -1;
+ }
+
+ // Check if chunk exits
+ // If it doesn't - this is package from loaded metadata and all its
+ // strings are in a metadata common chunk (cr_Metadata->chunk).
+ // In this case, we have to create a chunk for this package before
+ // inserting a new string.
+ if (!pkg->chunk)
+ pkg->chunk = g_string_chunk_new(0);
+
+ Py_ssize_t len = PyList_Size(list);
+
+ // Check all elements
+ for (Py_ssize_t x = 0; x < len; x++) {
+ PyObject *elem = PyList_GetItem(list, x);
+ if (convertor->t_check && convertor->t_check(elem)) // XXX
+ return -1;
+ }
+
+ for (Py_ssize_t x = 0; x < len; x++) {
+ glist = g_slist_prepend(glist, convertor->t(PyList_GetItem(list, x), pkg->chunk));
+ }
+
+ *((GSList **) ((size_t) pkg + (size_t) convertor->offset)) = glist;
+ return 0;
+}
+
+static PyGetSetDef package_getsetters[] = {
+ {"pkgId", (getter)get_str, (setter)set_str, NULL, OFFSET(pkgId)},
+ {"name", (getter)get_str, (setter)set_str, NULL, OFFSET(name)},
+ {"arch", (getter)get_str, (setter)set_str, NULL, OFFSET(arch)},
+ {"version", (getter)get_str, (setter)set_str, NULL, OFFSET(version)},
+ {"epoch", (getter)get_str, (setter)set_str, NULL, OFFSET(epoch)},
+ {"release", (getter)get_str, (setter)set_str, NULL, OFFSET(release)},
+ {"summary", (getter)get_str, (setter)set_str, NULL, OFFSET(summary)},
+ {"description", (getter)get_str, (setter)set_str, NULL, OFFSET(description)},
+ {"url", (getter)get_str, (setter)set_str, NULL, OFFSET(url)},
+ {"time_file", (getter)get_num, (setter)set_num, NULL, OFFSET(time_file)},
+ {"time_build", (getter)get_num, (setter)set_num, NULL, OFFSET(time_build)},
+ {"rpm_license", (getter)get_str, (setter)set_str, NULL, OFFSET(rpm_license)},
+ {"rpm_vendor", (getter)get_str, (setter)set_str, NULL, OFFSET(rpm_vendor)},
+ {"rpm_group", (getter)get_str, (setter)set_str, NULL, OFFSET(rpm_group)},
+ {"rpm_buildhost", (getter)get_str, (setter)set_str, NULL, OFFSET(rpm_buildhost)},
+ {"rpm_sourcerpm", (getter)get_str, (setter)set_str, NULL, OFFSET(rpm_sourcerpm)},
+ {"rpm_header_start", (getter)get_num, (setter)set_num, NULL, OFFSET(rpm_header_start)},
+ {"rpm_header_end", (getter)get_num, (setter)set_num, NULL, OFFSET(rpm_header_end)},
+ {"rpm_packager", (getter)get_str, (setter)set_str, NULL, OFFSET(rpm_packager)},
+ {"size_package", (getter)get_num, (setter)set_num, NULL, OFFSET(size_package)},
+ {"size_installed", (getter)get_num, (setter)set_num, NULL, OFFSET(size_installed)},
+ {"size_archive", (getter)get_num, (setter)set_num, NULL, OFFSET(size_archive)},
+ {"location_href", (getter)get_str, (setter)set_str, NULL, OFFSET(location_href)},
+ {"location_base", (getter)get_str, (setter)set_str, NULL, OFFSET(location_base)},
+ {"checksum_type", (getter)get_str, (setter)set_str, NULL, OFFSET(checksum_type)},
+ {"requires", (getter)get_list, (setter)set_list, NULL, &(list_convertors[0])},
+ {"provides", (getter)get_list, (setter)set_list, NULL, &(list_convertors[1])},
+ {"conflicts", (getter)get_list, (setter)set_list, NULL, &(list_convertors[2])},
+ {"obsoletes", (getter)get_list, (setter)set_list, NULL, &(list_convertors[3])},
+ {"files", (getter)get_list, (setter)set_list, NULL, &(list_convertors[4])},
+ {"changelogs", (getter)get_list, (setter)set_list, NULL, &(list_convertors[5])},
+ {NULL, NULL, NULL, NULL, NULL} /* sentinel */
+};
+
+/* Object */
+
+PyTypeObject Package_Type = {
+ PyObject_HEAD_INIT(NULL)
+ 0, /* ob_size */
+ "_librepo.Package", /* tp_name */
+ sizeof(_PackageObject), /* tp_basicsize */
+ 0, /* tp_itemsize */
+ (destructor) package_dealloc, /* tp_dealloc */
+ 0, /* tp_print */
+ 0, /* tp_getattr */
+ 0, /* tp_setattr */
+ 0, /* tp_compare */
+ (reprfunc) package_repr, /* tp_repr */
+ 0, /* tp_as_number */
+ 0, /* tp_as_sequence */
+ 0, /* tp_as_mapping */
+ 0, /* tp_hash */
+ 0, /* tp_call */
+ (reprfunc)package_str, /* tp_str */
+ 0, /* tp_getattro */
+ 0, /* tp_setattro */
+ 0, /* tp_as_buffer */
+ Py_TPFLAGS_DEFAULT|Py_TPFLAGS_BASETYPE, /* tp_flags */
+ "Package object", /* tp_doc */
+ 0, /* tp_traverse */
+ 0, /* tp_clear */
+ 0, /* tp_richcompare */
+ 0, /* tp_weaklistoffset */
+ PyObject_SelfIter, /* tp_iter */
+ 0, /* tp_iternext */
+ package_methods, /* tp_methods */
+ 0, /* tp_members */
+ package_getsetters, /* tp_getset */
+ 0, /* tp_base */
+ 0, /* tp_dict */
+ 0, /* tp_descr_get */
+ 0, /* tp_descr_set */
+ 0, /* tp_dictoffset */
+ (initproc) package_init, /* tp_init */
+ 0, /* tp_alloc */
+ package_new, /* tp_new */
+ 0, /* tp_free */
+ 0, /* tp_is_gc */
+};
--- /dev/null
+/* createrepo_c - Library of routines for manipulation with repodata
+ * Copyright (C) 2013 Tomas Mlcoch
+ *
+ * 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.
+ */
+
+#ifndef CR_PACKAGE_PY_H
+#define CR_PACKAGE_PY_H
+
+#include "src/createrepo_c.h"
+
+extern PyTypeObject Package_Type;
+
+#define PackageObject_Check(o) PyObject_TypeCheck(o, &Package_Type)
+
+PyObject *Object_FromPackage(cr_Package *pkg, int free_on_destroy);
+cr_Package *Package_FromPyObject(PyObject *o);
+PyObject * Object_FromPackage_WithParent(cr_Package *pkg, int free_on_destroy, PyObject *parent);
+
+#endif
--- /dev/null
+/* createrepo_c - Library of routines for manipulation with repodata
+ * Copyright (C) 2013 Tomas Mlcoch
+ *
+ * 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 <assert.h>
+#include <stddef.h>
+
+#include "src/createrepo_c.h"
+
+#include "typeconversion.h"
+#include "parsepkg-py.h"
+#include "package-py.h"
+#include "exception-py.h"
+
+PyObject *
+py_package_from_rpm(PyObject *self, PyObject *args)
+{
+ CR_UNUSED(self);
+
+ PyObject *ret;
+ cr_Package *pkg;
+ int checksum_type, changelog_limit;
+ char *filename, *location_href, *location_base;
+
+ if (!PyArg_ParseTuple(args, "sizzi:py_package_from_rpm",
+ &filename,
+ &checksum_type,
+ &location_href,
+ &location_base,
+ &changelog_limit)) {
+ return NULL;
+ }
+
+ if (!g_file_test(filename, G_FILE_TEST_IS_REGULAR)) {
+ PyErr_Format(PyExc_IOError, "File %s doesn't exist", filename);
+ return NULL;
+ }
+
+ pkg = cr_package_from_rpm(filename, checksum_type, location_href,
+ location_base, changelog_limit, NULL);
+ if (!pkg) {
+ PyErr_Format(CrErr_Exception, "Cannot load %s", filename);
+ return NULL;
+ }
+
+ ret = Object_FromPackage(pkg, 1);
+ return ret;
+}
+
+PyObject *
+py_xml_from_rpm(PyObject *self, PyObject *args)
+{
+ CR_UNUSED(self);
+
+ PyObject *tuple;
+ int checksum_type, changelog_limit;
+ char *filename, *location_href, *location_base;
+ struct cr_XmlStruct xml_res;
+
+ if (!PyArg_ParseTuple(args, "sizzi:py_xml_from_rpm",
+ &filename,
+ &checksum_type,
+ &location_href,
+ &location_base,
+ &changelog_limit)) {
+ return NULL;
+ }
+
+ if (!g_file_test(filename, G_FILE_TEST_IS_REGULAR)) {
+ PyErr_Format(PyExc_IOError, "File %s doesn't exist", filename);
+ return NULL;
+ }
+
+
+ xml_res = cr_xml_from_rpm(filename, checksum_type, location_href,
+ location_base, changelog_limit, NULL);
+
+ if (!xml_res.primary && !xml_res.filelists && !xml_res.other) {
+ PyErr_Format(CrErr_Exception, "Cannot load %s", filename);
+ return NULL;
+ }
+
+ if ((tuple = PyTuple_New(3)) == NULL)
+ goto py_xml_from_rpm_end; // Free xml_res and return NULL
+
+ PyTuple_SetItem(tuple, 0, PyStringOrNone_FromString(xml_res.primary));
+ PyTuple_SetItem(tuple, 1, PyStringOrNone_FromString(xml_res.filelists));
+ PyTuple_SetItem(tuple, 2, PyStringOrNone_FromString(xml_res.other));
+
+py_xml_from_rpm_end:
+ free(xml_res.primary);
+ free(xml_res.filelists);
+ free(xml_res.other);
+
+ return tuple;
+}
+
--- /dev/null
+/* createrepo_c - Library of routines for manipulation with repodata
+ * Copyright (C) 2013 Tomas Mlcoch
+ *
+ * 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.
+ */
+
+#ifndef CR_PARSEPKG_PY_H
+#define CR_PARSEPKG_PY_H
+
+#include "src/createrepo_c.h"
+
+PyObject *py_package_from_rpm(PyObject *self, PyObject *args);
+PyObject *py_xml_from_rpm(PyObject *self, PyObject *args);
+
+#endif
--- /dev/null
+/* createrepo_c - Library of routines for manipulation with repodata
+ * Copyright (C) 2013 Tomas Mlcoch
+ *
+ * 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 <assert.h>
+#include <stddef.h>
+
+#include "repomd-py.h"
+#include "repomdrecord-py.h"
+#include "exception-py.h"
+#include "typeconversion.h"
+
+typedef struct {
+ PyObject_HEAD
+ cr_Repomd repomd;
+} _RepomdObject;
+
+static int
+check_RepomdStatus(const _RepomdObject *self)
+{
+ assert(self != NULL);
+ assert(RepomdObject_Check(self));
+ if (self->repomd == NULL) {
+ PyErr_SetString(CrErr_Exception, "Improper createrepo_c Repomd object.");
+ return -1;
+ }
+ return 0;
+}
+
+/* Function on the type */
+
+static PyObject *
+repomd_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
+{
+ CR_UNUSED(args);
+ CR_UNUSED(kwds);
+
+ _RepomdObject *self = (_RepomdObject *)type->tp_alloc(type, 0);
+ if (self) {
+ self->repomd = NULL;
+ }
+ return (PyObject *)self;
+}
+
+static int
+repomd_init(_RepomdObject *self, PyObject *args, PyObject *kwds)
+{
+ CR_UNUSED(args);
+ CR_UNUSED(kwds);
+
+ /* Free all previous resources when reinitialization */
+ if (self->repomd) {
+ cr_repomd_free(self->repomd);
+ }
+
+ /* Init */
+ self->repomd = cr_repomd_new();
+ if (self->repomd == NULL) {
+ PyErr_SetString(CrErr_Exception, "Repomd initialization failed");
+ return -1;
+ }
+ return 0;
+}
+
+static void
+repomd_dealloc(_RepomdObject *self)
+{
+ if (self->repomd)
+ cr_repomd_free(self->repomd);
+ Py_TYPE(self)->tp_free(self);
+}
+
+static PyObject *
+repomd_repr(_RepomdObject *self)
+{
+ CR_UNUSED(self);
+ return PyString_FromFormat("<createrepo_c.Repomd object>");
+}
+
+/* Repomd methods */
+
+static PyObject *
+set_record(_RepomdObject *self, PyObject *args)
+{
+ PyObject *record;
+ char *type;
+
+ if (!PyArg_ParseTuple(args, "O!s:set_record", &RepomdRecord_Type, &record, &type))
+ return NULL;
+ if (check_RepomdStatus(self))
+ return NULL;
+ cr_repomd_set_record(self->repomd, RepomdRecord_FromPyObject(record), type);
+ Py_XINCREF(record);
+ Py_RETURN_NONE;
+}
+
+static PyObject *
+set_revision(_RepomdObject *self, PyObject *args)
+{
+ char *revision;
+ if (!PyArg_ParseTuple(args, "s:set_revision", &revision))
+ return NULL;
+ if (check_RepomdStatus(self))
+ return NULL;
+ cr_repomd_set_revision(self->repomd, revision);
+ Py_RETURN_NONE;
+}
+
+static PyObject *
+add_distro_tag(_RepomdObject *self, PyObject *args, PyObject *kwargs)
+{
+ static char *kwlist[] = { "tag", "cpeid", NULL };
+
+ char *tag = NULL, *cpeid = NULL;
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs, "s|z:add_distro_tag",
+ kwlist, &tag, &cpeid))
+ return NULL;
+ if (check_RepomdStatus(self))
+ return NULL;
+ cr_repomd_add_distro_tag(self->repomd, cpeid, tag);
+ Py_RETURN_NONE;
+}
+
+static PyObject *
+add_repo_tag(_RepomdObject *self, PyObject *args)
+{
+ char *tag;
+ if (!PyArg_ParseTuple(args, "s:add_repo_tag", &tag))
+ return NULL;
+ if (check_RepomdStatus(self))
+ return NULL;
+ cr_repomd_add_repo_tag(self->repomd, tag);
+ Py_RETURN_NONE;
+}
+
+static PyObject *
+add_content_tag(_RepomdObject *self, PyObject *args)
+{
+ char *tag;
+ if (!PyArg_ParseTuple(args, "s:add_content_tag", &tag))
+ return NULL;
+ if (check_RepomdStatus(self))
+ return NULL;
+ cr_repomd_add_content_tag(self->repomd, tag);
+ Py_RETURN_NONE;
+}
+
+static PyObject *
+xml_dump(_RepomdObject *self, void *nothing)
+{
+ CR_UNUSED(nothing);
+ PyObject *py_str;
+ char *xml = cr_repomd_xml_dump(self->repomd);
+ py_str = PyStringOrNone_FromString(xml);
+ free(xml);
+ return py_str;
+}
+
+static struct PyMethodDef repomd_methods[] = {
+ {"set_record", (PyCFunction)set_record, METH_VARARGS, NULL},
+ {"set_revision", (PyCFunction)set_revision, METH_VARARGS, NULL},
+ {"add_distro_tag", (PyCFunction)add_distro_tag, METH_VARARGS|METH_KEYWORDS, NULL},
+ {"add_repo_tag", (PyCFunction)add_repo_tag, METH_VARARGS, NULL},
+ {"add_content_tag", (PyCFunction)add_content_tag, METH_VARARGS, NULL},
+ {"xml_dump", (PyCFunction)xml_dump, METH_NOARGS, NULL},
+ {NULL} /* sentinel */
+};
+
+PyTypeObject Repomd_Type = {
+ PyObject_HEAD_INIT(NULL)
+ 0, /* ob_size */
+ "_librepo.Repomd", /* tp_name */
+ sizeof(_RepomdObject), /* tp_basicsize */
+ 0, /* tp_itemsize */
+ (destructor) repomd_dealloc, /* tp_dealloc */
+ 0, /* tp_print */
+ 0, /* tp_getattr */
+ 0, /* tp_setattr */
+ 0, /* tp_compare */
+ (reprfunc) repomd_repr, /* 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|Py_TPFLAGS_BASETYPE, /* tp_flags */
+ "Repomd object", /* tp_doc */
+ 0, /* tp_traverse */
+ 0, /* tp_clear */
+ 0, /* tp_richcompare */
+ 0, /* tp_weaklistoffset */
+ PyObject_SelfIter, /* tp_iter */
+ 0, /* tp_iternext */
+ repomd_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 */
+ (initproc) repomd_init, /* tp_init */
+ 0, /* tp_alloc */
+ repomd_new, /* tp_new */
+ 0, /* tp_free */
+ 0, /* tp_is_gc */
+};
--- /dev/null
+/* createrepo_c - Library of routines for manipulation with repodata
+ * Copyright (C) 2013 Tomas Mlcoch
+ *
+ * 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.
+ */
+
+#ifndef CR_REPOMD_PY_H
+#define CR_REPOMD_PY_H
+
+#include "src/createrepo_c.h"
+
+extern PyTypeObject Repomd_Type;
+
+#define RepomdObject_Check(o) PyObject_TypeCheck(o, &Repomd_Type)
+
+#endif
--- /dev/null
+/* createrepo_c - Library of routines for manipulation with repodata
+ * Copyright (C) 2013 Tomas Mlcoch
+ *
+ * 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 <assert.h>
+#include <stddef.h>
+
+#include "repomdrecord-py.h"
+#include "exception-py.h"
+#include "typeconversion.h"
+
+typedef struct {
+ PyObject_HEAD
+ cr_RepomdRecord record;
+} _RepomdRecordObject;
+
+cr_RepomdRecord
+RepomdRecord_FromPyObject(PyObject *o)
+{
+ if (!RepomdRecordObject_Check(o)) {
+ PyErr_SetString(PyExc_TypeError, "Expected a RepomdRecord object.");
+ return NULL;
+ }
+ return ((_RepomdRecordObject *)o)->record;
+}
+
+static int
+check_RepomdRecordStatus(const _RepomdRecordObject *self)
+{
+ assert(self != NULL);
+ assert(RepomdRecordObject_Check(self));
+ if (self->record == NULL) {
+ PyErr_SetString(CrErr_Exception, "Improper createrepo_c RepomdRecord object.");
+ return -1;
+ }
+ return 0;
+}
+
+/* Function on the type */
+
+static PyObject *
+repomdrecord_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
+{
+ CR_UNUSED(args);
+ CR_UNUSED(kwds);
+ _RepomdRecordObject *self = (_RepomdRecordObject *)type->tp_alloc(type, 0);
+ if (self) {
+ self->record = NULL;
+ }
+ return (PyObject *)self;
+}
+
+static int
+repomdrecord_init(_RepomdRecordObject *self, PyObject *args, PyObject *kwds)
+{
+ CR_UNUSED(kwds);
+
+ char *path;
+
+ if (!PyArg_ParseTuple(args, "s|:repomdrecord_init", &path))
+ return -1;
+
+ /* Free all previous resources when reinitialization */
+ if (self->record) {
+ cr_repomd_record_free(self->record);
+ }
+
+ /* Init */
+ self->record = cr_repomd_record_new(path);
+ if (self->record == NULL) {
+ PyErr_SetString(CrErr_Exception, "RepomdRecord initialization failed");
+ return -1;
+ }
+
+ return 0;
+}
+
+static void
+repomdrecord_dealloc(_RepomdRecordObject *self)
+{
+ if (self->record)
+ cr_repomd_record_free(self->record);
+ Py_TYPE(self)->tp_free(self);
+}
+
+static PyObject *
+repomdrecord_repr(_RepomdRecordObject *self)
+{
+ CR_UNUSED(self);
+ return PyString_FromFormat("<createrepo_c.RepomdRecord object>");
+}
+
+/* RepomdRecord methods */
+
+static PyObject *
+fill(_RepomdRecordObject *self, PyObject *args)
+{
+ int checksum_type;
+ if (!PyArg_ParseTuple(args, "i:fill", &checksum_type))
+ return NULL;
+ if (check_RepomdRecordStatus(self))
+ return NULL;
+ cr_repomd_record_fill(self->record, checksum_type);
+ Py_RETURN_NONE;
+}
+
+static PyObject *
+compress_and_fill(_RepomdRecordObject *self, PyObject *args)
+{
+ int checksum_type, compression_type;
+ PyObject *compressed_repomdrecord;
+ if (!PyArg_ParseTuple(args, "O!ii:fill",
+ &RepomdRecord_Type,
+ &compressed_repomdrecord,
+ &checksum_type,
+ &compression_type))
+ return NULL;
+ if (check_RepomdRecordStatus(self))
+ return NULL;
+ cr_repomd_record_groupfile(self->record,
+ RepomdRecord_FromPyObject(compressed_repomdrecord),
+ checksum_type,
+ compression_type);
+ Py_RETURN_NONE;
+}
+
+static PyObject *
+rename_file(_RepomdRecordObject *self, void *nothing)
+{
+ CR_UNUSED(nothing);
+ cr_repomd_record_rename_file(self->record);
+ Py_RETURN_NONE;
+}
+
+static struct PyMethodDef repomdrecord_methods[] = {
+ {"fill", (PyCFunction)fill, METH_VARARGS, NULL},
+ {"compress_and_fill", (PyCFunction)compress_and_fill, METH_VARARGS, NULL},
+ {"rename_file", (PyCFunction)rename_file, METH_NOARGS, NULL},
+ {NULL} /* sentinel */
+};
+
+/* getsetters */
+
+#define OFFSET(member) (void *) offsetof(struct _cr_RepomdRecord, member)
+
+static PyObject *
+get_num(_RepomdRecordObject *self, void *member_offset)
+{
+ if (check_RepomdRecordStatus(self))
+ return NULL;
+ cr_RepomdRecord rec = self->record;
+ gint64 val = *((gint64 *) ((size_t)rec + (size_t) member_offset));
+ return PyLong_FromLongLong((long long) val);
+}
+
+static PyObject *
+get_str(_RepomdRecordObject *self, void *member_offset)
+{
+ if (check_RepomdRecordStatus(self))
+ return NULL;
+ cr_RepomdRecord rec = self->record;
+ char *str = *((char **) ((size_t) rec + (size_t) member_offset));
+ if (str == NULL)
+ Py_RETURN_NONE;
+ return PyString_FromString(str);
+}
+
+static int
+set_num(_RepomdRecordObject *self, PyObject *value, void *member_offset)
+{
+ long val;
+ if (check_RepomdRecordStatus(self))
+ return -1;
+ if (PyLong_Check(value)) {
+ val = PyLong_AsLong(value);
+ } else if (PyInt_Check(value)) {
+ val = PyInt_AS_LONG(value);
+ } else {
+ PyErr_SetString(PyExc_ValueError, "Number expected!");
+ return -1;
+ }
+ cr_RepomdRecord rec = self->record;
+ *((long *) ((size_t) rec + (size_t) member_offset)) = val;
+ return 0;
+}
+
+static int
+set_str(_RepomdRecordObject *self, PyObject *value, void *member_offset)
+{
+ if (check_RepomdRecordStatus(self))
+ return -1;
+ if (!PyString_Check(value)) {
+ PyErr_SetString(PyExc_ValueError, "String expected!");
+ return -1;
+ }
+ cr_RepomdRecord rec = self->record;
+ char *str = g_string_chunk_insert(rec->chunk, PyString_AsString(value));
+ *((char **) ((size_t) rec + (size_t) member_offset)) = str;
+ return 0;
+}
+
+static PyGetSetDef repomdrecord_getsetters[] = {
+ {"location_real", (getter)get_str, (setter)set_str, NULL, OFFSET(location_real)},
+ {"location_href", (getter)get_str, (setter)set_str, NULL, OFFSET(location_href)},
+ {"checksum", (getter)get_str, (setter)set_str, NULL, OFFSET(checksum)},
+ {"checksum_type", (getter)get_str, (setter)set_str, NULL, OFFSET(checksum_type)},
+ {"checksum_open", (getter)get_str, (setter)set_str, NULL, OFFSET(checksum_open)},
+ {"checksum_open_type", (getter)get_str, (setter)set_str, NULL, OFFSET(checksum_open_type)},
+ {"timestamp", (getter)get_num, (setter)set_num, NULL, OFFSET(timestamp)},
+ {"size", (getter)get_num, (setter)set_num, NULL, OFFSET(size)},
+ {"size_open", (getter)get_num, (setter)set_num, NULL, OFFSET(size_open)},
+ {"db_ver", (getter)get_num, (setter)set_num, NULL, OFFSET(db_ver)},
+ {NULL, NULL, NULL, NULL, NULL} /* sentinel */
+};
+
+/* Object */
+
+PyTypeObject RepomdRecord_Type = {
+ PyObject_HEAD_INIT(NULL)
+ 0, /* ob_size */
+ "_librepo.RepomdRecord", /* tp_name */
+ sizeof(_RepomdRecordObject), /* tp_basicsize */
+ 0, /* tp_itemsize */
+ (destructor) repomdrecord_dealloc, /* tp_dealloc */
+ 0, /* tp_print */
+ 0, /* tp_getattr */
+ 0, /* tp_setattr */
+ 0, /* tp_compare */
+ (reprfunc) repomdrecord_repr, /* 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|Py_TPFLAGS_BASETYPE, /* tp_flags */
+ "RepomdRecord object", /* tp_doc */
+ 0, /* tp_traverse */
+ 0, /* tp_clear */
+ 0, /* tp_richcompare */
+ 0, /* tp_weaklistoffset */
+ PyObject_SelfIter, /* tp_iter */
+ 0, /* tp_iternext */
+ repomdrecord_methods, /* tp_methods */
+ 0, /* tp_members */
+ repomdrecord_getsetters, /* tp_getset */
+ 0, /* tp_base */
+ 0, /* tp_dict */
+ 0, /* tp_descr_get */
+ 0, /* tp_descr_set */
+ 0, /* tp_dictoffset */
+ (initproc) repomdrecord_init, /* tp_init */
+ 0, /* tp_alloc */
+ repomdrecord_new, /* tp_new */
+ 0, /* tp_free */
+ 0, /* tp_is_gc */
+};
--- /dev/null
+/* createrepo_c - Library of routines for manipulation with repodata
+ * Copyright (C) 2013 Tomas Mlcoch
+ *
+ * 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.
+ */
+
+#ifndef CR_REPOMDRECORD_PY_H
+#define CR_REPOMDRECORD_PY_H
+
+#include "src/createrepo_c.h"
+
+extern PyTypeObject RepomdRecord_Type;
+cr_RepomdRecord RepomdRecord_FromPyObject(PyObject *o);
+
+#define RepomdRecordObject_Check(o) PyObject_TypeCheck(o, &RepomdRecord_Type)
+
+#endif
--- /dev/null
+/* createrepo_c - Library of routines for manipulation with repodata
+ * Copyright (C) 2013 Tomas Mlcoch
+ *
+ * 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 <assert.h>
+#include <stddef.h>
+
+#include "sqlite-py.h"
+#include "package-py.h"
+#include "exception-py.h"
+#include "typeconversion.h"
+
+typedef struct {
+ PyObject_HEAD
+ int type; // cr_DatabaseType value or -1
+ void *statements;
+ sqlite3 *db;
+ int closed; // Is db closed?
+} _SqliteObject;
+
+// Forward declaration
+static PyObject *close_db(_SqliteObject *self, void *nothing);
+
+
+static int
+check_SqliteStatus(const _SqliteObject *self)
+{
+ assert(self != NULL);
+ assert(SqliteObject_Check(self));
+ if (self->db == NULL) {
+ PyErr_SetString(CrErr_Exception, "Improper createrepo_c Sqlite object.");
+ return -1;
+ }
+ return 0;
+}
+
+/* Function on the type */
+
+static PyObject *
+sqlite_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
+{
+ CR_UNUSED(args);
+ CR_UNUSED(kwds);
+ _SqliteObject *self = (_SqliteObject *)type->tp_alloc(type, 0);
+ if (self) {
+ self->type = -1;
+ self->statements = NULL;
+ self->db = NULL;
+ self->closed = 0;
+ }
+ return (PyObject *)self;
+}
+
+static int
+sqlite_init(_SqliteObject *self, PyObject *args, PyObject *kwds)
+{
+ char *path;
+ int db_type;
+ GError *err = NULL;
+
+ CR_UNUSED(kwds);
+
+ if (!PyArg_ParseTuple(args, "si|:sqlite_init", &path, &db_type))
+ return -1;
+
+ /* Check arguments */
+ if (db_type < CR_DB_PRIMARY || db_type > CR_DB_OTHER) {
+ PyErr_SetString(PyExc_ValueError, "Unknown type value");
+ return -1;
+ }
+
+ /* Free all previous resources when reinitialization */
+ Py_XDECREF(close_db(self, NULL));
+ if (self->statements) {
+ if (self->type == CR_DB_PRIMARY)
+ cr_db_destroy_primary_statements(self->statements);
+ else if (self->type == CR_DB_FILELISTS)
+ cr_db_destroy_filelists_statements(self->statements);
+ else if (self->type == CR_DB_OTHER)
+ cr_db_destroy_other_statements(self->statements);
+ self->statements = NULL;
+ }
+
+ if (err) {
+ PyErr_Format(CrErr_Exception, "Error while freeing previous content: %s", err->message);
+ g_clear_error(&err);
+ return -1;
+ }
+
+ /* Init */
+ self->type = db_type;
+ self->closed = 0;
+ if (self->type == CR_DB_PRIMARY)
+ self->db = cr_db_open_primary(path, &err);
+ else if (self->type == CR_DB_FILELISTS)
+ self->db = cr_db_open_filelists(path, &err);
+ else if (self->type == CR_DB_OTHER)
+ self->db = cr_db_open_other(path, &err);
+
+ if (self->db == NULL) {
+ PyErr_SetString(CrErr_Exception, "Sqlite initialization failed");
+ return -1;
+ }
+
+ if (err) {
+ PyErr_Format(CrErr_Exception, "Sqlite initialization failed: %s", err->message);
+ g_clear_error(&err);
+ Py_XDECREF(close_db(self, NULL));
+ return -1;
+ }
+
+ return 0;
+}
+
+static void
+sqlite_dealloc(_SqliteObject *self)
+{
+ if (self->statements) {
+ if (self->type == CR_DB_PRIMARY)
+ cr_db_destroy_primary_statements(self->statements);
+ else if (self->type == CR_DB_FILELISTS)
+ cr_db_destroy_filelists_statements(self->statements);
+ else if (self->type == CR_DB_OTHER)
+ cr_db_destroy_other_statements(self->statements);
+ self->statements = NULL;
+ }
+ Py_XDECREF(close_db(self, NULL));
+ Py_TYPE(self)->tp_free(self);
+}
+
+static PyObject *
+sqlite_repr(_SqliteObject *self)
+{
+ char *type;
+ if (self->type == CR_DB_PRIMARY) type = "PrimaryDb";
+ else if (self->type == CR_DB_FILELISTS) type = "FilelistsDb";
+ else if (self->type == CR_DB_OTHER) type = "OtherDb";
+ else type = "UnknownDb";
+ return PyString_FromFormat("<createrepo_c.Sqlite %s object>", type);
+}
+
+/* getsetters */
+
+/* TODO:
+ * Export sqlite object - Maybe in future version?
+ */
+
+/* Sqlite methods */
+
+static PyObject *
+prepare_statements(_SqliteObject *self, void *nothing)
+{
+ CR_UNUSED(nothing);
+ GError *err = NULL;
+
+ if (self->statements)
+ Py_RETURN_NONE;
+
+ if (check_SqliteStatus(self))
+ return NULL;
+
+ if (self->type == CR_DB_PRIMARY)
+ self->statements = cr_db_prepare_primary_statements(self->db, &err);
+ else if (self->type == CR_DB_FILELISTS)
+ self->statements = cr_db_prepare_filelists_statements(self->db, &err);
+ else if (self->type == CR_DB_OTHER)
+ self->statements = cr_db_prepare_other_statements(self->db, &err);
+
+ if (err) {
+ PyErr_Format(CrErr_Exception, "Sqlite statement error: %s", err->message);
+ g_clear_error(&err);
+ return NULL;
+ }
+
+ Py_RETURN_NONE;
+}
+
+static PyObject *
+add_pkg(_SqliteObject *self, PyObject *args)
+{
+ PyObject *py_pkg;
+ GError *err = NULL;
+
+ if (!PyArg_ParseTuple(args, "O!:add_pkg", &Package_Type, &py_pkg))
+ return NULL;
+ if (check_SqliteStatus(self))
+ return NULL;
+ if (!self->statements)
+ Py_XDECREF(prepare_statements(self, NULL));
+ if (self->type == CR_DB_PRIMARY)
+ cr_db_add_primary_pkg(self->statements, Package_FromPyObject(py_pkg), &err);
+ else if (self->type == CR_DB_FILELISTS)
+ cr_db_add_filelists_pkg(self->statements, Package_FromPyObject(py_pkg), &err);
+ else if (self->type == CR_DB_OTHER)
+ cr_db_add_other_pkg(self->statements, Package_FromPyObject(py_pkg), &err);
+
+ if (err) {
+ PyErr_Format(CrErr_Exception, "Cannot add package: %s", err->message);
+ g_clear_error(&err);
+ return NULL;
+ }
+
+ Py_RETURN_NONE;
+}
+
+static PyObject *
+dbinfo_update(_SqliteObject *self, PyObject *args)
+{
+ char *checksum;
+ GError *err = NULL;
+
+ if (!PyArg_ParseTuple(args, "s:dbinfo_update", &checksum))
+ return NULL;
+ if (check_SqliteStatus(self))
+ return NULL;
+ cr_db_dbinfo_update(self->db, checksum, &err);
+
+ if (err) {
+ PyErr_Format(CrErr_Exception, "Sqlite dbinfo_update error: %s", err->message);
+ g_clear_error(&err);
+ return NULL;
+ }
+
+ Py_RETURN_NONE;
+}
+
+static PyObject *
+close_db(_SqliteObject *self, void *nothing)
+{
+ CR_UNUSED(nothing);
+ if (self->db && !self->closed) {
+ self->closed = 1;
+ cr_db_close(self->db, self->type, NULL);
+ }
+ Py_RETURN_NONE;
+}
+
+static struct PyMethodDef sqlite_methods[] = {
+ {"_prepare_statements", (PyCFunction)prepare_statements, METH_NOARGS, NULL},
+ {"add_pkg", (PyCFunction)add_pkg, METH_VARARGS, NULL},
+ {"dbinfo_update", (PyCFunction)dbinfo_update, METH_VARARGS, NULL},
+ {"close", (PyCFunction)close_db, METH_NOARGS, NULL},
+ {NULL} /* sentinel */
+};
+
+PyTypeObject Sqlite_Type = {
+ PyObject_HEAD_INIT(NULL)
+ 0, /* ob_size */
+ "_librepo.Sqlite", /* tp_name */
+ sizeof(_SqliteObject), /* tp_basicsize */
+ 0, /* tp_itemsize */
+ (destructor) sqlite_dealloc, /* tp_dealloc */
+ 0, /* tp_print */
+ 0, /* tp_getattr */
+ 0, /* tp_setattr */
+ 0, /* tp_compare */
+ (reprfunc) sqlite_repr, /* 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|Py_TPFLAGS_BASETYPE, /* tp_flags */
+ "Sqlite object", /* tp_doc */
+ 0, /* tp_traverse */
+ 0, /* tp_clear */
+ 0, /* tp_richcompare */
+ 0, /* tp_weaklistoffset */
+ PyObject_SelfIter, /* tp_iter */
+ 0, /* tp_iternext */
+ sqlite_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 */
+ (initproc) sqlite_init, /* tp_init */
+ 0, /* tp_alloc */
+ sqlite_new, /* tp_new */
+ 0, /* tp_free */
+ 0, /* tp_is_gc */
+};
--- /dev/null
+/* createrepo_c - Library of routines for manipulation with repodata
+ * Copyright (C) 2013 Tomas Mlcoch
+ *
+ * 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.
+ */
+
+#ifndef CR_SQLITE_PY_H
+#define CR_SQLITE_PY_H
+
+#include "src/createrepo_c.h"
+
+extern PyTypeObject Sqlite_Type;
+
+#define SqliteObject_Check(o) PyObject_TypeCheck(o, &Sqlite_Type)
+
+#endif
--- /dev/null
+/* createrepo_c - Library of routines for manipulation with repodata
+ * Copyright (C) 2012-2013 Tomas Mlcoch
+ *
+ * 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 <assert.h>
+#include "typeconversion.h"
+
+PyObject *
+PyStringOrNone_FromString(const char *str)
+{
+ if (str == NULL)
+ Py_RETURN_NONE;
+ return PyString_FromString(str);
+}
+
+char *
+PyObject_ToStrOrNull(PyObject *pyobj)
+{
+ // String returned by this function shoud not be freed or modified
+ if (PyString_Check(pyobj))
+ return PyString_AsString(pyobj);
+ // TODO: ? Add support for pyobj like: ("xxx",) and ["xxx"]
+ return NULL;
+}
+
+long long
+PyObject_ToLongLongOrZero(PyObject *pyobj)
+{
+ long long num = 0;
+ if (PyLong_Check(pyobj))
+ num = (long long) PyLong_AsLongLong(pyobj);
+ else if (PyInt_Check(pyobj))
+ num = (long long) PyInt_AS_LONG(pyobj);
+ return num;
+}
+
+PyObject *
+PyObject_FromDependency(cr_Dependency *dep)
+{
+ PyObject *tuple;
+
+ if ((tuple = PyTuple_New(6)) == NULL)
+ return NULL;
+
+ PyTuple_SetItem(tuple, 0, PyStringOrNone_FromString(dep->name));
+ PyTuple_SetItem(tuple, 1, PyStringOrNone_FromString(dep->flags));
+ PyTuple_SetItem(tuple, 2, PyStringOrNone_FromString(dep->epoch));
+ PyTuple_SetItem(tuple, 3, PyStringOrNone_FromString(dep->version));
+ PyTuple_SetItem(tuple, 4, PyStringOrNone_FromString(dep->release));
+ PyTuple_SetItem(tuple, 5, PyBool_FromLong((long) dep->pre));
+
+ return tuple;
+}
+
+cr_Dependency *
+PyObject_ToDependency(PyObject *tuple, GStringChunk *chunk)
+{
+ PyObject *pyobj;
+ cr_Dependency *dep = cr_dependency_new();
+
+ pyobj = PyTuple_GetItem(tuple, 0);
+ dep->name = cr_safe_string_chunk_insert(chunk, PyObject_ToStrOrNull(pyobj));
+
+ pyobj = PyTuple_GetItem(tuple, 1);
+ dep->flags = cr_safe_string_chunk_insert(chunk, PyObject_ToStrOrNull(pyobj));
+
+ pyobj = PyTuple_GetItem(tuple, 2);
+ dep->epoch = cr_safe_string_chunk_insert(chunk, PyObject_ToStrOrNull(pyobj));
+
+ pyobj = PyTuple_GetItem(tuple, 3);
+ dep->version = cr_safe_string_chunk_insert(chunk, PyObject_ToStrOrNull(pyobj));
+
+ pyobj = PyTuple_GetItem(tuple, 4);
+ dep->release = cr_safe_string_chunk_insert(chunk, PyObject_ToStrOrNull(pyobj));
+
+ pyobj = PyTuple_GetItem(tuple, 5);
+ dep->pre = (PyObject_IsTrue(pyobj)) ? TRUE : FALSE;
+
+ return dep;
+}
+
+PyObject *
+PyObject_FromPackageFile(cr_PackageFile *file)
+{
+ PyObject *tuple;
+
+ if ((tuple = PyTuple_New(3)) == NULL)
+ return NULL;
+
+ PyTuple_SetItem(tuple, 0, PyStringOrNone_FromString(file->type));
+ PyTuple_SetItem(tuple, 1, PyStringOrNone_FromString(file->path));
+ PyTuple_SetItem(tuple, 2, PyStringOrNone_FromString(file->name));
+
+ return tuple;
+}
+
+cr_PackageFile *
+PyObject_ToPackageFile(PyObject *tuple, GStringChunk *chunk)
+{
+ PyObject *pyobj;
+ cr_PackageFile *file = cr_package_file_new();
+
+ pyobj = PyTuple_GetItem(tuple, 0);
+ file->type = cr_safe_string_chunk_insert(chunk, PyObject_ToStrOrNull(pyobj));
+
+ pyobj = PyTuple_GetItem(tuple, 1);
+ file->path = cr_safe_string_chunk_insert(chunk, PyObject_ToStrOrNull(pyobj));
+
+ pyobj = PyTuple_GetItem(tuple, 2);
+ file->name = cr_safe_string_chunk_insert(chunk, PyObject_ToStrOrNull(pyobj));
+
+ return file;
+}
+
+PyObject *
+PyObject_FromChangelogEntry(cr_ChangelogEntry *log)
+{
+ PyObject *tuple;
+
+ if ((tuple = PyTuple_New(3)) == NULL)
+ return NULL;
+
+ PyTuple_SetItem(tuple, 0, PyStringOrNone_FromString(log->author));
+ PyTuple_SetItem(tuple, 1, PyLong_FromLong((long) log->date));
+ PyTuple_SetItem(tuple, 2, PyStringOrNone_FromString(log->changelog));
+
+ return tuple;
+}
+
+cr_ChangelogEntry *
+PyObject_ToChangelogEntry(PyObject *tuple, GStringChunk *chunk)
+{
+ PyObject *pyobj;
+ cr_ChangelogEntry *log = cr_changelog_entry_new();
+
+ pyobj = PyTuple_GetItem(tuple, 0);
+ log->author = cr_safe_string_chunk_insert(chunk, PyObject_ToStrOrNull(pyobj));
+
+ pyobj = PyTuple_GetItem(tuple, 1);
+ log->date = PyObject_ToLongLongOrZero(pyobj);
+
+ pyobj = PyTuple_GetItem(tuple, 2);
+ log->changelog = cr_safe_string_chunk_insert(chunk, PyObject_ToStrOrNull(pyobj));
+
+ return log;
+}
+
+GSList *
+GSList_FromPyList_Str(PyObject *py_list)
+{
+ GSList *list = NULL;
+
+ if (!py_list)
+ return NULL;
+
+ if (!PyList_Check(py_list))
+ return NULL;
+
+ Py_ssize_t size = PyList_Size(py_list);
+ for (Py_ssize_t x=0; x < size; x++) {
+ PyObject *py_str = PyList_GetItem(py_list, x);
+ assert(py_str != NULL);
+ if (!PyString_Check(py_str))
+ // Hmm, element is not a string, just skip it
+ continue;
+ list = g_slist_prepend(list, PyString_AsString(py_str));
+ }
+
+ return list;
+}
--- /dev/null
+/* createrepo_c - Library of routines for manipulation with repodata
+ * Copyright (C) 2012-2013 Tomas Mlcoch
+ *
+ * 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.
+ */
+
+#ifndef CR_TYPECONVERSION_PY_H
+#define CR_TYPECONVERSION_PY_H
+
+#include <glib.h>
+#include "src/createrepo_c.h"
+
+PyObject *PyStringOrNone_FromString(const char *str);
+char *PyObject_ToStrOrNull(PyObject *pyobj);
+
+PyObject *PyObject_FromDependency(cr_Dependency *dep);
+cr_Dependency *PyObject_ToDependency(PyObject *tuple, GStringChunk *chunk);
+
+PyObject *PyObject_FromPackageFile(cr_PackageFile *file);
+cr_PackageFile *PyObject_ToPackageFile(PyObject *tuple, GStringChunk *chunk);
+
+PyObject *PyObject_FromChangelogEntry(cr_ChangelogEntry *log);
+cr_ChangelogEntry *PyObject_ToChangelogEntry(PyObject *tuple, GStringChunk *chunk);
+
+GSList *GSList_FromPyList_Str(PyObject *py_list);
+
+#endif
--- /dev/null
+/* createrepo_c - Library of routines for manipulation with repodata
+ * Copyright (C) 2013 Tomas Mlcoch
+ *
+ * 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 <assert.h>
+#include <stddef.h>
+
+#include "src/createrepo_c.h"
+
+#include "typeconversion.h"
+#include "package-py.h"
+#include "exception-py.h"
+
+PyObject *
+py_xml_dump_primary(PyObject *self, PyObject *args)
+{
+ CR_UNUSED(self);
+ PyObject *py_pkg, *py_str;
+ char *xml;
+ if (!PyArg_ParseTuple(args, "O!:py_xml_dump_primary", &Package_Type, &py_pkg))
+ return NULL;
+ xml = cr_xml_dump_primary(Package_FromPyObject(py_pkg));
+ py_str = PyStringOrNone_FromString(xml);
+ free(xml);
+ return py_str;
+}
+
+PyObject *
+py_xml_dump_filelists(PyObject *self, PyObject *args)
+{
+ CR_UNUSED(self);
+ PyObject *py_pkg, *py_str;
+ char *xml;
+ if (!PyArg_ParseTuple(args, "O!:py_xml_dump_filelists", &Package_Type, &py_pkg))
+ return NULL;
+ xml = cr_xml_dump_filelists(Package_FromPyObject(py_pkg));
+ py_str = PyStringOrNone_FromString(xml);
+ free(xml);
+ return py_str;
+}
+
+PyObject *
+py_xml_dump_other(PyObject *self, PyObject *args)
+{
+ CR_UNUSED(self);
+ PyObject *py_pkg, *py_str;
+ char *xml;
+ if (!PyArg_ParseTuple(args, "O!:py_xml_dump_other", &Package_Type, &py_pkg))
+ return NULL;
+ xml = cr_xml_dump_other(Package_FromPyObject(py_pkg));
+ py_str = PyStringOrNone_FromString(xml);
+ free(xml);
+ return py_str;
+}
+
+PyObject *
+py_xml_dump(PyObject *self, PyObject *args)
+{
+ CR_UNUSED(self);
+ PyObject *py_pkg, *tuple;
+ struct cr_XmlStruct xml_res;
+
+ if (!PyArg_ParseTuple(args, "O!:py_xml_dump", &Package_Type, &py_pkg))
+ return NULL;
+
+ if ((tuple = PyTuple_New(3)) == NULL)
+ return NULL;
+
+ xml_res = cr_xml_dump(Package_FromPyObject(py_pkg));
+
+ PyTuple_SetItem(tuple, 0, PyStringOrNone_FromString(xml_res.primary));
+ PyTuple_SetItem(tuple, 1, PyStringOrNone_FromString(xml_res.filelists));
+ PyTuple_SetItem(tuple, 2, PyStringOrNone_FromString(xml_res.other));
+
+ free(xml_res.primary);
+ free(xml_res.filelists);
+ free(xml_res.other);
+
+ return tuple;
+}
--- /dev/null
+/* createrepo_c - Library of routines for manipulation with repodata
+ * Copyright (C) 2013 Tomas Mlcoch
+ *
+ * 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.
+ */
+
+#ifndef CR_XML_DUMP_PY_H
+#define CR_XML_DUMP_PY_H
+
+#include "src/createrepo_c.h"
+
+PyObject *py_xml_dump_primary(PyObject *self, PyObject *args);
+PyObject *py_xml_dump_filelists(PyObject *self, PyObject *args);
+PyObject *py_xml_dump_other(PyObject *self, PyObject *args);
+PyObject *py_xml_dump(PyObject *self, PyObject *args);
+
+#endif
ADD_EXECUTABLE(testsqlite testsqlite.c)
TARGET_LINK_LIBRARIES(testsqlite libcreaterepo_c ${GLIB2_LIBRARIES})
ADD_DEPENDENCIES(tests testsqlite)
+
+#ADD_TEST(test_main gtester "${CMAKE_CURRENT_SOURCE_DIR}/test_data")
+CONFIGURE_FILE("run_gtester.sh.in" "run_gtester.sh")
+ADD_TEST(test_main run_gtester.sh)
+
+ADD_SUBDIRECTORY(python)
--- /dev/null
+ADD_SUBDIRECTORY (tests)
--- /dev/null
+CONFIGURE_FILE("run_nosetests.sh.in" "run_nosetests.sh")
+ADD_TEST(test_python run_nosetests.sh -s ${CMAKE_CURRENT_SOURCE_DIR})
--- /dev/null
+import os.path
+
+TEST_DATA_PATH = os.path.normpath(os.path.join(__file__, "../../../test_data"))
+
+PACKAGES_PATH = os.path.join(TEST_DATA_PATH, "packages")
+REPOS_PATH = TEST_DATA_PATH
+COMPRESSED_FILES_PATH = os.path.join(TEST_DATA_PATH, "compressed_files")
+TEST_FILES_PATH = os.path.join(TEST_DATA_PATH, "test_files")
+
+# Test packages
+PKG_ARCHER = "Archer-3.4.5-6.x86_64.rpm"
+PKG_ARCHER_PATH = os.path.join(PACKAGES_PATH, PKG_ARCHER)
+
+PKG_BALICEK_ISO88591 = "balicek-iso88591-1.1.1-1.x86_64.rpm"
+PKG_BALICEK_ISO88591_PATH = os.path.join(PACKAGES_PATH, PKG_BALICEK_ISO88591)
+
+PKG_BALICEK_ISO88592 = "balicek-iso88592-1.1.1-1.x86_64.rpm"
+PKG_BALICEK_ISO88592_PATH = os.path.join(PACKAGES_PATH, PKG_BALICEK_ISO88592)
+
+PKG_BALICEK_UTF8 = "balicek-utf8-1.1.1-1.x86_64.rpm"
+PKG_BALICEK_UTF8_PATH = os.path.join(PACKAGES_PATH, PKG_BALICEK_UTF8)
+
+PKG_EMPTY = "empty-0-0.x86_64.rpm"
+PKG_EMPTY_PATH = os.path.join(PACKAGES_PATH, PKG_EMPTY)
+
+PKG_EMPTY_SRC = "empty-0-0.x86_64.rpm"
+PKG_EMPTY_SRC_PATH = os.path.join(PACKAGES_PATH, PKG_EMPTY_SRC)
+
+PKG_FAKE_BASH = "fake_bash-1.1.1-1.x86_64.rpm"
+PKG_FAKE_BASH_PATH = os.path.join(PACKAGES_PATH, PKG_FAKE_BASH)
+
+PKG_SUPER_KERNEL = "super_kernel-6.0.1-2.x86_64.rpm"
+PKG_SUPER_KERNEL_PATH = os.path.join(PACKAGES_PATH, PKG_SUPER_KERNEL)
+
+# Test repositories
+REPO_00_PATH = os.path.join(REPOS_PATH, "repo_00")
+REPO_00_PRIXML = os.path.join(REPO_00_PATH, "repodata/",
+ "dabe2ce5481d23de1f4f52bdcfee0f9af98316c9e0de2ce8123adeefa0dd08b9-primary.xml.gz")
+
+REPO_01_PATH = os.path.join(REPOS_PATH, "repo_01")
+REPO_02_PATH = os.path.join(REPOS_PATH, "repo_02")
+
+# Other test files
+
+FILE_BINARY = "binary_file"
+FILE_BINARY_PATH = os.path.join(TEST_FILES_PATH, FILE_BINARY)
+FILE_TEXT = "text_file"
+FILE_TEXT = os.path.join(TEST_FILES_PATH, FILE_TEXT)
+FILE_EMPTY = "empty_file"
+FILE_EMPTY = os.path.join(TEST_FILES_PATH, FILE_EMPTY)
+
--- /dev/null
+PYTHONPATH=${CMAKE_BINARY_DIR}/src/python/ nosetests -s ${CMAKE_CURRENT_SOURCE_DIR}/
--- /dev/null
+import unittest
+import createrepo_c as cr
+
+from fixtures import *
+
+class TestCaseLoadMetadata(unittest.TestCase):
+ def test_load_metadata__repo00(self):
+ md = cr.Metadata()
+ md.locate_and_load_xml(REPO_00_PATH)
+ self.assertTrue(md)
+
+ self.assertEqual(md.key, cr.HT_KEY_DEFAULT)
+
+ self.assertEqual(md.len(), 0)
+ self.assertEqual(md.keys(), [])
+ self.assertFalse(md.has_key("foo"))
+ self.assertFalse(md.has_key(""))
+ self.assertFalse(md.remove("foo"))
+ self.assertFalse(md.get("xxx"))
+
+ def test_load_metadata_repo01(self):
+ md = cr.Metadata()
+ md.locate_and_load_xml(REPO_01_PATH)
+ self.assertTrue(md)
+
+ self.assertEqual(md.key, cr.HT_KEY_DEFAULT)
+
+ self.assertEqual(md.len(), 1)
+ self.assertEqual(md.keys(), ['152824bff2aa6d54f429d43e87a3ff3a0286505c6d93ec87692b5e3a9e3b97bf'])
+ self.assertFalse(md.has_key("foo"))
+ self.assertFalse(md.has_key(""))
+ self.assertFalse(md.remove("foo"))
+
+ pkg = md.get('152824bff2aa6d54f429d43e87a3ff3a0286505c6d93ec87692b5e3a9e3b97bf')
+ self.assertTrue(pkg)
+
+ self.assertEqual(pkg.name, "super_kernel")
+
+ def test_load_metadata_repo02(self):
+ md = cr.Metadata()
+ md.locate_and_load_xml(REPO_02_PATH)
+ self.assertTrue(md)
+
+ self.assertEqual(md.key, cr.HT_KEY_DEFAULT)
+
+ self.assertEqual(md.len(), 2)
+ self.assertEqual(md.keys(),
+ ['6d43a638af70ef899933b1fd86a866f18f65b0e0e17dcbf2e42bfd0cdd7c63c3',
+ '90f61e546938a11449b710160ad294618a5bd3062e46f8cf851fd0088af184b7'])
+ self.assertFalse(md.has_key("foo"))
+ self.assertFalse(md.has_key(""))
+ self.assertFalse(md.remove("foo"))
+
+ pkg = md.get('152824bff2aa6d54f429d43e87a3ff3a0286505c6d93ec87692b5e3a9e3b97bf')
+ self.assertEqual(pkg, None)
+
+ pkg = md.get('90f61e546938a11449b710160ad294618a5bd3062e46f8cf851fd0088af184b7')
+ self.assertEqual(pkg.name, "fake_bash")
+
+ def test_load_metadata_repo02_destructor(self):
+ md = cr.Metadata()
+ md.locate_and_load_xml(REPO_02_PATH)
+ pkg = md.get('90f61e546938a11449b710160ad294618a5bd3062e46f8cf851fd0088af184b7')
+ del(md) # in fact, md shoudnot be destroyed yet, because it is
+ # referenced from pkg!
+ self.assertEqual(pkg.name, "fake_bash")
--- /dev/null
+import unittest
+import createrepo_c as cr
+
+from fixtures import *
+
+class TestCaseMetadataLocation(unittest.TestCase):
+ def test_metadatalocation(self):
+ ml = cr.MetadataLocation(REPO_00_PATH, 1)
+ self.assertTrue(ml)
+ self.assertTrue(ml["primary"].endswith("/repodata/dabe2ce5481d23de1f4f52bdcfee0f9af98316c9e0de2ce8123adeefa0dd08b9-primary.xml.gz"))
+ self.assertTrue(ml["filelists"].endswith("/repodata/401dc19bda88c82c403423fb835844d64345f7e95f5b9835888189c03834cc93-filelists.xml.gz"))
+ self.assertTrue(ml["other"].endswith("/repodata/6bf9672d0862e8ef8b8ff05a2fd0208a922b1f5978e6589d87944c88259cb670-other.xml.gz"))
+ self.assertTrue(ml["primary_db"] is None)
+ self.assertTrue(ml["filelists_db"] is None)
+ self.assertTrue(ml["other_db"] is None)
+ self.assertTrue(ml["group"] is None)
+ self.assertTrue(ml["group_gz"] is None)
+ self.assertTrue(ml["updateinfo"] is None)
+ self.assertTrue(ml["foobarxyz"] is None)
--- /dev/null
+import unittest
+import createrepo_c as cr
+
+from fixtures import *
+
+class TestCasePackage(unittest.TestCase):
+ def test_package_empty(self):
+ pkg = cr.package_from_rpm(PKG_EMPTY_PATH)
+ self.assertTrue(pkg)
+ self.assertEqual(pkg.pkgId, "91afc5e3a124eedfc5bc52737940950b42a37c611dccecad4692a4eb317f9810")
+ self.assertEqual(pkg.name, "empty")
+ self.assertEqual(pkg.arch, "x86_64")
+ self.assertEqual(pkg.version, "0")
+ self.assertEqual(pkg.epoch, "0")
+ self.assertEqual(pkg.release, "0")
+ self.assertEqual(pkg.summary, '""')
+ self.assertEqual(pkg.description, "")
+ self.assertEqual(pkg.url, None)
+ self.assertEqual(pkg.time_file, 1340709886)
+ self.assertEqual(pkg.time_build, 1340696582)
+ self.assertEqual(pkg.rpm_license, "LGPL")
+ self.assertEqual(pkg.rpm_vendor, None)
+ self.assertEqual(pkg.rpm_group, "Unspecified")
+ self.assertEqual(pkg.rpm_buildhost, "localhost.localdomain")
+ self.assertEqual(pkg.rpm_sourcerpm, "empty-0-0.src.rpm")
+ self.assertEqual(pkg.rpm_header_start, 280)
+ self.assertEqual(pkg.rpm_header_end, 1285)
+ self.assertEqual(pkg.rpm_packager, None)
+ self.assertEqual(pkg.size_package, 1401)
+ self.assertEqual(pkg.size_installed, 0)
+ self.assertEqual(pkg.size_archive, 124)
+ self.assertEqual(pkg.location_href, None)
+ self.assertEqual(pkg.location_base, None)
+ self.assertEqual(pkg.checksum_type, "sha256")
+ self.assertEqual(pkg.requires, [])
+ self.assertEqual(pkg.provides, [
+ ('empty', 'EQ', '0', '0', '0', False),
+ ('empty(x86-64)', 'EQ', '0', '0', '0', False)
+ ])
+ self.assertEqual(pkg.conflicts, [])
+ self.assertEqual(pkg.obsoletes, [])
+ self.assertEqual(pkg.files, [])
+ self.assertEqual(pkg.changelogs, [])
+
+ self.assertEqual(pkg.nvra(), "empty-0-0.x86_64")
+ self.assertEqual(pkg.nevra(), "empty-0:0-0.x86_64")
+
+ def test_package_archer(self):
+ pkg = cr.package_from_rpm(PKG_ARCHER_PATH)
+ self.assertTrue(pkg)
+ self.assertEqual(pkg.pkgId, "4e0b775220c67f0f2c1fd2177e626b9c863a098130224ff09778ede25cea9a9e")
+ self.assertEqual(pkg.name, "Archer")
+ self.assertEqual(pkg.arch, "x86_64")
+ self.assertEqual(pkg.version, "3.4.5")
+ self.assertEqual(pkg.epoch, "2")
+ self.assertEqual(pkg.release, "6")
+ self.assertEqual(pkg.summary, "Complex package.")
+ self.assertEqual(pkg.description, "Archer package")
+ self.assertEqual(pkg.url, "http://soo_complex_package.eu/")
+ self.assertEqual(pkg.time_file, 1365416502)
+ self.assertEqual(pkg.time_build, 1365416480)
+ self.assertEqual(pkg.rpm_license, "GPL")
+ self.assertEqual(pkg.rpm_vendor, "ISIS")
+ self.assertEqual(pkg.rpm_group, "Development/Tools")
+ self.assertEqual(pkg.rpm_buildhost, "localhost.localdomain")
+ self.assertEqual(pkg.rpm_sourcerpm, "Archer-3.4.5-6.src.rpm")
+ self.assertEqual(pkg.rpm_header_start, 280)
+ self.assertEqual(pkg.rpm_header_end, 2865)
+ self.assertEqual(pkg.rpm_packager, "Sterling Archer")
+ self.assertEqual(pkg.size_package, 3101)
+ self.assertEqual(pkg.size_installed, 0)
+ self.assertEqual(pkg.size_archive, 544)
+ self.assertEqual(pkg.location_href, None)
+ self.assertEqual(pkg.location_base, None)
+ self.assertEqual(pkg.checksum_type, "sha256")
+ self.assertEqual(pkg.requires, [
+ ('fooa', 'LE', '0', '2', None, False),
+ ('foob', 'GE', '0', '1.0.0', '1', False),
+ ('fooc', 'EQ', '0', '3', None, False),
+ ('food', 'LT', '0', '4', None, False),
+ ('fooe', 'GT', '0', '5', None, False),
+ ('foof', 'EQ', '0', '6', None, True)
+ ])
+ self.assertEqual(pkg.provides, [
+ ('bara', 'LE', '0', '22', None, False),
+ ('barb', 'GE', '0', '11.22.33', '44', False),
+ ('barc', 'EQ', '0', '33', None, False),
+ ('bard', 'LT', '0', '44', None, False),
+ ('bare', 'GT', '0', '55', None, False),
+ ('Archer', 'EQ', '2', '3.4.5', '6', False),
+ ('Archer(x86-64)', 'EQ', '2', '3.4.5', '6', False)
+ ])
+ self.assertEqual(pkg.conflicts, [
+ ('bba', 'LE', '0', '2222', None, False),
+ ('bbb', 'GE', '0', '1111.2222.3333', '4444', False),
+ ('bbc', 'EQ', '0', '3333', None, False),
+ ('bbd', 'LT', '0', '4444', None, False),
+ ('bbe', 'GT', '0', '5555', None, False)
+ ])
+ self.assertEqual(pkg.obsoletes, [
+ ('aaa', 'LE', '0', '222', None, False),
+ ('aab', 'GE', '0', '111.2.3', '4', False),
+ ('aac', 'EQ', '0', '333', None, False),
+ ('aad', 'LT', '0', '444', None, False),
+ ('aae', 'GT', '0', '555', None, False)
+ ])
+ self.assertEqual(pkg.files, [
+ ('', '/usr/bin/', 'complex_a'),
+ ('dir', '/usr/share/doc/', 'Archer-3.4.5'),
+ ('', '/usr/share/doc/Archer-3.4.5/', 'README')
+ ])
+ self.assertEqual(pkg.changelogs, [
+ ('Tomas Mlcoch <tmlcoch@redhat.com> - 1.1.1-1', 1334664000L,
+ '- First changelog.'),
+ ('Tomas Mlcoch <tmlcoch@redhat.com> - 2.2.2-2', 1334750400L,
+ '- That was totally ninja!'),
+ ('Tomas Mlcoch <tmlcoch@redhat.com> - 3.3.3-3', 1365422400L,
+ '- 3. changelog.')
+ ])
+
+ self.assertEqual(pkg.nvra(), "Archer-3.4.5-6.x86_64")
+ self.assertEqual(pkg.nevra(), "Archer-2:3.4.5-6.x86_64")
+
+ def test_package_setters(self):
+ pkg = cr.Package()
+ self.assertTrue(pkg)
+
+ pkg.pkgId = "foo"
+ self.assertEqual(pkg.pkgId, "foo")
+ pkg.name = "bar"
+ self.assertEqual(pkg.name, "bar")
+ pkg.arch = "quantum"
+ self.assertEqual(pkg.arch, "quantum")
+ pkg.version = "11"
+ self.assertEqual(pkg.version, "11")
+ pkg.epoch = "22"
+ self.assertEqual(pkg.epoch, "22")
+ pkg.release = "33"
+ self.assertEqual(pkg.release, "33")
+ pkg.summary = "sum"
+ self.assertEqual(pkg.summary, "sum")
+ pkg.description = "desc"
+ self.assertEqual(pkg.description, "desc")
+ pkg.url = "http://foo"
+ self.assertEqual(pkg.url, "http://foo")
+ pkg.time_file = 111
+ self.assertEqual(pkg.time_file, 111)
+ pkg.time_build = 112
+ self.assertEqual(pkg.time_build, 112)
+ pkg.rpm_license = "EULA"
+ self.assertEqual(pkg.rpm_license, "EULA")
+ pkg.rpm_vendor = "Me"
+ self.assertEqual(pkg.rpm_vendor, "Me")
+ pkg.rpm_group = "a-team"
+ self.assertEqual(pkg.rpm_group, "a-team")
+ pkg.rpm_buildhost = "hal3000.space"
+ self.assertEqual(pkg.rpm_buildhost, "hal3000.space")
+ pkg.rpm_sourcerpm = "source.src.rpm"
+ self.assertEqual(pkg.rpm_sourcerpm, "source.src.rpm")
+ pkg.rpm_header_start = 1
+ self.assertEqual(pkg.rpm_header_start, 1)
+ pkg.rpm_header_end = 2
+ self.assertEqual(pkg.rpm_header_end, 2)
+ pkg.rpm_packager = "Arnold Rimmer"
+ self.assertEqual(pkg.rpm_packager, "Arnold Rimmer")
+ pkg.size_package = 33
+ self.assertEqual(pkg.size_package, 33)
+ pkg.size_installed = 44
+ self.assertEqual(pkg.size_installed, 44)
+ pkg.size_archive = 55
+ self.assertEqual(pkg.size_archive, 55)
+ pkg.location_href = "package/foo.rpm"
+ self.assertEqual(pkg.location_href, "package/foo.rpm")
+ pkg.location_base = "file://dir/"
+ self.assertEqual(pkg.location_base, "file://dir/")
+ pkg.checksum_type = "crc"
+ self.assertEqual(pkg.checksum_type, "crc")
+
+ pkg.requires = [('bar', 'GE', '1', '3.2.1', None, True)]
+ self.assertEqual(pkg.requires, [('bar', 'GE', '1', '3.2.1', None, True)])
+ pkg.provides = [('foo', None, None, None, None, False)]
+ self.assertEqual(pkg.provides, [('foo', None, None, None, None, False)])
+ pkg.conflicts = [('foobar', 'LT', '0', '1.0.0', None, False)]
+ self.assertEqual(pkg.conflicts, [('foobar', 'LT', '0', '1.0.0', None, False)])
+ pkg.obsoletes = [('foobar', 'GE', '0', '1.1.0', None, False)]
+ self.assertEqual(pkg.obsoletes, [('foobar', 'GE', '0', '1.1.0', None, False)])
+ pkg.files = [(None, '/foo/', 'bar')]
+ self.assertEqual(pkg.files, [(None, '/foo/', 'bar')])
+ pkg.changelogs = [('me', 123456L, 'first commit')]
+ self.assertEqual(pkg.changelogs, [('me', 123456L, 'first commit')])
+
+ self.assertEqual(pkg.nvra(), "bar-11-33.quantum")
+ self.assertEqual(pkg.nevra(), "bar-22:11-33.quantum")
+
+ def test_package_copying(self):
+ import copy
+
+ pkg_a = cr.Package()
+ pkg_a.name = "FooPackage"
+ pkg_b = pkg_a
+ self.assertEqual(id(pkg_a), id(pkg_b))
+ pkg_c = copy.copy(pkg_a)
+ self.assertFalse(id(pkg_a) == id(pkg_c))
+ pkg_d = copy.deepcopy(pkg_a)
+ self.assertFalse(id(pkg_a) == id(pkg_d))
+ self.assertFalse(id(pkg_c) == id(pkg_d))
+
+ # Next lines shoud not fail (if copy really works)
+ del(pkg_a)
+ del(pkg_b)
+ self.assertEqual(pkg_c.name, "FooPackage")
+ del(pkg_c)
+ self.assertEqual(pkg_d.name, "FooPackage")
+ del(pkg_d)
+
--- /dev/null
+import unittest
+import createrepo_c as cr
+
+from fixtures import *
+
+class TestCaseParsepkg(unittest.TestCase):
+ def test_package_from_rpm(self):
+ pkg = cr.package_from_rpm(PKG_ARCHER_PATH)
+ self.assertTrue(pkg)
+ self.assertEqual(pkg.name, "Archer")
+
+ pkg = cr.package_from_rpm(PKG_BALICEK_ISO88591_PATH)
+ self.assertTrue(pkg)
+ self.assertEqual(pkg.name, "balicek-iso88591")
+
+ pkg = cr.package_from_rpm(PKG_BALICEK_ISO88592_PATH)
+ self.assertTrue(pkg)
+ self.assertEqual(pkg.name, "balicek-iso88592")
+
+ pkg = cr.package_from_rpm(PKG_BALICEK_UTF8_PATH)
+ self.assertTrue(pkg)
+ self.assertEqual(pkg.name, "balicek-utf8")
+
+ pkg = cr.package_from_rpm(PKG_EMPTY_PATH)
+ self.assertTrue(pkg)
+ self.assertEqual(pkg.name, "empty")
+
+ pkg = cr.package_from_rpm(PKG_EMPTY_SRC_PATH)
+ self.assertTrue(pkg)
+ self.assertEqual(pkg.name, "empty")
+
+ pkg = cr.package_from_rpm(PKG_FAKE_BASH_PATH)
+ self.assertTrue(pkg)
+ self.assertEqual(pkg.name, "fake_bash")
+
+ pkg = cr.package_from_rpm(PKG_SUPER_KERNEL_PATH)
+ self.assertTrue(pkg)
+ self.assertEqual(pkg.name, "super_kernel")
+
+ # Test error cases
+
+ # Rpm doesn't exists
+ self.assertRaises(IOError, cr.package_from_rpm, "this_foo_pkg_shoud_not_exists.rpm")
+
+ # Path is a directory, not a file
+ self.assertRaises(IOError, cr.package_from_rpm, "./")
+
+ # File is not a rpm
+ self.assertRaises(cr.CreaterepoCException, cr.package_from_rpm, FILE_BINARY_PATH)
+
+ def test_xml_from_rpm(self):
+ xml = cr.xml_from_rpm(PKG_ARCHER_PATH)
+ self.assertTrue(xml)
+ self.assertTrue(len(xml) == 3)
+ self.assertTrue("<name>Archer</name>" in xml[0])
+ self.assertTrue('<package pkgid="4e0b775220c67f0f2c1fd2177e626b9c863a098130224ff09778ede25cea9a9e" name="Archer" arch="x86_64">' in xml[1])
+ self.assertTrue('<package pkgid="4e0b775220c67f0f2c1fd2177e626b9c863a098130224ff09778ede25cea9a9e" name="Archer" arch="x86_64">' in xml[2])
+
+ # Test error cases
+
+ # Rpm doesn't exists
+ self.assertRaises(IOError, cr.xml_from_rpm, "this_foo_pkg_shoud_not_exists.rpm")
+
+ # Path is a directory, not a file
+ self.assertRaises(IOError, cr.xml_from_rpm, "./")
+
+ # File is not a rpm
+ self.assertRaises(cr.CreaterepoCException, cr.xml_from_rpm, FILE_BINARY_PATH)
--- /dev/null
+import re
+import unittest
+import shutil
+import tempfile
+import os.path
+import createrepo_c as cr
+
+from fixtures import *
+
+class TestCaseRepomd(unittest.TestCase):
+
+ def setUp(self):
+ self.tmpdir = tempfile.mkdtemp(prefix="createrepo_ctest-")
+ self.FN_00 = "primary.xml.gz"
+ self.FN_01 = "primary.xml"
+ self.path00 = os.path.join(self.tmpdir, self.FN_00)
+ self.path01 = os.path.join(self.tmpdir, self.FN_01)
+
+ def tearDown(self):
+ shutil.rmtree(self.tmpdir)
+
+ def xxx_repomdrecord_fill(self):
+ self.assertRaises(TypeError, cr.RepomdRecord)
+ self.assertRaises(TypeError, cr.RepomdRecord, None)
+
+ shutil.copyfile(REPO_00_PRIXML, self.path00)
+ self.assertTrue(os.path.exists(self.path00))
+
+ rec = cr.RepomdRecord(self.path00)
+ self.assertTrue(rec)
+ rec.fill(cr.SHA256)
+ rec.rename_file()
+
+ # Filename shoud contain a (valid) checksum
+ self.assertEqual(os.listdir(self.tmpdir),
+ ['dabe2ce5481d23de1f4f52bdcfee0f9af98316c9e0de2ce8123adeefa0dd08b9-primary.xml.gz'])
+
+ def test_repomd(self):
+ shutil.copyfile(REPO_00_PRIXML, self.path00)
+ self.assertTrue(os.path.exists(self.path00))
+
+ md = cr.Repomd()
+ self.assertTrue(md)
+
+ xml = md.xml_dump()
+ # Revision shoud be current Unix time
+ self.assertTrue(re.search(r"<revision>[0-9]+</revision>", xml))
+
+ md.set_revision("foobar")
+
+ md.add_distro_tag("tag1")
+ md.add_distro_tag("tag2", "cpeid1")
+ md.add_distro_tag("tag3", cpeid="cpeid2")
+
+ md.add_repo_tag("repotag")
+
+ md.add_content_tag("contenttag")
+
+ xml = md.xml_dump()
+ self.assertEqual(xml,
+"""<?xml version="1.0" encoding="UTF-8"?>
+<repomd xmlns="http://linux.duke.edu/metadata/repo" xmlns:rpm="http://linux.duke.edu/metadata/rpm">
+ <revision>foobar</revision>
+ <tags>
+ <content>contenttag</content>
+ <repo>repotag</repo>
+ <distro cpeid="cpeid2">tag3</distro>
+ <distro cpeid="cpeid1">tag2</distro>
+ <distro>tag1</distro>
+ </tags>
+</repomd>
+""")
+
+ rec = cr.RepomdRecord(self.path00)
+ rec.fill(cr.SHA256)
+ rec.timestamp = 1
+ md.set_record(rec, "primary")
+
+ xml = md.xml_dump()
+ self.assertEqual(xml,
+"""<?xml version="1.0" encoding="UTF-8"?>
+<repomd xmlns="http://linux.duke.edu/metadata/repo" xmlns:rpm="http://linux.duke.edu/metadata/rpm">
+ <revision>foobar</revision>
+ <tags>
+ <content>contenttag</content>
+ <repo>repotag</repo>
+ <distro cpeid="cpeid2">tag3</distro>
+ <distro cpeid="cpeid1">tag2</distro>
+ <distro>tag1</distro>
+ </tags>
+ <data type="primary">
+ <checksum type="sha256">dabe2ce5481d23de1f4f52bdcfee0f9af98316c9e0de2ce8123adeefa0dd08b9</checksum>
+ <open-checksum type="sha256">e1e2ffd2fb1ee76f87b70750d00ca5677a252b397ab6c2389137a0c33e7b359f</open-checksum>
+ <location href="repodata/primary.xml.gz"/>
+ <timestamp>1</timestamp>
+ <size>134</size>
+ <open-size>167</open-size>
+ </data>
+</repomd>
+""")
--- /dev/null
+import unittest
+import shutil
+import tempfile
+import os.path
+import createrepo_c as cr
+
+from fixtures import *
+
+class TestCaseRepomdRecord(unittest.TestCase):
+
+ # TODO:
+ # - Test rename_file() for files with checksum
+
+ def setUp(self):
+ self.tmpdir = tempfile.mkdtemp(prefix="createrepo_ctest-")
+ self.FN_00 = "primary.xml.gz"
+ self.FN_01 = "primary.xml"
+ self.path00 = os.path.join(self.tmpdir, self.FN_00)
+ self.path01 = os.path.join(self.tmpdir, self.FN_01)
+
+ def tearDown(self):
+ shutil.rmtree(self.tmpdir)
+
+ def test_repomdrecord_fill(self):
+ self.assertRaises(TypeError, cr.RepomdRecord)
+ self.assertRaises(TypeError, cr.RepomdRecord, None)
+
+ shutil.copyfile(REPO_00_PRIXML, self.path00)
+ self.assertTrue(os.path.exists(self.path00))
+
+ rec = cr.RepomdRecord(self.path00)
+ self.assertTrue(rec)
+
+ self.assertEqual(rec.location_real, self.path00)
+ self.assertEqual(rec.location_href, "repodata/primary.xml.gz")
+ self.assertEqual(rec.checksum, None)
+ self.assertEqual(rec.checksum_type, None)
+ self.assertEqual(rec.checksum_open, None)
+ self.assertEqual(rec.checksum_open_type, None)
+ self.assertEqual(rec.timestamp, 0)
+ self.assertEqual(rec.size, 0)
+ self.assertEqual(rec.size_open, 0)
+ self.assertEqual(rec.db_ver, 0)
+
+ rec.fill(cr.SHA256)
+
+ self.assertEqual(rec.location_real, self.path00)
+ self.assertEqual(rec.location_href, "repodata/primary.xml.gz")
+ self.assertEqual(rec.checksum, "dabe2ce5481d23de1f4f52bdcfee0f9af98316c9e0de2ce8123adeefa0dd08b9")
+ self.assertEqual(rec.checksum_type, "sha256")
+ self.assertEqual(rec.checksum_open, "e1e2ffd2fb1ee76f87b70750d00ca5677a252b397ab6c2389137a0c33e7b359f")
+ self.assertEqual(rec.checksum_open_type, "sha256")
+ self.assertTrue(rec.timestamp > 0)
+ self.assertEqual(rec.size, 134)
+ self.assertEqual(rec.size_open, 167)
+ self.assertEqual(rec.db_ver, 10)
+
+ rec.rename_file()
+
+ # Filename shoud contain a (valid) checksum
+ self.assertEqual(os.listdir(self.tmpdir),
+ ['dabe2ce5481d23de1f4f52bdcfee0f9af98316c9e0de2ce8123adeefa0dd08b9-primary.xml.gz'])
+
+ def test_repomdrecord_setters(self):
+ self.assertRaises(TypeError, cr.RepomdRecord)
+ self.assertRaises(TypeError, cr.RepomdRecord, None)
+
+ shutil.copyfile(REPO_00_PRIXML, self.path00)
+ self.assertTrue(os.path.exists(self.path00))
+
+ rec = cr.RepomdRecord(self.path00)
+ self.assertTrue(rec)
+
+ rec.fill(cr.SHA256)
+
+ self.assertEqual(rec.location_real, self.path00)
+ self.assertEqual(rec.location_href, "repodata/primary.xml.gz")
+ self.assertEqual(rec.checksum, "dabe2ce5481d23de1f4f52bdcfee0f9af98316c9e0de2ce8123adeefa0dd08b9")
+ self.assertEqual(rec.checksum_type, "sha256")
+ self.assertEqual(rec.checksum_open, "e1e2ffd2fb1ee76f87b70750d00ca5677a252b397ab6c2389137a0c33e7b359f")
+ self.assertEqual(rec.checksum_open_type, "sha256")
+ self.assertTrue(rec.timestamp > 0)
+ self.assertEqual(rec.size, 134)
+ self.assertEqual(rec.size_open, 167)
+ self.assertEqual(rec.db_ver, 10)
+
+ # Set new values
+
+ rec.location_href = "repodata/foo.xml.gz"
+ rec.checksum = "foobar11"
+ rec.checksum_type = "foo1"
+ rec.checksum_open = "foobar22"
+ rec.checksum_open_type = "foo2"
+ rec.timestamp = 123
+ rec.size = 456
+ rec.size_open = 789
+ rec.db_ver = 11
+
+ # Check
+
+ self.assertEqual(rec.location_real, self.path00)
+ self.assertEqual(rec.location_href, "repodata/foo.xml.gz")
+ self.assertEqual(rec.checksum, "foobar11")
+ self.assertEqual(rec.checksum_type, "foo1")
+ self.assertEqual(rec.checksum_open, "foobar22")
+ self.assertEqual(rec.checksum_open_type, "foo2")
+ self.assertEqual(rec.timestamp, 123)
+ self.assertEqual(rec.size, 456)
+ self.assertEqual(rec.size_open, 789)
+ self.assertEqual(rec.db_ver, 11)
+
+ def test_repomdrecord_compress_and_fill(self):
+ self.assertRaises(TypeError, cr.RepomdRecord)
+ self.assertRaises(TypeError, cr.RepomdRecord, None)
+
+ open(self.path01, "w").write("foobar\ncontent\nhh\n")
+ self.assertTrue(os.path.exists(self.path01))
+
+ rec = cr.RepomdRecord(self.path01)
+ self.assertTrue(rec)
+ rec_compressed = rec.compress_and_fill(cr.SHA256, cr.GZ_COMPRESSION)
+
+ # A new compressed file shoud be created
+ self.assertEqual(sorted(os.listdir(self.tmpdir)),
+ sorted(['primary.xml.gz', 'primary.xml']))
+
+ rec.rename_file()
+ rec_compressed.rename_file()
+
+ # Filename shoud contain a (valid) checksum
+ self.assertEqual(sorted(os.listdir(self.tmpdir)),
+ sorted(['10091f8e2e235ae875cb18c91c443891c7f1a599d41f44d518e8af759a6c8109-primary.xml.gz',
+ 'b33fc63178d852333a826385bc15d9b72cb6658be7fb927ec28c4e40b5d426fb-primary.xml']))
+
--- /dev/null
+import unittest
+import shutil
+import tempfile
+import os.path
+import sqlite3
+import createrepo_c as cr
+
+from fixtures import *
+
+class TestCaseSqlite(unittest.TestCase):
+
+ def setUp(self):
+ self.tmpdir = tempfile.mkdtemp(prefix="createrepo_ctest-")
+
+ def tearDown(self):
+ shutil.rmtree(self.tmpdir)
+
+ def test_sqlite_basic_operations(self):
+ db_pri = cr.Sqlite(self.tmpdir+"/primary.db", cr.DB_PRIMARY)
+ self.assertTrue(db_pri)
+ self.assertTrue(os.path.isfile(self.tmpdir+"/primary.db"))
+
+ db_pri = cr.PrimarySqlite(self.tmpdir+"/primary2.db")
+ self.assertTrue(db_pri)
+ self.assertTrue(os.path.isfile(self.tmpdir+"/primary2.db"))
+
+ db_fil = cr.Sqlite(self.tmpdir+"/filelists.db", cr.DB_FILELISTS)
+ self.assertTrue(db_fil)
+ self.assertTrue(os.path.isfile(self.tmpdir+"/filelists.db"))
+
+ db_fil = cr.FilelistsSqlite(self.tmpdir+"/filelists2.db")
+ self.assertTrue(db_fil)
+ self.assertTrue(os.path.isfile(self.tmpdir+"/filelists2.db"))
+
+ db_oth = cr.Sqlite(self.tmpdir+"/other.db", cr.DB_OTHER)
+ self.assertTrue(db_oth)
+ self.assertTrue(os.path.isfile(self.tmpdir+"/other.db"))
+
+ db_oth = cr.OtherSqlite(self.tmpdir+"/other2.db")
+ self.assertTrue(db_oth)
+ self.assertTrue(os.path.isfile(self.tmpdir+"/other2.db"))
+
+ # Error cases
+
+ self.assertRaises(cr.CreaterepoCException, cr.Sqlite, self.tmpdir, cr.DB_PRIMARY)
+
+ self.assertRaises(ValueError, cr.Sqlite, self.tmpdir+"/foo.db", 55)
+
+ self.assertRaises(TypeError, cr.Sqlite, self.tmpdir+"/foo.db", None)
+
+ self.assertRaises(TypeError, cr.Sqlite, None, cr.DB_PRIMARY)
+
+ def test_sqlite_primary_schema(self):
+ path = os.path.join(self.tmpdir, "primary.db")
+ cr.PrimarySqlite(path)
+ self.assertTrue(os.path.isfile(path))
+
+ con = sqlite3.connect(path)
+ # Check tables
+ self.assertEqual(con.execute("""select name from sqlite_master where type="table";""").fetchall(),
+ [(u'db_info',),
+ (u'packages',),
+ (u'files',),
+ (u'requires',),
+ (u'provides',),
+ (u'conflicts',),
+ (u'obsoletes',)])
+ # Check indexes
+ self.assertEqual(con.execute("""select name from sqlite_master where type="index";""").fetchall(),
+ [(u'packagename',),
+ (u'packageId',),
+ (u'filenames',),
+ (u'pkgfiles',),
+ (u'pkgrequires',),
+ (u'requiresname',),
+ (u'pkgprovides',),
+ (u'providesname',),
+ (u'pkgconflicts',),
+ (u'pkgobsoletes',)])
+ # Check triggers
+ self.assertEqual(con.execute("""select name from sqlite_master where type="trigger";""").fetchall(),
+ [(u'removals',)])
+
+ def test_sqlite_filelists_schema(self):
+ path = os.path.join(self.tmpdir, "filelists.db")
+ cr.FilelistsSqlite(path)
+ self.assertTrue(os.path.isfile(path))
+
+ con = sqlite3.connect(path)
+ # Check tables
+ self.assertEqual(con.execute("""select name from sqlite_master where type="table";""").fetchall(),
+ [(u'db_info',), (u'packages',), (u'filelist',)])
+ # Check indexes
+ self.assertEqual(con.execute("""select name from sqlite_master where type="index";""").fetchall(),
+ [(u'keyfile',), (u'pkgId',), (u'dirnames',)])
+ # Check triggers
+ self.assertEqual(con.execute("""select name from sqlite_master where type="trigger";""").fetchall(),
+ [(u'remove_filelist',)])
+
+ def test_sqlite_other_schema(self):
+ path = os.path.join(self.tmpdir, "other.db")
+ cr.OtherSqlite(path)
+ self.assertTrue(os.path.isfile(path))
+
+ con = sqlite3.connect(path)
+ # Check tables
+ self.assertEqual(con.execute("""select name from sqlite_master where type="table";""").fetchall(),
+ [(u'db_info',), (u'packages',), (u'changelog',)])
+ # Check indexes
+ self.assertEqual(con.execute("""select name from sqlite_master where type="index";""").fetchall(),
+ [(u'keychange',), (u'pkgId',)])
+ # Check triggers
+ self.assertEqual(con.execute("""select name from sqlite_master where type="trigger";""").fetchall(),
+ [(u'remove_changelogs',)])
+
+ def test_sqlite_primary(self):
+ path = os.path.join(self.tmpdir, "primary.db")
+ db = cr.Sqlite(path, cr.DB_PRIMARY)
+ pkg = cr.package_from_rpm(PKG_ARCHER_PATH)
+ db.add_pkg(pkg)
+ db.dbinfo_update("somechecksum")
+ db.close()
+
+ self.assertTrue(os.path.isfile(path))
+
+ con = sqlite3.connect(path)
+
+ # Check packages table
+ self.assertEqual(con.execute("select * from packages").fetchall(),
+ [(1, u'4e0b775220c67f0f2c1fd2177e626b9c863a098130224ff09778ede25cea9a9e',
+ u'Archer', u'x86_64', u'3.4.5', u'2', u'6', u'Complex package.',
+ u'Archer package', u'http://soo_complex_package.eu/',
+ 1365416502, 1365416480, u'GPL', u'ISIS', u'Development/Tools',
+ u'localhost.localdomain', u'Archer-3.4.5-6.src.rpm', 280, 2865,
+ u'Sterling Archer', 3101, 0, 544, None, None, u'sha256')])
+
+ # Check provides table
+ self.assertEqual(con.execute("select * from provides").fetchall(),
+ [(u'bara', u'LE', u'0', u'22', None, 1),
+ (u'barb', u'GE', u'0', u'11.22.33', u'44', 1),
+ (u'barc', u'EQ', u'0', u'33', None, 1),
+ (u'bard', u'LT', u'0', u'44', None, 1),
+ (u'bare', u'GT', u'0', u'55', None, 1),
+ (u'Archer', u'EQ', u'2', u'3.4.5', u'6', 1),
+ (u'Archer(x86-64)', u'EQ', u'2', u'3.4.5', u'6', 1)])
+
+ # Check conflicts table
+ self.assertEqual(con.execute("select * from conflicts").fetchall(),
+ [(u'bba', u'LE', u'0', u'2222', None, 1),
+ (u'bbb', u'GE', u'0', u'1111.2222.3333', u'4444', 1),
+ (u'bbc', u'EQ', u'0', u'3333', None, 1),
+ (u'bbd', u'LT', u'0', u'4444', None, 1),
+ (u'bbe', u'GT', u'0', u'5555', None, 1)])
+
+ # Check obsoletes table
+ self.assertEqual(con.execute("select * from obsoletes").fetchall(),
+ [(u'aaa', u'LE', u'0', u'222', None, 1),
+ (u'aab', u'GE', u'0', u'111.2.3', u'4', 1),
+ (u'aac', u'EQ', u'0', u'333', None, 1),
+ (u'aad', u'LT', u'0', u'444', None, 1),
+ (u'aae', u'GT', u'0', u'555', None, 1)])
+
+ # Check requires table
+ self.assertEqual(con.execute("select * from requires").fetchall(),
+ [(u'fooa', u'LE', u'0', u'2', None, 1, u'FALSE'),
+ (u'foob', u'GE', u'0', u'1.0.0', u'1', 1, u'FALSE'),
+ (u'fooc', u'EQ', u'0', u'3', None, 1, u'FALSE'),
+ (u'food', u'LT', u'0', u'4', None, 1, u'FALSE'),
+ (u'fooe', u'GT', u'0', u'5', None, 1, u'FALSE'),
+ (u'foof', u'EQ', u'0', u'6', None, 1, u'TRUE')])
+
+ # Check files table
+ self.assertEqual(con.execute("select * from files").fetchall(),
+ [(u'/usr/bin/complex_a', u'file', 1)])
+
+ # Check db_info table
+ self.assertEqual(con.execute("select * from db_info").fetchall(),
+ [(10, u'somechecksum')])
+
+ def test_sqlite_filelists(self):
+ path = os.path.join(self.tmpdir, "filelists.db")
+ db = cr.Sqlite(path, cr.DB_FILELISTS)
+ pkg = cr.package_from_rpm(PKG_ARCHER_PATH)
+ db.add_pkg(pkg)
+ db.dbinfo_update("somechecksum2")
+ db.close()
+
+ self.assertTrue(os.path.isfile(path))
+
+ con = sqlite3.connect(path)
+
+ # Check packages table
+ self.assertEqual(con.execute("select * from packages").fetchall(),
+ [(1, u'4e0b775220c67f0f2c1fd2177e626b9c863a098130224ff09778ede25cea9a9e')])
+
+ # Check files table
+ self.assertEqual(con.execute("select * from filelist").fetchall(),
+ [(1, u'/usr/share/doc', u'Archer-3.4.5', u'd'),
+ (1, u'/usr/bin', u'complex_a', u'f'),
+ (1, u'/usr/share/doc/Archer-3.4.5', u'README', u'f')])
+
+ # Check db_info table
+ self.assertEqual(con.execute("select * from db_info").fetchall(),
+ [(10, u'somechecksum2')])
+
+ def test_sqlite_other(self):
+ path = os.path.join(self.tmpdir, "other.db")
+ db = cr.Sqlite(path, cr.DB_FILELISTS)
+ pkg = cr.package_from_rpm(PKG_ARCHER_PATH)
+ db.add_pkg(pkg)
+ db.dbinfo_update("somechecksum3")
+ db.close()
+
+ self.assertTrue(os.path.isfile(path))
+
+ con = sqlite3.connect(path)
+
+ # Check packages table
+ self.assertEqual(con.execute("select * from packages").fetchall(),
+ [(1, u'4e0b775220c67f0f2c1fd2177e626b9c863a098130224ff09778ede25cea9a9e')])
+
+ # Check filelist table
+ self.assertEqual(con.execute("select * from filelist").fetchall(),
+ [(1, u'/usr/share/doc', u'Archer-3.4.5', u'd'),
+ (1, u'/usr/bin', u'complex_a', u'f'),
+ (1, u'/usr/share/doc/Archer-3.4.5', u'README', u'f')])
+
+ # Check db_info table
+ self.assertEqual(con.execute("select * from db_info").fetchall(),
+ [(10, u'somechecksum3')])
--- /dev/null
+import unittest
+import createrepo_c as cr
+
+import fixtures
+
+class TestCaseVersion(unittest.TestCase):
+ def test_version(self):
+ self.assertIsInstance(cr.VERSION_MAJOR, int);
+ self.assertIsInstance(cr.VERSION_MINOR, int);
+ self.assertIsInstance(cr.VERSION_PATCH, int);
--- /dev/null
+cd ${CMAKE_CURRENT_SOURCE_DIR} && gtester ${CMAKE_BINARY_DIR}/tests/test*