BIND 10 master, updated. 081891b38f05f9a186814ab7d1cd5c572b8f777f [master] Merge branch 'trac905'
BIND 10 source code commits
bind10-changes at lists.isc.org
Fri May 13 17:38:31 UTC 2011
The branch, master has been updated
via 081891b38f05f9a186814ab7d1cd5c572b8f777f (commit)
via 4472e3c7cbfb5fd33ce575bd2666036d363b2ce2 (commit)
via a1a58a7382e82256f3f6785b7bebfa4643cced67 (commit)
via 928a439496c6040392cc03615c38aa3de45bfe87 (commit)
via 324cf344f3e0cd9e35e50076911fe7801a7d4690 (commit)
via 9eddc07cd32918f3b8e9ebd114d9c8f8f39a359b (commit)
via ee53746391bcfdaa75bba0db87add3dc7becb84d (commit)
via 92c0c95057cd98a5b8cf8099367a122684cf329b (commit)
via 296ec6be00c208aa9ab8cbaa20376749e8b6ab49 (commit)
via 7deb4955ef8816885376e558e7f0eaf65e22b4d4 (commit)
via b916d130e0799f457634cfba2b06fc1250db54a0 (commit)
via 1ea71dc16928433c1243375bd9210d5fceb28fe6 (commit)
via e7a49bee8af14cf5d4e16e14cb5bdd3ba83385b9 (commit)
via ed62a7f4dde0264cd60e4b739c41be9c98bbe3e4 (commit)
via 38932d7e76c4e35aca7382640e9e86360a389545 (commit)
via 55b3152ebb03fbe946986a52732f79a68d926163 (commit)
via e93f054a2d0cf1c21d00112ccaa93d5d2432a677 (commit)
from 04c0fbf9fa780b4a281c5982ec33e5632852cfa1 (commit)
Those revisions listed above that are new to this repository have
not appeared on any other notification email; so we list those
revisions in full, below.
- Log -----------------------------------------------------------------
commit 081891b38f05f9a186814ab7d1cd5c572b8f777f
Merge: 04c0fbf 4472e3c
Author: JINMEI Tatuya <jinmei at isc.org>
Date: Fri May 13 10:15:08 2011 -0700
[master] Merge branch 'trac905'
-----------------------------------------------------------------------
Summary of changes:
configure.ac | 2 +
doc/Doxyfile | 4 +-
src/lib/dns/python/Makefile.am | 2 +
src/lib/dns/python/message_python.cc | 8 +-
src/lib/dns/python/pydnspp.cc | 31 ++-
src/lib/dns/python/pydnspp_common.cc | 18 +-
src/lib/dns/python/pydnspp_common.h | 29 ++-
src/lib/dns/python/rcode_python.cc | 155 +++++-----
src/lib/dns/python/rcode_python.h | 57 +++
src/lib/dns/python/tests/Makefile.am | 1 +
src/lib/dns/python/tests/tsigerror_python_test.py | 97 ++++++
src/lib/dns/python/tsig_python.cc | 1 -
src/lib/dns/python/tsigerror_python.cc | 360 ++++++++++++++++++++
.../io_error.h => dns/python/tsigerror_python.h} | 33 ++-
src/lib/dns/python/tsigerror_python_inc.cc | 83 +++++
src/lib/dns/tsigerror.h | 5 +-
src/lib/util/Makefile.am | 2 +
src/lib/util/python/mkpywrapper.py.in | 100 ++++++
src/lib/util/python/pycppwrapper_util.h | 308 +++++++++++++++++
src/lib/util/python/wrapper_template.cc | 293 ++++++++++++++++
.../io_error.h => util/python/wrapper_template.h} | 35 ++-
21 files changed, 1493 insertions(+), 131 deletions(-)
create mode 100644 src/lib/dns/python/rcode_python.h
create mode 100644 src/lib/dns/python/tests/tsigerror_python_test.py
create mode 100644 src/lib/dns/python/tsigerror_python.cc
copy src/lib/{asiolink/io_error.h => dns/python/tsigerror_python.h} (63%)
create mode 100644 src/lib/dns/python/tsigerror_python_inc.cc
create mode 100755 src/lib/util/python/mkpywrapper.py.in
create mode 100644 src/lib/util/python/pycppwrapper_util.h
create mode 100644 src/lib/util/python/wrapper_template.cc
copy src/lib/{asiolink/io_error.h => util/python/wrapper_template.h} (58%)
-----------------------------------------------------------------------
diff --git a/configure.ac b/configure.ac
index a8378ce..c95396a 100644
--- a/configure.ac
+++ b/configure.ac
@@ -842,6 +842,7 @@ AC_OUTPUT([doc/version.ent
src/lib/cc/session_config.h.pre
src/lib/cc/tests/session_unittests_config.h
src/lib/log/tests/run_time_init_test.sh
+ src/lib/util/python/mkpywrapper.py
tests/system/conf.sh
tests/system/glue/setup.sh
tests/system/glue/nsx1/b10-config.db
@@ -867,6 +868,7 @@ AC_OUTPUT([doc/version.ent
chmod +x src/lib/dns/gen-rdatacode.py
chmod +x src/lib/dns/tests/testdata/gen-wiredata.py
chmod +x src/lib/log/tests/run_time_init_test.sh
+ chmod +x src/lib/util/python/mkpywrapper.py
chmod +x tests/system/conf.sh
])
AC_OUTPUT
diff --git a/doc/Doxyfile b/doc/Doxyfile
index 783e63a..4cbead8 100644
--- a/doc/Doxyfile
+++ b/doc/Doxyfile
@@ -1140,7 +1140,7 @@ MAN_LINKS = NO
# generate an XML file that captures the structure of
# the code including all documentation.
-GENERATE_XML = NO
+GENERATE_XML = YES
# The XML_OUTPUT tag is used to specify where the XML pages will be put.
# If a relative path is entered the value of OUTPUT_DIRECTORY will be
@@ -1165,7 +1165,7 @@ XML_DTD =
# and cross-referencing information) to the XML output. Note that
# enabling this will significantly increase the size of the XML output.
-XML_PROGRAMLISTING = YES
+XML_PROGRAMLISTING = NO
#---------------------------------------------------------------------------
# configuration options for the AutoGen Definitions output
diff --git a/src/lib/dns/python/Makefile.am b/src/lib/dns/python/Makefile.am
index 9162f4e..15b3b1c 100644
--- a/src/lib/dns/python/Makefile.am
+++ b/src/lib/dns/python/Makefile.am
@@ -6,6 +6,8 @@ AM_CXXFLAGS = $(B10_CXXFLAGS)
pyexec_LTLIBRARIES = pydnspp.la
pydnspp_la_SOURCES = pydnspp.cc pydnspp_common.cc
+pydnspp_la_SOURCES += rcode_python.cc rcode_python.h
+pydnspp_la_SOURCES += tsigerror_python.cc tsigerror_python.h
pydnspp_la_CPPFLAGS = $(AM_CPPFLAGS) $(PYTHON_INCLUDES)
pydnspp_la_LDFLAGS = $(PYTHON_LDFLAGS)
diff --git a/src/lib/dns/python/message_python.cc b/src/lib/dns/python/message_python.cc
index e3cc53f..381b7bc 100644
--- a/src/lib/dns/python/message_python.cc
+++ b/src/lib/dns/python/message_python.cc
@@ -336,15 +336,15 @@ Message_getRcode(s_Message* self) {
rcode = static_cast<s_Rcode*>(rcode_type.tp_alloc(&rcode_type, 0));
if (rcode != NULL) {
- rcode->rcode = NULL;
+ rcode->cppobj = NULL;
try {
- rcode->rcode = new Rcode(self->message->getRcode());
+ rcode->cppobj = new Rcode(self->message->getRcode());
} catch (const InvalidMessageOperation& imo) {
PyErr_SetString(po_InvalidMessageOperation, imo.what());
} catch (...) {
PyErr_SetString(po_IscException, "Unexpected exception");
}
- if (rcode->rcode == NULL) {
+ if (rcode->cppobj == NULL) {
Py_DECREF(rcode);
return (NULL);
}
@@ -360,7 +360,7 @@ Message_setRcode(s_Message* self, PyObject* args) {
return (NULL);
}
try {
- self->message->setRcode(*rcode->rcode);
+ self->message->setRcode(*rcode->cppobj);
Py_RETURN_NONE;
} catch (const InvalidMessageOperation& imo) {
PyErr_SetString(po_InvalidMessageOperation, imo.what());
diff --git a/src/lib/dns/python/pydnspp.cc b/src/lib/dns/python/pydnspp.cc
index 2138198..0be346e 100644
--- a/src/lib/dns/python/pydnspp.cc
+++ b/src/lib/dns/python/pydnspp.cc
@@ -32,20 +32,32 @@
#include <exceptions/exceptions.h>
#include <util/buffer.h>
+
#include <dns/exceptions.h>
#include <dns/name.h>
#include <dns/messagerenderer.h>
-#include <dns/python/pydnspp_common.h>
+#include "pydnspp_common.h"
+namespace isc {
+namespace dns {
+namespace python {
// For our 'general' isc::Exceptions
-static PyObject* po_IscException;
-static PyObject* po_InvalidParameter;
+PyObject* po_IscException;
+PyObject* po_InvalidParameter;
// For our own isc::dns::Exception
-static PyObject* po_DNSMessageBADVERS;
+PyObject* po_DNSMessageBADVERS;
+}
+}
+}
+
+#include "rcode_python.h"
+#include "tsigerror_python.h"
// order is important here!
+using namespace isc::dns::python;
+
#include <dns/python/messagerenderer_python.cc>
#include <dns/python/name_python.cc> // needs Messagerenderer
#include <dns/python/rrclass_python.cc> // needs Messagerenderer
@@ -58,14 +70,14 @@ static PyObject* po_DNSMessageBADVERS;
#include <dns/python/tsigkey_python.cc> // needs Name
#include <dns/python/tsig_python.cc> // needs tsigkey
#include <dns/python/opcode_python.cc>
-#include <dns/python/rcode_python.cc>
#include <dns/python/edns_python.cc> // needs Messagerenderer, Rcode
#include <dns/python/message_python.cc> // needs RRset, Question
//
// Definition of the module
//
-static PyModuleDef pydnspp = {
+namespace {
+PyModuleDef pydnspp = {
{ PyObject_HEAD_INIT(NULL) NULL, 0, NULL},
"pydnspp",
"Python bindings for the classes in the isc::dns namespace.\n\n"
@@ -80,10 +92,11 @@ static PyModuleDef pydnspp = {
NULL,
NULL
};
+}
PyMODINIT_FUNC
PyInit_pydnspp(void) {
- PyObject *mod = PyModule_Create(&pydnspp);
+ PyObject* mod = PyModule_Create(&pydnspp);
if (mod == NULL) {
return (NULL);
}
@@ -154,6 +167,10 @@ PyInit_pydnspp(void) {
return (NULL);
}
+ if (!initModulePart_TSIGError(mod)) {
+ return (NULL);
+ }
+
if (!initModulePart_TSIGContext(mod)) {
return (NULL);
}
diff --git a/src/lib/dns/python/pydnspp_common.cc b/src/lib/dns/python/pydnspp_common.cc
index 6c26367..8ca763a 100644
--- a/src/lib/dns/python/pydnspp_common.cc
+++ b/src/lib/dns/python/pydnspp_common.cc
@@ -15,6 +15,9 @@
#include <Python.h>
#include <pydnspp_common.h>
+namespace isc {
+namespace dns {
+namespace python {
int
readDataFromSequence(uint8_t *data, size_t len, PyObject* sequence) {
PyObject* el = NULL;
@@ -44,8 +47,15 @@ readDataFromSequence(uint8_t *data, size_t len, PyObject* sequence) {
}
-void addClassVariable(PyTypeObject& c, const char* name,
- PyObject* obj)
-{
- PyDict_SetItemString(c.tp_dict, name, obj);
+int
+addClassVariable(PyTypeObject& c, const char* name, PyObject* obj) {
+ if (obj == NULL) {
+ PyErr_SetString(PyExc_ValueError,
+ "NULL object is specified for a class variable");
+ return (-1);
+ }
+ return (PyDict_SetItemString(c.tp_dict, name, obj));
+}
+}
+}
}
diff --git a/src/lib/dns/python/pydnspp_common.h b/src/lib/dns/python/pydnspp_common.h
index 32e2b78..ed90998 100644
--- a/src/lib/dns/python/pydnspp_common.h
+++ b/src/lib/dns/python/pydnspp_common.h
@@ -15,9 +15,22 @@
#ifndef __LIBDNS_PYTHON_COMMON_H
#define __LIBDNS_PYTHON_COMMON_H 1
-//
-// Shared functions for python/c API
-//
+#include <Python.h>
+
+#include <stdexcept>
+#include <string>
+
+#include <util/python/pycppwrapper_util.h>
+
+namespace isc {
+namespace dns {
+namespace python {
+// For our 'general' isc::Exceptions
+extern PyObject* po_IscException;
+extern PyObject* po_InvalidParameter;
+
+// For our own isc::dns::Exception
+extern PyObject* po_DNSMessageBADVERS;
// This function reads 'bytes' from a sequence
// This sequence can be anything that implements the Sequence interface,
@@ -31,6 +44,12 @@
// case nothing is removed
int readDataFromSequence(uint8_t *data, size_t len, PyObject* sequence);
-void addClassVariable(PyTypeObject& c, const char* name, PyObject* obj);
-
+int addClassVariable(PyTypeObject& c, const char* name, PyObject* obj);
+} // namespace python
+} // namespace dns
+} // namespace isc
#endif // __LIBDNS_PYTHON_COMMON_H
+
+// Local Variables:
+// mode: c++
+// End:
diff --git a/src/lib/dns/python/rcode_python.cc b/src/lib/dns/python/rcode_python.cc
index b80a93c..efb0f43 100644
--- a/src/lib/dns/python/rcode_python.cc
+++ b/src/lib/dns/python/rcode_python.cc
@@ -12,9 +12,15 @@
// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
// PERFORMANCE OF THIS SOFTWARE.
+#include <exceptions/exceptions.h>
+
#include <dns/rcode.h>
+#include "pydnspp_common.h"
+#include "rcode_python.h"
+
using namespace isc::dns;
+using namespace isc::dns::python;
//
// Declaration of the custom exceptions (None for this class)
@@ -27,25 +33,14 @@ using namespace isc::dns;
// and static wrappers around the methods we export), a list of methods,
// and a type description
-namespace {
//
// Rcode
//
-// We added a helper variable static_code here
-// Since we can create Rcodes dynamically with Rcode(int), but also
-// use the static globals (Rcode::NOERROR() etc), we use this
-// variable to see if the code came from one of the latter, in which
-// case Rcode_destroy should not free it (the other option is to
-// allocate new Rcodes for every use of the static ones, but this
-// seems more efficient).
-class s_Rcode : public PyObject {
-public:
- s_Rcode() : rcode(NULL), static_code(false) {}
- const Rcode* rcode;
- bool static_code;
-};
+// Trivial constructor.
+s_Rcode::s_Rcode() : cppobj(NULL), static_code(false) {}
+namespace {
int Rcode_init(s_Rcode* const self, PyObject* args);
void Rcode_destroy(s_Rcode* const self);
@@ -118,57 +113,6 @@ PyMethodDef Rcode_methods[] = {
{ NULL, NULL, 0, NULL }
};
-PyTypeObject rcode_type = {
- PyVarObject_HEAD_INIT(NULL, 0)
- "pydnspp.Rcode",
- sizeof(s_Rcode), // tp_basicsize
- 0, // tp_itemsize
- (destructor)Rcode_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
- Rcode_str, // tp_str
- NULL, // tp_getattro
- NULL, // tp_setattro
- NULL, // tp_as_buffer
- Py_TPFLAGS_DEFAULT, // tp_flags
- "The Rcode class objects represent standard RCODEs"
- "of the header section of DNS messages.",
- NULL, // tp_traverse
- NULL, // tp_clear
- (richcmpfunc)Rcode_richcmp, // tp_richcompare
- 0, // tp_weaklistoffset
- NULL, // tp_iter
- NULL, // tp_iternext
- Rcode_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
- (initproc)Rcode_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
-};
-
int
Rcode_init(s_Rcode* const self, PyObject* args) {
long code = 0;
@@ -193,9 +137,9 @@ Rcode_init(s_Rcode* const self, PyObject* args) {
}
try {
if (ext_code == -1) {
- self->rcode = new Rcode(code);
+ self->cppobj = new Rcode(code);
} else {
- self->rcode = new Rcode(code, ext_code);
+ self->cppobj = new Rcode(code, ext_code);
}
self->static_code = false;
} catch (const isc::OutOfRange& ex) {
@@ -211,27 +155,27 @@ Rcode_init(s_Rcode* const self, PyObject* args) {
void
Rcode_destroy(s_Rcode* const self) {
// Depending on whether we created the rcode or are referring
- // to a global one, we do or do not delete self->rcode here
+ // to a global one, we do or do not delete self->cppobj here
if (!self->static_code) {
- delete self->rcode;
+ delete self->cppobj;
}
- self->rcode = NULL;
+ self->cppobj = NULL;
Py_TYPE(self)->tp_free(self);
}
PyObject*
Rcode_getCode(const s_Rcode* const self) {
- return (Py_BuildValue("I", self->rcode->getCode()));
+ return (Py_BuildValue("I", self->cppobj->getCode()));
}
PyObject*
Rcode_getExtendedCode(const s_Rcode* const self) {
- return (Py_BuildValue("I", self->rcode->getExtendedCode()));
+ return (Py_BuildValue("I", self->cppobj->getExtendedCode()));
}
PyObject*
Rcode_toText(const s_Rcode* const self) {
- return (Py_BuildValue("s", self->rcode->toText().c_str()));
+ return (Py_BuildValue("s", self->cppobj->toText().c_str()));
}
PyObject*
@@ -245,7 +189,7 @@ PyObject*
Rcode_createStatic(const Rcode& rcode) {
s_Rcode* ret = PyObject_New(s_Rcode, &rcode_type);
if (ret != NULL) {
- ret->rcode = &rcode;
+ ret->cppobj = &rcode;
ret->static_code = true;
}
return (ret);
@@ -357,10 +301,10 @@ Rcode_richcmp(const s_Rcode* const self, const s_Rcode* const other,
PyErr_SetString(PyExc_TypeError, "Unorderable type; Rcode");
return (NULL);
case Py_EQ:
- c = (*self->rcode == *other->rcode);
+ c = (*self->cppobj == *other->cppobj);
break;
case Py_NE:
- c = (*self->rcode != *other->rcode);
+ c = (*self->cppobj != *other->cppobj);
break;
case Py_GT:
PyErr_SetString(PyExc_TypeError, "Unorderable type; Rcode");
@@ -374,6 +318,61 @@ Rcode_richcmp(const s_Rcode* const self, const s_Rcode* const other,
else
Py_RETURN_FALSE;
}
+} // end of unnamed namespace
+
+namespace isc {
+namespace dns {
+namespace python {
+PyTypeObject rcode_type = {
+ PyVarObject_HEAD_INIT(NULL, 0)
+ "pydnspp.Rcode",
+ sizeof(s_Rcode), // tp_basicsize
+ 0, // tp_itemsize
+ (destructor)Rcode_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
+ Rcode_str, // tp_str
+ NULL, // tp_getattro
+ NULL, // tp_setattro
+ NULL, // tp_as_buffer
+ Py_TPFLAGS_DEFAULT, // tp_flags
+ "The Rcode class objects represent standard RCODEs"
+ "of the header section of DNS messages.",
+ NULL, // tp_traverse
+ NULL, // tp_clear
+ reinterpret_cast<richcmpfunc>(Rcode_richcmp), // tp_richcompare
+ 0, // tp_weaklistoffset
+ NULL, // tp_iter
+ NULL, // tp_iternext
+ Rcode_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
+ (initproc)Rcode_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
+};
// Module Initialization, all statics are initialized here
bool
@@ -428,4 +427,6 @@ initModulePart_Rcode(PyObject* mod) {
return (true);
}
-} // end of unnamed namespace
+} // namespace python
+} // namespace dns
+} // namespace isc
diff --git a/src/lib/dns/python/rcode_python.h b/src/lib/dns/python/rcode_python.h
new file mode 100644
index 0000000..9b5e699
--- /dev/null
+++ b/src/lib/dns/python/rcode_python.h
@@ -0,0 +1,57 @@
+// Copyright (C) 2011 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_RCODE_H
+#define __PYTHON_RCODE_H 1
+
+#include <Python.h>
+
+namespace isc {
+namespace dns {
+class Rcode;
+
+namespace python {
+
+// The s_* Class simply covers one instantiation of the object.
+//
+// We added a helper variable static_code here
+// Since we can create Rcodes dynamically with Rcode(int), but also
+// use the static globals (Rcode::NOERROR() etc), we use this
+// variable to see if the code came from one of the latter, in which
+// case Rcode_destroy should not free it (the other option is to
+// allocate new Rcodes for every use of the static ones, but this
+// seems more efficient).
+//
+// Follow-up note: we don't have to use the proxy function in the python lib;
+// we can just define class specific constants directly (see TSIGError).
+// We should make this cleanup later.
+class s_Rcode : public PyObject {
+public:
+ s_Rcode();
+ const Rcode* cppobj;
+ bool static_code;
+};
+
+extern PyTypeObject rcode_type;
+
+bool initModulePart_Rcode(PyObject* mod);
+
+} // namespace python
+} // namespace dns
+} // namespace isc
+#endif // __PYTHON_RCODE_H
+
+// Local Variables:
+// mode: c++
+// End:
diff --git a/src/lib/dns/python/tests/Makefile.am b/src/lib/dns/python/tests/Makefile.am
index 9ee98c7..1149a47 100644
--- a/src/lib/dns/python/tests/Makefile.am
+++ b/src/lib/dns/python/tests/Makefile.am
@@ -12,6 +12,7 @@ PYTESTS += rrset_python_test.py
PYTESTS += rrttl_python_test.py
PYTESTS += rrtype_python_test.py
PYTESTS += tsig_python_test.py
+PYTESTS += tsigerror_python_test.py
PYTESTS += tsigkey_python_test.py
EXTRA_DIST = $(PYTESTS)
diff --git a/src/lib/dns/python/tests/tsigerror_python_test.py b/src/lib/dns/python/tests/tsigerror_python_test.py
new file mode 100644
index 0000000..a968b6b
--- /dev/null
+++ b/src/lib/dns/python/tests/tsigerror_python_test.py
@@ -0,0 +1,97 @@
+# Copyright (C) 2011 Internet Systems Consortium.
+#
+# Permission to use, copy, modify, and 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 INTERNET SYSTEMS CONSORTIUM
+# DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL
+# INTERNET SYSTEMS CONSORTIUM 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.
+
+import unittest
+import sys
+from pydnspp import *
+
+class TSIGErrorTest(unittest.TestCase):
+ def test_from_code(self):
+ self.assertEqual(0, TSIGError(0).get_code())
+ self.assertEqual(18, TSIGError(18).get_code())
+ self.assertEqual(65535, TSIGError(65535).get_code())
+ self.assertRaises(ValueError, TSIGError, 65536)
+ self.assertRaises(ValueError, TSIGError, -1)
+ self.assertRaises(TypeError, TSIGError, "not yet supported")
+
+ def test_from_rcode(self):
+ # We use RCODE for code values from 0-15.
+ self.assertEqual(0, TSIGError(Rcode.NOERROR()).get_code())
+ self.assertEqual(15, TSIGError(Rcode(15)).get_code())
+
+ # From error code 16 TSIG errors define a separate space, so passing
+ # corresponding RCODE for such code values should be prohibited.
+ self.assertRaises(ValueError, TSIGError, Rcode(16))
+
+ def test_constants(self):
+ # We'll only test arbitrarily chosen subsets of the codes.
+ # This class is quite simple, so it should be suffice.
+ self.assertEqual(TSIGError.BAD_SIG_CODE, TSIGError(16).get_code())
+ self.assertEqual(TSIGError.BAD_KEY_CODE, TSIGError(17).get_code())
+ self.assertEqual(TSIGError.BAD_TIME_CODE, TSIGError(18).get_code())
+
+ self.assertEqual(0, TSIGError.NOERROR.get_code())
+ self.assertEqual(9, TSIGError.NOTAUTH.get_code())
+ self.assertEqual(14, TSIGError.RESERVED14.get_code())
+ self.assertEqual(TSIGError.BAD_SIG_CODE, TSIGError.BAD_SIG.get_code())
+ self.assertEqual(TSIGError.BAD_KEY_CODE, TSIGError.BAD_KEY.get_code())
+ self.assertEqual(TSIGError.BAD_TIME_CODE, TSIGError.BAD_TIME.get_code())
+
+ def test_equal(self):
+ self.assertTrue(TSIGError.NOERROR == TSIGError(Rcode.NOERROR()))
+ self.assertTrue(TSIGError(Rcode.NOERROR()) == TSIGError.NOERROR)
+
+ self.assertTrue(TSIGError.BAD_SIG == TSIGError(16))
+ self.assertTrue(TSIGError(16) == TSIGError.BAD_SIG)
+
+ def test_nequal(self):
+ self.assertTrue(TSIGError.BAD_KEY != TSIGError(Rcode.NOERROR()))
+ self.assertTrue(TSIGError(Rcode.NOERROR()) != TSIGError.BAD_KEY)
+
+ def test_to_text(self):
+ # TSIGError derived from the standard Rcode
+ self.assertEqual("NOERROR", TSIGError(Rcode.NOERROR()).to_text())
+
+ # Well known TSIG errors
+ self.assertEqual("BADSIG", TSIGError.BAD_SIG.to_text())
+ self.assertEqual("BADKEY", TSIGError.BAD_KEY.to_text())
+ self.assertEqual("BADTIME", TSIGError.BAD_TIME.to_text())
+
+ # Unknown (or not yet supported) codes. Simply converted as numeric.
+ self.assertEqual("19", TSIGError(19).to_text());
+ self.assertEqual("65535", TSIGError(65535).to_text());
+
+ # also check str() works same way
+ self.assertEqual("NOERROR", str(TSIGError(Rcode.NOERROR())))
+ self.assertEqual("BADSIG", str(TSIGError.BAD_SIG))
+
+ def test_to_rcode(self):
+ # TSIGError derived from the standard Rcode
+ self.assertEqual(Rcode.NOERROR(), TSIGError(Rcode.NOERROR()).to_rcode())
+
+ # Well known TSIG errors
+ self.assertEqual(Rcode.NOTAUTH(), TSIGError.BAD_SIG.to_rcode())
+ self.assertEqual(Rcode.NOTAUTH(), TSIGError.BAD_KEY.to_rcode())
+ self.assertEqual(Rcode.NOTAUTH(), TSIGError.BAD_TIME.to_rcode())
+
+ # Unknown (or not yet supported) codes are treated as SERVFAIL.
+ self.assertEqual(Rcode.SERVFAIL(), TSIGError(19).to_rcode())
+ self.assertEqual(Rcode.SERVFAIL(), TSIGError(65535).to_rcode())
+
+ # Check there's no redundant refcount (which would cause leak)
+ self.assertEqual(1, sys.getrefcount(TSIGError.BAD_SIG.to_rcode()))
+
+if __name__ == '__main__':
+ unittest.main()
diff --git a/src/lib/dns/python/tsig_python.cc b/src/lib/dns/python/tsig_python.cc
index 2e6d986..7b6ba01 100644
--- a/src/lib/dns/python/tsig_python.cc
+++ b/src/lib/dns/python/tsig_python.cc
@@ -26,7 +26,6 @@ using namespace isc::dns;
namespace {
// The s_* Class simply covers one instantiation of the object
-
class s_TSIGContext : public PyObject {
public:
TSIGContext* tsig_ctx;
diff --git a/src/lib/dns/python/tsigerror_python.cc b/src/lib/dns/python/tsigerror_python.cc
new file mode 100644
index 0000000..afabf7a
--- /dev/null
+++ b/src/lib/dns/python/tsigerror_python.cc
@@ -0,0 +1,360 @@
+// Copyright (C) 2011 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.
+
+#include <string>
+#include <stdexcept>
+
+#include <util/python/pycppwrapper_util.h>
+
+#include <dns/tsigerror.h>
+
+#include "pydnspp_common.h"
+#include "rcode_python.h"
+#include "tsigerror_python.h"
+
+using namespace std;
+using namespace isc::util::python;
+using namespace isc::dns;
+using namespace isc::dns::python;
+
+//
+// Definition of the classes
+//
+
+// For each class, we need a struct, a helper functions (init, destroy,
+// and static wrappers around the methods we export), a list of methods,
+// and a type description
+
+//
+// TSIGError
+//
+
+// Trivial constructor.
+s_TSIGError::s_TSIGError() : cppobj(NULL) {
+}
+
+// Import pydoc text
+#include "tsigerror_python_inc.cc"
+
+namespace {
+// Shortcut type which would be convenient for adding class variables safely.
+typedef CPPPyObjectContainer<s_TSIGError, TSIGError> TSIGErrorContainer;
+
+//
+// We declare the functions here, the definitions are below
+// the type definition of the object, since both can use the other
+//
+
+// General creation and destruction
+int TSIGError_init(s_TSIGError* self, PyObject* args);
+void TSIGError_destroy(s_TSIGError* self);
+
+// These are the functions we export
+PyObject* TSIGError_getCode(const s_TSIGError* const self);
+PyObject* TSIGError_toText(const s_TSIGError* const self);
+PyObject* TSIGError_toRcode(const s_TSIGError* const self);
+PyObject* TSIGError_str(PyObject* self);
+PyObject* TSIGError_richcmp(const s_TSIGError* const self,
+ const s_TSIGError* const other, int op);
+
+// These are the functions we export
+// For a minimal support, we don't need them.
+
+// 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 TSIGError_methods[] = {
+ { "get_code", reinterpret_cast<PyCFunction>(TSIGError_getCode),
+ METH_NOARGS,
+ TSIGError_getCode_doc },
+ { "to_text", reinterpret_cast<PyCFunction>(TSIGError_toText), METH_NOARGS,
+ TSIGError_toText_doc },
+ { "to_rcode", reinterpret_cast<PyCFunction>(TSIGError_toRcode),
+ METH_NOARGS,
+ TSIGError_toRcode_doc },
+ { NULL, NULL, 0, NULL }
+};
+
+int
+TSIGError_init(s_TSIGError* self, PyObject* args) {
+ try {
+ // Constructor from the code value
+ long code = 0;
+ if (PyArg_ParseTuple(args, "l", &code)) {
+ if (code < 0 || code > 0xffff) {
+ PyErr_SetString(PyExc_ValueError, "TSIG error out of range");
+ return (-1);
+ }
+ self->cppobj = new TSIGError(code);
+ return (0);
+ }
+
+ // Constructor from Rcode
+ s_Rcode* py_rcode;
+ if (PyArg_ParseTuple(args, "O!", &rcode_type, &py_rcode)) {
+ self->cppobj = new TSIGError(*py_rcode->cppobj);
+ return (0);
+ }
+ } catch (const isc::OutOfRange& ex) {
+ const string ex_what = "Failed to construct TSIGError object: " +
+ string(ex.what());
+ PyErr_SetString(PyExc_ValueError, ex_what.c_str());
+ return (-1);
+ } catch (const exception& ex) {
+ const string ex_what = "Failed to construct TSIGError object: " +
+ string(ex.what());
+ PyErr_SetString(po_IscException, ex_what.c_str());
+ return (-1);
+ } catch (...) {
+ PyErr_SetString(po_IscException,
+ "Unexpected exception in constructing TSIGError");
+ return (-1);
+ }
+
+ PyErr_SetString(PyExc_TypeError,
+ "Invalid arguments to TSIGError constructor");
+
+ return (-1);
+}
+
+void
+TSIGError_destroy(s_TSIGError* const self) {
+ delete self->cppobj;
+ self->cppobj = NULL;
+ Py_TYPE(self)->tp_free(self);
+}
+
+PyObject*
+TSIGError_getCode(const s_TSIGError* const self) {
+ return (Py_BuildValue("I", self->cppobj->getCode()));
+}
+
+PyObject*
+TSIGError_toText(const s_TSIGError* const self) {
+ try {
+ // toText() could throw, so we need to catch any exceptions below.
+ return (Py_BuildValue("s", self->cppobj->toText().c_str()));
+ } catch (const exception& ex) {
+ const string ex_what =
+ "Failed to convert TSIGError object to text: " +
+ string(ex.what());
+ PyErr_SetString(po_IscException, ex_what.c_str());
+ } catch (...) {
+ PyErr_SetString(PyExc_SystemError, "Unexpected failure in "
+ "converting TSIGError object to text");
+ }
+ return (NULL);
+}
+
+PyObject*
+TSIGError_str(PyObject* self) {
+ // Simply call the to_text method we already defined
+ return (PyObject_CallMethod(self, const_cast<char*>("to_text"),
+ const_cast<char*>("")));
+}
+
+PyObject*
+TSIGError_toRcode(const s_TSIGError* const self) {
+ typedef CPPPyObjectContainer<s_Rcode, Rcode> RcodePyObjectContainer;
+
+ try {
+ RcodePyObjectContainer rcode_container(PyObject_New(s_Rcode,
+ &rcode_type));
+ rcode_container.set(new Rcode(self->cppobj->toRcode()));
+ return (rcode_container.release());
+ } catch (const exception& ex) {
+ const string ex_what =
+ "Failed to convert TSIGError to Rcode: " + string(ex.what());
+ PyErr_SetString(po_IscException, ex_what.c_str());
+ } catch (...) {
+ PyErr_SetString(PyExc_SystemError, "Unexpected failure in "
+ "converting TSIGError to Rcode");
+ }
+ return (NULL);
+}
+
+PyObject*
+TSIGError_richcmp(const s_TSIGError* const self,
+ const s_TSIGError* const other,
+ const int op)
+{
+ bool c = false;
+
+ // Check for null and if the types match. If different type,
+ // simply return False
+ if (other == NULL || (self->ob_type != other->ob_type)) {
+ Py_RETURN_FALSE;
+ }
+
+ // Only equals and not equals here, unorderable type
+ switch (op) {
+ case Py_LT:
+ PyErr_SetString(PyExc_TypeError, "Unorderable type; TSIGError");
+ return (NULL);
+ case Py_LE:
+ PyErr_SetString(PyExc_TypeError, "Unorderable type; TSIGError");
+ return (NULL);
+ case Py_EQ:
+ c = (*self->cppobj == *other->cppobj);
+ break;
+ case Py_NE:
+ c = (*self->cppobj != *other->cppobj);
+ break;
+ case Py_GT:
+ PyErr_SetString(PyExc_TypeError, "Unorderable type; TSIGError");
+ return (NULL);
+ case Py_GE:
+ PyErr_SetString(PyExc_TypeError, "Unorderable type; TSIGError");
+ return (NULL);
+ }
+ if (c) {
+ Py_RETURN_TRUE;
+ } else {
+ Py_RETURN_FALSE;
+ }
+}
+} // end of unnamed namespace
+
+namespace isc {
+namespace dns {
+namespace python {
+// This defines the complete type for reflection in python and
+// parsing of PyObject* to s_TSIGError
+// Most of the functions are not actually implemented and NULL here.
+PyTypeObject tsigerror_type = {
+ PyVarObject_HEAD_INIT(NULL, 0)
+ "libdns_python.TSIGError",
+ sizeof(s_TSIGError), // tp_basicsize
+ 0, // tp_itemsize
+ reinterpret_cast<destructor>(TSIGError_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
+ // THIS MAY HAVE TO BE CHANGED TO NULL:
+ TSIGError_str, // tp_str
+ NULL, // tp_getattro
+ NULL, // tp_setattro
+ NULL, // tp_as_buffer
+ Py_TPFLAGS_DEFAULT, // tp_flags
+ TSIGError_doc,
+ NULL, // tp_traverse
+ NULL, // tp_clear
+ // THIS MAY HAVE TO BE CHANGED TO NULL:
+ reinterpret_cast<richcmpfunc>(TSIGError_richcmp), // tp_richcompare
+ 0, // tp_weaklistoffset
+ NULL, // tp_iter
+ NULL, // tp_iternext
+ TSIGError_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>(TSIGError_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
+};
+
+namespace {
+// Trivial shortcut to create and install TSIGError constants.
+inline void
+installTSIGErrorConstant(const char* name, const TSIGError& val) {
+ TSIGErrorContainer container(PyObject_New(s_TSIGError, &tsigerror_type));
+ container.installAsClassVariable(tsigerror_type, name, new TSIGError(val));
+}
+}
+
+// Module Initialization, all statics are initialized here
+bool
+initModulePart_TSIGError(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(&tsigerror_type) < 0) {
+ return (false);
+ }
+ void* p = &tsigerror_type;
+ if (PyModule_AddObject(mod, "TSIGError", static_cast<PyObject*>(p)) < 0) {
+ return (false);
+ }
+ Py_INCREF(&tsigerror_type);
+
+ try {
+ // Constant class variables
+ // Error codes (bare values)
+ installClassVariable(tsigerror_type, "BAD_SIG_CODE",
+ Py_BuildValue("H", TSIGError::BAD_SIG_CODE));
+ installClassVariable(tsigerror_type, "BAD_KEY_CODE",
+ Py_BuildValue("H", TSIGError::BAD_KEY_CODE));
+ installClassVariable(tsigerror_type, "BAD_TIME_CODE",
+ Py_BuildValue("H", TSIGError::BAD_TIME_CODE));
+
+ // Error codes (constant objects)
+ installTSIGErrorConstant("NOERROR", TSIGError::NOERROR());
+ installTSIGErrorConstant("FORMERR", TSIGError::FORMERR());
+ installTSIGErrorConstant("SERVFAIL", TSIGError::SERVFAIL());
+ installTSIGErrorConstant("NXDOMAIN", TSIGError::NXDOMAIN());
+ installTSIGErrorConstant("NOTIMP", TSIGError::NOTIMP());
+ installTSIGErrorConstant("REFUSED", TSIGError::REFUSED());
+ installTSIGErrorConstant("YXDOMAIN", TSIGError::YXDOMAIN());
+ installTSIGErrorConstant("YXRRSET", TSIGError::YXRRSET());
+ installTSIGErrorConstant("NXRRSET", TSIGError::NXRRSET());
+ installTSIGErrorConstant("NOTAUTH", TSIGError::NOTAUTH());
+ installTSIGErrorConstant("NOTZONE", TSIGError::NOTZONE());
+ installTSIGErrorConstant("RESERVED11", TSIGError::RESERVED11());
+ installTSIGErrorConstant("RESERVED12", TSIGError::RESERVED12());
+ installTSIGErrorConstant("RESERVED13", TSIGError::RESERVED13());
+ installTSIGErrorConstant("RESERVED14", TSIGError::RESERVED14());
+ installTSIGErrorConstant("RESERVED15", TSIGError::RESERVED15());
+ installTSIGErrorConstant("BAD_SIG", TSIGError::BAD_SIG());
+ installTSIGErrorConstant("BAD_KEY", TSIGError::BAD_KEY());
+ installTSIGErrorConstant("BAD_TIME", TSIGError::BAD_TIME());
+ } catch (const exception& ex) {
+ const string ex_what =
+ "Unexpected failure in TSIGError initialization: " +
+ string(ex.what());
+ PyErr_SetString(po_IscException, ex_what.c_str());
+ return (false);
+ } catch (...) {
+ PyErr_SetString(PyExc_SystemError,
+ "Unexpected failure in TSIGError initialization");
+ return (false);
+ }
+
+ return (true);
+}
+} // namespace python
+} // namespace dns
+} // namespace isc
diff --git a/src/lib/dns/python/tsigerror_python.h b/src/lib/dns/python/tsigerror_python.h
new file mode 100644
index 0000000..f0c66cc
--- /dev/null
+++ b/src/lib/dns/python/tsigerror_python.h
@@ -0,0 +1,44 @@
+// Copyright (C) 2011 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_TSIGERROR_H
+#define __PYTHON_TSIGERROR_H 1
+
+#include <Python.h>
+
+namespace isc {
+namespace dns {
+class TSIGError;
+
+namespace python {
+
+// The s_* Class simply covers one instantiation of the object
+class s_TSIGError : public PyObject {
+public:
+ s_TSIGError();
+ const TSIGError* cppobj;
+};
+
+extern PyTypeObject tsigerror_type;
+
+bool initModulePart_TSIGError(PyObject* mod);
+
+} // namespace python
+} // namespace dns
+} // namespace isc
+#endif // __PYTHON_TSIGERROR_H
+
+// Local Variables:
+// mode: c++
+// End:
diff --git a/src/lib/dns/python/tsigerror_python_inc.cc b/src/lib/dns/python/tsigerror_python_inc.cc
new file mode 100644
index 0000000..ed3b605
--- /dev/null
+++ b/src/lib/dns/python/tsigerror_python_inc.cc
@@ -0,0 +1,83 @@
+namespace {
+const char* const TSIGError_doc = "\n\
+TSIG errors.\n\
+\n\
+\n\
+The TSIGError class objects represent standard errors related to TSIG\n\
+protocol operations as defined in related specifications, mainly in\n\
+RFC2845.\n\
+\n\
+TSIGError(error_code)\n\
+\n\
+Constructor from the code value.\n\
+\n\
+Exceptions:\n\
+ None: \n\
+\n\
+Parameters:\n\
+ error_code: The underlying 16-bit error code value of the TSIGError.\n\
+\n\
+TSIGError(rcode)\n\
+\n\
+Constructor from Rcode.\n\
+\n\
+As defined in RFC2845, error code values from 0 to 15 (inclusive) are\n\
+derived from the DNS RCODEs, which are represented via the Rcode class\n\
+in this library. This constructor works as a converter from these\n\
+RCODEs to corresponding TSIGError objects.\n\
+\n\
+Exceptions:\n\
+ ValueError: Given rcode is not convertible to TSIGErrors.\n\
+\n\
+Parameters:\n\
+ rcode: the Rcode from which the TSIGError should be derived.\n\
+\n\
+";
+const char* const TSIGError_getCode_doc = "get_code() -> integer\n\
+\n\
+Returns the TSIGCode error code value.\n\
+\n\
+Exceptions:\n\
+ None: \n\
+\n\
+Return Value(s):\n\
+ The underlying code value corresponding to the TSIGError.\n\
+";
+const char* const TSIGError_toText_doc = "to_text() -> string\n\
+\n\
+Convert the TSIGError to a string.\n\
+\n\
+For codes derived from RCODEs up to 15, this method returns the same\n\
+string as Rcode.to_text() for the corresponding code. For other pre-\n\
+defined code values (see TSIGError.CodeValue), this method returns a\n\
+string representation of the \"mnemonic' used for the enum and\n\
+constant objects as defined in RFC2845. For example, the string for\n\
+code value 16 is \"BADSIG\", etc. For other code values it returns a\n\
+string representation of the decimal number of the value, e.g. \"32\",\n\
+\"100\", etc.\n\
+\n\
+Exceptions:\n\
+ None\n\
+\n\
+Return Value(s):\n\
+ A string representation of the TSIGError.\n\
+";
+const char* const TSIGError_toRcode_doc = "to_rcode() -> Rcode\n\
+\n\
+Convert the TSIGError to a Rcode.\n\
+\n\
+This method returns an Rcode object that is corresponding to the TSIG\n\
+error. The returned Rcode is expected to be used by a verifying server\n\
+to specify the RCODE of a response when TSIG verification fails.\n\
+\n\
+Specifically, this method returns Rcode.NOTAUTH() for the TSIG\n\
+specific errors, BADSIG, BADKEY, BADTIME, as described in RFC2845. For\n\
+errors derived from the standard Rcode (code 0-15), it returns the\n\
+corresponding Rcode. For others, this method returns Rcode.SERVFAIL()\n\
+as a last resort.\n\
+\n\
+Exceptions:\n\
+ None: \n\
+\n\
+";
+}
diff --git a/src/lib/dns/tsigerror.h b/src/lib/dns/tsigerror.h
index 9794c41..0dc908c 100644
--- a/src/lib/dns/tsigerror.h
+++ b/src/lib/dns/tsigerror.h
@@ -30,9 +30,6 @@ class RRClass;
/// The \c TSIGError class objects represent standard errors related to
/// TSIG protocol operations as defined in related specifications, mainly
/// in RFC2845.
-///
-/// (RCODEs) of the header section of DNS messages, and extended response
-/// codes as defined in the EDNS specification.
class TSIGError {
public:
/// Constants for pre-defined TSIG error values.
@@ -58,7 +55,7 @@ public:
///
/// \exception None
///
- /// \param code The underlying 16-bit error code value of the \c TSIGError.
+ /// \param error_code The underlying 16-bit error code value of the \c TSIGError.
explicit TSIGError(uint16_t error_code) : code_(error_code) {}
/// Constructor from \c Rcode.
diff --git a/src/lib/util/Makefile.am b/src/lib/util/Makefile.am
index 77a3e4a..6deec27 100644
--- a/src/lib/util/Makefile.am
+++ b/src/lib/util/Makefile.am
@@ -24,5 +24,7 @@ libutil_la_SOURCES += encode/binary_from_base16.h
libutil_la_SOURCES += random/qid_gen.h random/qid_gen.cc
libutil_la_SOURCES += random/random_number_generator.h
+EXTRA_DIST = python/pycppwrapper_util.h
+
libutil_la_LIBADD = $(top_builddir)/src/lib/exceptions/libexceptions.la
CLEANFILES = *.gcno *.gcda
diff --git a/src/lib/util/python/mkpywrapper.py.in b/src/lib/util/python/mkpywrapper.py.in
new file mode 100755
index 0000000..4bf7752
--- /dev/null
+++ b/src/lib/util/python/mkpywrapper.py.in
@@ -0,0 +1,100 @@
+#!@PYTHON@
+
+# Copyright (C) 2011 Internet Systems Consortium.
+#
+# Permission to use, copy, modify, and 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 INTERNET SYSTEMS CONSORTIUM
+# DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL
+# INTERNET SYSTEMS CONSORTIUM 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.
+
+"""This utility program generates a C++ header and implementation files
+that can be used as a template of C++ python binding for a C++ class.
+
+Usage: ./mkpywrapper.py ClassName
+(the script should be run on this directory)
+
+It will generate two files: classname_python.h and classname_python.cc,
+many of whose definitions are in the namespace isc::MODULE_NAME::python.
+By default MODULE_NAME will be 'dns' (because this tool is originally
+intended to be used for the C++ python binding of the DNS library), but
+can be changed via the -m command line option.
+
+The generated files contain code fragments that are commonly used in
+C++ python binding implementations. It will define a class named
+s_ClassName which is a derived class of PyModule and can meet the
+requirement of the CPPPyObjectContainer template class (see
+pycppwrapper_util.h). It also defines (and declares in the header file)
+"classname_type", which is of PyTypeObject and is intended to be used
+to define details of the python bindings for the ClassName class.
+
+In many cases the header file can be used as a startpoint of the
+binding development without modification. But you may want to make
+ClassName::cppobj a constant variable (and you should if you can).
+Many definitions of classname_python.cc should also be able to be used
+just as defined, but some will need to be changed or removed. In
+particular, you should at least adjust ClassName_init(). You'll
+probably also need to add more definitions to that file to provide
+complete features of the C++ class.
+"""
+
+import datetime, string, sys
+from optparse import OptionParser
+
+# Remember the current year to produce the copyright boilerplate
+YEAR = datetime.date.today().timetuple()[0]
+
+def dump_file(out_file, temp_file, class_name, module):
+ for line in temp_file.readlines():
+ line = line.replace("@YEAR@", str(YEAR))
+ line = line.replace("@CPPCLASS at _H", class_name.upper() + "_H")
+ line = line.replace("@CPPCLASS@", class_name)
+ line = line.replace("@cppclass@", class_name.lower())
+ line = line.replace("@MODULE@", module)
+ out_file.write(line)
+
+def dump_wrappers(class_name, output, module):
+ try:
+ if output == "-":
+ header_file = sys.stdout
+ else:
+ header_file = open(output + "_python.h", "w")
+ header_template_file = open("wrapper_template.h", "r")
+ if output == "-":
+ impl_file = sys.stdout
+ else:
+ impl_file = open(output + "_python.cc", "w")
+ impl_template_file = open("wrapper_template.cc", "r")
+ except:
+ sys.stderr.write('Failed to open C++ file(s)\n')
+ sys.exit(1)
+ dump_file(header_file, header_template_file, class_name, module)
+ dump_file(impl_file, impl_template_file, class_name, module)
+
+usage = '''usage: %prog [options] class_name'''
+
+if __name__ == "__main__":
+ parser = OptionParser(usage=usage)
+ parser.add_option('-o', '--output', action='store', dest='output',
+ default=None, metavar='FILE',
+ help='prefix of output file names [default: derived from the class name]')
+ parser.add_option('-m', '--module', action='store', dest='module',
+ default='dns',
+ help='C++ module name of the wrapper (for namespaces) [default: dns]')
+ (options, args) = parser.parse_args()
+
+ if len(args) == 0:
+ parser.error('input file is missing')
+
+ class_name = args[0]
+ if not options.output:
+ options.output = class_name.lower()
+
+ dump_wrappers(class_name, options.output, options.module)
diff --git a/src/lib/util/python/pycppwrapper_util.h b/src/lib/util/python/pycppwrapper_util.h
new file mode 100644
index 0000000..fd55c19
--- /dev/null
+++ b/src/lib/util/python/pycppwrapper_util.h
@@ -0,0 +1,308 @@
+// Copyright (C) 2011 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 __PYCPPWRAPPER_UTIL_H
+#define __PYCPPWRAPPER_UTIL_H 1
+
+#include <Python.h>
+
+#include <exceptions/exceptions.h>
+
+/**
+ * @file pycppwrapper_util.h
+ * @short Shared definitions for python/C(++) API
+ *
+ * This utility defines a set of convenient wrappers for the python C API
+ * to use it safely from our C++ bindings. The python C API has many pitfalls
+ * such as not-so-consistent reference count policies. Also, many existing
+ * examples are careless about error handling. It's easy to find on the net
+ * example (even of "production use") python extensions like this:
+ *
+ * \code
+ * new_exception = PyErr_NewException("mymodule.Exception", NULL, NULL);
+ * // new_exception can be NULL, in which case the call to
+ * // PyModule_AddObject will cause a surprising disruption.
+ * PyModule_AddObject(mymodule, "Exception", new_exception); \endcode
+ *
+ * When using the python C API with C++, we should also be careful about
+ * exception safety. The underlying C++ code (including standard C++ libraries
+ * and memory allocation) can throw exceptions, in which case we need to
+ * make sure any intermediate python objects are cleaned up (we also need to
+ * catch the C++ exceptions inside the binding and convert them to python
+ * errors, but that's a different subject). This is not a trivial task
+ * because the python objects are represented as bare C pointers (so there's
+ * no destructor) and we need to address the exception safety along with python
+ * reference counters (so we cannot naively apply standard smart pointers).
+ *
+ * This utility tries to help address these issues.
+ *
+ * Also, it's intentional that this is a header-only utility. This way the
+ * C++ loadable module won't depend on another C++ library (which is not
+ * necessarily wrong, but would increase management cost such as link-time
+ * troubles only for a small utility feature).
+ */
+
+namespace isc {
+namespace util {
+namespace python {
+
+/// This is thrown inside this utility when it finds a NULL pointer is passed
+/// when it should not be NULL.
+class PyCPPWrapperException : public isc::Exception {
+public:
+ PyCPPWrapperException(const char* file, size_t line, const char* what) :
+ isc::Exception(file, line, what) {}
+};
+
+/// This helper class is similar to the standard autoptr and manages PyObject
+/// using some kind of RAII techniques. It is, however, customized for the
+/// python C API.
+///
+/// A PyObjectContainer object is constructed with a pointer to PyObject,
+/// which is often just created dynamically. The caller will eventually
+/// attach the object to a different python object (often a module or class)
+/// via specific methods or directly return it to the python interpreter.
+///
+/// There are two cases in destructing the object: with or without decreasing
+/// a reference to the PyObject. If the object is intended to be an argument
+/// to another python C library that increases the reference to the object for
+/// itself, we should normally release our own reference; otherwise the
+/// reference will leak and the object won't be garbage collected. Also, when
+/// an unexpected error happens in the form of C++ exception, we should
+/// release the reference to prevent resource leak.
+///
+/// In some other cases, we should simply give our reference to the caller.
+/// That is the case when the created object itself is a return value of
+/// an extended python method written in the C++ binding. Likewise, some
+/// python C library functions "steal" the reference. In these cases we
+/// should not decrease the reference; otherwise it would cause duplicate free.
+///
+/// By default, the destructor of this class releases the reference to the
+/// PyObject. If this behavior is desirable, you can extract the original
+/// bare pointer to the PyObject by the \c get() method. If you don't want
+/// the reference to be decreased, the original bare pointer should be
+/// extracted using the \c release() method.
+///
+/// There are two convenience methods for commonly used operations:
+/// \c installAsClassVariable() to add the PyObject as a class variable
+/// and \c installToModule to add the PyObject to a specified python module.
+/// These methods (at least to some extent) take care of the reference to
+/// the object (either release or keep) depending on the usage context so
+/// that the user don't have to worry about it.
+///
+/// On construction, this class expects the pointer can be NULL.
+/// If it happens it immediately throws a \c PyCPPWrapperException exception.
+/// This behavior is to convert failures in the python C API (such as
+/// PyObject_New() returning NULL) to C++ exception so that we can unify
+/// error handling in the style of C++ exceptions.
+///
+/// Examples 1: To create a tuple of two python objects, do this:
+///
+/// \code
+/// try {
+/// PyObjectContainer container0(Py_BuildValue("I", 0));
+/// PyObjectContainer container1(Py_BuildValue("s", cppobj.toText().c_str()));
+/// return (Py_BuildValue("OO", container0.get(), container1.get()));
+/// } catch { ... set python exception, etc ... } \endcode
+///
+/// Commonly deployed buggy implementation to achieve this would be like this:
+/// \code
+/// return (Py_BuildValue("OO", Py_BuildValue("I", 0),
+/// Py_BuildValue("s", cppobj.toText().c_str())));
+/// \endcode
+/// One clear bug of this code is that references to the element objects of
+/// the tuple will leak.
+/// (Assuming \c cppobj.toText() can throw) this code is also not exception
+/// safe; if \c cppobj.toText() throws the reference to the first object
+/// will leak, even if the code tried to do the necessary cleanup in the
+/// successful case.
+/// Further, this code naively passes the result of the first two calls to
+/// \c Py_BuildValue() to the third one even if they can be NULL.
+/// In this specific case, it happens to be okay because \c Py_BuildValue()
+/// accepts NULL and treats it as an indication of error. But not all
+/// python C library works that way (remember, the API is so inconsistent)
+/// and we need to refer to the API manual every time we have to worry about
+/// passing a NULL object to a library function. We'd certainly like to
+/// avoid such development overhead. The code using \c PyObjectContainer
+/// addresses all these problems.
+///
+/// Examples 2: Install a (constant) variable to a class.
+///
+/// \code
+/// try {
+/// // installClassVariable is a wrapper of
+/// // PyObjectContainer::installAsClassVariable. See below.
+/// installClassVariable(myclass_type, "SOME_CONSTANT",
+/// Py_BuildValue("I", 0));
+/// } catch { ... }
+/// \endcode
+///
+/// Examples 3: Install a custom exception to a module.
+///
+/// \code
+/// PyObject* new_exception; // publicly visible
+/// ...
+/// try {
+/// new_exception = PyErr_NewException("mymodule.NewException",
+/// NULL, NULL);
+/// PyObjectContainer(new_exception).installToModule(mymodule,
+/// "NewException");
+/// } catch { ... }
+/// \endcode
+///
+/// Note that \c installToModule() keeps the reference to \c new_exception
+/// by default. This is a common practice when we introduce a custom
+/// exception in a python biding written in C/C++. See the code comment
+/// of the method for more details.
+struct PyObjectContainer {
+ PyObjectContainer(PyObject* obj) : obj_(obj) {
+ if (obj_ == NULL) {
+ isc_throw(PyCPPWrapperException, "Unexpected NULL PyObject, "
+ "probably due to short memory");
+ }
+ }
+ virtual ~PyObjectContainer() {
+ if (obj_ != NULL) {
+ Py_DECREF(obj_);
+ }
+ }
+ PyObject* get() {
+ return (obj_);
+ }
+ PyObject* release() {
+ PyObject* ret = obj_;
+ obj_ = NULL;
+ return (ret);
+ }
+
+ // Install the enclosed PyObject to the specified python class 'pyclass'
+ // as a variable named 'name'.
+ void installAsClassVariable(PyTypeObject& pyclass, const char* name) {
+ if (PyDict_SetItemString(pyclass.tp_dict, name, obj_) < 0) {
+ isc_throw(PyCPPWrapperException, "Failed to set a class variable, "
+ "probably due to short memory");
+ }
+ // Ownership successfully transferred to the class object. We'll let
+ // it be released in the destructor.
+ }
+
+ // Install the enclosed PyObject to the specified module 'mod' as an
+ // object named 'name'.
+ // By default, this method explicitly keeps the reference to the object
+ // even after the module "steals" it. To cancel this behavior and give
+ // the reference to the module completely, the third parameter 'keep_ref'
+ // should be set to false.
+ void installToModule(PyObject* mod, const char* name,
+ bool keep_ref = true)
+ {
+ if (PyModule_AddObject(mod, name, obj_) < 0) {
+ isc_throw(PyCPPWrapperException, "Failed to add an object to "
+ "module, probably due to short memory");
+ }
+ // PyModule_AddObject has "stolen" the reference, so unless we
+ // have to retain it ourselves we don't (shouldn't) decrease it.
+ // However, we actually often need to keep our own reference because
+ // objects added to a module are often referenced via non local
+ // C/C++ variables in various places of the C/C++ code. In order
+ // for the code to run safely even if some buggy/evil python program
+ // performs 'del mod.obj', we need the extra reference. See, e.g.:
+ // http://docs.python.org/py3k/c-api/init.html#Py_Initialize
+ // http://mail.python.org/pipermail/python-dev/2005-June/054238.html
+ if (keep_ref) {
+ Py_INCREF(obj_);
+ }
+ obj_ = NULL;
+ }
+
+protected:
+ PyObject* obj_;
+};
+
+/// This templated class is a derived class of \c PyObjectContainer and
+/// manages C++-class based python objects.
+///
+/// The template parameter \c PYSTRUCT must be a derived class (structure) of
+/// \c PyObject that has a member variable named \c cppobj, which must be a
+/// a pointer to \c CPPCLASS (the second template parameter).
+///
+/// For example, to define a custom python class based on a C++ class, MyClass,
+/// we'd define a class (struct) named \c s_MyClass like this:
+/// \code
+/// class s_MyClass : public PyObject {
+/// public:
+/// s_MyClass() : cppobj(NULL) {}
+/// MyClass* cppobj;
+/// };
+/// \endcode
+///
+/// And, to build and return a python version of MyClass object, write the
+/// following C++ code:
+/// \code
+/// typedef CPPPyObjectContainer<s_MyClass, MyClass> MyContainer;
+/// try {
+/// // below, myclass_type is of \c PyTypeObject that defines
+/// // a python class (type) for MyClass
+/// MyContainer container(PyObject_New(s_MyClass, myclass_type));
+/// container.set(new MyClass());
+/// return (container.release()); // give the reference to the caller
+/// } catch { ... }
+/// \endcode
+///
+/// This code prevents bugs like NULL pointer dereference when \c PyObject_New
+/// fails or resource leaks when new'ing \c MyClass results in an exception.
+/// Note that we use \c release() (derived from the base class) instead of
+/// \c get(); in this case we should simply pass the reference generated in
+/// \c PyObject_New() to the caller.
+template <typename PYSTRUCT, typename CPPCLASS>
+struct CPPPyObjectContainer : public PyObjectContainer {
+ CPPPyObjectContainer(PYSTRUCT* obj) : PyObjectContainer(obj) {}
+
+ // This method associates a C++ object with the corresponding python
+ // object enclosed in this class.
+ void set(CPPCLASS* value) {
+ if (value == NULL) {
+ isc_throw(PyCPPWrapperException, "Unexpected NULL C++ object, "
+ "probably due to short memory");
+ }
+ static_cast<PYSTRUCT*>(obj_)->cppobj = value;
+ }
+
+ // This is a convenience short cut to associate a C++ object with the
+ // python object and install it to the specified python class \c pyclass
+ // as a variable named \c name.
+ void installAsClassVariable(PyTypeObject& pyclass, const char* name,
+ CPPCLASS* value)
+ {
+ set(value);
+ PyObjectContainer::installAsClassVariable(pyclass, name);
+ }
+};
+
+/// A shortcut function to install a python class variable.
+///
+/// It installs a python object \c obj to a specified class \c pyclass
+/// as a variable named \c name.
+inline void
+installClassVariable(PyTypeObject& pyclass, const char* name, PyObject* obj) {
+ PyObjectContainer(obj).installAsClassVariable(pyclass, name);
+}
+
+} // namespace python
+} // namespace util
+} // namespace isc
+#endif // __PYCPPWRAPPER_UTIL_H
+
+// Local Variables:
+// mode: c++
+// End:
diff --git a/src/lib/util/python/wrapper_template.cc b/src/lib/util/python/wrapper_template.cc
new file mode 100644
index 0000000..2fe7567
--- /dev/null
+++ b/src/lib/util/python/wrapper_template.cc
@@ -0,0 +1,293 @@
+// Copyright (C) @YEAR@ 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.
+
+#include <string>
+#include <stdexcept>
+
+#include <util/python/pycppwrapper_util.h>
+
+#include "@cppclass at _python.h"
+
+using namespace std;
+using namespace isc::util::python;
+using namespace isc::@MODULE@;
+using namespace isc::@MODULE@::python;
+
+//
+// Definition of the classes
+//
+
+// For each class, we need a struct, a helper functions (init, destroy,
+// and static wrappers around the methods we export), a list of methods,
+// and a type description
+
+//
+// @CPPCLASS@
+//
+
+// Trivial constructor.
+s_ at CPPCLASS@::s_ at CPPCLASS@() : cppobj(NULL) {
+}
+
+namespace {
+// Shortcut type which would be convenient for adding class variables safely.
+typedef CPPPyObjectContainer<s_ at CPPCLASS@, @CPPCLASS@> @CPPCLASS at Container;
+
+//
+// We declare the functions here, the definitions are below
+// the type definition of the object, since both can use the other
+//
+
+// General creation and destruction
+int @CPPCLASS at _init(s_ at CPPCLASS@* self, PyObject* args);
+void @CPPCLASS at _destroy(s_ at CPPCLASS@* self);
+
+// These are the functions we export
+// ADD/REMOVE/MODIFY THE FOLLOWING AS APPROPRIATE FOR THE ACTUAL CLASS.
+//
+PyObject* @CPPCLASS at _toText(const s_ at CPPCLASS@* const self);
+PyObject* @CPPCLASS at _str(PyObject* self);
+PyObject* @CPPCLASS at _richcmp(const s_ at CPPCLASS@* const self,
+ const s_ at CPPCLASS@* const other, int op);
+
+// This is quite specific pydnspp. For other wrappers this should probably
+// be removed.
+PyObject* @CPPCLASS at _toWire(const s_ at CPPCLASS@* self, PyObject* args);
+
+// These are the functions we export
+// For a minimal support, we don't need them.
+
+// 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 @CPPCLASS at _methods[] = {
+ { "to_text", reinterpret_cast<PyCFunction>(@CPPCLASS at _toText), METH_NOARGS,
+ "Returns the text representation" },
+ // This is quite specific pydnspp. For other wrappers this should probably
+ // be removed:
+ { "to_wire", reinterpret_cast<PyCFunction>(@CPPCLASS at _toWire), METH_VARARGS,
+ "Converts the @CPPCLASS@ object to wire format.\n"
+ "The argument can be either a MessageRenderer or an object that "
+ "implements the sequence interface. If the object is mutable "
+ "(for instance a bytearray()), the wire data is added in-place.\n"
+ "If it is not (for instance a bytes() object), a new object is "
+ "returned" },
+ { NULL, NULL, 0, NULL }
+};
+
+// This is a template of typical code logic of python class initialization
+// with C++ backend. You'll need to adjust it according to details of the
+// actual C++ class.
+int
+ at CPPCLASS@_init(s_ at CPPCLASS@* self, PyObject* args) {
+ try {
+ if (PyArg_ParseTuple(args, "REPLACE ME")) {
+ // YOU'LL NEED SOME VALIDATION, PREPARATION, ETC, HERE.
+ self->cppobj = new @CPPCLASS@(/*NECESSARY PARAMS*/);
+ return (0);
+ }
+ } catch (const exception& ex) {
+ const string ex_what = "Failed to construct @CPPCLASS@ object: " +
+ string(ex.what());
+ PyErr_SetString(po_IscException, ex_what.c_str());
+ return (-1);
+ } catch (...) {
+ PyErr_SetString(po_IscException,
+ "Unexpected exception in constructing @CPPCLASS@");
+ return (-1);
+ }
+
+ PyErr_SetString(PyExc_TypeError,
+ "Invalid arguments to @CPPCLASS@ constructor");
+
+ return (-1);
+}
+
+// This is a template of typical code logic of python object destructor.
+// In many cases you can use it without modification, but check that carefully.
+void
+ at CPPCLASS@_destroy(s_ at CPPCLASS@* const self) {
+ delete self->cppobj;
+ self->cppobj = NULL;
+ Py_TYPE(self)->tp_free(self);
+}
+
+// This should be able to be used without modification as long as the
+// underlying C++ class has toText().
+PyObject*
+ at CPPCLASS@_toText(const s_ at CPPCLASS@* const self) {
+ try {
+ // toText() could throw, so we need to catch any exceptions below.
+ return (Py_BuildValue("s", self->cppobj->toText().c_str()));
+ } catch (const exception& ex) {
+ const string ex_what =
+ "Failed to convert @CPPCLASS@ object to text: " +
+ string(ex.what());
+ PyErr_SetString(po_IscException, ex_what.c_str());
+ } catch (...) {
+ PyErr_SetString(PyExc_SystemError, "Unexpected failure in "
+ "converting @CPPCLASS@ object to text");
+ }
+ return (NULL);
+}
+
+PyObject*
+ at CPPCLASS@_str(PyObject* self) {
+ // Simply call the to_text method we already defined
+ return (PyObject_CallMethod(self, const_cast<char*>("to_text"),
+ const_cast<char*>("")));
+}
+
+PyObject*
+ at CPPCLASS@_richcmp(const s_ at CPPCLASS@* const self,
+ const s_ at CPPCLASS@* const other,
+ const int op)
+{
+ bool c = false;
+
+ // Check for null and if the types match. If different type,
+ // simply return False
+ if (other == NULL || (self->ob_type != other->ob_type)) {
+ Py_RETURN_FALSE;
+ }
+
+ // Only equals and not equals here, unorderable type
+ switch (op) {
+ case Py_LT:
+ PyErr_SetString(PyExc_TypeError, "Unorderable type; @CPPCLASS@");
+ return (NULL);
+ case Py_LE:
+ PyErr_SetString(PyExc_TypeError, "Unorderable type; @CPPCLASS@");
+ return (NULL);
+ case Py_EQ:
+ c = (*self->cppobj == *other->cppobj);
+ break;
+ case Py_NE:
+ c = (*self->cppobj != *other->cppobj);
+ break;
+ case Py_GT:
+ PyErr_SetString(PyExc_TypeError, "Unorderable type; @CPPCLASS@");
+ return (NULL);
+ case Py_GE:
+ PyErr_SetString(PyExc_TypeError, "Unorderable type; @CPPCLASS@");
+ return (NULL);
+ }
+ if (c) {
+ Py_RETURN_TRUE;
+ } else {
+ Py_RETURN_FALSE;
+ }
+}
+} // end of unnamed namespace
+
+namespace isc {
+namespace @MODULE@ {
+namespace python {
+// This defines the complete type for reflection in python and
+// parsing of PyObject* to s_ at CPPCLASS@
+// Most of the functions are not actually implemented and NULL here.
+PyTypeObject @cppclass at _type = {
+ PyVarObject_HEAD_INIT(NULL, 0)
+ "libdns_python. at CPPCLASS@",
+ sizeof(s_ at CPPCLASS@), // tp_basicsize
+ 0, // tp_itemsize
+ reinterpret_cast<destructor>(@CPPCLASS at _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
+ // THIS MAY HAVE TO BE CHANGED TO NULL:
+ @CPPCLASS at _str, // tp_str
+ NULL, // tp_getattro
+ NULL, // tp_setattro
+ NULL, // tp_as_buffer
+ Py_TPFLAGS_DEFAULT, // tp_flags
+ "The @CPPCLASS@ class objects is...(COMPLETE THIS)",
+ NULL, // tp_traverse
+ NULL, // tp_clear
+ // THIS MAY HAVE TO BE CHANGED TO NULL:
+ reinterpret_cast<richcmpfunc>(@CPPCLASS at _richcmp), // tp_richcompare
+ 0, // tp_weaklistoffset
+ NULL, // tp_iter
+ NULL, // tp_iternext
+ @CPPCLASS at _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>(@CPPCLASS at _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
+};
+
+// Module Initialization, all statics are initialized here
+bool
+initModulePart_ at CPPCLASS@(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(&@cppclass at _type) < 0) {
+ return (false);
+ }
+ void* p = &@cppclass at _type;
+ if (PyModule_AddObject(mod, "@CPPCLASS@", static_cast<PyObject*>(p)) < 0) {
+ return (false);
+ }
+ Py_INCREF(&@cppclass at _type);
+
+ // The following template is the typical procedure for installing class
+ // variables. If the class doesn't have a class variable, remove the
+ // entire try-catch clauses.
+ try {
+ // Constant class variables
+ installClassVariable(@cppclass at _type, "REPLACE_ME",
+ Py_BuildValue("REPLACE ME"));
+ } catch (const exception& ex) {
+ const string ex_what =
+ "Unexpected failure in @CPPCLASS@ initialization: " +
+ string(ex.what());
+ PyErr_SetString(po_IscException, ex_what.c_str());
+ return (false);
+ } catch (...) {
+ PyErr_SetString(PyExc_SystemError,
+ "Unexpected failure in @CPPCLASS@ initialization");
+ return (false);
+ }
+
+ return (true);
+}
+} // namespace python
+} // namespace @MODULE@
+} // namespace isc
diff --git a/src/lib/util/python/wrapper_template.h b/src/lib/util/python/wrapper_template.h
new file mode 100644
index 0000000..1f983e4
--- /dev/null
+++ b/src/lib/util/python/wrapper_template.h
@@ -0,0 +1,44 @@
+// Copyright (C) @YEAR@ 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_ at CPPCLASS@_H
+#define __PYTHON_ at CPPCLASS@_H 1
+
+#include <Python.h>
+
+namespace isc {
+namespace @MODULE@ {
+class @CPPCLASS@;
+
+namespace python {
+
+// The s_* Class simply covers one instantiation of the object
+class s_ at CPPCLASS@ : public PyObject {
+public:
+ s_ at CPPCLASS@();
+ @CPPCLASS@* cppobj;
+};
+
+extern PyTypeObject @cppclass at _type;
+
+bool initModulePart_ at CPPCLASS@(PyObject* mod);
+
+} // namespace python
+} // namespace @MODULE@
+} // namespace isc
+#endif // __PYTHON_ at CPPCLASS@_H
+
+// Local Variables:
+// mode: c++
+// End:
More information about the bind10-changes
mailing list