aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAvatar Piotr Jaroszyński <peper@gentoo.org> 2007-08-13 23:27:04 +0000
committerAvatar Piotr Jaroszyński <peper@gentoo.org> 2007-08-13 23:27:04 +0000
commit87982bc544096c61bdcf38bbd855df01d89aa156 (patch)
tree52132e17161f3dcc2268cb6d5aea8eee834ced73
parentd1449162f3958443539a3f7067719e0be077b515 (diff)
downloadpaludis-87982bc544096c61bdcf38bbd855df01d89aa156.tar.gz
paludis-87982bc544096c61bdcf38bbd855df01d89aa156.tar.xz
(python) Allow Query subclassing.
-rw-r--r--python/Makefile.am1
-rw-r--r--python/exception.cc11
-rw-r--r--python/exception.hh13
-rw-r--r--python/iterable.hh146
-rw-r--r--python/name.cc9
-rw-r--r--python/package_id.cc3
-rw-r--r--python/query.cc172
-rwxr-xr-xpython/query_TEST.py32
-rwxr-xr-xpython/query_TEST_cleanup.sh8
-rwxr-xr-xpython/query_TEST_setup.sh34
10 files changed, 420 insertions, 9 deletions
diff --git a/python/Makefile.am b/python/Makefile.am
index de945ee..37ca2dd 100644
--- a/python/Makefile.am
+++ b/python/Makefile.am
@@ -79,6 +79,7 @@ EXTRA_DIST = $(IF_PYTHON_TESTS) $(IF_PYTHON_SOURCES) \
metadata_key_TEST_setup.sh metadata_key_TEST_cleanup.sh \
package_database_TEST_setup.sh package_database_TEST_cleanup.sh \
package_id_TEST_setup.sh package_id_TEST_cleanup.sh \
+ query_TEST_setup.sh query_TEST_cleanup.sh \
repository_TEST_setup.sh repository_TEST_cleanup.sh
TESTS_ENVIRONMENT = env \
diff --git a/python/exception.cc b/python/exception.cc
index 6e44aa8..c45a879 100644
--- a/python/exception.cc
+++ b/python/exception.cc
@@ -78,6 +78,13 @@ PythonMethodNotImplemented::PythonMethodNotImplemented(const std::string & class
{
}
+PythonContainerConversionError::PythonContainerConversionError(const std::string & class_name,
+ const std::string & container_name, const std::string & o_type) throw () :
+ PythonError("Cannot add object of type '" + o_type + "' to a '"
+ + class_name + "' " + container_name + " container.")
+{
+}
+
void PALUDIS_VISIBLE expose_exception()
{
/**
@@ -101,5 +108,9 @@ void PALUDIS_VISIBLE expose_exception()
ExceptionRegister::get_instance()->add_exception<PythonMethodNotImplemented>("PythonMethodNotImplemented",
"PythonError",
"Thrown if a not implemented virtual function was called from C++.");
+ ExceptionRegister::get_instance()->add_exception<PythonContainerConversionError>(
+ "PythonContainerConversionError",
+ "PythonError",
+ "Thrown if an error occurs during container conversion from python.");
}
diff --git a/python/exception.hh b/python/exception.hh
index df6c5e7..a5207c3 100644
--- a/python/exception.hh
+++ b/python/exception.hh
@@ -17,8 +17,8 @@
* Place, Suite 330, Boston, MA 02111-1307 USA
*/
-#ifndef PALUDIS_GUARD_PYTHON_PALUDIS_EXCEPTION_HH
-#define PALUDIS_GUARD_PYTHON_PALUDIS_EXCEPTION_HH 1
+#ifndef PALUDIS_GUARD_PYTHON_EXCEPTION_HH
+#define PALUDIS_GUARD_PYTHON_EXCEPTION_HH 1
#include <paludis/util/tr1_memory.hh>
#include <paludis/util/tr1_functional.hh>
@@ -134,8 +134,15 @@ namespace paludis
PythonMethodNotImplemented(const std::string & class_name,
const std::string & function_name) throw ();
};
+
+ class PALUDIS_VISIBLE PythonContainerConversionError :
+ public PythonError
+ {
+ public:
+ PythonContainerConversionError(const std::string & class_name,
+ const std::string & container_name, const std::string & o_type) throw ();
+ };
}
}
#endif
-
diff --git a/python/iterable.hh b/python/iterable.hh
index cd1be99..7c17074 100644
--- a/python/iterable.hh
+++ b/python/iterable.hh
@@ -21,26 +21,170 @@
#define PALUDIS_GUARD_PYTHON_ITERABLE_HH 1
#include <python/paludis_python.hh>
+#include <python/exception.hh>
+#include <paludis/util/tr1_type_traits.hh>
#include <paludis/util/sequence.hh>
#include <paludis/util/set.hh>
+#include <iostream>
+
namespace paludis
{
namespace python
{
+ template <typename V_>
+ struct RegisterSequenceSPTRFromPython
+ {
+ static std::string class_name;
+
+ RegisterSequenceSPTRFromPython(const std::string & name)
+ {
+ boost::python::converter::registry::push_back(&convertible, &construct,
+ boost::python::type_id<tr1::shared_ptr<Sequence<V_> > >());
+
+ class_name = name;
+ }
+
+ static void *
+ convertible(PyObject * obj_ptr)
+ {
+ if (boost::python::extract<boost::python::list>(obj_ptr).check()
+ || obj_ptr == Py_None)
+ return obj_ptr;
+ else
+ return 0;
+ }
+
+ static void
+ construct(PyObject * obj_ptr, boost::python::converter::rvalue_from_python_stage1_data * data)
+ {
+ typedef boost::python::converter::rvalue_from_python_storage<tr1::shared_ptr<Sequence<V_> > >
+ Storage;
+ void * storage = reinterpret_cast<Storage *>(data)->storage.bytes;
+
+ data->convertible = storage;
+
+ if (obj_ptr == Py_None)
+ {
+ new (storage) tr1::shared_ptr<Sequence<V_> >();
+ return;
+ }
+ else
+ new (storage) tr1::shared_ptr<Sequence<V_> >(new Sequence<V_>());
+
+ Sequence<V_> * s(reinterpret_cast<tr1::shared_ptr<Sequence<V_> > *>(storage)->get());
+
+ boost::python::list l = boost::python::extract<boost::python::list>(obj_ptr);
+
+ while (boost::python::len(l))
+ {
+ boost::python::object o(l.pop());
+ if (boost::python::extract<V_ *>(o).check())
+ {
+ V_ * ptr = boost::python::extract<V_ *>(o);
+ s->push_back(*ptr);
+ }
+ else
+ {
+ typedef tr1::shared_ptr<Sequence<V_> > sptr;
+ reinterpret_cast<sptr *>(storage)->sptr::~shared_ptr();
+
+ throw PythonContainerConversionError(class_name, "sequence", o.ptr()->ob_type->tp_name);
+ }
+ }
+ }
+ };
+ template <typename V_>
+ std::string RegisterSequenceSPTRFromPython<V_>::class_name("unknown");
+
+ template <typename V_>
+ struct RegisterSetSPTRFromPython
+ {
+ static std::string class_name;
+
+ RegisterSetSPTRFromPython(const std::string & name)
+ {
+ boost::python::converter::registry::push_back(&convertible, &construct,
+ boost::python::type_id<tr1::shared_ptr<Set<V_> > >());
+
+ class_name = name;
+ }
+
+ static void *
+ convertible(PyObject * obj_ptr)
+ {
+ if (boost::python::extract<boost::python::list>(obj_ptr).check()
+ || obj_ptr == Py_None)
+ return obj_ptr;
+ else
+ return 0;
+ }
+
+ static void
+ construct(PyObject * obj_ptr, boost::python::converter::rvalue_from_python_stage1_data * data)
+ {
+ typedef boost::python::converter::rvalue_from_python_storage<tr1::shared_ptr<Set<V_> > >
+ Storage;
+ void * storage = reinterpret_cast<Storage *>(data)->storage.bytes;
+
+ data->convertible = storage;
+
+ if (obj_ptr == Py_None)
+ {
+ new (storage) tr1::shared_ptr<Set<V_> >();
+ return;
+ }
+ else
+ new (storage) tr1::shared_ptr<Set<V_> >(new Set<V_>());
+
+ Set<V_> * s(reinterpret_cast<tr1::shared_ptr<Set<V_> > *>(storage)->get());
+
+ boost::python::list l = boost::python::extract<boost::python::list>(obj_ptr);
+
+ while (boost::python::len(l))
+ {
+ boost::python::object o(l.pop());
+ if (boost::python::extract<V_ *>(o).check())
+ {
+ V_ * ptr = boost::python::extract<V_ *>(o);
+ s->insert(*ptr);
+ }
+ else
+ {
+ typedef tr1::shared_ptr<Set<V_> > sptr;
+ reinterpret_cast<sptr *>(storage)->sptr::~shared_ptr();
+
+ throw PythonContainerConversionError(class_name, "set", o.ptr()->ob_type->tp_name);
+ }
+ }
+ }
+ };
+ template <typename V_>
+ std::string RegisterSetSPTRFromPython<V_>::class_name("unknown");
+
// expose iterable classes
template <typename C_>
class class_iterable :
public boost::python::class_<C_, boost::noncopyable>
{
public:
- class_iterable(const std::string & name, const std::string & class_doc) :
+ class_iterable(const std::string & name, const std::string & class_doc, bool converter=false) :
boost::python::class_<C_, boost::noncopyable>(name.c_str(), class_doc.c_str(),
boost::python::no_init)
{
this->def("__iter__", boost::python::range(&C_::begin, &C_::end));
register_shared_ptrs_to_python<C_>();
+
+ if (converter)
+ {
+ if (tr1::is_same<C_, Sequence<typename C_::value_type> >::value)
+ RegisterSequenceSPTRFromPython<typename C_::value_type> tmp(name);
+ else if (tr1::is_same<C_, Set<typename C_::value_type> >::value)
+ RegisterSetSPTRFromPython<typename C_::value_type> tmp(name);
+ else
+ throw PythonError("Can't register l-value converter for '" + name +"'.");
+ }
}
};
} // namespace paludis::python
diff --git a/python/name.cc b/python/name.cc
index 548f477..dcd9274 100644
--- a/python/name.cc
+++ b/python/name.cc
@@ -109,7 +109,8 @@ void PALUDIS_VISIBLE expose_name()
class_iterable<CategoryNamePartSet>
(
"CategoryNamePartIterable",
- "Iterable of CategoryNamePart"
+ "Iterable of CategoryNamePart",
+ true
);
/**
@@ -156,7 +157,8 @@ void PALUDIS_VISIBLE expose_name()
class_iterable<RepositoryNameSequence>
(
"RepositoryNameIterable",
- "Iterable of RepositoryName"
+ "Iterable of RepositoryName",
+ true
);
/**
@@ -232,7 +234,8 @@ void PALUDIS_VISIBLE expose_name()
class_iterable<QualifiedPackageNameSet>
(
"QualifiedPackageNameIterable",
- "Iterable of QualifiedPackageName"
+ "Iterable of QualifiedPackageName",
+ true
);
/**
diff --git a/python/package_id.cc b/python/package_id.cc
index d2645bd..712222f 100644
--- a/python/package_id.cc
+++ b/python/package_id.cc
@@ -136,6 +136,7 @@ void PALUDIS_VISIBLE expose_package_id()
class_iterable<PackageIDSequence>
(
"PackageIDIterable",
- "Iterable of PackageID"
+ "Iterable of PackageID",
+ true
);
}
diff --git a/python/query.cc b/python/query.cc
index 18b23b8..c7d1325 100644
--- a/python/query.cc
+++ b/python/query.cc
@@ -18,15 +18,172 @@
*/
#include <python/paludis_python.hh>
+#include <python/exception.hh>
#include <paludis/query.hh>
#include <paludis/dep_spec.hh>
#include <paludis/util/fs_entry.hh>
+#include <paludis/environment.hh>
using namespace paludis;
using namespace paludis::python;
namespace bp = boost::python;
+class PythonQueryDelegate;
+
+class PythonQuery :
+ public Query
+{
+ public:
+ PythonQuery();
+
+ virtual tr1::shared_ptr<RepositoryNameSequence> repositories(const Environment &) const
+ {
+ return tr1::shared_ptr<RepositoryNameSequence>();
+ }
+
+ virtual tr1::shared_ptr<CategoryNamePartSet> categories(const Environment &,
+ tr1::shared_ptr<const RepositoryNameSequence>) const
+ {
+ return tr1::shared_ptr<CategoryNamePartSet>();
+ }
+
+ virtual tr1::shared_ptr<QualifiedPackageNameSet> packages(const Environment &,
+ tr1::shared_ptr<const RepositoryNameSequence>,
+ tr1::shared_ptr<const CategoryNamePartSet>) const
+ {
+ return tr1::shared_ptr<QualifiedPackageNameSet>();
+ }
+
+ virtual tr1::shared_ptr<PackageIDSequence> ids(const Environment &,
+ tr1::shared_ptr<const RepositoryNameSequence>,
+ tr1::shared_ptr<const QualifiedPackageNameSet>) const
+ {
+ return tr1::shared_ptr<PackageIDSequence>();
+ }
+
+ virtual std::string as_human_readable_string() const = 0;
+};
+
+class PythonQueryDelegate :
+ public QueryDelegate
+{
+ private:
+ PythonQuery * _q;
+
+ public:
+ PythonQueryDelegate(PythonQuery * pq) :
+ _q(pq)
+ {
+ }
+
+ tr1::shared_ptr<RepositoryNameSequence> repositories(const Environment & e) const
+ {
+ return _q->repositories(e);
+ }
+
+ tr1::shared_ptr<CategoryNamePartSet> categories(const Environment & e,
+ tr1::shared_ptr<const RepositoryNameSequence> r) const
+ {
+ return _q->categories(e, r);
+ }
+
+ tr1::shared_ptr<QualifiedPackageNameSet> packages(const Environment & e,
+ tr1::shared_ptr<const RepositoryNameSequence> r,
+ tr1::shared_ptr<const CategoryNamePartSet> c) const
+ {
+ return _q->packages(e, r, c);
+ }
+
+ tr1::shared_ptr<PackageIDSequence> ids(const Environment & e,
+ tr1::shared_ptr<const RepositoryNameSequence> r,
+ tr1::shared_ptr<const QualifiedPackageNameSet> q) const
+ {
+ return _q->ids(e, r, q);
+ }
+
+ std::string as_human_readable_string() const
+ {
+ return _q->as_human_readable_string();
+ }
+};
+
+PythonQuery::PythonQuery() :
+ Query(tr1::shared_ptr<const PythonQueryDelegate>(new PythonQueryDelegate(this)))
+{
+}
+
+struct PythonQueryWrapper :
+ PythonQuery,
+ bp::wrapper<PythonQuery>
+{
+ tr1::shared_ptr<RepositoryNameSequence> repositories(const Environment & e) const
+ {
+ if (bp::override f = get_override("repositories"))
+ return f(boost::cref(e));
+ return PythonQuery::repositories(e);
+ }
+
+ tr1::shared_ptr<RepositoryNameSequence> default_repositories(const Environment & e) const
+ {
+ return PythonQuery::repositories(e);
+ }
+
+ tr1::shared_ptr<CategoryNamePartSet> categories(const Environment & e,
+ tr1::shared_ptr<const RepositoryNameSequence> r) const
+ {
+ if (bp::override f = get_override("categories"))
+ return f(boost::cref(e), r);
+ return PythonQuery::categories(e, r);
+ }
+
+ tr1::shared_ptr<CategoryNamePartSet> default_categories(const Environment & e,
+ tr1::shared_ptr<const RepositoryNameSequence> r) const
+ {
+ return PythonQuery::categories(e, r);
+ }
+
+ tr1::shared_ptr<QualifiedPackageNameSet> packages(const Environment & e,
+ tr1::shared_ptr<const RepositoryNameSequence> r,
+ tr1::shared_ptr<const CategoryNamePartSet> c) const
+ {
+ if (bp::override f = get_override("packages"))
+ return f(boost::cref(e), r, c);
+ return PythonQuery::packages(e, r, c);
+ }
+
+ tr1::shared_ptr<QualifiedPackageNameSet> default_packages(const Environment & e,
+ tr1::shared_ptr<const RepositoryNameSequence> r,
+ tr1::shared_ptr<const CategoryNamePartSet> c) const
+ {
+ return PythonQuery::packages(e, r, c);
+ }
+
+ tr1::shared_ptr<PackageIDSequence> ids(const Environment & e,
+ tr1::shared_ptr<const RepositoryNameSequence> r,
+ tr1::shared_ptr<const QualifiedPackageNameSet> q) const
+ {
+ if (bp::override f = get_override("ids"))
+ return f(boost::cref(e), r, q);
+ return PythonQuery::ids(e, r, q);
+ }
+
+ tr1::shared_ptr<PackageIDSequence> default_ids(const Environment & e,
+ tr1::shared_ptr<const RepositoryNameSequence> r,
+ tr1::shared_ptr<const QualifiedPackageNameSet> q) const
+ {
+ return PythonQuery::ids(e, r, q);
+ }
+
+ std::string as_human_readable_string() const
+ {
+ if (bp::override f = get_override("as_human_readable_string"))
+ return f();
+ else
+ throw PythonMethodNotImplemented("Query", "as_human_readable_string");
+ }
+};
+
template <typename A_>
class class_supports_action :
public bp::class_<query::SupportsAction<A_>, bp::bases<Query> >
@@ -56,6 +213,21 @@ void PALUDIS_VISIBLE expose_query()
);
q.def("__and__", operator&);
+ bp::class_<PythonQueryWrapper, bp::bases<Query>, boost::noncopyable>
+ (
+ "QueryBase",
+ "Parameter for a PackageDatabase query.",
+ bp::init<>()
+ )
+ .def("repositories", &PythonQuery::repositories, &PythonQueryWrapper::default_repositories)
+
+ .def("categories", &PythonQuery::categories, &PythonQueryWrapper::default_categories)
+
+ .def("packages", &PythonQuery::packages, &PythonQueryWrapper::default_packages)
+
+ .def("ids", &PythonQuery::ids, &PythonQueryWrapper::default_ids)
+ ;
+
/* I need to think about it yet... */
bp::scope query = q;
diff --git a/python/query_TEST.py b/python/query_TEST.py
index 85826c8..090d592 100755
--- a/python/query_TEST.py
+++ b/python/query_TEST.py
@@ -18,10 +18,14 @@
# Place, Suite 330, Boston, MA 02111-1307 USA
#
+import os
+
+repo_path = os.path.join(os.getcwd(), "query_TEST_dir/testrepo")
+
from paludis import *
import unittest
-class TestCase_Queries(unittest.TestCase):
+class TestCase_01_Queries(unittest.TestCase):
def test_1_create(self):
self.queries = []
self.queries.append(Query.SupportsInstallAction())
@@ -52,5 +56,31 @@ class TestCase_Queries(unittest.TestCase):
for j in xrange(length):
self.assert_(isinstance(self.queries[i] & self.queries[j], Query))
+class TestCase_02_QueryBase(unittest.TestCase):
+ class TestQuery(QueryBase):
+ def __init__(self):
+ QueryBase.__init__(self)
+
+ def as_human_readable_string(self):
+ return "TestQuery"
+
+ def categories(self, e, repos):
+ return None
+
+ def packages(self, e, repos, cats):
+ return QueryBase.packages(self, e, repos, cats)
+
+ def ids(self, e, repos, pkgs):
+ r = e.package_database.fetch_repository("testrepo")
+ return list(r.package_ids(iter(pkgs).next()))
+
+ def test_1_create(self):
+ e = NoConfigEnvironment(repo_path, "/var/empty")
+ db = e.package_database
+ q = self.TestQuery()
+ pid = iter(db.query(q, QueryOrder.REQUIRE_EXACTLY_ONE)).next()
+
+ self.assertEquals(pid.name, "cat/package")
+
if __name__ == "__main__":
unittest.main()
diff --git a/python/query_TEST_cleanup.sh b/python/query_TEST_cleanup.sh
new file mode 100755
index 0000000..9de073e
--- /dev/null
+++ b/python/query_TEST_cleanup.sh
@@ -0,0 +1,8 @@
+#!/bin/bash
+# vim: set ft=sh sw=4 sts=4 et :
+
+if [ -d query_TEST_dir ] ; then
+ rm -fr query_TEST_dir
+else
+ true
+fi
diff --git a/python/query_TEST_setup.sh b/python/query_TEST_setup.sh
new file mode 100755
index 0000000..9ee6f80
--- /dev/null
+++ b/python/query_TEST_setup.sh
@@ -0,0 +1,34 @@
+#!/bin/bash
+# vim: set ft=sh sw=4 sts=4 et :
+
+mkdir query_TEST_dir || exit 1
+cd query_TEST_dir || exit 1
+
+mkdir -p testrepo/{eclass,distfiles,profiles/testprofile,cat/package} || exit 1
+cd testrepo || exit 1
+echo "testrepo" > profiles/repo_name || exit 1
+cat <<END > profiles/categories || exit 1
+cat
+END
+cat <<END > profiles/profiles.desc
+test testprofile stable
+END
+cat <<END > profiles/testprofile/make.defaults
+ARCH=test
+USERLAND=test
+KERNEL=test
+END
+
+cat <<"END" > cat/package/package-1.0.ebuild || exit 1
+DESCRIPTION="Query for me"
+HOMEPAGE="http://paludis.pioto.org/"
+SRC_URI="http://example.com/${P}.tar.bz2"
+SLOT="0"
+IUSE=""
+LICENSE="GPL-2"
+KEYWORDS="test
+RESTRICT=""
+DEPEND=""
+RDEPEND=""
+END
+