mirror of
https://gitlab.isc.org/isc-projects/kea
synced 2025-08-31 05:55:28 +00:00
Merge branch 'master' into trac848
This commit is contained in:
@@ -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
|
||||
|
@@ -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
|
||||
|
@@ -3,6 +3,13 @@ PYTESTS = tsig_keys_test.py
|
||||
|
||||
EXTRA_DIST = $(PYTESTS)
|
||||
|
||||
# If necessary (rare cases), explicitly specify paths to dynamic libraries
|
||||
# required by loadable python modules.
|
||||
LIBRARY_PATH_PLACEHOLDER =
|
||||
if SET_ENV_LIBRARY_PATH
|
||||
LIBRARY_PATH_PLACEHOLDER += $(ENV_LIBRARY_PATH)=$(abs_top_builddir)/src/lib/dns/.libs:$(abs_top_builddir)/src/lib/cryptolink/.libs:$(abs_top_builddir)/src/lib/util/.libs:$(abs_top_builddir)/src/lib/exceptions/.libs
|
||||
endif
|
||||
|
||||
# test using command-line arguments, so use check-local target instead of TESTS
|
||||
check-local:
|
||||
if ENABLE_PYTHON_COVERAGE
|
||||
@@ -14,6 +21,7 @@ endif
|
||||
echo Running test: $$pytest ; \
|
||||
env B10_TEST_PLUGIN_DIR=$(abs_srcdir)/..:$(abs_builddir)/.. \
|
||||
env PYTHONPATH=$(abs_top_srcdir)/src/lib/python:$(abs_top_builddir)/src/lib/python:$(abs_top_builddir)/src/bin/cfgmgr:$(abs_top_builddir)/src/lib/dns/python/.libs \
|
||||
$(LIBRARY_PATH_PLACEHOLDER) \
|
||||
$(PYCOVERAGE_RUN) $(abs_srcdir)/$$pytest || exit ; \
|
||||
done
|
||||
|
||||
|
@@ -6,6 +6,9 @@ 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_SOURCES += tsigerror_python_inc.cc
|
||||
pydnspp_la_CPPFLAGS = $(AM_CPPFLAGS) $(PYTHON_INCLUDES)
|
||||
pydnspp_la_LDFLAGS = $(PYTHON_LDFLAGS)
|
||||
|
||||
|
@@ -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());
|
||||
|
@@ -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);
|
||||
}
|
||||
|
@@ -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));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -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:
|
||||
|
@@ -12,9 +12,17 @@
|
||||
// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
||||
// PERFORMANCE OF THIS SOFTWARE.
|
||||
|
||||
#include <Python.h>
|
||||
|
||||
#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 +35,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 +115,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 +139,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 +157,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 +191,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 +303,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 +320,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 +429,6 @@ initModulePart_Rcode(PyObject* mod) {
|
||||
|
||||
return (true);
|
||||
}
|
||||
} // end of unnamed namespace
|
||||
} // namespace python
|
||||
} // namespace dns
|
||||
} // namespace isc
|
||||
|
57
src/lib/dns/python/rcode_python.h
Normal file
57
src/lib/dns/python/rcode_python.h
Normal file
@@ -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:
|
@@ -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)
|
||||
|
97
src/lib/dns/python/tests/tsigerror_python_test.py
Normal file
97
src/lib/dns/python/tests/tsigerror_python_test.py
Normal file
@@ -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()
|
@@ -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;
|
||||
|
362
src/lib/dns/python/tsigerror_python.cc
Normal file
362
src/lib/dns/python/tsigerror_python.cc
Normal file
@@ -0,0 +1,362 @@
|
||||
// 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 <Python.h>
|
||||
|
||||
#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
|
44
src/lib/dns/python/tsigerror_python.h
Normal file
44
src/lib/dns/python/tsigerror_python.h
Normal file
@@ -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:
|
83
src/lib/dns/python/tsigerror_python_inc.cc
Normal file
83
src/lib/dns/python/tsigerror_python_inc.cc
Normal file
@@ -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\
|
||||
";
|
||||
}
|
@@ -22,17 +22,11 @@
|
||||
|
||||
namespace isc {
|
||||
namespace dns {
|
||||
|
||||
class RRClass;
|
||||
|
||||
/// TSIG errors
|
||||
///
|
||||
/// 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 +52,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.
|
||||
|
@@ -27,6 +27,7 @@ def reload():
|
||||
"@PACKAGE_NAME@",
|
||||
"msgq_socket").replace("${prefix}",
|
||||
"@prefix@")
|
||||
PREFIX = "@prefix@"
|
||||
|
||||
# If B10_FROM_SOURCE is set in the environment, we use data files
|
||||
# from a directory relative to the value of that variable, or, if defined,
|
||||
@@ -43,7 +44,6 @@ def reload():
|
||||
DATA_PATH = os.environ["B10_FROM_SOURCE"]
|
||||
PLUGIN_PATHS = [DATA_PATH + '/src/bin/cfgmgr/plugins']
|
||||
else:
|
||||
PREFIX = "@prefix@"
|
||||
DATA_PATH = "@localstatedir@/@PACKAGE@".replace("${prefix}", PREFIX)
|
||||
PLUGIN_PATHS = ["@prefix@/share/@PACKAGE@/config_plugins"]
|
||||
# For testing the plugins so they can find their own spec files
|
||||
|
@@ -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
|
||||
|
100
src/lib/util/python/mkpywrapper.py.in
Executable file
100
src/lib/util/python/mkpywrapper.py.in
Executable file
@@ -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@_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)
|
308
src/lib/util/python/pycppwrapper_util.h
Normal file
308
src/lib/util/python/pycppwrapper_util.h
Normal file
@@ -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:
|
293
src/lib/util/python/wrapper_template.cc
Normal file
293
src/lib/util/python/wrapper_template.cc
Normal file
@@ -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@_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_@CPPCLASS@::s_@CPPCLASS@() : cppobj(NULL) {
|
||||
}
|
||||
|
||||
namespace {
|
||||
// Shortcut type which would be convenient for adding class variables safely.
|
||||
typedef CPPPyObjectContainer<s_@CPPCLASS@, @CPPCLASS@> @CPPCLASS@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@_init(s_@CPPCLASS@* self, PyObject* args);
|
||||
void @CPPCLASS@_destroy(s_@CPPCLASS@* self);
|
||||
|
||||
// These are the functions we export
|
||||
// ADD/REMOVE/MODIFY THE FOLLOWING AS APPROPRIATE FOR THE ACTUAL CLASS.
|
||||
//
|
||||
PyObject* @CPPCLASS@_toText(const s_@CPPCLASS@* const self);
|
||||
PyObject* @CPPCLASS@_str(PyObject* self);
|
||||
PyObject* @CPPCLASS@_richcmp(const s_@CPPCLASS@* const self,
|
||||
const s_@CPPCLASS@* const other, int op);
|
||||
|
||||
// This is quite specific pydnspp. For other wrappers this should probably
|
||||
// be removed.
|
||||
PyObject* @CPPCLASS@_toWire(const s_@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@_methods[] = {
|
||||
{ "to_text", reinterpret_cast<PyCFunction>(@CPPCLASS@_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@_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
|
||||
@CPPCLASS@_init(s_@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
|
||||
@CPPCLASS@_destroy(s_@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*
|
||||
@CPPCLASS@_toText(const s_@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*
|
||||
@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*
|
||||
@CPPCLASS@_richcmp(const s_@CPPCLASS@* const self,
|
||||
const s_@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_@CPPCLASS@
|
||||
// Most of the functions are not actually implemented and NULL here.
|
||||
PyTypeObject @cppclass@_type = {
|
||||
PyVarObject_HEAD_INIT(NULL, 0)
|
||||
"libdns_python.@CPPCLASS@",
|
||||
sizeof(s_@CPPCLASS@), // tp_basicsize
|
||||
0, // tp_itemsize
|
||||
reinterpret_cast<destructor>(@CPPCLASS@_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@_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@_richcmp), // tp_richcompare
|
||||
0, // tp_weaklistoffset
|
||||
NULL, // tp_iter
|
||||
NULL, // tp_iternext
|
||||
@CPPCLASS@_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@_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_@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@_type) < 0) {
|
||||
return (false);
|
||||
}
|
||||
void* p = &@cppclass@_type;
|
||||
if (PyModule_AddObject(mod, "@CPPCLASS@", static_cast<PyObject*>(p)) < 0) {
|
||||
return (false);
|
||||
}
|
||||
Py_INCREF(&@cppclass@_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@_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
|
44
src/lib/util/python/wrapper_template.h
Normal file
44
src/lib/util/python/wrapper_template.h
Normal file
@@ -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_@CPPCLASS@_H
|
||||
#define __PYTHON_@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_@CPPCLASS@ : public PyObject {
|
||||
public:
|
||||
s_@CPPCLASS@();
|
||||
@CPPCLASS@* cppobj;
|
||||
};
|
||||
|
||||
extern PyTypeObject @cppclass@_type;
|
||||
|
||||
bool initModulePart_@CPPCLASS@(PyObject* mod);
|
||||
|
||||
} // namespace python
|
||||
} // namespace @MODULE@
|
||||
} // namespace isc
|
||||
#endif // __PYTHON_@CPPCLASS@_H
|
||||
|
||||
// Local Variables:
|
||||
// mode: c++
|
||||
// End:
|
Reference in New Issue
Block a user