aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAvatar Piotr Jaroszyński <peper@gentoo.org> 2007-05-04 21:47:07 +0000
committerAvatar Piotr Jaroszyński <peper@gentoo.org> 2007-05-04 21:47:07 +0000
commit02e73e7f00154bdfebac199dd6a6ea1620f83132 (patch)
treeb080378daed0a54623ceddc3bb207298ab81723e
parent62ca07c8be79011d6eb1640571b1d8cc71908922 (diff)
downloadpaludis-02e73e7f00154bdfebac199dd6a6ea1620f83132.tar.gz
paludis-02e73e7f00154bdfebac199dd6a6ea1620f83132.tar.xz
Initial import of Python bindings.
-rw-r--r--Makefile.am2
-rw-r--r--configure.ac85
-rw-r--r--python/Makefile.am155
-rw-r--r--python/contents.cc113
-rwxr-xr-xpython/contents_TEST.py92
-rw-r--r--python/dep_spec.cc167
-rwxr-xr-xpython/dep_spec_TEST.py105
-rw-r--r--python/environment.cc152
-rwxr-xr-xpython/environment_TEST.py123
-rwxr-xr-xpython/environment_TEST_cleanup.sh8
-rwxr-xr-xpython/environment_TEST_setup.sh97
-rw-r--r--python/fs_entry.cc35
-rw-r--r--python/log.cc57
-rwxr-xr-xpython/log_TEST.py55
-rw-r--r--python/mask_reasons.cc36
-rwxr-xr-xpython/mask_reasons_TEST.py86
-rw-r--r--python/name.cc137
-rwxr-xr-xpython/name_TEST.py70
-rw-r--r--python/package_database.cc104
-rwxr-xr-xpython/package_database_TEST.py72
-rwxr-xr-xpython/package_database_TEST_cleanup.sh8
-rwxr-xr-xpython/package_database_TEST_setup.sh84
-rwxr-xr-xpython/paludis_python.hh246
-rw-r--r--python/paludis_python_so.cc55
-rw-r--r--python/portage_dep_parser.cc75
-rwxr-xr-xpython/portage_dep_parser_TEST.py82
-rw-r--r--python/query.cc73
-rwxr-xr-xpython/query_TEST.py51
-rw-r--r--python/repository.cc434
-rwxr-xr-xpython/repository_TEST.py332
-rwxr-xr-xpython/repository_TEST_cleanup.sh8
-rwxr-xr-xpython/repository_TEST_setup.sh134
-rw-r--r--python/version_metadata.cc195
-rwxr-xr-xpython/version_metadata_TEST.py115
-rwxr-xr-xpython/version_metadata_TEST_cleanup.sh8
-rwxr-xr-xpython/version_metadata_TEST_setup.sh45
-rw-r--r--python/version_operator.cc59
-rwxr-xr-xpython/version_operator_TEST.py42
-rw-r--r--python/version_requirements.cc51
-rwxr-xr-xpython/version_requirements_TEST.py46
-rw-r--r--python/version_spec.cc68
-rwxr-xr-xpython/version_spec_TEST.py66
42 files changed, 4027 insertions, 1 deletions
diff --git a/Makefile.am b/Makefile.am
index be34792..2a73c81 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -4,7 +4,7 @@ MAINTAINERCLEANFILES = Makefile.in configure config/* aclocal.m4 \
config.h config.h.in INSTALL COPYING
AUTOMAKE_OPTIONS = dist-bzip2 no-dist-gzip std-options
EXTRA_DIST = autogen.bash automake-deps-dist-hack.tmp
-SUBDIRS = misc tr1 test paludis ruby src doc hooks eselect vim bash-completion zsh-completion
+SUBDIRS = misc tr1 test paludis python ruby src doc hooks eselect vim bash-completion zsh-completion
DISTCHECK_CONFIGURE_FLAGS = --enable-qa --enable-ruby --enable-glsa \
--with-ruby-install-dir='$${DESTDIR}$${prefix}/ruby_dir' \
diff --git a/configure.ac b/configure.ac
index e5308f3..25b6f7f 100644
--- a/configure.ac
+++ b/configure.ac
@@ -416,6 +416,8 @@ PALUDIS_CXXFLAGS=
PALUDIS_CXXFLAGS_NO_WOLD_STYLE_CAST=
PALUDIS_CXXFLAGS_NO_WREDUNDANT_DECLS=
PALUDIS_CXXFLAGS_NO_WSHADOW=
+PALUDIS_CXXFLAGS_NO_WUNUSED=
+PALUDIS_CXXFLAGS_NO_WEXTRA=
PALUDIS_CXXFLAGS_WITHOUT_PEDANTIC=
PALUDIS_CXXFLAGS_VISIBILITY=
AC_MSG_CHECKING([whether our compiler is iccy])
@@ -464,6 +466,8 @@ elif test "x${ac_cv_cxx_compiler_gnu}" = "xyes" ; then
CHECK_CXXFLAG([-Wextra])
if ! test "x$cxxflag_success" = "xyes" ; then
CHECK_CXXFLAG([-W])
+ else
+ PALUDIS_CXXFLAGS_NO_WEXTRA=-Wno-extra
fi
CHECK_CXXFLAG([-Wold-style-cast])
if test "x$cxxflag_success" = "xyes" ; then
@@ -478,6 +482,10 @@ elif test "x${ac_cv_cxx_compiler_gnu}" = "xyes" ; then
CHECK_CXXFLAG([-Woverloaded-virtual])
CHECK_CXXFLAG([-Winit-self])
CHECK_CXXFLAG([-Wunreachable-code])
+ CHECK_CXXFLAG([-Wunused])
+ if test "x$cxxflag_success" = "xyes" ; then
+ PALUDIS_CXXFLAGS_NO_WUNUSED=-Wno-unused
+ fi
CHECK_CXXFLAG([-Wunused-function])
CHECK_CXXFLAG([-Wshadow])
if test "x$cxxflag_success" = "xyes" ; then
@@ -494,6 +502,8 @@ AC_SUBST([PALUDIS_CXXFLAGS_WITHOUT_PEDANTIC])
AC_SUBST([PALUDIS_CXXFLAGS_NO_WOLD_STYLE_CAST])
AC_SUBST([PALUDIS_CXXFLAGS_NO_WSHADOW])
AC_SUBST([PALUDIS_CXXFLAGS_NO_WREDUNDANT_DECLS])
+AC_SUBST([PALUDIS_CXXFLAGS_NO_WEXTRA])
+AC_SUBST([PALUDIS_CXXFLAGS_NO_WUNUSED])
dnl AC_MSG_CHECKING([whether to enable visibility])
dnl AC_ARG_ENABLE([visibility],
@@ -683,6 +693,80 @@ fi
AM_CONDITIONAL([ENABLE_RUBY], test "x$enable_ruby" = "xyes")
dnl }}}
+dnl {{{ python interface
+AC_ARG_VAR(PYTHON, path to python interpreter)
+AC_MSG_CHECKING([whether to build the python interface])
+AC_ARG_ENABLE([python],
+ AS_HELP_STRING([--enable-python], [Enable python interface (default: disable)]),
+ enable_python=$enableval,
+ enable_python=no)
+AC_MSG_RESULT($enable_python)
+
+if test "x$enable_python" = "xyes" ; then
+
+ AM_PATH_PYTHON([2.3])
+ if test "x$PYTHON" = "x" ; then
+ AC_MSG_ERROR([Python is required for --enable-python])
+ fi
+ AC_SUBST([PYTHON_INSTALL_DIR])
+
+ AC_MSG_CHECKING(for headers required to compile python extensions)
+ py_prefix=`$PYTHON -c "import sys; print sys.prefix"`
+ py_exec_prefix=`$PYTHON -c "import sys; print sys.exec_prefix"`
+ PYTHON_INCLUDE_DIR="${py_prefix}/include/python${PYTHON_VERSION}"
+ if test "$py_prefix" != "$py_exec_prefix"; then
+ PYTHON_INCLUDE_DIR="$PYTHON_INCLUDE_DIR -I${py_exec_prefix}/include/python${PYTHON_VERSION}"
+ fi
+ AC_MSG_RESULT([$PYTHON_INCLUDE_DIR])
+ AC_SUBST(PYTHON_INCLUDE_DIR)
+
+ dnl {{{ check for Python.h
+ AC_MSG_CHECKING([for Python.h])
+ save_CXXFLAGS="$CXXFLAGS"
+ CXXFLAGS="$CXXFLAGS -I$PYTHON_INCLUDE_DIR"
+ AC_COMPILE_IFELSE([
+ #include <Python.h>
+ ],
+ [AC_MSG_RESULT([yes])],
+ [AC_MSG_RESULT([no])
+ AC_MSG_ERROR([Python.h not found in $PYTHON_INCLUDE_DIR])])
+ dnl Restore the C++ flags
+ CXXFLAGS="$save_CXXFLAGS"
+ dnl }}}
+
+ AC_ARG_WITH([python-install-dir],
+ AS_HELP_STRING([--with-python-install-dir=dir], [Specify Python installation dir]),
+ PYTHON_INSTALL_DIR=`eval echo $withval`,
+ PYTHON_INSTALL_DIR=)
+ AC_MSG_CHECKING([for Python installation dir])
+ if test "x$PYTHON_INSTALL_DIR" = "x" ; then
+ PYTHON_INSTALL_DIR=$pyexecdir
+ fi
+ AC_MSG_RESULT([$PYTHON_INSTALL_DIR])
+
+ dnl {{{ we need boost.Python
+ AC_MSG_CHECKING([for boost.python])
+ save_CXXFLAGS="$CXXFLAGS"
+ CXXFLAGS="$CXXFLAGS -I$PYTHON_INCLUDE_DIR"
+ AC_COMPILE_IFELSE([
+ #include <boost/python.hpp>
+ struct Foo {};
+ BOOST_PYTHON_MODULE(foo)
+ {
+ boost::python::class_<Foo> ("Foo");
+ }
+ ],
+ [AC_MSG_RESULT([yes])],
+ [AC_MSG_RESULT([no])
+ AC_MSG_ERROR([boost.python (http://www.boost.org/libs/python/doc/) is required])])
+ dnl Restore the C++ flags
+ CXXFLAGS="$save_CXXFLAGS"
+ dnl }}}
+
+fi
+AM_CONDITIONAL([ENABLE_PYTHON], test "x$enable_python" = "xyes")
+dnl }}}
+
dnl {{{ vim
AC_MSG_CHECKING([whether to install Vim scripts])
AC_ARG_ENABLE([vim],
@@ -947,6 +1031,7 @@ AC_OUTPUT(
paludis/syncers/Makefile
paludis/tasks/Makefile
paludis/util/Makefile
+ python/Makefile
ruby/Makefile
ruby/demos/Makefile
src/Makefile
diff --git a/python/Makefile.am b/python/Makefile.am
new file mode 100644
index 0000000..9654034
--- /dev/null
+++ b/python/Makefile.am
@@ -0,0 +1,155 @@
+SUBDIRS = .
+
+AM_CXXFLAGS = -I$(top_srcdir) -I$(srcdir)/ \
+ -I$(top_builddir)/ \
+ @PALUDIS_CXXFLAGS_WITHOUT_PEDANTIC@ \
+ @PALUDIS_CXXFLAGS_NO_WREDUNDANT_DECLS@ \
+ @PALUDIS_CXXFLAGS_NO_WUNUSED@ \
+ @PALUDIS_CXXFLAGS_NO_WSHADOW@ \
+ @PALUDIS_CXXFLAGS_NO_WEXTRA@ \
+ @PALUDIS_CXXFLAGS_NO_WOLD_STYLE_CAST@
+
+DEFS= \
+ -DSYSCONFDIR=\"$(sysconfdir)\" \
+ -DDATADIR=\"$(datadir)\" \
+ -DLIBEXECDIR=\"$(libexecdir)\" \
+ -DLIBDIR=\"$(libdir)\"
+
+CLEANFILES = *~ gmon.out *.gcov *.gcno *.gcda *.o paludis.so .libs/paludis.so
+MAINTAINERCLEANFILES = Makefile.in
+
+IF_PYTHON_TESTS = \
+ version_spec_TEST.py \
+ version_operator_TEST.py \
+ version_requirements_TEST.py \
+ mask_reasons_TEST.py \
+ contents_TEST.py \
+ dep_spec_TEST.py \
+ portage_dep_parser_TEST.py \
+ log_TEST.py \
+ name_TEST.py \
+ query_TEST.py \
+ environment_TEST.py \
+ package_database_TEST.py \
+ repository_TEST.py \
+ version_metadata_TEST.py
+
+IF_PYTHON_QA_TESTS =
+
+IF_PYTHON_SOURCES = \
+ contents.cc \
+ dep_spec.cc \
+ environment.cc \
+ fs_entry.cc \
+ mask_reasons.cc \
+ name.cc \
+ log.cc \
+ package_database.cc \
+ portage_dep_parser.cc \
+ query.cc \
+ repository.cc \
+ version_metadata.cc \
+ version_operator.cc \
+ version_requirements.cc \
+ version_spec.cc
+
+IF_PYTHON_QA_SOURCES =
+
+EXTRA_DIST = $(IF_PYTHON_TESTS) $(IF_PYTHON_SOURCES) \
+ $(IF_PYTHON_QA_TESTS) $(IF_PYTHON_QA_SOURCES) \
+ paludis_python_so.cc
+
+TESTS_ENVIRONMENT = env \
+ PALUDIS_NO_GLOBAL_HOOKS="yes" \
+ PALUDIS_NO_XTERM_TITLES="yes" \
+ PALUDIS_EBUILD_DIR="`$(top_srcdir)/paludis/repositories/gentoo/ebuild/utils/canonicalise $(top_srcdir)/paludis/repositories/gentoo/ebuild/`" \
+ PALUDIS_EBUILD_DIR_FALLBACK="`$(top_srcdir)/paludis/repositories/gentoo/ebuild/utils/canonicalise $(top_builddir)/paludis/repositories/gentoo/ebuild/`" \
+ PALUDIS_REPOSITORY_SO_DIR="`$(top_srcdir)/paludis/repositories/gentoo/ebuild/utils/canonicalise $(top_builddir)/paludis/repositories`" \
+ PALUDIS_ENVIRONMENT_SO_DIR="`$(top_srcdir)/paludis/repositories/gentoo/ebuild/utils/canonicalise $(top_builddir)/paludis/environments`" \
+ SYSCONFDIR="$(sysconfdir)" \
+ LD_LIBRARY_PATH=`echo "\`$(top_srcdir)/paludis/repositories/gentoo/ebuild/utils/canonicalise $(top_builddir)/paludis/.libs\`: \
+ \`$(top_srcdir)/paludis/repositories/gentoo/ebuild/utils/canonicalise $(top_builddir)/paludis/util/.libs\`: \
+ \`$(top_srcdir)/paludis/repositories/gentoo/ebuild/utils/canonicalise $(top_builddir)/paludis/environments/.libs\`: \
+ \`$(top_srcdir)/paludis/repositories/gentoo/ebuild/utils/canonicalise $(top_builddir)/paludis/environments/paludis/.libs\`: \
+ \`$(top_srcdir)/paludis/repositories/gentoo/ebuild/utils/canonicalise $(top_builddir)/paludis/environments/no_config/.libs\`: \
+ \`$(top_srcdir)/paludis/repositories/gentoo/ebuild/utils/canonicalise $(top_builddir)/paludis/dep_list/.libs\`: \
+ \`$(top_srcdir)/paludis/repositories/gentoo/ebuild/utils/canonicalise $(top_builddir)/paludis/repositories/.libs\`: \
+ \`$(top_srcdir)/paludis/repositories/gentoo/ebuild/utils/canonicalise $(top_builddir)/paludis/repositories/gentoo/.libs\`: \
+ \`$(top_srcdir)/paludis/repositories/gentoo/ebuild/utils/canonicalise $(top_builddir)/paludis/qa/.libs\`: \
+ \`$(top_srcdir)/paludis/repositories/gentoo/ebuild/utils/canonicalise $(top_builddir)/python/.libs\`" \
+ | tr -d ' '` \
+ bash $(top_srcdir)/test/run_test.sh "$(PYTHON)"
+
+if ENABLE_PYTHON
+
+lib_LTLIBRARIES = libpaludispython.la
+
+libpaludispython_la_CXXFLAGS = $(AM_CXXFLAGS) -I. -I@PYTHON_INCLUDE_DIR@
+
+paludis_python_so.o : paludis_python_so.cc
+ if $(LIBTOOL) --tag=CXX --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) \
+ $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(CXXFLAGS) $(AM_CXXFLAGS) -I$(PYTHON_INCLUDE_DIR) -c \
+ -o $@ $(srcdir)/paludis_python_so.cc ; then ln -s .libs/paludis_python_so.o $@ ; else rm -f $@ ; exit 1 ; fi
+
+if ENABLE_QA
+
+libpaludispython_la_SOURCES = $(IF_PYTHON_SOURCES) $(IF_PYTHON_QA_SOURCES)
+libpaludispython_la_LIBADD = \
+ $(top_builddir)/paludis/libpaludis.la \
+ $(top_builddir)/paludis/util/libpaludisutil.la \
+ $(top_builddir)/paludis/dep_list/libpaludisdeplist.la \
+ $(top_builddir)/paludis/tasks/libpaludistasks.la \
+ $(top_builddir)/paludis/repositories/libpaludisrepositories.la \
+ $(top_builddir)/paludis/repositories/gentoo/libpaludisgentoorepository.la \
+ $(top_builddir)/paludis/environments/paludis/libpaludispaludisenvironment.la \
+ $(top_builddir)/paludis/environments/no_config/libpaludisnoconfigenvironment.la \
+ $(top_builddir)/paludis/environments/libpaludisenvironments.la \
+ $(top_builddir)/paludis/qa/libpaludisqa.la
+
+TESTS = $(IF_PYTHON_QA_TESTS) $(IF_PYTHON_TESTS)
+
+else
+
+libpaludispython_la_SOURCES = $(IF_PYTHON_SOURCES)
+libpaludispython_la_LIBADD = \
+ $(top_builddir)/paludis/libpaludis.la \
+ $(top_builddir)/paludis/util/libpaludisutil.la \
+ $(top_builddir)/paludis/dep_list/libpaludisdeplist.la \
+ $(top_builddir)/paludis/tasks/libpaludistasks.la \
+ $(top_builddir)/paludis/repositories/libpaludisrepositories.la \
+ $(top_builddir)/paludis/repositories/gentoo/libpaludisgentoorepository.la \
+ $(top_builddir)/paludis/environments/paludis/libpaludispaludisenvironment.la \
+ $(top_builddir)/paludis/environments/no_config/libpaludisnoconfigenvironment.la \
+ $(top_builddir)/paludis/environments/libpaludisenvironments.la
+
+TESTS = $(IF_PYTHON_TESTS)
+
+endif
+
+check_DATA = .libs/paludis.so
+pythonlibdir = @PYTHON_INSTALL_DIR@
+pythonlib_DATA = paludis.so
+
+.libs/paludis.so : libpaludispython.la paludis_python_so.o
+ mkdir -p .libs
+ $(CXX) -fPIC -shared $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \
+ $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) \
+ $(AM_LDFLAGS) $(LDFLAGS) \
+ -I@PYTHON_INCLUDE_DIR@ -lboost_python \
+ -o $@ \
+ .libs/paludis_python_so.o \
+ -L$(top_builddir)/python/.libs -lpaludispython
+
+paludis.so : libpaludispython.la paludis_python_so.o
+ $(CXX) -fPIC -shared $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \
+ $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) \
+ $(AM_LDFLAGS) $(LDFLAGS) \
+ -I@PYTHON_INCLUDE_DIR@ -lboost_python \
+ -o $@ \
+ .libs/paludis_python_so.o \
+ -L$(top_builddir)/python/.libs -lpaludispython
+
+endif
+
+built-sources : $(BUILT_SOURCES)
+ for s in `echo $(SUBDIRS) | tr -d .` ; do $(MAKE) -C $$s built-sources || exit 1 ; done
diff --git a/python/contents.cc b/python/contents.cc
new file mode 100644
index 0000000..24167a0
--- /dev/null
+++ b/python/contents.cc
@@ -0,0 +1,113 @@
+/* vim: set sw=4 sts=4 et foldmethod=syntax : */
+
+/*
+ * Copyright (c) 2007 Piotr Jaroszyński <peper@gentoo.org>
+ *
+ * This file is part of the Paludis package manager. Paludis is free software;
+ * you can redistribute it and/or modify it under the terms of the GNU General
+ * Public License version 2, as published by the Free Software Foundation.
+ *
+ * Paludis 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., 59 Temple
+ * Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <paludis_python.hh>
+
+#include <paludis/contents.hh>
+
+using namespace paludis;
+using namespace paludis::python;
+namespace bp = boost::python;
+
+// For classes derived from ContentsEntry
+template <typename C_>
+class class_contents:
+ public bp::class_<C_, std::tr1::shared_ptr<C_>, bp::bases<ContentsEntry>, boost::noncopyable>
+{
+ public:
+ template <class Init_>
+ class_contents(const std::string & name, const std::string & class_doc, Init_ initspec) :
+ bp::class_<C_, std::tr1::shared_ptr<C_>, bp::bases<ContentsEntry>, boost::noncopyable>(
+ name.c_str(), class_doc.c_str(), initspec)
+ {
+ bp::register_ptr_to_python<std::tr1::shared_ptr<const C_> >();
+ bp::implicitly_convertible<std::tr1::shared_ptr<C_>, std::tr1::shared_ptr<ContentsEntry> >();
+ }
+};
+
+void expose_contents()
+{
+ bp::register_ptr_to_python<std::tr1::shared_ptr<const ContentsEntry> >();
+ bp::implicitly_convertible<std::tr1::shared_ptr<ContentsEntry>,
+ std::tr1::shared_ptr<const ContentsEntry> >();
+ bp::class_<ContentsEntry, boost::noncopyable>
+ ce("ContentsEntry",
+ "Base class for a contents entry.",
+ bp::no_init
+ );
+ ce.add_property("name", &ContentsEntry::name,
+ "[ro] string\n"
+ "Our name."
+ );
+ ce.def(bp::self_ns::str(bp::self));
+
+ class_contents<ContentsFileEntry>
+ cfilee("ContentsFileEntry",
+ "A file contents entry.",
+ bp::init<const std::string &>("__init__(name_string)")
+ );
+
+ class_contents<ContentsDirEntry>
+ cdire("ContentsDirEntry",
+ "A directory contents entry.",
+ bp::init<const std::string &>("__init__(name_string)")
+ );
+
+ class_contents<ContentsMiscEntry>
+ cme("ContentsMiscEntry",
+ "A misc contents entry.",
+ bp::init<const std::string &>("__init__(name_string)")
+ );
+
+ class_contents<ContentsFifoEntry>
+ cfifoe("ContentsFifoEntry",
+ "A fifo contents entry.",
+ bp::init<const std::string &>("__init__(name_string)")
+ );
+
+ class_contents<ContentsDevEntry>
+ cdeve("ContentsDevEntry",
+ "A dev contents entry.",
+ bp::init<const std::string &>("__init__(name_string)")
+ );
+
+ class_contents<ContentsSymEntry>
+ cse("ContentsSymEntry",
+ "A sym contents entry.",
+ bp::init<const std::string &, const std::string &>("__init__(name_string, target_string)")
+ );
+ cse.add_property("target", &ContentsSymEntry::target,
+ "[ro] string\n"
+ "Our target (as per readlink)."
+ );
+ cse.def(bp::self_ns::str(bp::self));
+
+ register_shared_ptrs_to_python<Contents>();
+ bp::class_<Contents, boost::noncopyable>
+ c("Contents",
+ "Iterable of ContentsEntry.\n"
+ "A package's contents.",
+ bp::init<>("__init__()")
+ );
+ c.def("add", &Contents::add,
+ "add(ContentsEntry)\n"
+ "Add a new entry."
+ );
+ c.def("__iter__", bp::range(&Contents::begin, &Contents::end));
+}
diff --git a/python/contents_TEST.py b/python/contents_TEST.py
new file mode 100755
index 0000000..328e883
--- /dev/null
+++ b/python/contents_TEST.py
@@ -0,0 +1,92 @@
+#!/usr/bin/env python
+# vim: set fileencoding=utf-8 sw=4 sts=4 et :
+
+#
+# Copyright (c) 2007 Piotr Jaroszyński <peper@gentoo.org>
+#
+# This file is part of the Paludis package manager. Paludis is free software;
+# you can redistribute it and/or modify it under the terms of the GNU General
+# Public License version 2, as published by the Free Software Foundation.
+#
+# Paludis 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., 59 Temple
+# Place, Suite 330, Boston, MA 02111-1307 USA
+#
+
+from paludis import *
+import unittest
+
+class TestCase_Contents(unittest.TestCase):
+ def test_01_contents_entry(self):
+ self.assertRaises(Exception, ContentsEntry)
+
+ def test_02_file_entry(self):
+ e = ContentsFileEntry("/foo")
+
+ self.assert_(isinstance(e, ContentsEntry))
+ self.assertEquals(str(e), "/foo")
+ self.assertEquals(e.name, "/foo")
+
+ def test_03_dir_entry(self):
+ e = ContentsDirEntry("/foo")
+
+ self.assert_(isinstance(e, ContentsEntry))
+ self.assertEquals(str(e), "/foo")
+ self.assertEquals(e.name, "/foo")
+
+ def test_04_misc_entry(self):
+ e = ContentsMiscEntry("/foo")
+
+ self.assert_(isinstance(e, ContentsEntry))
+ self.assertEquals(str(e), "/foo")
+ self.assertEquals(e.name, "/foo")
+
+ def test_05_fifo_entry(self):
+ e = ContentsFifoEntry("/foo")
+
+ self.assert_(isinstance(e, ContentsEntry))
+ self.assertEquals(str(e), "/foo")
+ self.assertEquals(e.name, "/foo")
+
+ def test_06_dev_entry(self):
+ e = ContentsDevEntry("/foo")
+
+ self.assert_(isinstance(e, ContentsEntry))
+ self.assertEquals(str(e), "/foo")
+ self.assertEquals(e.name, "/foo")
+
+ def test_07_sym_entry(self):
+ e = ContentsSymEntry("/foo", "/blah")
+
+ self.assert_(isinstance(e, ContentsEntry))
+ self.assertEquals(str(e), "/foo -> /blah")
+ self.assertEquals(e.name, "/foo")
+ self.assertEquals(e.target, "/blah")
+
+ def test_08_contents(self):
+ entries = []
+ entries.append(ContentsSymEntry("/foo", "/blah"))
+ entries.append(ContentsFileEntry("/foo"))
+ entries.append(ContentsDevEntry("/dev/foo"))
+ entries.append(ContentsDirEntry("/bar"))
+ entries.append(ContentsFifoEntry("/baz"))
+
+ c = Contents()
+ for entry in entries:
+ c.add(entry)
+
+ for (i, entry) in enumerate(c):
+ self.assertEquals(entry.name, entries[i].name)
+ self.assertEquals(type(entry), type(entries[i]))
+ if i==0:
+ self.assertEquals(entry.target, entries[i].target)
+ if i>4:
+ self.assertEquals("TOO MANY ENTRIES", "OK")
+
+if __name__ == "__main__":
+ unittest.main()
diff --git a/python/dep_spec.cc b/python/dep_spec.cc
new file mode 100644
index 0000000..4af29d7
--- /dev/null
+++ b/python/dep_spec.cc
@@ -0,0 +1,167 @@
+/* vim: set sw=4 sts=4 et foldmethod=syntax : */
+
+/*
+ * Copyright (c) 2007 Piotr Jaroszyński <peper@gentoo.org>
+ *
+ * This file is part of the Paludis package manager. Paludis is free software;
+ * you can redistribute it and/or modify it under the terms of the GNU General
+ * Public License version 2, as published by the Free Software Foundation.
+ *
+ * Paludis 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., 59 Temple
+ * Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <paludis_python.hh>
+
+#include <paludis/dep_spec.hh>
+
+using namespace paludis;
+using namespace paludis::python;
+namespace bp = boost::python;
+
+void expose_dep_spec()
+{
+ static register_exception<PackageDepSpecError>
+ PackageDepSpecError("PackageDepSpecError");
+
+ enum_auto("PackageDepSpecParseMode", last_pds_pm);
+
+ register_shared_ptrs_to_python<DepSpec>();
+ bp::class_<DepSpec, boost::noncopyable>
+ ds("DepSpec",
+ "Base class for a dependency spec.",
+ bp::no_init
+ );
+ ds.def("as_use_dep_spec", &DepSpec::as_use_dep_spec,
+ bp::return_value_policy<bp::reference_existing_object>(),
+ "as_use_dep_spec() -> UseDepSpec\n"
+ "Return us as a UseDepSpec, or None if we are not a UseDepSpec."
+ );
+ ds.def("as_package_dep_spec", &DepSpec::as_package_dep_spec,
+ bp::return_value_policy<bp::reference_existing_object>(),
+ "as_package_dep_spec() -> PackageDepSpec\n"
+ "Return us as a PackageDepSpec, or None if we are not a UseDepSpec."
+ );
+
+ register_shared_ptrs_to_python<CompositeDepSpec>();
+ bp::class_<CompositeDepSpec, bp::bases<DepSpec>, boost::noncopyable>
+ cds("CompositeDepSpec",
+ "Iterable class for dependency specs that have a number of child dependency specs.",
+ bp::no_init
+ );
+ cds.def("__iter__", bp::range(&CompositeDepSpec::begin, &CompositeDepSpec::end));
+
+ bp::class_<AnyDepSpec, bp::bases<CompositeDepSpec>, boost::noncopyable>
+ anyds("AnyDepSpec",
+ "Represents a \"|| ( )\" dependency block.",
+ bp::no_init
+ );
+
+ bp::class_<AllDepSpec, bp::bases<CompositeDepSpec>, boost::noncopyable>
+ allds("AllDepSpec",
+ "Represents a ( first second third ) or top level group of dependency specs.",
+ bp::no_init
+ );
+
+ bp::class_<UseDepSpec, bp::bases<CompositeDepSpec>, boost::noncopyable>
+ useds("UseDepSpec",
+ "Represents a use? ( ) dependency spec.",
+ bp::no_init
+ );
+ useds.add_property("flag", &UseDepSpec::flag,
+ "[ro] UseFlagName\n"
+ "Our use flag name."
+ );
+ useds.add_property("inverse", &UseDepSpec::inverse,
+ "[ro] bool\n"
+ "Are we a ! flag?"
+ );
+
+ bp::class_<StringDepSpec, bp::bases<DepSpec>, boost::noncopyable>
+ strds("StringDepSpec",
+ "A StringDepSpec represents a non-composite dep spec with an associated piece of text.",
+ bp::no_init
+ );
+
+ bp::to_python_converter<std::pair<const UseFlagName, UseFlagState>,
+ pair_to_tuple<const UseFlagName, UseFlagState> >();
+ bp::class_<UseRequirements>
+ ur("UseRequirements",
+ "A selection of USE flag requirements.",
+ bp::no_init
+ );
+ ur.def("state", &UseRequirements::state,
+ "state(UseFlagName) -> UseFlagState\n"
+ "What state is desired for a particular use flag?"
+ );
+ ur.def("__iter__", bp::range(&UseRequirements::begin, &UseRequirements::end));
+ register_shared_ptrs_to_python<UseRequirements>();
+
+ bp::class_<PackageDepSpec, std::tr1::shared_ptr<const PackageDepSpec>, bp::bases<StringDepSpec> >
+ pkgds("PackageDepSpec",
+ "A PackageDepSpec represents a package name (for example, 'app-editors/vim'),"
+ " possibly with associated version and SLOT restrictions.",
+ bp::init<const std::string &, const PackageDepSpecParseMode>(
+ "__init__(string, PackageDepSpecParseMode)"
+ )
+ );
+ pkgds.add_property("package", &PackageDepSpec::package_ptr,
+ "[ro] QualifiedPackageName\n"
+ "Qualified package name."
+ );
+ pkgds.add_property("package_name_part", &PackageDepSpec::package_name_part_ptr,
+ "[ro] PackageNamePart\n"
+ "Package name part (may be None)"
+ );
+ pkgds.add_property("category_name_part", &PackageDepSpec::category_name_part_ptr,
+ "[ro] CategoryNamePart\n"
+ "Category name part (may be None)."
+ );
+ std::tr1::shared_ptr<const VersionRequirements> (PackageDepSpec::*version_requirements)() const =
+ &PackageDepSpec::version_requirements_ptr;
+ pkgds.add_property("version_requirements", version_requirements,
+ "[ro] VersionRequirements\n"
+ "Version requirements (may be None)."
+ );
+ pkgds.add_property("version_requirements_mode", &PackageDepSpec::version_requirements_mode,
+ "[ro] VersionRequirementsMode\n"
+ "Version requirements mode."
+ );
+ pkgds.add_property("slot", &PackageDepSpec::slot_ptr,
+ "[ro] SlotName\n"
+ "Slot name (may be None)."
+ );
+ pkgds.add_property("repository", &PackageDepSpec::repository_ptr,
+ "[ro] RepositoryName\n"
+ "Repository name (may be None)."
+ );
+ pkgds.add_property("use_requirements", &PackageDepSpec::use_requirements_ptr,
+ "[ro] UseRequirements\n"
+ "Use requirements (may be None)."
+ );
+ pkgds.def(bp::self_ns::str(bp::self));
+
+ bp::class_<PlainTextDepSpec, bp::bases<StringDepSpec>, boost::noncopyable >
+ ptds("PlainTextDepSpec",
+ "A PlainTextDepSpec represents a plain text entry (for example, a URI in SRC_URI).",
+ bp::init<const std::string &>("__init__(string)")
+ );
+ ptds.def(bp::self_ns::str(bp::self));
+
+ bp::class_<BlockDepSpec, bp::bases<StringDepSpec>, boost::noncopyable >
+ bds("BlockDepSpec",
+ "A BlockDepSpec represents a block on a package name (for example, 'app-editors/vim'),"
+ "possibly with associated version and SLOT restrictions.",
+ bp::init<std::tr1::shared_ptr<const PackageDepSpec> >("__init__(PackageDepSpec)")
+ );
+ bds.add_property("blocked_spec", &BlockDepSpec::blocked_spec,
+ "[ro] PackageDepSpec\n"
+ "The spec we're blocking."
+ );
+}
diff --git a/python/dep_spec_TEST.py b/python/dep_spec_TEST.py
new file mode 100755
index 0000000..bb1a6ad
--- /dev/null
+++ b/python/dep_spec_TEST.py
@@ -0,0 +1,105 @@
+#!/usr/bin/env python
+# vim: set fileencoding=utf-8 sw=4 sts=4 et :
+
+#
+# Copyright (c) 2007 Piotr Jaroszyński <peper@gentoo.org>
+#
+# This file is part of the Paludis package manager. Paludis is free software;
+# you can redistribute it and/or modify it under the terms of the GNU General
+# Public License version 2, as published by the Free Software Foundation.
+#
+# Paludis 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., 59 Temple
+# Place, Suite 330, Boston, MA 02111-1307 USA
+#
+
+from paludis import *
+import unittest
+
+class TestCase_1_DepSpecs(unittest.TestCase):
+ def get_depspecs(self):
+ self.ptds = PlainTextDepSpec("foo")
+ self.pds = PackageDepSpec(">=foo/bar-1:100::testrepo", PackageDepSpecParseMode.PERMISSIVE)
+ self.bds = BlockDepSpec(self.pds)
+
+ def test_01_init(self):
+ self.get_depspecs()
+
+ def test_02_create_error(self):
+ self.assertRaises(Exception, DepSpec)
+ self.assertRaises(Exception, AllDepSpec)
+ self.assertRaises(Exception, AnyDepSpec)
+ self.assertRaises(Exception, StringDepSpec)
+ self.assertRaises(BadVersionOperatorError, PackageDepSpec, "<>foo/bar", PackageDepSpecParseMode.PERMISSIVE)
+ self.assertRaises(PackageDepSpecError, PackageDepSpec, "=foo/bar", PackageDepSpecParseMode.PERMISSIVE)
+
+ def test_03_str(self):
+ self.get_depspecs()
+ self.assertEqual(str(self.ptds), "foo")
+ self.assertEqual(str(self.pds), ">=foo/bar-1:100::testrepo")
+ self.assertEqual(str(self.bds.blocked_spec), ">=foo/bar-1:100::testrepo")
+
+ def test_04_slot(self):
+ self.get_depspecs()
+ self.assertEqual(str(self.pds.slot), "100")
+
+ def test_05_package(self):
+ self.get_depspecs()
+ self.assertEqual(str(self.pds.package), "foo/bar")
+
+ def test_06_repository(self):
+ self.get_depspecs()
+ self.assertEqual(str(self.pds.repository), "testrepo")
+
+ def test_07_version_requirements(self):
+ self.get_depspecs()
+ vrc = self.pds.version_requirements
+
+ self.assertEquals(len(list(vrc)), 1)
+ self.assertEquals(iter(vrc).next().version_spec, VersionSpec("1"))
+ self.assertEquals(iter(vrc).next().version_operator.value, VersionOperator(">=").value)
+
+ def test_08_version_requirements_mode(self):
+ self.get_depspecs()
+ self.assertEquals(self.pds.version_requirements_mode, VersionRequirementsMode.AND)
+
+ def test_09_use_requirements(self):
+ spec = PortageDepParser.parse_depend("foo/monkey[foo]", PackageDepSpecParseMode.PERMISSIVE)
+ ur = iter(iter(spec).next().use_requirements).next()
+ self.assertEquals(str(ur[0]), "foo")
+ self.assertEquals(ur[1], UseFlagState.ENABLED)
+
+ def test_10_composites(self):
+ spec = PortageDepParser.parse_depend("|| ( foo/bar foo/baz ) foo/monkey",
+ PackageDepSpecParseMode.PERMISSIVE)
+
+ self.assert_(isinstance(spec, CompositeDepSpec))
+ self.assert_(isinstance(spec, AllDepSpec))
+
+ self.assertEqual(len(list(spec)), 2)
+
+ for i, subspec1 in enumerate(spec):
+ if i == 0:
+ self.assert_(isinstance(subspec1, AnyDepSpec))
+ for j, subspec2 in enumerate(subspec1):
+ if j == 0:
+ self.assert_(isinstance(subspec2, PackageDepSpec))
+ self.assertEquals(str(subspec2), "foo/bar")
+ elif j == 1:
+ self.assert_(isinstance(subspec2, PackageDepSpec))
+ self.assertEquals(str(subspec2), "foo/baz")
+ else:
+ self.assertEquals("Too many items", "OK")
+ elif i == 1:
+ self.assert_(isinstance(subspec1, PackageDepSpec))
+ self.assertEquals(str(subspec1), "foo/monkey")
+ else:
+ self.assertEquals("Too many items", "OK")
+
+if __name__ == "__main__":
+ unittest.main()
diff --git a/python/environment.cc b/python/environment.cc
new file mode 100644
index 0000000..e801277
--- /dev/null
+++ b/python/environment.cc
@@ -0,0 +1,152 @@
+/* vim: set sw=4 sts=4 et foldmethod=syntax : */
+
+/*
+ * Copyright (c) 2007 Piotr Jaroszyński <peper@gentoo.org>
+ *
+ * This file is part of the Paludis package manager. Paludis is free software;
+ * you can redistribute it and/or modify it under the terms of the GNU General
+ * Public License version 2, as published by the Free Software Foundation.
+ *
+ * Paludis 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., 59 Temple
+ * Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <paludis_python.hh>
+
+#include <paludis/environment.hh>
+#include <paludis/environments/environment_maker.hh>
+#include <paludis/environments/paludis/paludis_environment.hh>
+#include <paludis/environments/paludis/paludis_config.hh>
+#include <paludis/environments/no_config/no_config_environment.hh>
+
+using namespace paludis;
+using namespace paludis::python;
+namespace bp = boost::python;
+
+struct NoConfigEnvironmentWrapper :
+ NoConfigEnvironment
+{
+ NoConfigEnvironmentWrapper(const FSEntry & env_dir, const FSEntry & cache_dir,
+ const FSEntry & master_repo_dir) :
+ NoConfigEnvironment(NoConfigEnvironmentParams(env_dir, cache_dir, false,
+ ncer_auto, master_repo_dir)
+ )
+ {
+ }
+};
+
+void expose_environment()
+{
+ static register_exception<NoSuchEnvironmentTypeError>
+ NoSuchEnvironmentTypeError("NoSuchEnvironmentTypeError");
+ static register_exception<PaludisEnvironmentSoDirNotADirectoryError>
+ PaludisEnvironmentSoDirNotADirectoryError("PaludisEnvironmentSoDirNotADirectoryError");
+ static register_exception<PaludisEnvironmentSoDirCannotDlopenError>
+ PaludisEnvironmentSoDirCannotDlopenError("PaludisEnvironmentSoDirCannotDlopenError");
+ static register_exception<PaludisConfigError>
+ PaludisConfigError("PaludisConfigError");
+ static register_exception<PaludisConfigNoDirectoryError>
+ PaludisConfigNoDirectoryError("PaludisConfigNoDirectoryError");
+
+ bp::class_<EnvironmentMaker, boost::noncopyable> em("EnvironmentMaker",
+ "Virtual constructor for environments.",
+ bp::no_init);
+ em.def("make_from_spec", &EnvironmentMaker::make_from_spec,
+ "make_from_spec(spec_string) -> Environment\n"
+ "Make Environment from specification."
+ );
+ em.add_static_property("instance", bp::make_function(&EnvironmentMaker::get_instance,
+ bp::return_value_policy<bp::reference_existing_object>()),
+ "Singleton instance."
+ );
+
+ enum_auto("MaskReasonsOption", last_mro);
+
+ class_options<MaskReasonsOptions>
+ mrs("MaskReasonsOptions", "MaskReasonsOption",
+ "Options for Environment.mask_reasons()."
+ );
+
+ bp::class_<Environment, std::tr1::shared_ptr<Environment>, boost::noncopyable>
+ e("Environment",
+ "Represents a working environment, which contains an available packages database\n"
+ "and provides various methods for querying package visibility and options.",
+ bp::no_init
+ );
+ e.def("default_destinations", &Environment::default_destinations,
+ "default_destinations() -> DestinationsCollection\n"
+ "Default destination candidates for installing packages."
+ );
+ std::tr1::shared_ptr<PackageDatabase> (Environment::* package_database)() =
+ &Environment::package_database;
+ e.add_property("package_database", bp::make_function(package_database,
+ bp::with_custodian_and_ward_postcall<0, 1>()),
+ "[ro] PackageDatabase\n"
+ "Our package database."
+ );
+ e.def("set", &Environment::set,
+ "set(SetName) -> DepSpec\n"
+ "Fetch a named set."
+ );
+ e.def("query_use", &Environment::query_use,
+ "query_use(UseFlagName, PackageDatabaseEntry) -> bool\n"
+ "Is a particular use flag enabled for a particular package?"
+ );
+ e.def("mask_reasons", &Environment::mask_reasons,
+ (bp::arg("PackageDatabaseEntry"), bp::arg("MaskReasonOptions")=MaskReasonsOptions()),
+ "mask_reasons(PackageDatabaseEntry, MaskReasonsOptions=MaskReasonsOptions())"
+ " -> set of MaskReason\n"
+ "Return the reasons for a package being masked."
+ );
+ e.def("root", &Environment::root,
+ "root() -> string\n"
+ "Our root location for installs."
+ );
+ e.def("set_names", &Environment::set_names,
+ "set_names() -> SetNamesCollection\n"
+ "Return all known named sets."
+ );
+
+ bp::class_<PaludisEnvironment, bp::bases<Environment>, boost::noncopyable>
+ pe("PaludisEnvironment",
+ "The PaludisEnvironment is an Environment that corresponds to the normal operating evironment.",
+ bp::init<const std::string &>("__init__(string)")
+ );
+ pe.add_property("config_dir", &PaludisEnvironment::config_dir,
+ "[ro] string\n"
+ "The config directory."
+ );
+
+ bp::class_<NoConfigEnvironmentWrapper, bp::bases<Environment>, boost::noncopyable>
+ nce("NoConfigEnvironment",
+ "An environment that uses a single repository, with no user configuration.",
+ bp::init<const FSEntry &, const FSEntry &, const FSEntry &>(
+ (bp::arg("environment_dir"), bp::arg("write_cache_dir")="/var/empty",
+ bp::arg("master_repository_dir")="/var/empty"),
+ "__init__(environment_dir, write_cache_dir=\"/var/empty\", "
+ "master_repository_dir=\"/var/empty\")"
+ )
+ );
+ std::tr1::shared_ptr<Repository> (NoConfigEnvironment::*main_repository)() =
+ &NoConfigEnvironment::main_repository;
+ nce.add_property("main_repository", main_repository,
+ "[ro] Repository\n"
+ "Main repository."
+ );
+ std::tr1::shared_ptr<Repository> (NoConfigEnvironment::*master_repository)()
+ = &NoConfigEnvironment::master_repository;
+ nce.add_property("master_repository", master_repository,
+ "[ro] Repository\n"
+ "Master repository."
+ );
+ nce.add_property("accept_unstable", bp::object(), &NoConfigEnvironment::set_accept_unstable,
+ "[wo] bool\n"
+ "Should we accept unstable keywords?"
+ );
+}
diff --git a/python/environment_TEST.py b/python/environment_TEST.py
new file mode 100755
index 0000000..e767340
--- /dev/null
+++ b/python/environment_TEST.py
@@ -0,0 +1,123 @@
+#!/usr/bin/env python
+# vim: set fileencoding=utf-8 sw=4 sts=4 et :
+
+#
+# Copyright (c) 2007 Piotr Jaroszyński <peper@gentoo.org>
+#
+# This file is part of the Paludis package manager. Paludis is free software;
+# you can redistribute it and/or modify it under the terms of the GNU General
+# Public License version 2, as published by the Free Software Foundation.
+#
+# Paludis 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., 59 Temple
+# Place, Suite 330, Boston, MA 02111-1307 USA
+#
+
+import os
+
+ph = os.path.join(os.getcwd(), "environment_TEST_dir/home")
+repo = os.path.join(os.getcwd(), "environment_TEST_dir/testrepo")
+slaverepo = os.path.join(os.getcwd(), "environment_TEST_dir/slaverepo")
+os.environ["PALUDIS_HOME"] = ph
+
+from paludis import *
+import unittest
+
+Log.instance.log_level = LogLevel.WARNING;
+
+class TestCase_Environments(unittest.TestCase):
+ def get_envs(self):
+ self.e = EnvironmentMaker.instance.make_from_spec("")
+ self.nce = NoConfigEnvironment(repo)
+
+ def test_01_create(self):
+ NoConfigEnvironment(repo)
+ NoConfigEnvironment(repo, "/var/empty")
+ NoConfigEnvironment(repo, "/var/empty", "/var/empty")
+ NoConfigEnvironment(repo, master_repository_dir="/var/empty")
+ NoConfigEnvironment(repo, write_cache_dir="/var/empty")
+
+ def test_02_create_error(self):
+ self.assertRaises(Exception, Environment)
+ self.assertRaises(Exception, NoConfigEnvironment)
+
+ def test_03_subclass(self):
+ self.assert_(isinstance(NoConfigEnvironment(repo), Environment))
+
+ def test_04_query_use(self):
+ self.get_envs()
+
+ pde = PackageDatabaseEntry("x/x", "1.0", "testrepo")
+
+ self.assert_(self.e.query_use("enabled", pde))
+ self.assert_(not self.e.query_use("not_enabled", pde))
+ self.assert_(not self.e.query_use("sometimes_enabled", pde))
+
+ pde = PackageDatabaseEntry("foo/bar", "1.0", "testrepo")
+
+ self.assert_(self.e.query_use("enabled", pde))
+ self.assert_(not self.e.query_use("not_enabled", pde))
+ self.assert_(self.e.query_use("sometimes_enabled", pde))
+
+ self.assert_(not self.nce.query_use("foo", pde))
+
+ def test_05_mask_reasons(self):
+ self.get_envs()
+ pde = PackageDatabaseEntry("foo/bar", "1.0", "testrepo")
+
+ self.nce.mask_reasons(pde)
+
+ def test_06_package_database(self):
+ self.get_envs()
+
+ self.assert_(isinstance(self.e.package_database, PackageDatabase))
+ self.assert_(isinstance(self.nce.package_database, PackageDatabase))
+
+ def test_07_sets(self):
+ self.get_envs()
+
+ self.assert_(isinstance(self.e.set("everything"), AllDepSpec))
+ self.assert_(isinstance(self.nce.set("everything"), AllDepSpec))
+
+ self.assert_(isinstance(self.e.set_names(), SetNameCollection))
+ self.assert_(isinstance(self.nce.set_names(), SetNameCollection))
+
+ def test_08_repositories(self):
+ self.get_envs()
+ self.nce2 = NoConfigEnvironment(repo, master_repository_dir=slaverepo)
+
+ self.assert_(isinstance(self.nce.main_repository, Repository))
+ self.assertEquals(self.nce.master_repository, None)
+ self.assert_(isinstance(self.nce2.main_repository, Repository))
+ self.assert_(isinstance(self.nce2.master_repository, Repository))
+
+ def test_09_root(self):
+ self.get_envs()
+
+ self.assert_(isinstance(self.e.root(), str))
+ self.assert_(isinstance(self.nce.root(), str))
+
+ def test_10_default_destinations(self):
+ self.get_envs()
+
+ self.assert_(isinstance(self.e.default_destinations(), DestinationsCollection))
+ self.assert_(isinstance(self.nce.default_destinations(), DestinationsCollection))
+
+ def test_11_set_accept_unstable(self):
+ self.get_envs()
+
+ self.nce.accept_unstable = True
+ self.assertRaises(AttributeError, lambda: self.nce.accept_unstable)
+
+ def test_12_config_dir(self):
+ self.get_envs()
+
+ self.assert_(isinstance(self.e.config_dir, str))
+
+if __name__ == "__main__":
+ unittest.main()
diff --git a/python/environment_TEST_cleanup.sh b/python/environment_TEST_cleanup.sh
new file mode 100755
index 0000000..06ff0de
--- /dev/null
+++ b/python/environment_TEST_cleanup.sh
@@ -0,0 +1,8 @@
+#!/bin/bash
+# vim: set ft=sh sw=4 sts=4 et :
+
+if [ -d environment_TEST_dir ] ; then
+ rm -fr environment_TEST_dir
+else
+ true
+fi
diff --git a/python/environment_TEST_setup.sh b/python/environment_TEST_setup.sh
new file mode 100755
index 0000000..f83a38a
--- /dev/null
+++ b/python/environment_TEST_setup.sh
@@ -0,0 +1,97 @@
+#!/bin/bash
+# vim: set ft=sh sw=4 sts=4 et :
+
+mkdir environment_TEST_dir || exit 1
+cd environment_TEST_dir || exit 1
+
+mkdir -p home/.paludis/repositories
+
+cat <<END > home/.paludis/repositories/testrepo.conf
+location = `pwd`/testrepo
+format = ebuild
+names_cache = /var/empty
+cache = /var/empty
+profiles = \${location}/profiles/testprofile
+END
+
+cat <<END > home/.paludis/keywords.conf
+*/* test
+~foo/bar-1.0 ~test
+END
+
+cat <<END > home/.paludis/use.conf
+*/* enabled
+~foo/bar-1.0 sometimes_enabled
+END
+
+cat <<END > home/.paludis/package_mask.conf
+=foo/bar-3*
+END
+
+cat <<END > home/.paludis/package_unmask.conf
+=foo/bar-2*
+END
+
+cat <<END > home/.paludis/licenses.conf
+*/* *
+END
+
+mkdir -p testrepo/{eclass,distfiles,profiles/testprofile,foo/bar/files} || exit 1
+cd testrepo || exit 1
+echo "testrepo" > profiles/repo_name || exit 1
+cat <<END > profiles/categories || exit 1
+foo
+END
+cat <<END > profiles/testprofile/make.defaults
+ARCH=test
+USERLAND=test
+KERNEL=test
+END
+cat <<END > profiles/profiles.desc
+test testprofile stable
+END
+
+cat <<"END" > foo/bar/bar-1.0.ebuild || exit 1
+DESCRIPTION="Test package"
+HOMEPAGE="http://paludis.pioto.org/"
+SRC_URI=""
+SLOT="0"
+IUSE=""
+LICENSE="GPL-2"
+KEYWORDS="test"
+END
+
+cat <<"END" > foo/bar/bar-2.0.ebuild || exit 1
+DESCRIPTION="Test package"
+HOMEPAGE="http://paludis.pioto.org/"
+SRC_URI=""
+SLOT="0"
+IUSE=""
+LICENSE="GPL-2"
+KEYWORDS="~test"
+END
+cd ..
+
+mkdir -p slaverepo/{eclass,distfiles,profiles/testprofile,foo/bar/files} || exit 1
+cd slaverepo || exit 1
+echo "slaverepo" > profiles/repo_name || exit 1
+cat <<END > profiles/testprofile/make.defaults
+ARCH=test
+USERLAND=test
+KERNEL=test
+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 > profiles/profiles.desc
+test testprofile stable
+END
+
+cd ..
diff --git a/python/fs_entry.cc b/python/fs_entry.cc
new file mode 100644
index 0000000..2f7ecac
--- /dev/null
+++ b/python/fs_entry.cc
@@ -0,0 +1,35 @@
+/* vim: set sw=4 sts=4 et foldmethod=syntax : */
+
+/*
+ * Copyright (c) 2007 Piotr Jaroszyński <peper@gentoo.org>
+ *
+ * This file is part of the Paludis package manager. Paludis is free software;
+ * you can redistribute it and/or modify it under the terms of the GNU General
+ * Public License version 2, as published by the Free Software Foundation.
+ *
+ * Paludis 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., 59 Temple
+ * Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <paludis_python.hh>
+
+#include <paludis/util/fs_entry.hh>
+
+using namespace paludis;
+using namespace paludis::python;
+namespace bp = boost::python;
+
+void expose_fs_entry()
+{
+ static register_exception<FSError>
+ FSError("FSError");
+
+ bp::implicitly_convertible<std::string, FSEntry>();
+ bp::to_python_converter<FSEntry, to_string<FSEntry> >();
+}
diff --git a/python/log.cc b/python/log.cc
new file mode 100644
index 0000000..61d9e3e
--- /dev/null
+++ b/python/log.cc
@@ -0,0 +1,57 @@
+/* vim: set sw=4 sts=4 et foldmethod=syntax : */
+
+/*
+ * Copyright (c) 2007 Piotr Jaroszyński <peper@gentoo.org>
+ *
+ * This file is part of the Paludis package manager. Paludis is free software;
+ * you can redistribute it and/or modify it under the terms of the GNU General
+ * Public License version 2, as published by the Free Software Foundation.
+ *
+ * Paludis 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., 59 Temple
+ * Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <paludis_python.hh>
+
+#include <paludis/util/log.hh>
+
+using namespace paludis;
+using namespace paludis::python;
+namespace bp = boost::python;
+
+void expose_log()
+{
+ enum_auto("LogLevel", last_ll);
+ enum_auto("LogContext", last_lc);
+
+ bp::class_<Log, boost::noncopyable>
+ l("Log",
+ "Singleton class that handles log messages.",
+ bp::no_init
+ );
+ l.add_static_property("instance", bp::make_function(&Log::get_instance,
+ bp::return_value_policy<bp::reference_existing_object>()),
+ "[ro] Log\n"
+ "Singleton instance."
+ );
+ void (Log::*message_ptr)(const LogLevel l, const LogContext c, const std::string & s) =
+ &Log::message;
+ l.def("message", message_ptr,
+ "message(LogLevel, LogContext, string) -> None\n"
+ "Log a message at the specified level."
+ );
+ l.add_property("log_level", &Log::log_level, &Log::set_log_level,
+ "[rw] LogLevel\n"
+ "Log level - only display messages of at least this level."
+ );
+ l.add_property("program_name", bp::object(), &Log::set_program_name,
+ "[wo] string\n"
+ "Program name"
+ );
+}
diff --git a/python/log_TEST.py b/python/log_TEST.py
new file mode 100755
index 0000000..67c2ab5
--- /dev/null
+++ b/python/log_TEST.py
@@ -0,0 +1,55 @@
+#!/usr/bin/env python
+# vim: set fileencoding=utf-8 sw=4 sts=4 et :
+
+#
+# Copyright (c) 2007 Piotr Jaroszyński <peper@gentoo.org>
+#
+# This file is part of the Paludis package manager. Paludis is free software;
+# you can redistribute it and/or modify it under the terms of the GNU General
+# Public License version 2, as published by the Free Software Foundation.
+#
+# Paludis 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., 59 Temple
+# Place, Suite 330, Boston, MA 02111-1307 USA
+#
+
+from paludis import *
+import unittest
+
+class TestCase_Log(unittest.TestCase):
+ def test_01_get_instance(self):
+ Log.instance
+
+ def test_02_no_init(self):
+ self.assertRaises(Exception, Log)
+
+ def test_03_log_level(self):
+ ll = Log.instance.log_level
+
+ self.assertEquals(ll, LogLevel.DEBUG)
+ self.assert_(ll >= LogLevel.DEBUG)
+ self.assert_(ll <= LogLevel.SILENT)
+
+ ll = LogLevel.WARNING
+ self.assertEquals(ll, LogLevel.WARNING)
+
+ self.assertRaises(Exception, ll, 123)
+
+ def test_04_program_name(self):
+ Log.instance.program_name = "foo"
+ self.assertRaises(AttributeError, lambda: Log.instance.program_name)
+
+ def test_05_log_message(self):
+ l = Log.instance
+ l.log_level = LogLevel.SILENT
+
+ l.message(LogLevel.DEBUG, LogContext.CONTEXT, "foooo")
+
+
+if __name__ == "__main__":
+ unittest.main()
diff --git a/python/mask_reasons.cc b/python/mask_reasons.cc
new file mode 100644
index 0000000..4c131fd
--- /dev/null
+++ b/python/mask_reasons.cc
@@ -0,0 +1,36 @@
+/* vim: set sw=4 sts=4 et foldmethod=syntax : */
+
+/*
+ * Copyright (c) 2007 Piotr Jaroszyński <peper@gentoo.org>
+ *
+ * This file is part of the Paludis package manager. Paludis is free software;
+ * you can redistribute it and/or modify it under the terms of the GNU General
+ * Public License version 2, as published by the Free Software Foundation.
+ *
+ * Paludis 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., 59 Temple
+ * Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <paludis_python.hh>
+
+#include <paludis/mask_reasons.hh>
+
+using namespace paludis;
+using namespace paludis::python;
+namespace bp = boost::python;
+
+void expose_mask_reasons()
+{
+ enum_auto("MaskReason", last_mr);
+
+ class_options<MaskReasons>
+ mrs("MaskReasons", "MaskReason",
+ "A collection of reasons for why a package is masked."
+ );
+}
diff --git a/python/mask_reasons_TEST.py b/python/mask_reasons_TEST.py
new file mode 100755
index 0000000..7ae8a50
--- /dev/null
+++ b/python/mask_reasons_TEST.py
@@ -0,0 +1,86 @@
+#!/usr/bin/env python
+# vim: set fileencoding=utf-8 sw=4 sts=4 et :
+
+#
+# Copyright (c) 2007 Piotr Jaroszyński <peper@gentoo.org>
+#
+# This file is part of the Paludis package manager. Paludis is free software;
+# you can redistribute it and/or modify it under the terms of the GNU General
+# Public License version 2, as published by the Free Software Foundation.
+#
+# Paludis 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., 59 Temple
+# Place, Suite 330, Boston, MA 02111-1307 USA
+#
+
+from paludis import *
+import unittest
+
+class TestCase_MaskReasons(unittest.TestCase):
+ def test_01_init(self):
+ MaskReasons()
+
+ def test_02_none(self):
+ self.assert_(MaskReasons().none);
+
+ def test_03_any(self):
+ self.assert_(not MaskReasons().any);
+
+ def test_04_add(self):
+ m1 = MaskReasons()
+ m1 = m1 + MaskReason.KEYWORD
+ self.assert_(m1[MaskReason.KEYWORD])
+
+ def test_05_iadd(self):
+ m1 = MaskReasons()
+ m1 += MaskReason.KEYWORD
+ self.assert_(m1[MaskReason.KEYWORD])
+
+ def test_06_sub(self):
+ m1 = MaskReasons()
+ m1 += MaskReason.KEYWORD
+ m1 = m1 - MaskReason.KEYWORD
+ self.assert_(m1.none)
+
+ def test_07_isub(self):
+ m1 = MaskReasons()
+ m1 += MaskReason.KEYWORD
+ m1 -= MaskReason.KEYWORD
+ self.assert_(m1.none)
+
+ def test_08_or(self):
+ m1 = MaskReasons()
+ m2 = MaskReasons()
+ m1 += MaskReason.KEYWORD
+ m2 += MaskReason.EAPI
+ m3 = m1 | m2
+ self.assert_(m3[MaskReason.KEYWORD])
+ self.assert_(m3[MaskReason.EAPI])
+
+ def test_09_ior(self):
+ m1 = MaskReasons()
+ m2 = MaskReasons()
+ m1 += MaskReason.KEYWORD
+ m2 += MaskReason.EAPI
+ m2 |= m1
+ self.assert_(m2[MaskReason.KEYWORD])
+ self.assert_(m2[MaskReason.EAPI])
+
+ def test_10_subtract(self):
+ m1 = MaskReasons()
+ m2 = MaskReasons()
+ m1 += MaskReason.KEYWORD
+ m1 += MaskReason.EAPI
+ m2 += MaskReason.EAPI
+ m1.subtract(m2)
+ self.assert_(m1[MaskReason.KEYWORD])
+ self.assert_(not m1[MaskReason.EAPI])
+
+
+if __name__ == "__main__":
+ unittest.main()
diff --git a/python/name.cc b/python/name.cc
new file mode 100644
index 0000000..d1b6f8f
--- /dev/null
+++ b/python/name.cc
@@ -0,0 +1,137 @@
+/* vim: set sw=4 sts=4 et foldmethod=syntax : */
+
+/*
+ * Copyright (c) 2007 Piotr Jaroszyński <peper@gentoo.org>
+ *
+ * This file is part of the Paludis package manager. Paludis is free software;
+ * you can redistribute it and/or modify it under the terms of the GNU General
+ * Public License version 2, as published by the Free Software Foundation.
+ *
+ * Paludis 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., 59 Temple
+ * Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <paludis_python.hh>
+
+#include <paludis/name.hh>
+
+using namespace paludis;
+using namespace paludis::python;
+namespace bp = boost::python;
+
+void expose_name()
+{
+ static register_exception<PackageNamePartError>
+ PackageNamePartError("PackageNamePartError");
+ static register_exception<CategoryNamePartError>
+ CategoryNamePartError("CategoryNamePartError");
+ static register_exception<QualifiedPackageNameError>
+ QualifiedPackageNameError("QualifiedPackageNameError");
+ static register_exception<UseFlagNameError>
+ UseFlagNameError("UseFlagNameError");
+ static register_exception<SlotNameError>
+ SlotNameError("SlotNameError");
+ static register_exception<RepositoryNameError>
+ RepositoryNameError("RepositoryNameError");
+ static register_exception<KeywordNameError>
+ KeywordNameError("KeywordNameError");
+ static register_exception<SetNameError>
+ SetNameError("SetNameError");
+
+ class_validated<PackageNamePart>
+ pnp("PackageNamePart",
+ "Holds a string that is a valid name for the package part of a QualifiedPackageName."
+ );
+ register_shared_ptrs_to_python<PackageNamePart>();
+
+ class_collection<PackageNamePartCollection>
+ pnpc("PackageNamePartCollection",
+ "Iterable collection of PackageNamePart instances."
+ );
+
+ class_validated<CategoryNamePart>
+ cnp("CategoryNamePart",
+ "Holds a string that is a valid name for the category part of a QualifiedPackageName."
+ );
+ // CategoryNamePart + PackageNamePart = QualifiedPackageName
+ cnp.def(bp::self + bp::other<PackageNamePart>());
+ register_shared_ptrs_to_python<CategoryNamePart>();
+
+ class_collection<CategoryNamePartCollection>
+ cnpc("CategoryNamePartCollection",
+ "Iterable collection of CategoryNamePart instances."
+ );
+
+ class_validated<UseFlagName>
+ ufn("UseFlagName",
+ "Holds a string that is a valid name for a USE flag."
+ );
+
+ class_collection<UseFlagNameCollection>
+ ufnc("UseFlagNameCollection",
+ "Iterable collection of UseFlagName instances."
+ );
+
+ class_validated<SlotName>
+ sln("SlotName",
+ "Holds a string that is a valid name for a SLOT."
+ );
+ register_shared_ptrs_to_python<SlotName>();
+
+ class_validated<RepositoryName>
+ rn("RepositoryName",
+ "Holds a string that is a valid name for a Repository."
+ );
+ register_shared_ptrs_to_python<RepositoryName>();
+
+ class_collection<RepositoryNameCollection>
+ rnc("RepositoryNameCollection",
+ "Iterable collection of RepositoryName instances."
+ );
+
+ class_validated<KeywordName>
+ kn("KeywordName",
+ "Holds a string that is a valid name for a KEYWORD."
+ );
+
+ class_validated<SetName>
+ stn("SetName",
+ "Holds a string that is a valid name for a set."
+ );
+
+ class_collection<SetNameCollection>
+ sc("SetNameCollection",
+ "Iterable of SetName\n"
+ "A collection of set names."
+ );
+
+ register_shared_ptrs_to_python<QualifiedPackageName>();
+ bp::class_<QualifiedPackageName>
+ qpn("QualifiedPackageName",
+ "Represents a category plus package name.",
+ bp::init<const std::string &>("__init__(string)")
+ );
+ qpn.def(bp::init<const CategoryNamePart &, const PackageNamePart &>());
+ qpn.def_readwrite("category", &QualifiedPackageName::category);
+ qpn.def_readwrite("package", &QualifiedPackageName::package);
+ qpn.def("__cmp__", &QualifiedPackageName::compare);
+ qpn.def(bp::self_ns::str(bp::self));
+ bp::implicitly_convertible<std::string, QualifiedPackageName>();
+
+ class_collection<QualifiedPackageNameCollection>
+ qpnc("QualifiedPackageNameCollection",
+ "Iterable collection of QualifiedPackageName instances."
+ );
+
+ bp::enum_<UseFlagState>
+ ufs("UseFlagState");
+ ufs.value("UNSPECIFIED", use_unspecified);
+ ufs.value("DISABLED", use_disabled);
+ ufs.value("ENABLED", use_enabled);
+}
diff --git a/python/name_TEST.py b/python/name_TEST.py
new file mode 100755
index 0000000..8027d88
--- /dev/null
+++ b/python/name_TEST.py
@@ -0,0 +1,70 @@
+#!/usr/bin/env python
+# vim: set fileencoding=utf-8 sw=4 sts=4 et :
+
+#
+# Copyright (c) 2007 Piotr Jaroszyński <peper@gentoo.org>
+#
+# This file is part of the Paludis package manager. Paludis is free software;
+# you can redistribute it and/or modify it under the terms of the GNU General
+# Public License version 2, as published by the Free Software Foundation.
+#
+# Paludis 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., 59 Temple
+# Place, Suite 330, Boston, MA 02111-1307 USA
+#
+
+from paludis import *
+import unittest
+
+class TestCase_Names(unittest.TestCase):
+ def test_1_create(self):
+ self.names = {}
+ self.names["cat-foo"] = CategoryNamePart("cat-foo")
+ self.names["pkg"] = PackageNamePart("pkg")
+ self.names["cat-foo/pkg"] = QualifiedPackageName(self.names["cat-foo"], self.names["pkg"])
+ self.names["cat-blah/pkg"] = QualifiedPackageName("cat-blah/pkg")
+ self.names["useflag"] = UseFlagName("useflag")
+ self.names["3.3"] = SlotName("3.3")
+ self.names["repo"] = RepositoryName("repo")
+ self.names["keyword"] = KeywordName("keyword")
+ self.names["set"] = SetName("set")
+
+ def test_2_create_error(self):
+ self.assertRaises(PackageNamePartError, PackageNamePart, ":bad")
+ self.assertRaises(CategoryNamePartError, CategoryNamePart, ":bad")
+ self.assertRaises(QualifiedPackageNameError, QualifiedPackageName, ":bad")
+ self.assertRaises(UseFlagNameError, UseFlagName, "-bad")
+ self.assertRaises(SlotNameError, SlotName, ":bad")
+ self.assertRaises(RepositoryNameError, RepositoryName, ":bad")
+ self.assertRaises(KeywordNameError, KeywordName, ":bad")
+ self.assertRaises(SetNameError, SetName, ":bad")
+ self.assertRaises(Exception, PackageNamePartCollection)
+ self.assertRaises(Exception, CategoryNamePartCollection)
+ self.assertRaises(Exception, QualifiedPackageNameCollection)
+ self.assertRaises(Exception, UseFlagNameCollection)
+ self.assertRaises(Exception, RepositoryNameCollection)
+
+ def test_3_str(self):
+ self.test_1_create()
+ for (k, v) in self.names.items():
+ self.assertEqual(str(v), k)
+
+ def test_4_operators(self):
+ self.assert_(CategoryNamePart("cat-foo") + PackageNamePart("pkg") == QualifiedPackageName("cat-foo/pkg"))
+
+ def test_5_data_members(self):
+ qpn = QualifiedPackageName("cat/foo")
+ self.assertEqual(str(qpn.category), "cat")
+ self.assertEqual(str(qpn.package), "foo")
+ qpn.category = "blah"
+ qpn.package = "bar"
+ self.assertEqual(str(qpn.category), "blah")
+ self.assertEqual(str(qpn.package), "bar")
+
+if __name__ == "__main__":
+ unittest.main()
diff --git a/python/package_database.cc b/python/package_database.cc
new file mode 100644
index 0000000..716d82e
--- /dev/null
+++ b/python/package_database.cc
@@ -0,0 +1,104 @@
+/* vim: set sw=4 sts=4 et foldmethod=syntax : */
+
+/*
+ * Copyright (c) 2007 Piotr Jaroszyński <peper@gentoo.org>
+ *
+ * This file is part of the Paludis package manager. Paludis is free software;
+ * you can redistribute it and/or modify it under the terms of the GNU General
+ * Public License version 2, as published by the Free Software Foundation.
+ *
+ * Paludis 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., 59 Temple
+ * Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <paludis_python.hh>
+
+#include <paludis/dep_spec.hh>
+#include <paludis/query.hh>
+#include <paludis/environment.hh>
+#include <paludis/package_database_entry.hh>
+#include <paludis/package_database.hh>
+#include <paludis/util/collection.hh>
+
+using namespace paludis;
+using namespace paludis::python;
+namespace bp = boost::python;
+
+void expose_package_database()
+{
+ static register_exception<PackageDatabaseError>
+ PackageDatabaseError("PackageDatabaseError");
+ static register_exception<PackageDatabaseLookupError>
+ PackageDatabaseLookupError("PackageDatabaseLookupError");
+ static register_exception<AmbiguousPackageNameError>
+ AmbiguousPackageNameError("AmbiguousPackageNameError");
+ static register_exception<DuplicateRepositoryError>
+ DuplicateRepositoryError("DuplicateRepositoryError");
+ static register_exception<NoSuchPackageError>
+ NoSuchPackageError("NoSuchPackageError");
+ static register_exception<NoSuchRepositoryError>
+ NoSuchRepositoryError("NoSuchRepositoryError");
+
+ enum_auto("QueryOrder", last_qo);
+
+ register_shared_ptrs_to_python<PackageDatabase>();
+ bp::class_<PackageDatabase, boost::noncopyable>
+ pd("PackageDatabase",
+ "A PackageDatabase can be queried for Package instances.\n",
+ bp::no_init
+ );
+ std::tr1::shared_ptr<PackageDatabaseEntryCollection>
+ (PackageDatabase::*query)(const Query &, const QueryOrder) const = &PackageDatabase::query;
+ pd.def("query", query,
+ "query(Query, QueryOrder) -> PackageDatabaseEntryCollection\n"
+ "Query the repository."
+ );
+ pd.add_property("favourite_repository", &PackageDatabase::favourite_repository,
+ "[ro] RepositoryName\n"
+ "Name of our 'favourite' repository"
+ );
+ std::tr1::shared_ptr<const Repository>
+ (PackageDatabase::* fetch_repository)(const RepositoryName &) const =
+ &PackageDatabase::fetch_repository;
+ pd.def("fetch_repository", fetch_repository,
+ "fetch_repository(RepositoryName) -> Repository\n"
+ "Fetch a named repository."
+ );
+ pd.def("fetch_unique_qualified_package_name", &PackageDatabase::fetch_unique_qualified_package_name,
+ "fetch_unique_qualified_package_name(PackageNamePart) -> QualifiedPackageName\n"
+ "Disambiguate a package name."
+ );
+ pd.def("more_important_than", &PackageDatabase::more_important_than,
+ "more_important_than(RepositoryName, RepositoryName) -> bool\n"
+ "Return true if the first repository is more important than the second."
+ );
+ pd.add_property("repositories",
+ bp::range(&PackageDatabase::begin_repositories, &PackageDatabase::end_repositories),
+ "[ro] Iterable of Repository\n"
+ "Our repositories"
+ );
+
+ bp::register_ptr_to_python<std::tr1::shared_ptr<PackageDatabaseEntry> >();
+ bp::class_<PackageDatabaseEntry>
+ pde("PackageDatabaseEntry",
+ "Holds an entry in a PackageDatabase, and used to identify"
+ " a specific version of a package in a particular repository.",
+ bp::init<const QualifiedPackageName &, const VersionSpec &, const RepositoryName &>(
+ "__init__(QualifiedPackageName, VersionSpec, RepositoryName)"
+ )
+ );
+ pde.def(bp::self_ns::str(bp::self));
+ pde.def("__eq__", &PackageDatabaseEntry::operator==);
+ pde.def("__ne__", &PackageDatabaseEntry::operator!=);
+
+ class_collection<PackageDatabaseEntryCollection>
+ pdec("PackageDatabaseEntryCollection",
+ "An iterable collection of PackageDatabaseEntry instances."
+ );
+}
diff --git a/python/package_database_TEST.py b/python/package_database_TEST.py
new file mode 100755
index 0000000..7cbb868
--- /dev/null
+++ b/python/package_database_TEST.py
@@ -0,0 +1,72 @@
+#!/usr/bin/env python
+# vim: set fileencoding=utf-8 sw=4 sts=4 et :
+
+#
+# Copyright (c) 2007 Piotr Jaroszyński <peper@gentoo.org>
+#
+# This file is part of the Paludis package manager. Paludis is free software;
+# you can redistribute it and/or modify it under the terms of the GNU General
+# Public License version 2, as published by the Free Software Foundation.
+#
+# Paludis 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., 59 Temple
+# Place, Suite 330, Boston, MA 02111-1307 USA
+#
+
+import os
+
+os.environ["PALUDIS_HOME"] = os.path.join(os.getcwd(), "package_database_TEST_dir/home")
+
+from paludis import *
+import unittest
+
+Log.instance.log_level = LogLevel.WARNING;
+
+class TestCase_PackageDatabase(unittest.TestCase):
+ def get_db(self):
+ self.env = EnvironmentMaker.instance.make_from_spec("")
+ self.db = self.env.package_database
+
+ def test_01_create_error(self):
+ self.assertRaises(Exception, PackageDatabase)
+
+ def test_02_favourite_repo(self):
+ self.get_db()
+ self.assertEqual(str(self.db.favourite_repository), "testrepo")
+
+ def test_03_fech_unique_qpn(self):
+ self.get_db()
+ self.assertEqual(str(QualifiedPackageName("foo/bar")), str(self.db.fetch_unique_qualified_package_name("bar")))
+
+ def test_04_exceptions(self):
+ self.get_db()
+ self.assertRaises(AmbiguousPackageNameError, self.db.fetch_unique_qualified_package_name, "baz")
+ self.assertRaises(NoSuchPackageError, self.db.fetch_unique_qualified_package_name, "foobarbaz")
+
+ def test_5_query(self):
+ self.get_db()
+ pkgs = list(self.db.query(Query.Package("foo/bar"), QueryOrder.ORDER_BY_VERSION))
+ self.assertEqual(pkgs, [
+ PackageDatabaseEntry("foo/bar", "1.0", "testrepo"),
+ PackageDatabaseEntry("foo/bar", "2.0", "testrepo"),
+ ])
+
+ pkgs = list(self.db.query(Query.Matches(PackageDepSpec(">=foo/bar-10", PackageDepSpecParseMode.PERMISSIVE)),
+ QueryOrder.ORDER_BY_VERSION))
+ self.assertEqual(len(pkgs), 0)
+
+ def test_6_repositories(self):
+ self.get_db()
+ self.assert_(self.db.more_important_than("testrepo", "virtuals"))
+ self.assert_(not self.db.more_important_than("virtuals", "testrepo"))
+ self.assertRaises(NoSuchRepositoryError, self.db.fetch_repository, "blah")
+
+ self.assertEqual(len(list(self.db.repositories)), 3)
+
+if __name__ == "__main__":
+ unittest.main()
diff --git a/python/package_database_TEST_cleanup.sh b/python/package_database_TEST_cleanup.sh
new file mode 100755
index 0000000..73ec3fe
--- /dev/null
+++ b/python/package_database_TEST_cleanup.sh
@@ -0,0 +1,8 @@
+#!/bin/bash
+# vim: set ft=sh sw=4 sts=4 et :
+
+if [ -d package_database_TEST_dir ] ; then
+ rm -fr package_database_TEST_dir
+else
+ true
+fi
diff --git a/python/package_database_TEST_setup.sh b/python/package_database_TEST_setup.sh
new file mode 100755
index 0000000..0d758cf
--- /dev/null
+++ b/python/package_database_TEST_setup.sh
@@ -0,0 +1,84 @@
+#!/bin/bash
+# vim: set ft=sh sw=4 sts=4 et :
+
+mkdir package_database_TEST_dir || exit 1
+cd package_database_TEST_dir || exit 1
+
+mkdir -p home/.paludis/repositories
+
+cat <<END > home/.paludis/repositories/testrepo.conf
+location = `pwd`/testrepo
+format = ebuild
+names_cache = /var/empty
+cache = /var/empty
+profiles = \${location}/profiles/testprofile
+END
+
+cat <<END > home/.paludis/keywords.conf
+*/* test
+~foo/bar-1 ~test
+END
+
+cat <<END > home/.paludis/use.conf
+*/* enabled
+~foo/bar-1 sometimes_enabled
+END
+
+cat <<END > home/.paludis/licenses.conf
+*/* *
+END
+
+mkdir -p testrepo/{eclass,distfiles,profiles/testprofile,foo/bar/files,foo/baz/files,quux/baz/files} || exit 1
+cd testrepo || exit 1
+echo "testrepo" > profiles/repo_name || exit 1
+cat <<END > profiles/categories || exit 1
+foo
+quux
+END
+cat <<END > profiles/testprofile/make.defaults
+ARCH=test
+USERLAND=test
+KERNEL=test
+END
+
+cat <<"END" > foo/bar/bar-1.0.ebuild || exit 1
+DESCRIPTION="Test package"
+HOMEPAGE="http://paludis.pioto.org/"
+SRC_URI=""
+SLOT="0"
+IUSE=""
+LICENSE="GPL-2"
+KEYWORDS="test"
+END
+
+cat <<"END" > foo/bar/bar-2.0.ebuild || exit 1
+DESCRIPTION="Test package"
+HOMEPAGE="http://paludis.pioto.org/"
+SRC_URI=""
+SLOT="0"
+IUSE=""
+LICENSE="GPL-2"
+KEYWORDS="~test"
+END
+
+cat <<"END" > foo/baz/baz-1.0.ebuild || exit 1
+DESCRIPTION="Test package"
+HOMEPAGE="http://paludis.pioto.org/"
+SRC_URI=""
+SLOT="0"
+IUSE=""
+LICENSE="GPL-2"
+KEYWORDS="test"
+END
+
+cat <<"END" > quux/baz/baz-2.0.ebuild || exit 1
+DESCRIPTION="Test package"
+HOMEPAGE="http://paludis.pioto.org/"
+SRC_URI=""
+SLOT="0"
+IUSE=""
+LICENSE="GPL-2"
+KEYWORDS="test"
+END
+
+cd ..
diff --git a/python/paludis_python.hh b/python/paludis_python.hh
new file mode 100755
index 0000000..08bf8be
--- /dev/null
+++ b/python/paludis_python.hh
@@ -0,0 +1,246 @@
+/* vim: set sw=4 sts=4 et foldmethod=syntax : */
+
+/*
+ * Copyright (c) 2007 Piotr Jaroszyński <peper@gentoo.org>
+ *
+ * This file is part of the Paludis package manager. Paludis is free software;
+ * you can redistribute it and/or modify it under the terms of the GNU General
+ * Public License version 2, as published by the Free Software Foundation.
+ *
+ * Paludis 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., 59 Temple
+ * Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <map>
+#include <utility>
+#include <tr1/memory>
+#include <tr1/functional>
+
+#include <paludis/util/stringify.hh>
+
+#include <boost/python.hpp>
+
+#ifndef PALUDIS_GUARD_PYTHON_PALUDIS_PYTHON_HH
+#define PALUDIS_GUARD_PYTHON_PALUDIS_PYTHON_HH 1
+
+using namespace paludis;
+namespace bp = boost::python;
+
+namespace paludis
+{
+ // Make Boost.Python work with std::tr1::shared_ptr<>
+ template <typename T_>
+ inline T_ * get_pointer(std::tr1::shared_ptr<T_> const & p)
+ {
+ return p.get();
+ }
+
+ // Make Boost.Python work with std::tr1::shared_ptr<const>
+ template <typename T_>
+ inline T_ * get_pointer(std::tr1::shared_ptr<const T_> const & p)
+ {
+ return const_cast<T_*>(p.get());
+ }
+}
+
+namespace boost
+{
+ namespace python
+ {
+ // Make Boost.Python work with std::tr1::shared_ptr<>
+ template <typename T_>
+ struct pointee<std::tr1::shared_ptr<T_> >
+ {
+ typedef T_ type;
+ };
+
+ // Make Boost.Python work with std::tr1::shared_ptr<const>
+ template <typename T_>
+ struct pointee<std::tr1::shared_ptr<const T_> >
+ {
+ typedef T_ type;
+ };
+ }
+}
+
+namespace paludis
+{
+ namespace python
+ {
+ // register shared_ptrs
+ template <typename T_>
+ void
+ register_shared_ptrs_to_python()
+ {
+ bp::register_ptr_to_python<std::tr1::shared_ptr<T_> >();
+ bp::register_ptr_to_python<std::tr1::shared_ptr<const T_> >();
+ bp::implicitly_convertible<std::tr1::shared_ptr<T_>, std::tr1::shared_ptr<const T_> >();
+ }
+
+ // expose stringifyable enums
+ template <typename E_>
+ void
+ enum_auto(const std::string & name, E_ e_last)
+ {
+ bp::enum_<E_> enum_(name.c_str());
+ for (E_ e(static_cast<E_>(0)); e != e_last ; e = static_cast<E_>(static_cast<int>(e) + 1))
+ {
+ const std::string e_name_low = stringify(e);
+ std::string e_name_up;
+ std::transform(e_name_low.begin(), e_name_low.end(), std::back_inserter(e_name_up), &::toupper);
+ enum_.value(e_name_up.c_str(), e);
+ }
+ }
+
+ // Translates Paludis C++ exception to a Python one with output of message() and backtrace() saved
+ // in the corresponding string attributes of the Python exception.
+ template <typename Ex_>
+ class register_exception
+ {
+ private:
+ PyObject * _e;
+ const std::string _name;
+ const std::string _longname;
+
+ public:
+ register_exception(const std::string & name) :
+ _name(name),
+ _longname("paludis."+name)
+ {
+ _e = PyErr_NewException(const_cast<char*>(_longname.c_str()), NULL, NULL);
+ PyModule_AddObject(bp::detail::current_scope, const_cast<char*>(_name.c_str()), _e);
+ bp::register_exception_translator<Ex_>(
+ std::tr1::bind(std::tr1::mem_fn(&register_exception<Ex_>::translator),
+ this, std::tr1::placeholders::_1)
+ );
+ }
+
+ void
+ translator(const Ex_ & x) const
+ {
+ PyObject * backtrace = PyString_FromString(x.backtrace("\n").c_str());
+ PyObject * message = PyString_FromString(x.message().c_str());
+ PyObject * what = PyString_FromString(x.what());
+ PyObject_SetAttrString(_e, "backtrace", backtrace);
+ PyObject_SetAttrString(_e, "message", message);
+ PyObject_SetAttrString(_e, "what", what);
+
+ PyErr_SetString(_e, x.message().c_str());
+ }
+ };
+
+ // expose Validated classes
+ template <typename V_, typename Data_=std::string>
+ class class_validated :
+ public bp::class_<V_>
+ {
+ public:
+ class_validated(const std::string & name,
+ const std::string & class_doc, const std::string & init_arg="string") :
+ bp::class_<V_>(name.c_str(), class_doc.c_str(),
+ bp::init<const Data_ &>(("__init__("+init_arg+")").c_str())
+ )
+ {
+ this->def(bp::self_ns::str(bp::self));
+ bp::implicitly_convertible<Data_, V_>();
+ }
+ };
+
+ // expose *Collection classes
+ template <typename C_>
+ class class_collection :
+ public bp::class_<C_, boost::noncopyable>
+ {
+ public:
+ class_collection(const std::string & name, const std::string & class_doc) :
+ bp::class_<C_, boost::noncopyable>(name.c_str(), class_doc.c_str(), bp::no_init)
+ {
+ this->def("__iter__", bp::range(&C_::begin, &C_::end));
+ register_shared_ptrs_to_python<C_>();
+ }
+ };
+
+ // expose Options classes
+ template <typename O_>
+ class class_options :
+ public bp::class_<O_>
+ {
+ public:
+ class_options(const std::string & set_name, const std::string & bit_name,
+ const std::string & class_doc) :
+ bp::class_<O_>(set_name.c_str(), class_doc.c_str(), bp::init<>("__init__()"))
+ {
+ this->add_property("any", &O_::any,
+ "[ro] bool\n"
+ "Is any bit enabled."
+ );
+ this->add_property("none", &O_::none,
+ "[ro] bool\n"
+ "Are all bits disabled."
+ );
+ this->def("__add__", &O_::operator+,
+ ("__add__("+bit_name+") -> "+set_name+"\n"
+ "Return a copy of ourself with the specified bit enabled.").c_str()
+ );
+ this->def("__iadd__", &O_::operator+=, bp::return_self<>(),
+ ("__iadd__("+bit_name+") -> "+set_name+"\n"
+ "Enable the specified bit.").c_str()
+ );
+ this->def("__sub__", &O_::operator-,
+ ("__sub__("+bit_name+") -> "+set_name+"\n"
+ "Return a copy of ourself with the specified bit disabled.").c_str()
+ );
+ this->def("__isub__", &O_::operator-=, bp::return_self<>(),
+ ("__isub__("+bit_name+") -> "+set_name+"\n"
+ "Disable the specified bit.").c_str()
+ );
+ this->def("__or__", &O_::operator|,
+ ("__or__("+set_name+") -> "+set_name+"\n"
+ "Return a copy of ourself, bitwise 'or'ed with another Options set.").c_str()
+ );
+ this->def("__ior__", &O_::operator|=, bp::return_self<>(),
+ ("__ior__("+set_name+") -> "+set_name+"\n"
+ "Enable any bits that are enabled in the parameter.").c_str()
+ );
+ this->def("__getitem__", &O_::operator[],
+ ("__getitem__("+bit_name+") -> bool\n"
+ "Returns whether the specified bit is enabled.").c_str()
+ );
+ this->def("subtract", &O_::subtract, bp::return_self<>(),
+ ("subtract("+set_name+") -> "+set_name+"\n"
+ "Disable any bits that are enabled in the parameter.").c_str()
+ );
+ }
+ };
+
+ // Convert to string
+ template <typename T_>
+ struct to_string
+ {
+ static PyObject *
+ convert(const T_ & x)
+ {
+ return PyString_FromString(stringify<T_>(x).c_str());
+ }
+ };
+
+ // Convert pair to tuple
+ template <typename first_, typename second_>
+ struct pair_to_tuple
+ {
+ static PyObject *
+ convert(const std::pair<first_, second_> & x)
+ {
+ return bp::incref(bp::make_tuple(x.first, x.second).ptr());
+ }
+ };
+ } // namespace paludis::python
+} // namespace paludis
+
+#endif
diff --git a/python/paludis_python_so.cc b/python/paludis_python_so.cc
new file mode 100644
index 0000000..e3aa3a3
--- /dev/null
+++ b/python/paludis_python_so.cc
@@ -0,0 +1,55 @@
+/* vim: set sw=4 sts=4 et foldmethod=syntax : */
+
+/*
+ * Copyright (c) 2007 Piotr Jaroszyński <peper@gentoo.org>
+ *
+ * This file is part of the Paludis package manager. Paludis is free software;
+ * you can redistribute it and/or modify it under the terms of the GNU General
+ * Public License version 2, as published by the Free Software Foundation.
+ *
+ * Paludis 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., 59 Temple
+ * Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <paludis_python.hh>
+
+void expose_contents();
+void expose_dep_spec();
+void expose_environment();
+void expose_fs_entry();
+void expose_log();
+void expose_mask_reasons();
+void expose_name();
+void expose_package_database();
+void expose_portage_dep_parser();
+void expose_query();
+void expose_repository();
+void expose_version_metadata();
+void expose_version_operator();
+void expose_version_requirements();
+void expose_version_spec();
+
+BOOST_PYTHON_MODULE(paludis)
+{
+ expose_version_spec();
+ expose_version_operator();
+ expose_version_requirements();
+ expose_fs_entry();
+ expose_contents();
+ expose_mask_reasons();
+ expose_version_metadata();
+ expose_dep_spec();
+ expose_portage_dep_parser();
+ expose_name();
+ expose_log();
+ expose_query();
+ expose_environment();
+ expose_package_database();
+ expose_repository();
+}
diff --git a/python/portage_dep_parser.cc b/python/portage_dep_parser.cc
new file mode 100644
index 0000000..b4b9cbe
--- /dev/null
+++ b/python/portage_dep_parser.cc
@@ -0,0 +1,75 @@
+/* vim: set sw=4 sts=4 et foldmethod=syntax : */
+
+/*
+ * Copyright (c) 2007 Piotr Jaroszyński <peper@gentoo.org>
+ *
+ * This file is part of the Paludis package manager. Paludis is free software;
+ * you can redistribute it and/or modify it under the terms of the GNU General
+ * Public License version 2, as published by the Free Software Foundation.
+ *
+ * Paludis 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., 59 Temple
+ * Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <paludis_python.hh>
+
+#include <paludis/portage_dep_parser.hh>
+
+using namespace paludis;
+using namespace paludis::python;
+namespace bp = boost::python;
+
+void expose_portage_dep_parser()
+{
+ static register_exception<DepStringParseError>
+ DepStringParseError("DepStringParseError");
+ static register_exception<DepStringNestingError>
+ DepStringNestingError("DepStringNestingError");
+
+ bp::class_<PortageDepParser, boost::noncopyable>
+ pdp("PortageDepParser",
+ "The PortageDepParser converts string representations "
+ "of a dependency specification into a DepSpec instance.",
+ bp::no_init
+ );
+ pdp.def("parse", &PortageDepParser::parse,
+ "parse(string, Policy) -> CompositeDepSpec\n"
+ "Parse a given dependency string, and return an appropriate DepSpec tree."
+ );
+ pdp.staticmethod("parse");
+ pdp.def("parse_depend", &PortageDepParser::parse_depend,
+ "parse_depend(string, PackageDepSpecParseMode) -> CompositeDepSpec\n"
+ "Convenience wrapper for parse for depend strings, for VersionMetadata."
+ );
+ pdp.staticmethod("parse_depend");
+ pdp.def("parse_license", &PortageDepParser::parse_license,
+ "parse_license(string) -> CompositeDepSpec\n"
+ "Convenience wrapper for parse for license strings, for VersionMetadata."
+ );
+ pdp.staticmethod("parse_license");
+
+ bp::scope s = pdp;
+ bp::class_<PortageDepParser::Policy>
+ pdpp("Policy",
+ "The Policy class describes how to convert a string representation"
+ "of a dependency specification into a DepSpec instance.",
+ bp::no_init
+ );
+ pdpp.def("text_is_text_dep_spec", &PortageDepParser::Policy::text_is_text_dep_spec,
+ "text_is_text_dep_spec(permit_any_deps_boolean) -> Policy\n"
+ "Returns a new policy for a PlainTextDepSpec."
+ );
+ pdpp.staticmethod("text_is_text_dep_spec");
+ pdpp.def("text_is_package_dep_spec", &PortageDepParser::Policy::text_is_package_dep_spec,
+ "text_is_package_dep_spec(permit_any_deps_boolean, PackageDepSpecParseMode) -> Policy\n"
+ "Returns a new policy for a PackageDepSpec."
+ );
+ pdpp.staticmethod("text_is_package_dep_spec");
+ pdpp.add_property("permit_any_deps", &PortageDepParser::Policy::permit_any_deps);
+}
diff --git a/python/portage_dep_parser_TEST.py b/python/portage_dep_parser_TEST.py
new file mode 100755
index 0000000..e283ac1
--- /dev/null
+++ b/python/portage_dep_parser_TEST.py
@@ -0,0 +1,82 @@
+#!/usr/bin/env python
+# vim: set fileencoding=utf-8 sw=4 sts=4 et :
+
+#
+# Copyright (c) 2007 Piotr Jaroszyński <peper@gentoo.org>
+#
+# This file is part of the Paludis package manager. Paludis is free software;
+# you can redistribute it and/or modify it under the terms of the GNU General
+# Public License version 2, as published by the Free Software Foundation.
+#
+# Paludis 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., 59 Temple
+# Place, Suite 330, Boston, MA 02111-1307 USA
+#
+
+from paludis import *
+import unittest
+
+class TestCase_01_PortageDepParser_Policy(unittest.TestCase):
+ def test_01_init_error(self):
+ self.assertRaises(Exception, PortageDepParser.Policy)
+
+ def test_02_create_text(self):
+ PortageDepParser.Policy.text_is_text_dep_spec(True)
+
+ def test_03_create_package(self):
+ PortageDepParser.Policy.text_is_package_dep_spec(True, PackageDepSpecParseMode.PERMISSIVE)
+
+class TestCase_02_PortageDepParser(unittest.TestCase):
+ def get_policies(self):
+ self.text_true = PortageDepParser.Policy.text_is_text_dep_spec(True)
+ self.text_false = PortageDepParser.Policy.text_is_text_dep_spec(False)
+ self.package_true = PortageDepParser.Policy.text_is_package_dep_spec(True, PackageDepSpecParseMode.PERMISSIVE)
+ self.package_false = PortageDepParser.Policy.text_is_package_dep_spec(False, PackageDepSpecParseMode.PERMISSIVE)
+
+ def test_01_init_error(self):
+ self.assertRaises(Exception, PortageDepParser)
+
+ def test_02_parse(self):
+ self.get_policies()
+
+ spec1 = PortageDepParser.parse("foo/boo", self.text_true)
+ self.assert_(isinstance(spec1, AllDepSpec))
+ self.assert_(isinstance(iter(spec1).next(), PlainTextDepSpec))
+ self.assertEquals(len(list(spec1)), 1)
+ self.assertEquals(str(iter(spec1).next()), "foo/boo")
+
+ spec2 = PortageDepParser.parse("foo/boo", self.text_false)
+ self.assert_(isinstance(spec2, AllDepSpec))
+ self.assert_(isinstance(iter(spec2).next(), PlainTextDepSpec))
+ self.assertEquals(len(list(spec2)), 1)
+ self.assertEquals(str(iter(spec2).next()), "foo/boo")
+
+ spec3 = PortageDepParser.parse("foo/boo", self.package_true)
+ self.assert_(isinstance(spec3, AllDepSpec))
+ self.assert_(isinstance(iter(spec3).next(), PackageDepSpec))
+ self.assertEquals(len(list(spec3)), 1)
+ self.assertEquals(str(iter(spec3).next()), "foo/boo")
+
+ spec4 = PortageDepParser.parse("foo/boo", self.package_false)
+ self.assert_(isinstance(spec4, AllDepSpec))
+ self.assert_(isinstance(iter(spec4).next(), PackageDepSpec))
+ self.assertEquals(len(list(spec4)), 1)
+ self.assertEquals(str(iter(spec4).next()), "foo/boo")
+
+ PortageDepParser.parse("|| ( foo/boo )", self.text_true)
+ PortageDepParser.parse("|| ( foo/boo )", self.package_true)
+
+ def test_03_exceptions(self):
+ self.get_policies()
+
+ self.assertRaises(DepStringParseError, PortageDepParser.parse, "|| ( foo/boo )", self.text_false)
+ self.assertRaises(DepStringParseError, PortageDepParser.parse, "|| ( foo/boo )", self.package_false)
+ self.assertRaises(DepStringNestingError, PortageDepParser.parse, "|| ( foo/boo", self.package_true)
+
+if __name__ == "__main__":
+ unittest.main()
diff --git a/python/query.cc b/python/query.cc
new file mode 100644
index 0000000..0cf5d07
--- /dev/null
+++ b/python/query.cc
@@ -0,0 +1,73 @@
+/* vim: set sw=4 sts=4 et foldmethod=syntax : */
+
+/*
+ * Copyright (c) 2007 Piotr Jaroszyński <peper@gentoo.org>
+ *
+ * This file is part of the Paludis package manager. Paludis is free software;
+ * you can redistribute it and/or modify it under the terms of the GNU General
+ * Public License version 2, as published by the Free Software Foundation.
+ *
+ * Paludis 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., 59 Temple
+ * Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <paludis_python.hh>
+
+#include <paludis/query.hh>
+#include <paludis/dep_spec.hh>
+#include <paludis/util/fs_entry.hh>
+
+using namespace paludis;
+using namespace paludis::python;
+namespace bp = boost::python;
+
+void expose_query()
+{
+ bp::class_<Query>
+ q("Query", bp::no_init);
+ q.def("__and__", operator&);
+
+ bp::scope query = q;
+
+ bp::class_<query::Matches, bp::bases<Query> >
+ qm("Matches",
+ bp::init<const PackageDepSpec &>("__init__(PackageDepSpec)")
+ );
+
+ bp::class_<query::Package, bp::bases<Query> >
+ qp("Package",
+ bp::init<const QualifiedPackageName &>("__init__(QualifiedPackageName)")
+ );
+
+ bp::class_<query::NotMasked, bp::bases<Query> >
+ qnm("NotMasked",
+ bp::init<>("__init__()")
+ );
+
+ bp::class_<query::RepositoryHasInstalledInterface, bp::bases<Query> >
+ qrhii1("RepositoryHasInstalledInterface",
+ "Matches...",
+ bp::init<>("__init__()")
+ );
+
+ bp::class_<query::RepositoryHasInstallableInterface, bp::bases<Query> >
+ qrhii2("RepositoryHasInstallableInterface",
+ bp::init<>("__init__()")
+ );
+
+ bp::class_<query::RepositoryHasUninstallableInterface, bp::bases<Query> >
+ qrhui("RepositoryHasUninstallableInterface",
+ bp::init<>("__init__()")
+ );
+
+ bp::class_<query::InstalledAtRoot, bp::bases<Query> >
+ qiar("InstalledAtRoot",
+ bp::init<const FSEntry &>("__init__(path)")
+ );
+}
diff --git a/python/query_TEST.py b/python/query_TEST.py
new file mode 100755
index 0000000..2400578
--- /dev/null
+++ b/python/query_TEST.py
@@ -0,0 +1,51 @@
+#!/usr/bin/env python
+# vim: set fileencoding=utf-8 sw=4 sts=4 et :
+
+#
+# Copyright (c) 2007 Piotr Jaroszyński <peper@gentoo.org>
+#
+# This file is part of the Paludis package manager. Paludis is free software;
+# you can redistribute it and/or modify it under the terms of the GNU General
+# Public License version 2, as published by the Free Software Foundation.
+#
+# Paludis 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., 59 Temple
+# Place, Suite 330, Boston, MA 02111-1307 USA
+#
+
+from paludis import *
+import unittest
+
+class TestCase_Queries(unittest.TestCase):
+ def test_1_create(self):
+ self.queries = []
+ self.queries.append(Query.RepositoryHasInstallableInterface())
+ self.queries.append(Query.RepositoryHasInstalledInterface())
+ self.queries.append(Query.RepositoryHasUninstallableInterface())
+ self.queries.append(Query.InstalledAtRoot("/bar"))
+ self.queries.append(Query.NotMasked())
+ self.queries.append(Query.Package("foo/bar"))
+ self.queries.append(Query.Matches(PackageDepSpec(">=foo/bar-1", PackageDepSpecParseMode.PERMISSIVE)))
+
+ def test_2_create_error(self):
+ self.assertRaises(Exception, Query)
+
+ def test_3_subclass(self):
+ self.test_1_create()
+ for query in self.queries:
+ self.assert_(isinstance(query, Query))
+
+ def test_4_and(self):
+ self.test_1_create()
+ length = len(self.queries)
+ for i in xrange(length):
+ for j in xrange(length):
+ self.assert_(isinstance(self.queries[i] & self.queries[j], Query))
+
+if __name__ == "__main__":
+ unittest.main()
diff --git a/python/repository.cc b/python/repository.cc
new file mode 100644
index 0000000..a0e5254
--- /dev/null
+++ b/python/repository.cc
@@ -0,0 +1,434 @@
+/* vim: set sw=4 sts=4 et foldmethod=syntax : */
+
+/*
+ * Copyright (c) 2007 Piotr Jaroszyński <peper@gentoo.org>
+ *
+ * This file is part of the Paludis package manager. Paludis is free software;
+ * you can redistribute it and/or modify it under the terms of the GNU General
+ * Public License version 2, as published by the Free Software Foundation.
+ *
+ * Paludis 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., 59 Temple
+ * Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <paludis_python.hh>
+
+#include <datetime.h>
+
+#include <paludis/repository.hh>
+#include <paludis/repositories/gentoo/portage_repository.hh>
+
+using namespace paludis;
+using namespace paludis::python;
+namespace bp = boost::python;
+
+struct RepositoryWrapper :
+ Repository,
+ bp::wrapper<Repository>
+{
+ std::tr1::shared_ptr<const RepositoryInfo>
+ info(bool verbose) const
+ {
+ if (bp::override info = this->get_override("info"))
+ return info(verbose);
+ return Repository::info(verbose);
+ }
+
+ std::tr1::shared_ptr<const RepositoryInfo>
+ default_info(bool verbose) const
+ {
+ return Repository::info(verbose);
+ }
+};
+
+struct RepositoryInstalledInterfaceWrapper :
+ RepositoryInstalledInterface
+{
+ static PyObject *
+ installed_time(const RepositoryInstalledInterface & self,
+ const QualifiedPackageName & qpn, const VersionSpec & vs)
+ {
+ PyDateTime_IMPORT;
+ return PyDateTime_FromTimestamp(bp::make_tuple(self.installed_time(qpn, vs)).ptr());
+ }
+
+};
+
+struct RepositoryLicensesInterfaceWrapper :
+ RepositoryLicensesInterface
+{
+ static PyObject *
+ license_exists(const RepositoryLicensesInterface & self, const std::string & license)
+ {
+ std::tr1::shared_ptr<FSEntry> p(self.license_exists(license));
+ if (p)
+ return to_string<FSEntry>::convert(*p);
+ else
+ return 0;
+ }
+};
+
+struct RepositoryPortageInterfaceWrapper :
+ RepositoryPortageInterface
+{
+ static bp::object
+ my_find_profile(const RepositoryPortageInterface & self, const FSEntry & location)
+ {
+ ProfilesIterator p(self.find_profile(location));
+ if (p == self.end_profiles())
+ return bp::object();
+ return bp::object(bp::ptr(&*p));
+ }
+
+ static void
+ my_set_profile(RepositoryPortageInterface & self, const ProfilesDescLine & pdl)
+ {
+ self.set_profile(self.find_profile(pdl.path));
+ }
+
+};
+
+
+void expose_repository()
+{
+ static register_exception<PackageActionError>
+ PackageActionError("PackageActionError");
+ static register_exception<PackageInstallActionError>
+ PackageInstallActionError("PackageInstallActionError");
+ static register_exception<PackageFetchActionError>
+ PackageFetchActionError("PackageFetchActionError");
+ static register_exception<PackageUninstallActionError>
+ PackageUninstallActionError("PackageUninstallActionError");
+ static register_exception<PackageConfigActionError>
+ PackageConfigActionError("PackageConfigActionError");
+ static register_exception<EnvironmentVariableActionError>
+ EnvironmentVariableActionError("EnvironmentVariableActionError");
+
+ class_collection<DestinationsCollection>
+ dc("DestinationsCollection",
+ "Iterable of Repository.\n"
+ "A set of Destinations."
+
+ );
+
+ bp::to_python_converter<std::pair<const std::string, std::string>,
+ pair_to_tuple<const std::string, std::string> >();
+ register_shared_ptrs_to_python<RepositoryInfoSection>();
+ bp::class_<RepositoryInfoSection, boost::noncopyable>
+ ris("RepositoryInfoSection",
+ "A section of information about a Repository.",
+ bp::init<const std::string &>()
+ );
+ ris.add_property("heading", &RepositoryInfoSection::heading,
+ "[ro] string\n"
+ "Heading."
+ );
+ ris.add_property("kvs", bp::range(&RepositoryInfoSection::begin_kvs, &RepositoryInfoSection::end_kvs),
+ "[ro] Iterable of tuples\n"
+ "Key-value pairs."
+ );
+
+ register_shared_ptrs_to_python<RepositoryInfo>();
+ bp::class_<RepositoryInfo, boost::noncopyable>
+ ri("RepositoryInfo",
+ "Information about a Repository, for the end user.",
+ bp::no_init
+ );
+ ri.add_property("sections", bp::range(&RepositoryInfo::begin_sections, &RepositoryInfo::end_sections),
+ "[ro] Iterable of RepositoryInfoSection."
+ );
+
+ register_shared_ptrs_to_python<Repository>();
+ bp::class_<RepositoryWrapper, boost::noncopyable>
+ r("Repository",
+ "A Repository provides a representation of a physical repository to a PackageDatabase.",
+ bp::no_init
+ );
+ r.def("info", &Repository::info, &RepositoryWrapper::default_info,
+ "info() -> RepositoryInfo"
+ "Fetch information about the repository."
+ );
+ r.add_property("name", bp::make_function(&Repository::name,
+ bp::return_value_policy<bp::copy_const_reference>()),
+ "[ro] RepositoryName\n"
+ "Our name."
+ );
+ r.add_property("format", &Repository::format,
+ "[ro] string\n"
+ "Our format."
+ );
+ r.def("has_category_named", &Repository::has_category_named,
+ "has_category_named(CategoryNamePart) -> bool\n"
+ "Do we have a category with the given name?"
+ );
+ r.def("has_package_named", &Repository::has_package_named,
+ "Do we have a package in the given category with the given name?"
+ );
+ r.add_property("category_names", &Repository::category_names,
+ "[ro] CategoryNamePartCollection\n"
+ "Our category names."
+ );
+ r.def("category_names_containing_package", &Repository::category_names_containing_package,
+ "category_names_containing_package(PackageNamePart) -> CategoryNamePartCollection\n"
+ "Fetch categories that contain a named package."
+ );
+ r.def("package_names", &Repository::package_names,
+ "package_names(CategoryNamePart) -> QualifiedPackageNameCollection\n"
+ "Fetch our package names."
+ );
+ r.def("version_specs", &Repository::version_specs,
+ "version_specs(QualifiedPackageName) -> VersionSpecCollection\n"
+ "Fetch our versions."
+ );
+ r.def("has_version", &Repository::has_version,
+ "has_version(QualifiedPackageName, VersionSpec) -> bool\n"
+ "Do we have a version spec?"
+ );
+ r.def("version_metadata", &Repository::version_metadata,
+ "version_metadata(QualifiedPackageName, VersionSpec) -> VersionMetadata\n"
+ "Fetch metadata."
+ );
+ r.def_readonly("mask_interface", &Repository::mask_interface,
+ "[ro] RepositoryMaskInterface"
+ );
+ r.def_readonly("use_interface", &Repository::use_interface,
+ "[ro] RepositoryUseInterface"
+ );
+ r.def_readonly("installed_interface", &Repository::installed_interface,
+ "[ro] RepositoryInstalledInterface"
+ );
+ r.def_readonly("installable_interface", &Repository::installable_interface,
+ "[ro] RepositoryInstallableInterface"
+ );
+ r.def_readonly("uninstallable_interface", &Repository::uninstallable_interface,
+ "[ro] RepositoryUninstallableInterface"
+ );
+ r.def_readonly("sets_interface", &Repository::sets_interface,
+ "[ro] RepositorySetsInterface"
+ );
+ r.def_readonly("syncable_interface", &Repository::syncable_interface,
+ "[ro] RepositorySyncableInterface"
+ );
+ r.def_readonly("world_interface", &Repository::world_interface,
+ "[ro] RepositoryWorldInterface"
+ );
+ r.def_readonly("environment_variable_interface", &Repository::environment_variable_interface,
+ "[ro] RepositoryEnvironmentInterface"
+ );
+ r.def_readonly("mirrors_interface", &Repository::mirrors_interface,
+ "[ro] RepositoryMirrorsInterface"
+ );
+ r.def_readonly("virtuals_interface", &Repository::virtuals_interface,
+ "[ro] RepositoryVirtualsInterface"
+ );
+ r.def_readonly("provides_interface", &Repository::provides_interface,
+ "[ro] RepositoryProvidesInterface"
+ );
+ r.def_readonly("destination_interface", &Repository::destination_interface,
+ "[ro] RepositoryDestinationInterface"
+ );
+ r.def_readonly("contents_interface", &Repository::contents_interface,
+ "[ro] RepositoryContentsInterface"
+ );
+ r.def_readonly("config_interface", &Repository::config_interface,
+ "[ro] RepositoryConfigInterface"
+ );
+ r.def_readonly("licenses_interface", &Repository::licenses_interface,
+ "[ro] RepositoryLicensesInterface"
+ );
+ r.def_readonly("portage_interface", &Repository::portage_interface,
+ "[ro] RepositoryPortageInterface"
+ );
+
+
+ bp::class_<RepositoryPortageInterfaceProfilesDescLine>
+ rpipd("RepositoryPortageInterfaceProfilesDescLine",
+ "A profiles.desc line in a Repository implementing RepositoryPortageInterface.",
+ bp::no_init
+ );
+ rpipd.add_property("path", bp::make_getter(&RepositoryPortageInterfaceProfilesDescLine::path,
+ bp::return_value_policy<bp::return_by_value>())
+ );
+ rpipd.def_readonly("arch", &RepositoryPortageInterfaceProfilesDescLine::arch);
+ rpipd.def_readonly("status", &RepositoryPortageInterfaceProfilesDescLine::status);
+
+ // Interfaces
+ bp::register_ptr_to_python<RepositoryMaskInterface *>();
+ bp::class_<RepositoryMaskInterface, boost::noncopyable>
+ rmaski("RepositoryMaskInterface",
+ "Interface for handling masks for the Repository class.",
+ bp::no_init
+ );
+ rmaski.def("query_repository_masks", &RepositoryMaskInterface::query_repository_masks,
+ "query_repository_masks(QualifiedPackageName, VersionSpec) -> bool\n"
+ "Query repository masks."
+ );
+ rmaski.def("query_profile_masks", &RepositoryMaskInterface::query_profile_masks,
+ "query_profile_masks(QualifiedPackageName, VersionSpec) -> bool\n"
+ "Query profile masks."
+ );
+
+ bp::register_ptr_to_python<RepositoryUseInterface *>();
+ bp::class_<RepositoryUseInterface, boost::noncopyable>
+ rusei("RepositoryUseInterface",
+ "Interface for handling USE flags for the Repository class.",
+ bp::no_init
+ );
+ rusei.def("query_use", &RepositoryUseInterface::query_use,
+ ("ufn", bp::arg("pde")=bp::object()),
+ "query_use(UseFlagName, PackageDatabaseEntry=None) -> UseFlagState\n"
+ "Query the state of the specified use flag."
+ );
+ rusei.def("query_use_mask", &RepositoryUseInterface::query_use_mask,
+ ("ufn", bp::arg("pde")=bp::object()),
+ "query_use_mask(UseFlagName, PackageDatabaseEntry=None) -> bool\n"
+ "Query whether the specified use flag is masked."
+ );
+ rusei.def("query_use_force", &RepositoryUseInterface::query_use_force,
+ ("ufn", bp::arg("pde")=bp::object()),
+ "query_use_force(UseFlagName, PackageDatabaseEntry=None) -> bool\n"
+ "Query whether the specified use flag is forceed."
+ );
+ rusei.def("describe_use_flag", &RepositoryUseInterface::describe_use_flag,
+ ("ufn", bp::arg("pde")=bp::object()),
+ "describe_use_flag(UseFlagName, PackageDatabaseEntry=None) -> string\n"
+ "Describe a use flag."
+ );
+
+ bp::register_ptr_to_python<RepositoryInstalledInterface *>();
+ bp::class_<RepositoryInstalledInterface, boost::noncopyable>
+ rinstalledi("RepositoryInstalledInterface",
+ "Interface for handling actions for installed repositories.",
+ bp::no_init
+ );
+ rinstalledi.def("installed_time", &RepositoryInstalledInterfaceWrapper::installed_time,
+ "installed_time(QualifiedPackageName, VersionSpec) -> datetime\n"
+ "When was a package installed."
+ );
+ rinstalledi.def("root", bp::pure_virtual(&RepositoryInstalledInterface::root),
+ "What is our filesystem root?"
+ );
+
+ bp::register_ptr_to_python<RepositoryInstallableInterface *>();
+ bp::class_<RepositoryInstallableInterface, boost::noncopyable>
+ rinstallablei("RepositoryInstallableInterface",
+ "Interface for handling installs for repositories.",
+ bp::no_init
+ );
+
+ bp::register_ptr_to_python<RepositoryUninstallableInterface *>();
+ bp::class_<RepositoryUninstallableInterface, boost::noncopyable>
+ runinstallablei("RepositoryUninstallableInterface",
+ "Interface for handling uninstalls for repositories.",
+ bp::no_init
+ );
+
+ bp::register_ptr_to_python<RepositorySetsInterface *>();
+ bp::class_<RepositorySetsInterface, boost::noncopyable>
+ rsetsi("RepositorySetsInterface",
+ "Interface for package sets for repositories.",
+ bp::no_init
+ );
+
+ bp::register_ptr_to_python<RepositorySyncableInterface *>();
+ bp::class_<RepositorySyncableInterface, boost::noncopyable>
+ rsynci("RepositorySyncableInterface",
+ "Interface for syncing for repositories.",
+ bp::no_init
+ );
+
+ bp::register_ptr_to_python<RepositoryWorldInterface *>();
+ bp::class_<RepositoryWorldInterface, boost::noncopyable>
+ rworldi("RepositoryWorldInterface",
+ "Interface for world handling for repositories.",
+ bp::no_init
+ );
+
+ bp::register_ptr_to_python<RepositoryEnvironmentVariableInterface *>();
+ bp::class_<RepositoryEnvironmentVariableInterface, boost::noncopyable>
+ renvvari("RepositoryEnvironmentVariableInterface",
+ "Interface for environment variable querying for repositories.",
+ bp::no_init
+ );
+
+ bp::register_ptr_to_python<RepositoryMirrorsInterface *>();
+ bp::class_<RepositoryMirrorsInterface, boost::noncopyable>
+ rmirrorsi("RepositoryMirrorsInterface",
+ "Interface for mirror querying for repositories.",
+ bp::no_init
+ );
+
+ bp::register_ptr_to_python<RepositoryVirtualsInterface *>();
+ bp::class_<RepositoryVirtualsInterface, boost::noncopyable>
+ rvirtualsi("RepositoryVirtualsInterface",
+ "Interface for repositories that define virtuals.",
+ bp::no_init
+ );
+
+ bp::register_ptr_to_python<RepositoryProvidesInterface *>();
+ bp::class_<RepositoryProvidesInterface, boost::noncopyable>
+ rprovidesi("RepositoryProvidesInterface",
+ "Interface for repositories that provide packages.",
+ bp::no_init
+ );
+
+ bp::register_ptr_to_python<RepositoryDestinationInterface *>();
+ bp::class_<RepositoryDestinationInterface, boost::noncopyable>
+ rdesti("RepositoryDestinationInterface",
+ "Interface for repositories that can be used as an install destination.",
+ bp::no_init
+ );
+
+ bp::register_ptr_to_python<RepositoryContentsInterface *>();
+ bp::class_<RepositoryContentsInterface, boost::noncopyable>
+ rcontentsi("RepositoryContentsInterface",
+ "Interface for handling actions for repositories supporting contents queries.",
+ bp::no_init
+ );
+ rcontentsi.def("contents", &RepositoryContentsInterface::contents,
+ "contents(QualifiedPackageName, VersionSpec) -> Contents\n"
+ "Fetch contents."
+ );
+
+ bp::register_ptr_to_python<RepositoryConfigInterface *>();
+ bp::class_<RepositoryConfigInterface, boost::noncopyable>
+ rconfigi("RepositoryConfigInterface",
+ "Interface for handling actions for repositories supporting package configuration.",
+ bp::no_init
+ );
+
+ bp::register_ptr_to_python<RepositoryLicensesInterface *>();
+ bp::class_<RepositoryLicensesInterface, boost::noncopyable>
+ rlici("RepositoryLicensesInterface",
+ "Interface for handling actions relating to licenses.",
+ bp::no_init
+ );
+ rlici.def("license_exists", &RepositoryLicensesInterfaceWrapper::license_exists,
+ "license_exists(string) -> path_string or None\n"
+ "Check if a license exists."
+ );
+
+ bp::register_ptr_to_python<RepositoryPortageInterface *>();
+ bp::class_<RepositoryPortageInterface, boost::noncopyable>
+ rporti("RepositoryPortageInterface",
+ "Interface for handling PortageRepository specific functionality.",
+ bp::no_init
+ );
+ rporti.add_property("profiles", bp::range(&RepositoryPortageInterface::begin_profiles,
+ &RepositoryPortageInterface::end_profiles),
+ "[ro] Iterable of Profiles"
+ );
+ rporti.def("profile_variable", &RepositoryPortageInterface::profile_variable);
+ rporti.def("find_profile", &RepositoryPortageInterfaceWrapper::my_find_profile,
+ "find_profile(path_string) -> RepositoryPortageInterfaceProfilesDescLine"
+ );
+ rporti.add_property("profile", bp::object(), &RepositoryPortageInterfaceWrapper::my_set_profile,
+ "[wo] RepositoryPortageInterfaceProfilesDescLine"
+ );
+}
diff --git a/python/repository_TEST.py b/python/repository_TEST.py
new file mode 100755
index 0000000..01c8573
--- /dev/null
+++ b/python/repository_TEST.py
@@ -0,0 +1,332 @@
+#!/usr/bin/env python
+# vim: set fileencoding=utf-8 sw=4 sts=4 et :
+
+#
+# Copyright (c) 2007 Piotr Jaroszyński <peper@gentoo.org>
+#
+# This file is part of the Paludis package manager. Paludis is free software;
+# you can redistribute it and/or modify it under the terms of the GNU General
+# Public License version 2, as published by the Free Software Foundation.
+#
+# Paludis 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., 59 Temple
+# Place, Suite 330, Boston, MA 02111-1307 USA
+#
+
+import os
+
+repo = os.path.join(os.getcwd(), "repository_TEST_dir/testrepo")
+os.environ["PALUDIS_HOME"] = os.path.join(os.getcwd(), "repository_TEST_dir/home")
+
+from paludis import *
+import unittest
+
+Log.instance.log_level = LogLevel.WARNING;
+
+class TestCase_Repository(unittest.TestCase):
+ def get_foo(self):
+ self.e = EnvironmentMaker.instance.make_from_spec("")
+ self.nce = NoConfigEnvironment(repo)
+ self.db = self.e.package_database
+ self.repo = self.db.fetch_repository("testrepo")
+ self.irepo = self.db.fetch_repository("installed")
+
+ def test_01_get(self):
+ self.get_foo()
+
+ def test_02_create_error(self):
+ self.assertRaises(Exception, Repository)
+
+ def test_03_name(self):
+ self.get_foo()
+
+ self.assertEquals(str(self.repo.name), "testrepo")
+ self.assertEquals(str(self.irepo.name), "installed")
+
+ def test_04_format(self):
+ self.get_foo()
+
+ self.assertEquals(str(self.repo.format), "ebuild")
+ self.assertEquals(str(self.irepo.format), "vdb")
+
+ def test_05_has_version(self):
+ self.get_foo()
+
+ self.assert_(self.repo.has_version("foo/bar", "1.0"))
+ self.assert_(self.repo.has_version("foo/bar", "2.0"))
+ self.assert_(not self.repo.has_version("foo/barbar", "1.0"))
+ self.assert_(not self.repo.has_version("foo/bar", "3.0"))
+
+ def test_06_has_category_named(self):
+ self.get_foo()
+
+ self.assert_(self.repo.has_category_named("foo"))
+ self.assert_(not self.repo.has_category_named("bar"))
+
+ def test_07_has_package_named(self):
+ self.get_foo()
+
+ self.assert_(self.repo.has_package_named("foo/bar"))
+ self.assert_(not self.repo.has_package_named("foo/foo"))
+ self.assert_(not self.repo.has_package_named("bar/foo"))
+
+ def test_08_version_specs(self):
+ self.get_foo()
+
+ self.assertEquals(list(self.repo.version_specs("foo/bar")),
+ [VersionSpec("1.0"), VersionSpec("2.0")]
+ )
+ self.assertEquals(len(list(self.repo.version_specs("bar/baz"))), 0)
+
+ def test_09_category_names(self):
+ self.get_foo()
+
+ for (i, cat) in enumerate(self.repo.category_names):
+ self.assertEquals(i, 0)
+ self.assertEquals(str(cat), "foo")
+
+ def test_10_category_names_containing_package(self):
+ self.get_foo()
+
+ for (i, cat) in enumerate(self.repo.category_names_containing_package("bar")):
+ self.assertEquals(i, 0)
+ self.assertEquals(str(cat), "foo")
+
+ def test_11_package_names(self):
+ self.get_foo()
+
+ for (i, qpn) in enumerate(self.repo.package_names("bar")):
+ self.assertEquals(i, 0)
+ self.assertEquals(str(qpn), "foo/bar")
+
+ def test_12_repository_info(self):
+ self.get_foo()
+
+ self.assert_(isinstance(self.repo.info(True), RepositoryInfo))
+ self.assert_(isinstance(self.repo.info(False), RepositoryInfo))
+
+ for (i, section) in enumerate(self.repo.info(False).sections):
+ self.assert_(isinstance(section, RepositoryInfoSection))
+ if i == 0:
+ self.assertEquals(section.heading, "Configuration information")
+ for (k, v) in section.kvs:
+ if k == "format":
+ self.assertEquals(v, "ebuild")
+
+ for (i, section) in enumerate(self.irepo.info(False).sections):
+ self.assert_(isinstance(section, RepositoryInfoSection))
+ if i == 0:
+ self.assertEquals(section.heading, "Configuration information")
+ for (k, v) in section.kvs:
+ if k == "format":
+ self.assertEquals(v, "vdb")
+
+ def test_13_version_metadata(self):
+ self.get_foo()
+
+ vm = self.repo.version_metadata("foo/bar", "1.0")
+ self.assert_(isinstance(vm, VersionMetadata))
+
+
+ def test_14_installable_interface(self):
+ self.get_foo()
+
+ ii = self.repo.installable_interface
+ self.assert_(isinstance(ii, RepositoryInstallableInterface))
+
+ def test_15_installed_interface(self):
+ import datetime
+ self.get_foo()
+
+ ii = self.irepo.installed_interface
+
+ self.assert_(isinstance(ii, RepositoryInstalledInterface))
+
+ time = ii.installed_time("cat-one/pkg-one", "1")
+ self.assert_(type(time) == datetime.datetime)
+
+ def test_16_mask_interface(self):
+ self.get_foo()
+
+ mi = self.repo.mask_interface
+ self.assert_(isinstance(mi, RepositoryMaskInterface))
+
+ self.assert_(mi.query_profile_masks("foo1/bar", "1.0"))
+ self.assert_(not mi.query_profile_masks("foo2/bar", "1.0"))
+ self.assert_(mi.query_profile_masks("foo3/bar", "1.0"))
+ self.assert_(not mi.query_profile_masks("foo4/bar", "1.0"))
+
+ def test_17_sets_interface(self):
+ self.get_foo()
+
+ si = self.repo.sets_interface
+ self.assert_(isinstance(si, RepositorySetsInterface))
+
+ def test_18_syncable_interface(self):
+ self.get_foo()
+
+ si = self.repo.syncable_interface
+ self.assert_(isinstance(si, RepositorySyncableInterface))
+
+ def test_19_uninstallable_interface(self):
+ self.get_foo()
+
+ ui = self.irepo.uninstallable_interface
+ self.assert_(isinstance(ui, RepositoryUninstallableInterface))
+
+ def test_20_use_interface(self):
+ self.get_foo()
+
+ ui = self.repo.use_interface
+ self.assert_(isinstance(ui, RepositoryUseInterface))
+
+ p = PackageDatabaseEntry("foo/bar", "2.0", self.repo.name)
+
+ self.assertEquals(ui.query_use("test1",p), UseFlagState.ENABLED)
+ self.assertEquals(ui.query_use("test2",p), UseFlagState.DISABLED)
+ self.assertEquals(ui.query_use("test3",p), UseFlagState.ENABLED)
+ self.assertEquals(ui.query_use("test4",p), UseFlagState.UNSPECIFIED)
+ self.assertEquals(ui.query_use("test5",p), UseFlagState.DISABLED)
+ self.assertEquals(ui.query_use("test6",p), UseFlagState.ENABLED)
+ self.assertEquals(ui.query_use("test7",p), UseFlagState.ENABLED)
+
+ self.assert_(not ui.query_use_mask("test1"))
+ self.assert_(not ui.query_use_mask("test2"))
+ self.assert_(not ui.query_use_mask("test3"))
+ self.assert_(ui.query_use_mask("test4"))
+ self.assert_(not ui.query_use_mask("test5"))
+ self.assert_(not ui.query_use_mask("test6"))
+ self.assert_(not ui.query_use_mask("test7"))
+
+ self.assert_(not ui.query_use_mask("test1", p))
+ self.assert_(not ui.query_use_mask("test2", p))
+ self.assert_(not ui.query_use_mask("test3", p))
+ self.assert_(not ui.query_use_mask("test4", p))
+ self.assert_(ui.query_use_mask("test5", p))
+ self.assert_(not ui.query_use_mask("test6", p))
+ self.assert_(not ui.query_use_mask("test7", p))
+
+ self.assert_(not ui.query_use_force("test1"))
+ self.assert_(not ui.query_use_force("test2"))
+ self.assert_(not ui.query_use_force("test3"))
+ self.assert_(not ui.query_use_force("test4"))
+ self.assert_(not ui.query_use_force("test5"))
+ self.assert_(ui.query_use_force("test6"))
+ self.assert_(not ui.query_use_force("test7"))
+
+ self.assert_(not ui.query_use_force("test1", p))
+ self.assert_(not ui.query_use_force("test2", p))
+ self.assert_(not ui.query_use_force("test3", p))
+ self.assert_(not ui.query_use_force("test4", p))
+ self.assert_(not ui.query_use_force("test5", p))
+ self.assert_(ui.query_use_force("test6", p))
+ self.assert_(ui.query_use_force("test7", p))
+
+ self.assert_(ui.describe_use_flag("test1"), "A test use flag")
+ self.assert_(ui.describe_use_flag("test1", p), "A test use flag")
+
+ def test_21_world_interface(self):
+ self.get_foo()
+
+ wi = self.irepo.world_interface
+ self.assert_(isinstance(wi, RepositoryWorldInterface))
+
+ def test_22_environment_variable_interface(self):
+ self.get_foo()
+
+ evi = self.repo.environment_variable_interface
+ self.assert_(isinstance(evi, RepositoryEnvironmentVariableInterface))
+
+ def test_23_mirrors_interface(self):
+ self.get_foo()
+
+ mi = self.repo.mirrors_interface
+ self.assert_(isinstance(mi, RepositoryMirrorsInterface))
+
+ def test_24_provides_interface(self):
+ self.get_foo()
+
+ pi = self.irepo.provides_interface
+ self.assert_(isinstance(pi, RepositoryProvidesInterface))
+
+ def test_25_virtuals_interface(self):
+ self.get_foo()
+
+ vi = self.repo.virtuals_interface
+ self.assert_(isinstance(vi, RepositoryVirtualsInterface))
+
+ def test_26_destination_interface(self):
+ self.get_foo()
+
+ di = self.irepo.destination_interface
+ self.assert_(isinstance(di, RepositoryDestinationInterface))
+
+ def test_27_contents_interface(self):
+ self.get_foo()
+
+ self.assert_(isinstance(self.irepo.contents_interface, RepositoryContentsInterface))
+
+ contents = self.irepo.contents_interface.contents("cat-one/pkg-one", "1")
+
+ for (i, entry) in enumerate(contents):
+ if i == 0:
+ self.assert_(isinstance(entry, ContentsDirEntry))
+ self.assertEqual(str(entry), "//test")
+ self.assertEqual(entry.name, "//test")
+ elif i == 1:
+ self.assert_(isinstance(entry, ContentsFileEntry))
+ self.assertEqual(str(entry), "/test/test_file")
+ self.assertEqual(entry.name, "/test/test_file")
+ elif i == 2:
+ self.assert_(isinstance(entry, ContentsSymEntry))
+ self.assertEqual(str(entry), "/test/test_link -> /test/test_file")
+ self.assertEqual(entry.name, "/test/test_link")
+ self.assertEqual(entry.target, "/test/test_file")
+ else:
+ self.assertEqual("TOO MANY ENTRIES", "OK")
+
+ def test_28_config_interface(self):
+ self.get_foo()
+
+ ci = self.irepo.config_interface
+ self.assert_(isinstance(ci, RepositoryConfigInterface))
+
+ def test_29_licenses_interface(self):
+ self.get_foo()
+
+ li = self.repo.licenses_interface
+ self.assert_(isinstance(li, RepositoryLicensesInterface))
+
+ self.assertEquals(os.path.realpath(li.license_exists("foo")), os.path.join(repo, "licenses/foo"))
+ self.assertEquals(li.license_exists("bad"), None)
+
+ def test_30_portage_interface(self):
+ self.get_foo()
+
+ pi = self.nce.main_repository.portage_interface
+
+ self.assert_(isinstance(pi, RepositoryPortageInterface))
+
+ path = os.path.join(os.getcwd(), "repository_TEST_dir/testrepo/profiles/testprofile")
+ profile = pi.find_profile(path)
+
+ self.assert_(isinstance(profile, RepositoryPortageInterfaceProfilesDescLine))
+ self.assertEquals(profile.path, path)
+ self.assertEquals(profile.arch, "x86")
+ self.assertEquals(profile.status, "stable")
+
+
+ self.assertEquals(pi.find_profile("broken"), None)
+
+ profile = iter(pi.profiles).next()
+ pi.profile = profile
+
+ self.assertEquals(pi.profile_variable("ARCH"), "test")
+
+if __name__ == "__main__":
+ unittest.main()
diff --git a/python/repository_TEST_cleanup.sh b/python/repository_TEST_cleanup.sh
new file mode 100755
index 0000000..d9bbfa9
--- /dev/null
+++ b/python/repository_TEST_cleanup.sh
@@ -0,0 +1,8 @@
+#!/bin/bash
+# vim: set ft=sh sw=4 sts=4 et :
+
+if [ -d repository_TEST_dir ] ; then
+ rm -fr repository_TEST_dir
+else
+ true
+fi
diff --git a/python/repository_TEST_setup.sh b/python/repository_TEST_setup.sh
new file mode 100755
index 0000000..0950a74
--- /dev/null
+++ b/python/repository_TEST_setup.sh
@@ -0,0 +1,134 @@
+#!/bin/bash
+# vim: set ft=sh sw=4 sts=4 et :
+
+mkdir repository_TEST_dir || exit 1
+cd repository_TEST_dir || exit 1
+
+mkdir -p home/.paludis/repositories
+
+cat <<END > home/.paludis/repositories/testrepo.conf
+location = `pwd`/testrepo
+format = ebuild
+names_cache = /var/empty
+cache = /var/empty
+profiles = \${location}/profiles/testprofile
+END
+
+cat <<END > home/.paludis/repositories/installed.conf
+location = `pwd`/installed
+format = vdb
+names_cache = /var/empty
+provides_cache = /var/empty
+END
+
+cat <<END > home/.paludis/keywords.conf
+*/* test
+~foo/bar-1 ~test
+END
+
+cat <<END > home/.paludis/use.conf
+*/* enabled
+~foo/bar-1 sometimes_enabled
+END
+
+cat <<END > home/.paludis/licenses.conf
+*/* *
+END
+
+mkdir -p testrepo/{eclass,licenses,distfiles,profiles/testprofile,foo/bar/files} || exit 1
+cd testrepo || exit 1
+
+echo "testrepo" > profiles/repo_name || exit 1
+cat <<END > profiles/categories || exit 1
+foo
+END
+
+cat <<END > profiles/profiles.desc || exit 1
+x86 testprofile stable
+END
+
+cat <<END > profiles/package.mask || exit 1
+foo1/bar
+foo2/bar
+END
+
+cat <<END > profiles/testprofile/make.defaults || exit 1
+ARCH=test
+USERLAND=test
+KERNEL=test
+USE="test1 test2 -test5"
+END
+
+cat <<END > profiles/testprofile/package.mask || exit 1
+foo1/bar
+foo3/bar
+END
+
+cat <<END > profiles/testprofile/package.use || exit 1
+foo/bar -test2 test3
+END
+
+cat <<END > profiles/testprofile/use.mask || exit 1
+test4
+END
+
+cat <<END > profiles/testprofile/package.use.mask || exit 1
+foo/bar -test4 test5
+END
+
+cat <<END > profiles/testprofile/use.force || exit 1
+test6
+END
+
+cat <<END > profiles/testprofile/package.use.force || exit 1
+foo/bar test7
+END
+
+cat <<END > profiles/use.desc || exit 1
+test1 - A test use flag
+END
+
+cat <<END > profiles/use.local.desc || exit 1
+foo/bar:test2 - A test local use flag
+END
+
+touch licenses/foo
+
+cat <<"END" > foo/bar/bar-1.0.ebuild || exit 1
+DESCRIPTION="Test package"
+HOMEPAGE="http://paludis.pioto.org/"
+SRC_URI=""
+SLOT="0"
+IUSE="test1"
+LICENSE="GPL-2"
+KEYWORDS="test"
+END
+
+cat <<"END" > foo/bar/bar-2.0.ebuild || exit 1
+DESCRIPTION="Test package"
+HOMEPAGE="http://paludis.pioto.org/"
+SRC_URI=""
+SLOT="0"
+IUSE="test2"
+LICENSE="GPL-2"
+KEYWORDS="~test"
+END
+cd ..
+
+mkdir -p installed/cat-one/pkg-one-1 || exit 1
+
+for i in SLOT EAPI; do
+ echo "0" >installed/cat-one/pkg-one-1/${i}
+done
+
+for i in DEPEND RDEPEND LICENSE INHERITED IUSE PDEPEND PROVIDE; do
+ touch installed/cat-one/pkg-one-1/${i}
+done
+
+echo "flag1 flag2" >>installed/cat-one/pkg-one-1/USE
+
+cat <<END >installed/cat-one/pkg-one-1/CONTENTS
+dir //test
+obj /test/test_file de54c26b0678df67aca147575523b3c2 1165250496
+sym /test/test_link -> /test/test_file 1165250496
+END
diff --git a/python/version_metadata.cc b/python/version_metadata.cc
new file mode 100644
index 0000000..ecdf6a3
--- /dev/null
+++ b/python/version_metadata.cc
@@ -0,0 +1,195 @@
+/* vim: set sw=4 sts=4 et foldmethod=syntax : */
+
+/*
+ * Copyright (c) 2007 Piotr Jaroszyński <peper@gentoo.org>
+ *
+ * This file is part of the Paludis package manager. Paludis is free software;
+ * you can redistribute it and/or modify it under the terms of the GNU General
+ * Public License version 2, as published by the Free Software Foundation.
+ *
+ * Paludis 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., 59 Temple
+ * Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <paludis_python.hh>
+
+#include <paludis/version_metadata.hh>
+
+using namespace paludis;
+using namespace paludis::python;
+namespace bp = boost::python;
+
+VersionMetadataEbuildInterface *
+get_ei(const VersionMetadata & self)
+{
+ return self.ebuild_interface;
+}
+
+void expose_version_metadata()
+{
+ bp::register_ptr_to_python<std::tr1::shared_ptr<const VersionMetadata> >();
+ bp::class_<VersionMetadata, boost::noncopyable>
+ vm("VersionMetadata",
+ "Version metadata.",
+ bp::no_init
+ );
+ vm.def_readonly("slot", &VersionMetadata::slot,
+ "[ro] SlotName"
+ );
+ vm.def_readonly("homepage", &VersionMetadata::homepage,
+ "[ro] string"
+ );
+ vm.def_readonly("description", &VersionMetadata::description,
+ "[ro] string"
+ );
+ vm.def_readonly("eapi", &VersionMetadata::eapi,
+ "[ro] string"
+ );
+ vm.def_readonly("ebuild_interface", &VersionMetadata::ebuild_interface,
+ "[ro] EbuildInterface"
+ );
+ vm.def_readonly("ebin_interface", &VersionMetadata::ebin_interface,
+ "[ro] EbinInterface"
+ );
+ vm.def_readonly("cran_interface", &VersionMetadata::cran_interface,
+ "[ro] CRANInterface"
+ );
+ vm.def_readonly("deps_interface", &VersionMetadata::deps_interface,
+ "[ro] DepsInterface"
+ );
+ vm.def_readonly("origins_interface", &VersionMetadata::origins_interface,
+ "[ro] OriginsInterface"
+ );
+ vm.def_readonly("virtual_interface", &VersionMetadata::virtual_interface,
+ "[ro] VirtualInterface"
+ );
+ vm.def_readonly("license_interface", &VersionMetadata::license_interface,
+ "[ro] LicenseInterface"
+ );
+
+ bp::register_ptr_to_python<VersionMetadataEbuildInterface *>();
+ bp::class_<VersionMetadataEbuildInterface, boost::noncopyable>
+ ebuild_i("VersionMetadataEbuildInterface",
+ "Version metadata for ebuilds.",
+ bp::no_init
+ );
+ ebuild_i.def_readonly("provide_string", &VersionMetadataEbuildInterface::provide_string,
+ "[ro] string"
+ );
+ ebuild_i.def_readonly("src_uri_string", &VersionMetadataEbuildInterface::src_uri_string,
+ "[ro] string"
+ );
+ ebuild_i.def_readonly("restrict_string", &VersionMetadataEbuildInterface::restrict_string,
+ "[ro] string"
+ );
+ ebuild_i.def_readonly("keywords_string", &VersionMetadataEbuildInterface::keywords_string,
+ "[ro] string"
+ );
+ ebuild_i.def_readonly("eclass_keywords", &VersionMetadataEbuildInterface::eclass_keywords,
+ "[ro] string"
+ );
+ ebuild_i.def_readonly("iuse", &VersionMetadataEbuildInterface::iuse,
+ "[ro] string"
+ );
+ ebuild_i.def_readonly("inherited", &VersionMetadataEbuildInterface::inherited,
+ "[ro] string"
+ );
+
+ bp::register_ptr_to_python<VersionMetadataEbinInterface *>();
+ bp::class_<VersionMetadataEbinInterface, boost::noncopyable>
+ ebin_i("VersionMetadataEbinInterface",
+ "Version metadata for Ebins.",
+ bp::no_init
+ );
+ ebin_i.def_readonly("bin_uri_string", &VersionMetadataEbinInterface::bin_uri_string,
+ "[ro] string"
+ );
+
+ bp::register_ptr_to_python<VersionMetadataCRANInterface *>();
+ bp::class_<VersionMetadataCRANInterface, boost::noncopyable>
+ cran_i("VersionMetadataCRANInterface",
+ "Version metadata for CRAN packages.",
+ bp::no_init
+ );
+ cran_i.def_readonly("keywords", &VersionMetadataCRANInterface::keywords,
+ "[ro] string"
+ );
+ cran_i.def_readonly("package", &VersionMetadataCRANInterface::package,
+ "[ro] string"
+ );
+ cran_i.def_readonly("version", &VersionMetadataCRANInterface::version,
+ "[ro] string"
+ );
+
+ bp::register_ptr_to_python<VersionMetadataDepsInterface *>();
+ bp::class_<VersionMetadataDepsInterface, boost::noncopyable>
+ deps_i("VersionMetadataDepsInterface",
+ "Dependency data for VersionMetadata.",
+ bp::no_init
+ );
+ deps_i.def_readonly("build_depend_string", &VersionMetadataDepsInterface::build_depend_string,
+ "[ro] string"
+ );
+ deps_i.def_readonly("run_depend_string", &VersionMetadataDepsInterface::run_depend_string,
+ "[ro] string"
+ );
+ deps_i.def_readonly("post_depend_string", &VersionMetadataDepsInterface::post_depend_string,
+ "[ro] string"
+ );
+ deps_i.def_readonly("suggested_depend_string", &VersionMetadataDepsInterface::suggested_depend_string,
+ "[ro] string"
+ );
+ deps_i.add_property("build_depend", &VersionMetadataDepsInterface::build_depend,
+ "[ro] DepSpec"
+ );
+ deps_i.add_property("run_depend", &VersionMetadataDepsInterface::run_depend,
+ "[ro] DepSpec"
+ );
+ deps_i.add_property("post_depend", &VersionMetadataDepsInterface::post_depend,
+ "[ro] DepSpec"
+ );
+ deps_i.add_property("suggested_depend", &VersionMetadataDepsInterface::suggested_depend,
+ "[ro] DepSpec"
+ );
+
+ bp::register_ptr_to_python<VersionMetadataOriginsInterface *>();
+ bp::class_<VersionMetadataOriginsInterface, boost::noncopyable>
+ origins_i("VersionMetadataOriginsInterface",
+ "Origins data for VersionMetadata.",
+ bp::no_init
+ );
+ origins_i.add_property("source", bp::make_getter(&VersionMetadataOriginsInterface::source,
+ bp::return_value_policy<bp::return_by_value>()),
+ "[ro] PackageDatabaseEntry"
+ );
+ origins_i.add_property("binary", bp::make_getter(&VersionMetadataOriginsInterface::binary,
+ bp::return_value_policy<bp::return_by_value>()),
+ "[ro] PackageDatabaseEntry"
+ );
+
+ bp::register_ptr_to_python<VersionMetadataVirtualInterface *>();
+ bp::class_<VersionMetadataVirtualInterface, boost::noncopyable>
+ virtual_i("VersionMetadataVirtualInterface",
+ "Version metadata for virtual packages.",
+ bp::no_init
+ );
+ virtual_i.def_readonly("virtual_for", &VersionMetadataVirtualInterface::virtual_for,
+ "[ro] PackageDatabaseEntry"
+ );
+
+ bp::register_ptr_to_python<VersionMetadataLicenseInterface *>();
+ bp::class_<VersionMetadataLicenseInterface, boost::noncopyable>
+ license_i("VersionMetadataLicenseInterface",
+ "License data for VersionMetadata.",
+ bp::no_init
+ );
+ license_i.def_readonly("license_string", &VersionMetadataLicenseInterface::license_string,
+ "[ro] string"
+ );
+}
diff --git a/python/version_metadata_TEST.py b/python/version_metadata_TEST.py
new file mode 100755
index 0000000..6c5d96d
--- /dev/null
+++ b/python/version_metadata_TEST.py
@@ -0,0 +1,115 @@
+#!/usr/bin/env python
+# vim: set fileencoding=utf-8 sw=4 sts=4 et :
+
+#
+# Copyright (c) 2007 Piotr Jaroszyński <peper@gentoo.org>
+#
+# This file is part of the Paludis package manager. Paludis is free software;
+# you can redistribute it and/or modify it under the terms of the GNU General
+# Public License version 2, as published by the Free Software Foundation.
+#
+# Paludis 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., 59 Temple
+# Place, Suite 330, Boston, MA 02111-1307 USA
+#
+
+import os
+
+repo = os.path.join(os.getcwd(), "version_metadata_TEST_dir/testrepo")
+irepo = os.path.join(os.getcwd(), "version_metadata_TEST_dir/installed")
+
+from paludis import *
+import unittest
+
+Log.instance.log_level = LogLevel.WARNING;
+
+class TestCase_VersionMetadata(unittest.TestCase):
+ e = NoConfigEnvironment(repo)
+ evdb = NoConfigEnvironment(irepo)
+
+ def vmd(self, version):
+ return self.e.package_database.fetch_repository("testrepo").version_metadata("foo/bar", version)
+
+ def vmd_vdb(self, package, version):
+ return self.evdb.package_database.fetch_repository("installed").version_metadata(package, version)
+
+ def test_01_get(self):
+ self.vmd("1.0")
+
+ def test_02_create_error(self):
+ self.assertRaises(Exception, VersionMetadata)
+
+ def test_03_data_members(self):
+ vmd = self.vmd("1.0")
+
+ self.assertEquals(str(vmd.slot), "0")
+ self.assertEquals(vmd.homepage, "http://paludis.pioto.org/")
+ self.assertEquals(vmd.description, "Test package")
+ self.assertEquals(vmd.eapi, "0")
+
+ def test_04_ebuild_interface(self):
+ ei = self.vmd("1.0").ebuild_interface
+ ei2 = self.vmd_vdb("cat-one/pkg-one", "1").ebuild_interface
+
+ self.assert_(isinstance(ei, VersionMetadataEbuildInterface))
+ self.assert_(isinstance(ei2, VersionMetadataEbuildInterface))
+
+ self.assertEquals(ei.provide_string, "")
+ self.assertEquals(ei.src_uri_string, "http://example.com/bar-1.0.tar.bz2")
+ self.assertEquals(ei.restrict_string, "monkey")
+ self.assertEquals(ei.keywords_string, "test ")
+ self.assertEquals(ei.eclass_keywords, "")
+ self.assertEquals(ei.iuse, " ")
+ self.assertEquals(ei.inherited, "")
+
+ def test_05_ebin_interface_TODO(self):
+ pass
+
+ def test_06_cran_interface_TODO(self):
+ pass
+
+ def test_07_deps_interface(self):
+ di = self.vmd("1.0").deps_interface
+ di2 = self.vmd_vdb("cat-one/pkg-one", "1").deps_interface
+
+ self.assert_(isinstance(di, VersionMetadataDepsInterface))
+ self.assert_(isinstance(di2, VersionMetadataDepsInterface))
+
+ self.assertEquals(di.build_depend_string.strip(), "foo/bar")
+ self.assertEquals(di.run_depend_string.strip(), "")
+ self.assertEquals(di.post_depend_string.strip(), "")
+ self.assertEquals(di.suggested_depend_string, "")
+
+ self.assert_(isinstance(di.build_depend, AllDepSpec))
+ self.assert_(isinstance(di.run_depend, AllDepSpec))
+ self.assert_(isinstance(di.suggested_depend, AllDepSpec))
+ self.assert_(isinstance(di.post_depend, AllDepSpec))
+
+ def test_08_origins_interface(self):
+ oi = self.vmd_vdb("cat-one/pkg-one", "1").origins_interface
+
+ self.assert_(isinstance(oi, VersionMetadataOriginsInterface))
+
+ self.assertEquals(str(oi.source), "cat-one/pkg-one-1::origin_test")
+ self.assertEquals(oi.binary, None)
+
+ def test_09_virutal_interface_TODO(self):
+ pass
+
+ def test_10_license_interface(self):
+ li = self.vmd("1.0").license_interface
+ li2 = self.vmd_vdb("cat-one/pkg-one", "1").license_interface
+
+ self.assert_(isinstance(li, VersionMetadataLicenseInterface))
+ self.assert_(isinstance(li2, VersionMetadataLicenseInterface))
+
+ self.assertEquals(li.license_string, "GPL-2")
+ self.assertEquals(li2.license_string, "")
+
+if __name__ == "__main__":
+ unittest.main()
diff --git a/python/version_metadata_TEST_cleanup.sh b/python/version_metadata_TEST_cleanup.sh
new file mode 100755
index 0000000..c031e3f
--- /dev/null
+++ b/python/version_metadata_TEST_cleanup.sh
@@ -0,0 +1,8 @@
+#!/bin/bash
+# vim: set ft=sh sw=4 sts=4 et :
+
+if [ -d version_metadata_TEST_dir ] ; then
+ rm -fr version_metadata_TEST_dir
+else
+ true
+fi
diff --git a/python/version_metadata_TEST_setup.sh b/python/version_metadata_TEST_setup.sh
new file mode 100755
index 0000000..96dd1e8
--- /dev/null
+++ b/python/version_metadata_TEST_setup.sh
@@ -0,0 +1,45 @@
+#!/bin/bash
+# vim: set ft=sh sw=4 sts=4 et :
+
+mkdir version_metadata_TEST_dir || exit 1
+cd version_metadata_TEST_dir || exit 1
+
+mkdir -p testrepo/{eclass,distfiles,profiles/testprofile,foo/bar/files} || exit 1
+cd testrepo || exit 1
+echo "testrepo" > profiles/repo_name || exit 1
+cat <<END > profiles/categories || exit 1
+foo
+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" > foo/bar/bar-1.0.ebuild || exit 1
+DESCRIPTION="Test package"
+HOMEPAGE="http://paludis.pioto.org/"
+SRC_URI="http://example.com/${P}.tar.bz2"
+SLOT="0"
+IUSE=""
+LICENSE="GPL-2"
+KEYWORDS="test"
+RESTRICT="monkey"
+DEPEND="foo/bar"
+RDEPEND=""
+END
+
+cd ..
+
+mkdir -p installed/cat-one/pkg-{one,two}-1 || exit 1
+
+touch installed/cat-one/pkg-one-1/CONTENTS
+touch installed/cat-one/pkg-two-1/CONTENTS
+
+echo "origin_test" > installed/cat-one/pkg-one-1/REPOSITORY
+echo "0" > installed/cat-one/pkg-one-1/SLOT
+echo "0" > installed/cat-one/pkg-two-1/SLOT
+
diff --git a/python/version_operator.cc b/python/version_operator.cc
new file mode 100644
index 0000000..eb166db
--- /dev/null
+++ b/python/version_operator.cc
@@ -0,0 +1,59 @@
+/* vim: set sw=4 sts=4 et foldmethod=syntax : */
+
+/*
+ * Copyright (c) 2007 Piotr Jaroszyński <peper@gentoo.org>
+ *
+ * This file is part of the Paludis package manager. Paludis is free software;
+ * you can redistribute it and/or modify it under the terms of the GNU General
+ * Public License version 2, as published by the Free Software Foundation.
+ *
+ * Paludis 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., 59 Temple
+ * Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <paludis_python.hh>
+
+#include <paludis/version_operator.hh>
+#include <paludis/version_spec.hh>
+
+using namespace paludis;
+using namespace paludis::python;
+namespace bp = boost::python;
+
+bool
+compare(const VersionOperator & self, const VersionSpec & v1, const VersionSpec & v2)
+{
+ return (v1.*(self.as_version_spec_operator()))(v2);
+}
+
+void expose_version_operator()
+{
+ static register_exception<BadVersionOperatorError>
+ BadVersionOperatorError("BadVersionOperatorError");
+
+ enum_auto("VersionOperatorValue", last_vo);
+
+ bp::class_<VersionOperator>
+ vo("VersionOperator",
+ "An operator attached to a VersionSpec, validated.",
+ bp::init<const VersionOperatorValue>("__init__(VersionOperatorValue)")
+ );
+ vo.def(bp::init<const std::string &>("__init__(string)"));
+ vo.add_property("value", &VersionOperator::value,
+ "[ro] VersionOperatorValue"
+ );
+ vo.def("compare", &compare,
+ "compare(VersionSpec, VersionSpec) -> bool\n"
+ "Compare two VersionSpecs with this operator."
+ );
+ vo.def(bp::self_ns::str(bp::self));
+
+ bp::implicitly_convertible<std::string, VersionOperator>();
+ bp::implicitly_convertible<VersionOperatorValue, VersionOperator>();
+}
diff --git a/python/version_operator_TEST.py b/python/version_operator_TEST.py
new file mode 100755
index 0000000..4a49db6
--- /dev/null
+++ b/python/version_operator_TEST.py
@@ -0,0 +1,42 @@
+#!/usr/bin/env python
+# vim: set fileencoding=utf-8 sw=4 sts=4 et :
+
+#
+# Copyright (c) 2007 Piotr Jaroszyński <peper@gentoo.org>
+#
+# This file is part of the Paludis package manager. Paludis is free software;
+# you can redistribute it and/or modify it under the terms of the GNU General
+# Public License version 2, as published by the Free Software Foundation.
+#
+# Paludis 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., 59 Temple
+# Place, Suite 330, Boston, MA 02111-1307 USA
+#
+
+from paludis import *
+import unittest
+
+class TestCase_VersionOperator(unittest.TestCase):
+ def test_01_init(self):
+ VersionOperator("<")
+ VersionOperator(VersionOperatorValue.LESS)
+
+ def test_02_exceptions(self):
+ self.assertRaises(BadVersionOperatorError, VersionOperator, "<>")
+
+ def test_03_str(self):
+ self.assertEqual(">", str(VersionOperator(">")))
+ self.assertEqual("<", str(VersionOperator(VersionOperatorValue.LESS)))
+
+ def test_04_compare(self):
+ self.assert_(VersionOperator("<").compare("1.0", "2.0"))
+ self.assert_(VersionOperator(">").compare("3.0", "2.0"))
+ self.assert_(VersionOperator(VersionOperatorValue.EQUAL_STAR).compare("2.0.1-r1", "2.0"))
+
+if __name__ == "__main__":
+ unittest.main()
diff --git a/python/version_requirements.cc b/python/version_requirements.cc
new file mode 100644
index 0000000..ec7df28
--- /dev/null
+++ b/python/version_requirements.cc
@@ -0,0 +1,51 @@
+/* vim: set sw=4 sts=4 et foldmethod=syntax : */
+
+/*
+ * Copyright (c) 2007 Piotr Jaroszyński <peper@gentoo.org>
+ *
+ * This file is part of the Paludis package manager. Paludis is free software;
+ * you can redistribute it and/or modify it under the terms of the GNU General
+ * Public License version 2, as published by the Free Software Foundation.
+ *
+ * Paludis 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., 59 Temple
+ * Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <paludis_python.hh>
+
+#include <paludis/version_requirements.hh>
+
+using namespace paludis;
+using namespace paludis::python;
+namespace bp = boost::python;
+
+void expose_version_requirements()
+{
+ bp::class_<VersionRequirement>
+ vr("VersionRequirement",
+ bp::init<const VersionOperator &, const VersionSpec &>(
+ "__init__(VersionOperator, VersionSpec)"
+ )
+ );
+ vr.def_readwrite("version_operator", &VersionRequirement::version_operator,
+ "[rw] VersionOperator"
+ );
+ vr.def_readwrite("version_spec", &VersionRequirement::version_spec,
+ "[rw] VersionSpec"
+ );
+ vr.def("__eq__", &VersionRequirement::operator==);
+ vr.def("__neq__", &VersionRequirement::operator!=);
+
+ enum_auto("VersionRequirementsMode", last_vr);
+
+ class_collection<VersionRequirements>
+ vrc("VersionRequirements",
+ "Iterable collection of VersionRequirement instances, usually for a PackageDepSpec."
+ );
+}
diff --git a/python/version_requirements_TEST.py b/python/version_requirements_TEST.py
new file mode 100755
index 0000000..0129095
--- /dev/null
+++ b/python/version_requirements_TEST.py
@@ -0,0 +1,46 @@
+#!/usr/bin/env python
+# vim: set fileencoding=utf-8 sw=4 sts=4 et :
+
+#
+# Copyright (c) 2007 Piotr Jaroszyński <peper@gentoo.org>
+#
+# This file is part of the Paludis package manager. Paludis is free software;
+# you can redistribute it and/or modify it under the terms of the GNU General
+# Public License version 2, as published by the Free Software Foundation.
+#
+# Paludis 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., 59 Temple
+# Place, Suite 330, Boston, MA 02111-1307 USA
+#
+
+from paludis import *
+import unittest
+
+class TestCase_VersionRequirements(unittest.TestCase):
+ def test_01_init(self):
+ VersionRequirement("<", "0")
+ VersionRequirement(VersionOperatorValue.LESS, "0")
+
+ def test_02_data_members(self):
+ v1 = VersionRequirement("<", "0")
+ v1.version_operator = ">"
+ v1.version_spec = "1"
+
+ self.assertEquals(str(v1.version_operator), ">")
+ self.assertEquals(str(v1.version_spec), "1")
+
+ def test_03_compare(self):
+ v1 = VersionRequirement("<", "0")
+ v2 = VersionRequirement("<", "0")
+
+ self.assert_(v1 == v2)
+ v1.version_operator = ">"
+ self.assert_(v1 != v2)
+
+if __name__ == "__main__":
+ unittest.main()
diff --git a/python/version_spec.cc b/python/version_spec.cc
new file mode 100644
index 0000000..165e69b
--- /dev/null
+++ b/python/version_spec.cc
@@ -0,0 +1,68 @@
+/* vim: set sw=4 sts=4 et foldmethod=syntax : */
+
+/*
+ * Copyright (c) 2007 Piotr Jaroszyński <peper@gentoo.org>
+ *
+ * This file is part of the Paludis package manager. Paludis is free software;
+ * you can redistribute it and/or modify it under the terms of the GNU General
+ * Public License version 2, as published by the Free Software Foundation.
+ *
+ * Paludis 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., 59 Temple
+ * Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <paludis_python.hh>
+
+#include <paludis/version_spec.hh>
+#include <paludis/util/compare.hh>
+
+using namespace paludis;
+using namespace paludis::python;
+namespace bp = boost::python;
+
+void expose_version_spec()
+{
+ static register_exception<BadVersionSpecError>
+ BadVersionSpecError("BadVersionSpecError");
+
+ bp::class_<VersionSpec>
+ vs("VersionSpec",
+ "A VersionSpec represents a version number (for example, 1.2.3b-r1).",
+ bp::init<const std::string &>("__init__(string)")
+ );
+ vs.def("bump", &VersionSpec::bump,
+ "bump() -> VersionSpec\n"
+ "This is used by the ~> operator. It returns a VersionSpec where the next to last number "
+ "is one greater (e.g. 5.3.1 => 5.4).\n"
+ "Any non number parts are stripped (e.g. 1.2.3_alpha4-r5 => 1.3)."
+ );
+ vs.add_property("is_scm", &VersionSpec::is_scm,
+ "[ro] bool\n"
+ "Are we an -scm package, or something pretending to be one?"
+ );
+ vs.def("remove_revision", &VersionSpec::remove_revision,
+ "remove_revision() -> VersionSpec\n"
+ "Remove the revision part."
+ );
+ vs.def("revision_only", &VersionSpec::revision_only,
+ "revision_only() -> string\n"
+ "Revision part only (or \"r0\")."
+ );
+ int (*compare_ptr)(const VersionSpec &, const VersionSpec &) = &compare;
+ vs.def("__cmp__", compare_ptr);
+ vs.def(bp::self_ns::str(bp::self));
+
+ bp::implicitly_convertible<std::string, VersionSpec>();
+
+ class_collection<VersionSpecCollection>
+ vsc("VersionSpecCollection",
+ "Iterable of VersionSpec.\n"
+ "Collection of VersionSpec instances."
+ );
+}
diff --git a/python/version_spec_TEST.py b/python/version_spec_TEST.py
new file mode 100755
index 0000000..58c925c
--- /dev/null
+++ b/python/version_spec_TEST.py
@@ -0,0 +1,66 @@
+#!/usr/bin/env python
+# vim: set fileencoding=utf-8 sw=4 sts=4 et :
+
+#
+# Copyright (c) 2007 Piotr Jaroszyński <peper@gentoo.org>
+#
+# This file is part of the Paludis package manager. Paludis is free software;
+# you can redistribute it and/or modify it under the terms of the GNU General
+# Public License version 2, as published by the Free Software Foundation.
+#
+# Paludis 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., 59 Temple
+# Place, Suite 330, Boston, MA 02111-1307 USA
+#
+
+from paludis import *
+import unittest
+
+class TestCase_VersionSpec(unittest.TestCase):
+ def test_01_init(self):
+ VersionSpec("0")
+
+ def test_02_exceptions(self):
+ self.assertRaises(BadVersionSpecError, VersionSpec, "1.0-r1-")
+
+ def test_03_compare(self):
+ v0 = VersionSpec("0")
+ v1 = VersionSpec("0.1")
+ v2 = VersionSpec("1.0_beta3")
+ v3 = VersionSpec("1.0")
+
+ self.assert_(v0 < v1);
+ self.assert_(v1 < v2);
+ self.assert_(v2 < v3);
+
+ self.assert_(v0 >= v0);
+ self.assert_(v1 >= v1);
+ self.assert_(v3 >= v2);
+
+ def test_04_str(self):
+ self.assertEqual("0.1_beta2-r3", str(VersionSpec("0.1_beta2-r3")))
+ self.assertEqual("1", str(VersionSpec("0.1_beta2-r3").bump()))
+
+ def test_05_remove_revision(self):
+ self.assertEqual(VersionSpec("0.1"), VersionSpec("0.1-r1").remove_revision())
+
+ def test_06_revision_only(self):
+ self.assertEqual("r3", VersionSpec("0.1r_alpha1-r3").revision_only())
+
+ def test_07_bump(self):
+ self.assertEqual(VersionSpec("2"), VersionSpec("1").bump())
+ self.assertEqual(VersionSpec("2"), VersionSpec("1.2").bump())
+ self.assertEqual(VersionSpec("1.3"), VersionSpec("1.2.3").bump())
+ self.assertEqual(VersionSpec("1.3"), VersionSpec("1.2.3_beta1-r4").bump())
+ self.assertEqual(VersionSpec("scm"), VersionSpec("scm").bump())
+
+ def test_08_is_scm(self):
+ self.assert_(VersionSpec("scm").is_scm)
+
+if __name__ == "__main__":
+ unittest.main()