Browse Source

[2908] add Python wrappers for ZoneTableAccessor and friends

Paul Selkirk 12 years ago
parent
commit
ca04c55332

+ 0 - 2
src/lib/datasrc/client_list.h

@@ -121,8 +121,6 @@ private:
     MemorySegmentState state_;
     MemorySegmentState state_;
 };
 };
 
 
-typedef boost::shared_ptr<const ZoneTableAccessor> ConstZoneTableAccessorPtr;
-
 /// \brief The list of data source clients.
 /// \brief The list of data source clients.
 ///
 ///
 /// The purpose of this class is to hold several data source clients and search
 /// The purpose of this class is to hold several data source clients and search

+ 3 - 0
src/lib/datasrc/zone_table_accessor.h

@@ -202,6 +202,9 @@ public:
     virtual IteratorPtr getIterator() const = 0;
     virtual IteratorPtr getIterator() const = 0;
 };
 };
 
 
+typedef boost::shared_ptr<ZoneTableAccessor> ZoneTableAccessorPtr;
+typedef boost::shared_ptr<const ZoneTableAccessor> ConstZoneTableAccessorPtr;
+
 }
 }
 }
 }
 
 

+ 2 - 0
src/lib/python/isc/datasrc/Makefile.am

@@ -21,6 +21,8 @@ datasrc_la_SOURCES += journal_reader_python.cc journal_reader_python.h
 datasrc_la_SOURCES += configurableclientlist_python.cc
 datasrc_la_SOURCES += configurableclientlist_python.cc
 datasrc_la_SOURCES += configurableclientlist_python.h
 datasrc_la_SOURCES += configurableclientlist_python.h
 datasrc_la_SOURCES += zone_loader_python.cc zone_loader_python.h
 datasrc_la_SOURCES += zone_loader_python.cc zone_loader_python.h
+datasrc_la_SOURCES += zonetable_accessor_python.cc zonetable_accessor_python.h
+datasrc_la_SOURCES += zonetable_iterator_python.cc zonetable_iterator_python.h
 
 
 datasrc_la_CPPFLAGS = $(AM_CPPFLAGS) $(PYTHON_INCLUDES)
 datasrc_la_CPPFLAGS = $(AM_CPPFLAGS) $(PYTHON_INCLUDES)
 datasrc_la_CXXFLAGS = $(AM_CXXFLAGS) $(PYTHON_CXXFLAGS)
 datasrc_la_CXXFLAGS = $(AM_CXXFLAGS) $(PYTHON_CXXFLAGS)

+ 44 - 4
src/lib/python/isc/datasrc/configurableclientlist_python.cc

@@ -34,6 +34,7 @@
 #include "datasrc.h"
 #include "datasrc.h"
 #include "finder_python.h"
 #include "finder_python.h"
 #include "client_python.h"
 #include "client_python.h"
+#include "zonetable_accessor_python.h"
 
 
 using namespace std;
 using namespace std;
 using namespace isc::util::python;
 using namespace isc::util::python;
@@ -170,6 +171,36 @@ ConfigurableClientList_find(PyObject* po_self, PyObject* args) {
     }
     }
 }
 }
 
 
+PyObject*
+ConfigurableClientList_getZoneTableAccessor(PyObject* po_self, PyObject* args) {
+    s_ConfigurableClientList* self =
+        static_cast<s_ConfigurableClientList*>(po_self);
+    try {
+        const char* datasrc_name;
+        int use_cache;
+        if (PyArg_ParseTuple(args, "si", &datasrc_name, &use_cache)) {
+            const ConstZoneTableAccessorPtr
+                z(self->cppobj->getZoneTableAccessor(datasrc_name, use_cache));
+            PyObjectContainer accessor;
+            if (z == NULL) {
+                accessor.reset(Py_BuildValue(""));
+            } else {
+                accessor.reset(createZoneTableAccessorObject(z));
+            }
+            return (Py_BuildValue("O", accessor.get()));
+        } else {
+            return (NULL);
+        }
+    } catch (const std::exception& exc) {
+        PyErr_SetString(getDataSourceException("Error"), exc.what());
+        return (NULL);
+    } catch (...) {
+        PyErr_SetString(getDataSourceException("Error"),
+                        "Unknown C++ exception");
+        return (NULL);
+    }
+}
+
 // This list contains the actual set of functions we have in
 // This list contains the actual set of functions we have in
 // python. Each entry has
 // python. Each entry has
 // 1. Python method name
 // 1. Python method name
@@ -212,6 +243,15 @@ you don't need it, but if you do need it, it is better to set it to True\
 instead of getting it from the datasrc_client later.\n\
 instead of getting it from the datasrc_client later.\n\
 \n\
 \n\
 If no answer is found, the datasrc_client and zone_finder are None." },
 If no answer is found, the datasrc_client and zone_finder are None." },
+    { "get_accessor", ConfigurableClientList_getZoneTableAccessor,
+      METH_VARARGS,
+"get_accessor(datasrc_name, use_cache) -> isc.datasrc.ZoneTableAccessor\n\
+\n\
+Create a ZoneTableAccessor object for the specified data source.\n\
+\n\
+Parameters:\n\
+  datasrc_name      If not empty, the name of the data source\
+  use_cache         If true, create a zone table for in-memory cache." },
     { NULL, NULL, 0, NULL }
     { NULL, NULL, 0, NULL }
 };
 };
 
 
@@ -237,9 +277,9 @@ namespace python {
 PyTypeObject configurableclientlist_type = {
 PyTypeObject configurableclientlist_type = {
     PyVarObject_HEAD_INIT(NULL, 0)
     PyVarObject_HEAD_INIT(NULL, 0)
     "datasrc.ConfigurableClientList",
     "datasrc.ConfigurableClientList",
-    sizeof(s_ConfigurableClientList),                 // tp_basicsize
+    sizeof(s_ConfigurableClientList),   // tp_basicsize
     0,                                  // tp_itemsize
     0,                                  // tp_itemsize
-    ConfigurableClientList_destroy,                 // tp_dealloc
+    ConfigurableClientList_destroy,     // tp_dealloc
     NULL,                               // tp_print
     NULL,                               // tp_print
     NULL,                               // tp_getattr
     NULL,                               // tp_getattr
     NULL,                               // tp_setattr
     NULL,                               // tp_setattr
@@ -262,7 +302,7 @@ PyTypeObject configurableclientlist_type = {
     0,                                  // tp_weaklistoffset
     0,                                  // tp_weaklistoffset
     NULL,                               // tp_iter
     NULL,                               // tp_iter
     NULL,                               // tp_iternext
     NULL,                               // tp_iternext
-    ConfigurableClientList_methods,                   // tp_methods
+    ConfigurableClientList_methods,     // tp_methods
     NULL,                               // tp_members
     NULL,                               // tp_members
     NULL,                               // tp_getset
     NULL,                               // tp_getset
     NULL,                               // tp_base
     NULL,                               // tp_base
@@ -270,7 +310,7 @@ PyTypeObject configurableclientlist_type = {
     NULL,                               // tp_descr_get
     NULL,                               // tp_descr_get
     NULL,                               // tp_descr_set
     NULL,                               // tp_descr_set
     0,                                  // tp_dictoffset
     0,                                  // tp_dictoffset
-    ConfigurableClientList_init,                    // tp_init
+    ConfigurableClientList_init,        // tp_init
     NULL,                               // tp_alloc
     NULL,                               // tp_alloc
     PyType_GenericNew,                  // tp_new
     PyType_GenericNew,                  // tp_new
     NULL,                               // tp_free
     NULL,                               // tp_free

+ 48 - 0
src/lib/python/isc/datasrc/datasrc.cc

@@ -33,6 +33,8 @@
 #include "journal_reader_python.h"
 #include "journal_reader_python.h"
 #include "configurableclientlist_python.h"
 #include "configurableclientlist_python.h"
 #include "zone_loader_python.h"
 #include "zone_loader_python.h"
+#include "zonetable_accessor_python.h"
+#include "zonetable_iterator_python.h"
 
 
 #include <util/python/pycppwrapper_util.h>
 #include <util/python/pycppwrapper_util.h>
 #include <dns/python/pydnspp_common.h>
 #include <dns/python/pydnspp_common.h>
@@ -255,6 +257,42 @@ initModulePart_ZoneJournalReader(PyObject* mod) {
     return (true);
     return (true);
 }
 }
 
 
+bool
+initModulePart_ZoneTableAccessor(PyObject* mod) {
+    // We initialize the static description object with PyType_Ready(),
+    // then add it to the module. This is not just a check! (leaving
+    // this out results in segmentation faults)
+    if (PyType_Ready(&zonetableaccessor_type) < 0) {
+        return (false);
+    }
+    void* p = &zonetableaccessor_type;
+    if (PyModule_AddObject(mod, "ZoneTableAccessor",
+                           static_cast<PyObject*>(p)) < 0) {
+        return (false);
+    }
+    Py_INCREF(&zonetableaccessor_type);
+
+    return (true);
+}
+
+bool
+initModulePart_ZoneTableIterator(PyObject* mod) {
+    // We initialize the static description object with PyType_Ready(),
+    // then add it to the module. This is not just a check! (leaving
+    // this out results in segmentation faults)
+    if (PyType_Ready(&zonetableiterator_type) < 0) {
+        return (false);
+    }
+    void* p = &zonetableiterator_type;
+    if (PyModule_AddObject(mod, "ZoneTableIterator",
+                           static_cast<PyObject*>(p)) < 0) {
+        return (false);
+    }
+    Py_INCREF(&zonetableiterator_type);
+
+    return (true);
+}
+
 PyObject* po_DataSourceError;
 PyObject* po_DataSourceError;
 PyObject* po_MasterFileError;
 PyObject* po_MasterFileError;
 PyObject* po_NotImplemented;
 PyObject* po_NotImplemented;
@@ -340,5 +378,15 @@ PyInit_datasrc(void) {
         return (NULL);
         return (NULL);
     }
     }
 
 
+    if (!initModulePart_ZoneTableAccessor(mod)) {
+        Py_DECREF(mod);
+        return (NULL);
+    }
+
+    if (!initModulePart_ZoneTableIterator(mod)) {
+        Py_DECREF(mod);
+        return (NULL);
+    }
+
     return (mod);
     return (mod);
 }
 }

+ 2 - 2
src/lib/python/isc/datasrc/finder_python.cc

@@ -64,7 +64,7 @@ getFindResultFlags(const ZoneFinder::Context& context) {
 
 
 namespace isc_datasrc_internal {
 namespace isc_datasrc_internal {
 // This is the shared code for the find() call in the finder and the updater
 // This is the shared code for the find() call in the finder and the updater
-// Is is intentionally not available through any header, nor at our standard
+// It is intentionally not available through any header, nor at our standard
 // namespace, as it is not supposed to be called anywhere but from finder and
 // namespace, as it is not supposed to be called anywhere but from finder and
 // updater
 // updater
 PyObject* ZoneFinder_helper(ZoneFinder* finder, PyObject* args) {
 PyObject* ZoneFinder_helper(ZoneFinder* finder, PyObject* args) {
@@ -184,7 +184,7 @@ public:
     ZoneFinderPtr cppobj;
     ZoneFinderPtr cppobj;
     // This is a reference to a base object; if the object of this class
     // This is a reference to a base object; if the object of this class
     // depends on another object to be in scope during its lifetime,
     // depends on another object to be in scope during its lifetime,
-    // we use INCREF the base object upon creation, and DECREF it at
+    // we INCREF the base object upon creation, and DECREF it at
     // the end of the destructor
     // the end of the destructor
     // This is an optional argument to createXXX(). If NULL, it is ignored.
     // This is an optional argument to createXXX(). If NULL, it is ignored.
     PyObject* base_obj;
     PyObject* base_obj;

+ 90 - 2
src/lib/python/isc/datasrc/tests/clientlist_test.py

@@ -1,4 +1,4 @@
-# Copyright (C) 2012  Internet Systems Consortium.
+# Copyright (C) 2012-2013  Internet Systems Consortium.
 #
 #
 # Permission to use, copy, modify, and distribute this software for any
 # Permission to use, copy, modify, and distribute this software for any
 # purpose with or without fee is hereby granted, provided that the above
 # purpose with or without fee is hereby granted, provided that the above
@@ -57,7 +57,7 @@ class ClientListTest(unittest.TestCase):
     def test_configure(self):
     def test_configure(self):
         """
         """
         Test we can configure the client list. This tests if the valid
         Test we can configure the client list. This tests if the valid
-        ones are acceptend and invalid rejected. We check the changes
+        ones are accepted and invalid rejected. We check the changes
         have effect.
         have effect.
         """
         """
         self.clist = isc.datasrc.ConfigurableClientList(isc.dns.RRClass.IN)
         self.clist = isc.datasrc.ConfigurableClientList(isc.dns.RRClass.IN)
@@ -151,6 +151,94 @@ class ClientListTest(unittest.TestCase):
         self.assertRaises(TypeError, self.clist.find, "example.org")
         self.assertRaises(TypeError, self.clist.find, "example.org")
         self.assertRaises(TypeError, self.clist.find)
         self.assertRaises(TypeError, self.clist.find)
 
 
+    def test_get_accessor(self):
+        """
+       Test that we can get the zone table accessor and, thereby,
+        the zone table iterator.
+        """
+        self.clist = isc.datasrc.ConfigurableClientList(isc.dns.RRClass.IN)
+
+        # null configuration
+        self.clist.configure("[]", True)
+        accessor = self.clist.get_accessor("", True)
+        self.assertIsNone(accessor)
+
+        # empty configuration
+        self.clist.configure('''[{
+            "type": "MasterFiles",
+            "params": {},
+            "cache-enable": true
+        }]''', True)
+        accessor = self.clist.get_accessor("bogus", True)
+        self.assertIsNone(accessor)
+        accessor = self.clist.get_accessor("", True)
+        self.assertIsNotNone(accessor)
+        iterator = accessor.get_iterator()
+        self.assertIsNotNone(iterator)
+        self.assertTrue(iterator.is_last())
+        self.assertRaises(isc.datasrc.Error, iterator.get_current)
+        self.assertRaises(isc.datasrc.Error, iterator.next)
+
+        # normal configuration
+        self.clist.configure('''[{
+            "type": "MasterFiles",
+            "params": {
+                "example.org": "''' + TESTDATA_PATH + '''example.org.zone"
+            },
+            "cache-enable": true
+        }]''', True)
+
+        # use_cache = false
+        self.assertRaises(isc.datasrc.Error,
+                          self.clist.get_accessor, "", False)
+
+        # bogus datasrc
+        accessor = self.clist.get_accessor("bogus", True)
+        self.assertIsNone(accessor)
+
+        # first datasrc
+        accessor = self.clist.get_accessor("", True)
+        self.assertIsNotNone(accessor)
+        iterator = accessor.get_iterator()
+        self.assertIsNotNone(iterator)
+        self.assertFalse(iterator.is_last())
+        index, origin = iterator.get_current()
+        self.assertEqual(origin, "example.org.")
+        iterator.next()
+        self.assertTrue(iterator.is_last())
+        # reset iterator and count zones
+        iterator = accessor.get_iterator()
+        self.assertEqual(1, len(list(iterator)))
+
+        # named datasrc
+        accessor = self.clist.get_accessor("MasterFiles", True)
+        iterator = accessor.get_iterator()
+        index, origin = iterator.get_current()
+        self.assertEqual(origin, "example.org.")
+        self.assertEqual(1, len(list(iterator)))
+
+        # longer zone list for non-trivial iteration
+        self.clist.configure('''[{
+            "type": "MasterFiles",
+            "params": {
+                "example.org": "''' + TESTDATA_PATH + '''example.org.zone",
+                "example.com": "''' + TESTDATA_PATH + '''example.com.zone",
+                "example.net": "''' + TESTDATA_PATH + '''example.net.zone",
+                "example.biz": "''' + TESTDATA_PATH + '''example.biz.zone",
+                "example.edu": "''' + TESTDATA_PATH + '''example.edu.zone"
+            },
+            "cache-enable": true
+        }]''', True)
+        accessor = self.clist.get_accessor("", True)
+        iterator = accessor.get_iterator()
+        self.assertEqual(5, len(list(iterator)))
+        iterator = accessor.get_iterator()
+        found = False
+        for index, origin in iterator:
+            if origin == "example.net.":
+                found = True
+        self.assertTrue(found)
+
 if __name__ == "__main__":
 if __name__ == "__main__":
     isc.log.init("bind10")
     isc.log.init("bind10")
     isc.log.resetUnitTestRootLogger()
     isc.log.resetUnitTestRootLogger()

+ 171 - 0
src/lib/python/isc/datasrc/zonetable_accessor_python.cc

@@ -0,0 +1,171 @@
+// Copyright (C) 2013  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+// Enable this if you use s# variants with PyArg_ParseTuple(), see
+// http://docs.python.org/py3k/c-api/arg.html#strings-and-buffers
+//#define PY_SSIZE_T_CLEAN
+
+// Python.h needs to be placed at the head of the program file, see:
+// http://docs.python.org/py3k/extending/extending.html#a-simple-example
+#include <Python.h>
+
+//#include <util/python/pycppwrapper_util.h>
+//#include <datasrc/exceptions.h>
+#include <datasrc/zone_table_accessor.h>
+
+#include "datasrc.h"
+#include "zonetable_accessor_python.h"
+#include "zonetable_iterator_python.h"
+
+using namespace std;
+using namespace isc::datasrc;
+using namespace isc::datasrc::python;
+
+namespace {
+// The s_* Class simply covers one instantiation of the object
+class s_ZoneTableAccessor : public PyObject {
+public:
+    s_ZoneTableAccessor() : cppobj(ConstZoneTableAccessorPtr()) {};
+    ConstZoneTableAccessorPtr cppobj;
+};
+
+PyObject*
+ZoneTableAccessor_getIterator(PyObject* po_self, PyObject* args) {
+    s_ZoneTableAccessor* const self =
+        static_cast<s_ZoneTableAccessor*>(po_self);
+    try {
+        return (createZoneTableIteratorObject(self->cppobj->getIterator(),
+                                              po_self));
+    } catch (const std::exception& exc) {
+        PyErr_SetString(getDataSourceException("Error"), exc.what());
+        return (NULL);
+    } catch (...) {
+        PyErr_SetString(getDataSourceException("Error"),
+                        "Unexpected exception");
+        return (NULL);
+    }
+}
+
+// This list contains the actual set of functions we have in
+// python. Each entry has
+// 1. Python method name
+// 2. Our static function here
+// 3. Argument type
+// 4. Documentation
+PyMethodDef ZoneTableAccessor_methods[] = {
+    { "get_iterator",
+      ZoneTableAccessor_getIterator, METH_NOARGS,
+"getIterator() -> isc.datasrc.ZoneTableIterator\n\
+\n\
+Return a zone table iterator.\n\
+\n" },
+    { NULL, NULL, 0, NULL }
+};
+
+const char* const ZoneTableAccessor_doc = "\
+An accessor to a zone table for a data source.\n\
+\n\
+This class object is intended to be used by applications that load zones\
+into memory, so that the application can get a list of zones to be loaded.";
+
+int
+ZoneTableAccessor_init(PyObject*, PyObject*, PyObject*) {
+    // can't be called directly
+    PyErr_SetString(PyExc_TypeError,
+                    "ZoneTableAccessor cannot be constructed directly");
+
+    return (-1);
+}
+
+void
+ZoneTableAccessor_destroy(PyObject* po_self) {
+    s_ZoneTableAccessor* const self =
+        static_cast<s_ZoneTableAccessor*>(po_self);
+    // cppobj is a shared ptr, but to make sure things are not destroyed in
+    // the wrong order, we reset it here.
+    self->cppobj.reset();
+    Py_TYPE(self)->tp_free(self);
+}
+
+} // end anonymous namespace
+
+namespace isc {
+namespace datasrc {
+namespace python {
+// This defines the complete type for reflection in python and
+// parsing of PyObject* to s_ZoneTableAccessor
+// Most of the functions are not actually implemented and NULL here.
+PyTypeObject zonetableaccessor_type = {
+    PyVarObject_HEAD_INIT(NULL, 0)
+    "datasrc.ZoneTableAccessor",
+    sizeof(s_ZoneTableAccessor),         // tp_basicsize
+    0,                                  // tp_itemsize
+    ZoneTableAccessor_destroy,           // tp_dealloc
+    NULL,                               // tp_print
+    NULL,                               // tp_getattr
+    NULL,                               // tp_setattr
+    NULL,                               // tp_reserved
+    NULL,                               // tp_repr
+    NULL,                               // tp_as_number
+    NULL,                               // tp_as_sequence
+    NULL,                               // tp_as_mapping
+    NULL,                               // tp_hash
+    NULL,                               // tp_call
+    NULL,                               // tp_str
+    NULL,                               // tp_getattro
+    NULL,                               // tp_setattro
+    NULL,                               // tp_as_buffer
+    Py_TPFLAGS_DEFAULT,                 // tp_flags
+    ZoneTableAccessor_doc,
+    NULL,                               // tp_traverse
+    NULL,                               // tp_clear
+    NULL,                               // tp_richcompare
+    0,                                  // tp_weaklistoffset
+    NULL,                               // tp_iter
+    NULL,                               // tp_iternext
+    ZoneTableAccessor_methods,           // tp_methods
+    NULL,                               // tp_members
+    NULL,                               // tp_getset
+    NULL,                               // tp_base
+    NULL,                               // tp_dict
+    NULL,                               // tp_descr_get
+    NULL,                               // tp_descr_set
+    0,                                  // tp_dictoffset
+    ZoneTableAccessor_init,             // tp_init
+    NULL,                               // tp_alloc
+    PyType_GenericNew,                  // tp_new
+    NULL,                               // tp_free
+    NULL,                               // tp_is_gc
+    NULL,                               // tp_bases
+    NULL,                               // tp_mro
+    NULL,                               // tp_cache
+    NULL,                               // tp_subclasses
+    NULL,                               // tp_weaklist
+    NULL,                               // tp_del
+    0                                   // tp_version_tag
+};
+
+PyObject*
+createZoneTableAccessorObject(isc::datasrc::ConstZoneTableAccessorPtr source) {
+    s_ZoneTableAccessor* py_zt = static_cast<s_ZoneTableAccessor*>(
+        zonetableaccessor_type.tp_alloc(&zonetableaccessor_type, 0));
+    if (py_zt != NULL) {
+        py_zt->cppobj = source;
+    }
+    return (py_zt);
+}
+
+} // namespace python
+} // namespace datasrc
+} // namespace isc

+ 43 - 0
src/lib/python/isc/datasrc/zonetable_accessor_python.h

@@ -0,0 +1,43 @@
+// Copyright (C) 2013  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#ifndef PYTHON_ZONETABLE_ACCESSOR_H
+#define PYTHON_ZONETABLE_ACCESSOR_H 1
+
+#include <Python.h>
+
+namespace isc {
+namespace datasrc {
+namespace python {
+
+extern PyTypeObject zonetableaccessor_type;
+
+/// \brief Create a ZoneTableAccessor python object
+///
+/// \param source The zone iterator pointer to wrap
+/// \param base_obj An optional PyObject that this ZoneFinder depends on
+///                 Its refcount is increased, and will be decreased when
+///                 this zone iterator is destroyed, making sure that the
+///                 base object is never destroyed before this zonefinder.
+PyObject* createZoneTableAccessorObject(
+    isc::datasrc::ConstZoneTableAccessorPtr source);
+
+} // namespace python
+} // namespace datasrc
+} // namespace isc
+#endif // PYTHON_ZONETABLE_ACCESSOR_H
+
+// Local Variables:
+// mode: c++
+// End:

+ 255 - 0
src/lib/python/isc/datasrc/zonetable_iterator_python.cc

@@ -0,0 +1,255 @@
+// Copyright (C) 2013  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+// Enable this if you use s# variants with PyArg_ParseTuple(), see
+// http://docs.python.org/py3k/c-api/arg.html#strings-and-buffers
+//#define PY_SSIZE_T_CLEAN
+
+// Python.h needs to be placed at the head of the program file, see:
+// http://docs.python.org/py3k/extending/extending.html#a-simple-example
+#include <Python.h>
+
+//#include <util/python/pycppwrapper_util.h>
+#include <datasrc/zone_table_accessor.h>
+
+#include "datasrc.h"
+#include "zonetable_iterator_python.h"
+
+using namespace std;
+using namespace isc::datasrc;
+using namespace isc::datasrc::python;
+
+namespace {
+// The s_* Class simply covers one instantiation of the object
+class s_ZoneTableIterator : public PyObject {
+public:
+    s_ZoneTableIterator() :
+        cppobj(ZoneTableAccessor::IteratorPtr()), base_obj(NULL)
+    {};
+
+    ZoneTableAccessor::IteratorPtr cppobj;
+    // This is a reference to a base object; if the object of this class
+    // depends on another object to be in scope during its lifetime,
+    // we use INCREF the base object upon creation, and DECREF it at
+    // the end of the destructor
+    // This is an optional argument to createXXX(). If NULL, it is ignored.
+    PyObject* base_obj;
+};
+
+// General creation and destruction
+int
+ZoneTableIterator_init(s_ZoneTableIterator* self, PyObject* args) {
+    // can't be called directly
+    PyErr_SetString(PyExc_TypeError,
+                    "ZoneTableIterator cannot be constructed directly");
+
+    return (-1);
+}
+
+void
+ZoneTableIterator_destroy(s_ZoneTableIterator* const self) {
+    // cppobj is a shared ptr, but to make sure things are not destroyed in
+    // the wrong order, we reset it here.
+    self->cppobj.reset();
+    if (self->base_obj != NULL) {
+        Py_DECREF(self->base_obj);
+    }
+    Py_TYPE(self)->tp_free(self);
+}
+
+//
+// We declare the functions here, the definitions are below
+// the type definition of the object, since both can use the other
+//
+PyObject*
+ZoneTableIterator_isLast(PyObject* po_self, PyObject*) {
+    s_ZoneTableIterator* self = static_cast<s_ZoneTableIterator*>(po_self);
+    try {
+        return (Py_BuildValue("i", self->cppobj->isLast()));
+    } catch (...) {
+        return (NULL);
+    }
+}
+
+PyObject*
+ZoneTableIterator_next(PyObject* po_self, PyObject*) {
+    s_ZoneTableIterator* self = static_cast<s_ZoneTableIterator*>(po_self);
+    try {
+        self->cppobj->next();
+        return (Py_BuildValue(""));
+    } catch (const std::exception& ex) {
+        // isc::InvalidOperation is thrown when we call next()
+        // when we are already done iterating ('iterating past end')
+        // We could also simply return None again
+        PyErr_SetString(getDataSourceException("Error"), ex.what());
+        return (NULL);
+    } catch (...) {
+        return (NULL);
+    }
+}
+
+PyObject*
+ZoneTableIterator_getCurrent(PyObject* po_self, PyObject*) {
+    s_ZoneTableIterator* self = static_cast<s_ZoneTableIterator*>(po_self);
+    try {
+        const isc::datasrc::ZoneSpec& zs = self->cppobj->getCurrent();
+        return (Py_BuildValue("is", zs.index, zs.origin.toText().c_str()));
+    } catch (const std::exception& ex) {
+        // isc::InvalidOperation is thrown when we call getCurrent()
+        // when we are already done iterating ('iterating past end')
+        // We could also simply return None again
+        PyErr_SetString(getDataSourceException("Error"), ex.what());
+        return (NULL);
+    } catch (...) {
+        PyErr_SetString(getDataSourceException("Error"),
+                        "Unexpected exception");
+        return (NULL);
+    }
+}
+
+PyObject*
+ZoneTableIterator_piter(PyObject *self) {
+    Py_INCREF(self);
+    return (self);
+}
+
+PyObject*
+ZoneTableIterator_pnext(PyObject* po_self) {
+    s_ZoneTableIterator* self = static_cast<s_ZoneTableIterator*>(po_self);
+    if (!self->cppobj || self->cppobj->isLast()) {
+        return (NULL);
+    }
+    try {
+        PyObject* result = ZoneTableIterator_getCurrent(po_self, NULL);
+        self->cppobj->next();
+        return (result);
+    } catch (const std::exception& exc) {
+        PyErr_SetString(getDataSourceException("Error"), exc.what());
+        return (NULL);
+    } catch (...) {
+        PyErr_SetString(getDataSourceException("Error"),
+                        "Unexpected exception");
+        return (NULL);
+    }
+}
+
+PyMethodDef ZoneTableIterator_methods[] = {
+    { "is_last", ZoneTableIterator_isLast, METH_NOARGS,
+"is_last() -> bool\n\
+\n\
+Return whether the iterator is at the end of the zone table.\n" },
+    { "next", ZoneTableIterator_next, METH_NOARGS,
+"next()\n\
+\n\
+Move the iterator to the next zone of the table.\n" },
+    { "get_current", ZoneTableIterator_getCurrent, METH_NOARGS,
+"get_current() -> isc.datasrc.ZoneSpec\n\
+\n\
+Return the information of the zone at which the iterator is\
+currently located\n\
+\n\
+This method must not be called once the iterator reaches the end\
+of the zone table.\n" },
+    { NULL, NULL, 0, NULL }
+};
+
+const char* const ZoneTableIterator_doc = "\
+Read-only iterator to a zone table.\n\
+\n\
+You can get an instance of the ZoneTableIterator from the\
+ZoneTableAccessor.get_iterator() method. The actual concrete\
+C++ implementation will be different depending on the actual data source\
+used. This is the abstract interface.\n\
+\n\
+There's no way to start iterating from the beginning again or return.\n\
+\n\
+The ZoneTableIterator is a Python iterator, and can be iterated over\
+directly.\n\
+";
+
+} // end of unnamed namespace
+
+namespace isc {
+namespace datasrc {
+namespace python {
+PyTypeObject zonetableiterator_type = {
+    PyVarObject_HEAD_INIT(NULL, 0)
+    "datasrc.ZoneTableIterator",
+    sizeof(s_ZoneTableIterator),        // tp_basicsize
+    0,                                  // tp_itemsize
+    reinterpret_cast<destructor>(ZoneTableIterator_destroy),// tp_dealloc
+    NULL,                               // tp_print
+    NULL,                               // tp_getattr
+    NULL,                               // tp_setattr
+    NULL,                               // tp_reserved
+    NULL,                               // tp_repr
+    NULL,                               // tp_as_number
+    NULL,                               // tp_as_sequence
+    NULL,                               // tp_as_mapping
+    NULL,                               // tp_hash
+    NULL,                               // tp_call
+    NULL,                               // tp_str
+    NULL,                               // tp_getattro
+    NULL,                               // tp_setattro
+    NULL,                               // tp_as_buffer
+    Py_TPFLAGS_DEFAULT,                 // tp_flags
+    ZoneTableIterator_doc,
+    NULL,                               // tp_traverse
+    NULL,                               // tp_clear
+    NULL,                               // tp_richcompare
+    0,                                  // tp_weaklistoffset
+    ZoneTableIterator_piter,            // tp_iter
+    ZoneTableIterator_pnext,            // tp_iternext
+    ZoneTableIterator_methods,          // tp_methods
+    NULL,                               // tp_members
+    NULL,                               // tp_getset
+    NULL,                               // tp_base
+    NULL,                               // tp_dict
+    NULL,                               // tp_descr_get
+    NULL,                               // tp_descr_set
+    0,                                  // tp_dictoffset
+    reinterpret_cast<initproc>(ZoneTableIterator_init),// tp_init
+    NULL,                               // tp_alloc
+    PyType_GenericNew,                  // tp_new
+    NULL,                               // tp_free
+    NULL,                               // tp_is_gc
+    NULL,                               // tp_bases
+    NULL,                               // tp_mro
+    NULL,                               // tp_cache
+    NULL,                               // tp_subclasses
+    NULL,                               // tp_weaklist
+    NULL,                               // tp_del
+    0                                   // tp_version_tag
+};
+
+PyObject*
+createZoneTableIteratorObject(ZoneTableAccessor::IteratorPtr source,
+                              PyObject* base_obj)
+{
+    s_ZoneTableIterator* py_zi = static_cast<s_ZoneTableIterator*>(
+        zonetableiterator_type.tp_alloc(&zonetableiterator_type, 0));
+    if (py_zi != NULL) {
+        py_zi->cppobj = source;
+        py_zi->base_obj = base_obj;
+        if (base_obj != NULL) {
+            Py_INCREF(base_obj);
+        }
+    }
+    return (py_zi);
+}
+
+} // namespace python
+} // namespace datasrc
+} // namespace isc
+

+ 44 - 0
src/lib/python/isc/datasrc/zonetable_iterator_python.h

@@ -0,0 +1,44 @@
+// Copyright (C) 2013  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#ifndef PYTHON_ZONETABLE_ITERATOR_H
+#define PYTHON_ZONETABLE_ITERATOR_H 1
+
+#include <Python.h>
+
+namespace isc {
+namespace datasrc {
+namespace python {
+
+extern PyTypeObject zonetableiterator_type;
+
+/// \brief Create a ZoneTableIterator python object
+///
+/// \param source The zone table iterator pointer to wrap
+/// \param base_obj An optional PyObject that this ZoneIterator depends on
+///                 Its refcount is increased, and will be decreased when
+///                 this zone iterator is destroyed, making sure that the
+///                 base object is never destroyed before this zone iterator.
+PyObject* createZoneTableIteratorObject(
+    isc::datasrc::ZoneTableAccessor::IteratorPtr source,
+    PyObject* base_obj = NULL);
+
+} // namespace python
+} // namespace datasrc
+} // namespace isc
+#endif // PYTHON_ZONETABLE_ITERATOR_H
+
+// Local Variables:
+// mode: c++
+// End: