summaryrefslogtreecommitdiffstats
path: root/src/lib/util
diff options
context:
space:
mode:
authorTomek Mrugalski <tomasz@isc.org>2014-06-03 16:34:54 +0200
committerTomek Mrugalski <tomasz@isc.org>2014-06-03 16:34:54 +0200
commita28843919086b628724c00e965d6717fb66fa63c (patch)
treeb5e9e241db40a1f06b8edc8a2f879c8249afbeab /src/lib/util
parent[3413] setproctitle check removed from configure.ac (diff)
downloadkea-a28843919086b628724c00e965d6717fb66fa63c.tar.xz
kea-a28843919086b628724c00e965d6717fb66fa63c.zip
[3413] Several build steps no longer require python3
- *.spec files in src/lib/dns/tests/testsdata are now included in dist - src/lib/util/pyunittests is removed - src/lib/util/python trimmed down a lot - fix for missing dhcp6_shutdown_test.sh in src/bin/dhcp6 - many python macros in configure.ac removed (more of them to be removed in Makefiles)
Diffstat (limited to 'src/lib/util')
-rw-r--r--src/lib/util/Makefile.am3
-rw-r--r--src/lib/util/io/Makefile.am11
-rw-r--r--src/lib/util/io/fdshare_python.cc98
-rw-r--r--src/lib/util/python/.gitignore3
-rw-r--r--src/lib/util/python/Makefile.am5
-rwxr-xr-xsrc/lib/util/python/doxygen2pydoc.py.in680
-rwxr-xr-xsrc/lib/util/python/mkpywrapper.py.in100
-rw-r--r--src/lib/util/python/pycppwrapper_util.h335
-rw-r--r--src/lib/util/python/pythonize_constants.py57
-rw-r--r--src/lib/util/python/wrapper_template.cc291
-rw-r--r--src/lib/util/python/wrapper_template.h59
-rw-r--r--src/lib/util/pyunittests/Makefile.am22
-rw-r--r--src/lib/util/pyunittests/pyunittests_util.cc84
13 files changed, 3 insertions, 1745 deletions
diff --git a/src/lib/util/Makefile.am b/src/lib/util/Makefile.am
index ffb84719d5..75a3d24844 100644
--- a/src/lib/util/Makefile.am
+++ b/src/lib/util/Makefile.am
@@ -1,4 +1,4 @@
-SUBDIRS = . io unittests tests pyunittests python threads
+SUBDIRS = . io unittests tests python threads
AM_CPPFLAGS = -I$(top_srcdir)/src/lib -I$(top_builddir)/src/lib
AM_CPPFLAGS += -I$(top_srcdir)/src/lib/util -I$(top_builddir)/src/lib/util
@@ -41,7 +41,6 @@ libkea_util_la_SOURCES += encode/binary_from_base16.h
libkea_util_la_SOURCES += random/qid_gen.h random/qid_gen.cc
libkea_util_la_SOURCES += random/random_number_generator.h
-EXTRA_DIST = python/pycppwrapper_util.h
libkea_util_la_LIBADD = $(top_builddir)/src/lib/exceptions/libkea-exceptions.la
CLEANFILES = *.gcno *.gcda
diff --git a/src/lib/util/io/Makefile.am b/src/lib/util/io/Makefile.am
index 4011bde483..98ba21ec65 100644
--- a/src/lib/util/io/Makefile.am
+++ b/src/lib/util/io/Makefile.am
@@ -10,14 +10,3 @@ libkea_util_io_la_SOURCES += pktinfo_utilities.h
libkea_util_io_la_LIBADD = $(top_builddir)/src/lib/exceptions/libkea-exceptions.la
CLEANFILES = *.gcno *.gcda
-
-pyexec_LTLIBRARIES = libutil_io_python.la
-# Python prefers .so, while some OSes (specifically MacOS) use a different
-# suffix for dynamic objects. -module is necessary to work this around.
-libutil_io_python_la_LDFLAGS = -module -avoid-version
-libutil_io_python_la_SOURCES = fdshare_python.cc
-libutil_io_python_la_LIBADD = libkea-util-io.la
-libutil_io_python_la_CPPFLAGS = $(AM_CPPFLAGS) $(PYTHON_INCLUDES)
-# Note: PYTHON_CXXFLAGS may have some -Wno... workaround, which must be
-# placed after -Wextra defined in AM_CXXFLAGS
-libutil_io_python_la_CXXFLAGS = $(AM_CXXFLAGS) $(PYTHON_CXXFLAGS)
diff --git a/src/lib/util/io/fdshare_python.cc b/src/lib/util/io/fdshare_python.cc
deleted file mode 100644
index 249f8b08f8..0000000000
--- a/src/lib/util/io/fdshare_python.cc
+++ /dev/null
@@ -1,98 +0,0 @@
-// Copyright (C) 2010 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.
-
-#define PY_SSIZE_T_CLEAN
-#include <Python.h>
-#include <structmember.h>
-
-#include <config.h>
-
-#include "fd_share.h"
-
-
-static PyObject*
-fdshare_recv_fd(PyObject*, PyObject* args) {
- int sock, fd;
- if (!PyArg_ParseTuple(args, "i", &sock)) {
- return (NULL);
- }
- fd = isc::util::io::recv_fd(sock);
- return (Py_BuildValue("i", fd));
-}
-
-static PyObject*
-fdshare_send_fd(PyObject*, PyObject* args) {
- int sock, fd, result;
- if (!PyArg_ParseTuple(args, "ii", &sock, &fd)) {
- return (NULL);
- }
- result = isc::util::io::send_fd(sock, fd);
- return (Py_BuildValue("i", result));
-}
-
-static PyMethodDef fdshare_Methods[] = {
- {"send_fd", fdshare_send_fd, METH_VARARGS, ""},
- {"recv_fd", fdshare_recv_fd, METH_VARARGS, ""},
- {NULL, NULL, 0, NULL} /* Sentinel */
-};
-
-
-static PyModuleDef bind10_fdshare_python = {
- { PyObject_HEAD_INIT(NULL) NULL, 0, NULL},
- "bind10_fdshare",
- "Python bindings for fdshare",
- -1,
- fdshare_Methods,
- NULL,
- NULL,
- NULL,
- NULL
-};
-
-PyMODINIT_FUNC
-PyInit_libutil_io_python(void) {
- PyObject *mod = PyModule_Create(&bind10_fdshare_python);
- if (mod == NULL) {
- return (NULL);
- }
-
- PyObject* FD_SYSTEM_ERROR = Py_BuildValue("i",
- isc::util::io::FD_SYSTEM_ERROR);
- if (FD_SYSTEM_ERROR == NULL) {
- Py_XDECREF(mod);
- return (NULL);
- }
- int ret = PyModule_AddObject(mod, "FD_SYSTEM_ERROR", FD_SYSTEM_ERROR);
- if (ret == -1) {
- Py_XDECREF(FD_SYSTEM_ERROR);
- Py_XDECREF(mod);
- return (NULL);
- }
-
- PyObject* FD_OTHER_ERROR = Py_BuildValue("i",
- isc::util::io::FD_OTHER_ERROR);
- if (FD_OTHER_ERROR == NULL) {
- Py_XDECREF(mod);
- return (NULL);
- }
- ret = PyModule_AddObject(mod, "FD_OTHER_ERROR", FD_OTHER_ERROR);
- if (-1 == ret) {
- Py_XDECREF(FD_OTHER_ERROR);
- Py_XDECREF(mod);
- return (NULL);
- }
-
- return (mod);
-}
-
diff --git a/src/lib/util/python/.gitignore b/src/lib/util/python/.gitignore
deleted file mode 100644
index 619c69fd65..0000000000
--- a/src/lib/util/python/.gitignore
+++ /dev/null
@@ -1,3 +0,0 @@
-/doxygen2pydoc.py
-/gen_wiredata.py
-/mkpywrapper.py
diff --git a/src/lib/util/python/Makefile.am b/src/lib/util/python/Makefile.am
index c7ddf3d175..dd584be2b2 100644
--- a/src/lib/util/python/Makefile.am
+++ b/src/lib/util/python/Makefile.am
@@ -1,3 +1,2 @@
-noinst_SCRIPTS = doxygen2pydoc.py gen_wiredata.py mkpywrapper.py const2hdr.py \
- pythonize_constants.py
-EXTRA_DIST = const2hdr.py pythonize_constants.py
+noinst_SCRIPTS = const2hdr.py gen_wiredata.py
+EXTRA_DIST = const2hdr.py
diff --git a/src/lib/util/python/doxygen2pydoc.py.in b/src/lib/util/python/doxygen2pydoc.py.in
deleted file mode 100755
index 7aa74ec6c6..0000000000
--- a/src/lib/util/python/doxygen2pydoc.py.in
+++ /dev/null
@@ -1,680 +0,0 @@
-#!@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.
-
-r"""
-A helper to semi-auto generate Python docstring text from C++ Doxygen
-documentation.
-
-This script converts an XML-format doxygen documentation for C++ library
-into a template Python docstring for the corresponding Python version
-of the library. While it's not perfect and you'll still need to edit the
-output by hand, but past experiments showed the script produces a pretty
-good template. It will help provide more compatible documentation for
-both C++ and Python versions of library from a unified source (C++ Doxygen
-documentation) with minimizing error-prone and boring manual conversion.
-
-HOW TO USE IT
-
-1. Generate XML output by doxygen. Use bind10/doc/Doxyfile-xml:
-
- % cd bind10/doc
- % doxygen Doxyfile-xml
- (XML files will be generated under bind10/doc/html/xml)
-
-2. Identify the xml file of the conversion target (C++ class, function, etc)
-
- This is a bit tricky. You'll probably need to do manual search.
- For example, to identify the xml file for a C++ class
- isc::datasrc::memory::ZoneWriter, you might do:
-
- % cd bind10/doc/html/xml
- % grep ZoneWriter *.xml | grep 'kind="class"'
- index.xml: <compound refid="d4/d3c/classisc_1_1datasrc_1_1memory_1_1ZoneWriter" kind="class"><name>isc::datasrc::memory::ZoneWriter</name>
-
- In this case the file under the d4/d3c directory (with .xml suffix) would
- be the file you're looking for.
-
-3. Run this script for the xml file:
-
- % python3 doxygen2pydoc.py <top_srcdir>/doc/html/xml/d4/d3c/classisc_1_1datasrc_1_1memory_1_1ZoneWriter.xml > output.cc
-
- The template content is dumped to standard out (redirected to file
- "output.cc" in this example).
-
- Sometimes the script produces additional output to standard error,
- like this:
-
- Replaced camelCased terms:
- resetMemorySegment => reset_memory_segment
- getConfiguration => get_configuration
-
- In BIND 10 naming convention for methods is different for C++ and
- Python. This script uses some heuristic guess to convert the
- C++-style method names to likely Python-style ones, and the converted
- method names are used in the dumped template. In many cases the guessed
- names are correct, but you should check this list and make adjustments
- by hand if necessary.
-
- If there's no standard error output, this type of conversion didn't
- happen.
-
-4. Edit and copy the template
-
- The dumped template has the following organization:
-
- namespace {
- #ifdef COPY_THIS_TO_MAIN_CC
- { "cleanup", ZoneWriter_cleanup, METH_NOARGS,
- ZoneWriter_cleanup_doc },
- { "install", ZoneWriter_install, METH_NOARGS,
- ZoneWriter_install_doc },
- { "load", ZoneWriter_load, METH_VARARGS,
- ZoneWriter_load_doc },
- #endif // COPY_THIS_TO_MAIN_CC
-
- const char* const ZoneWriter_doc = "\
- ...
- ";
-
- const char* const ZoneWriter_install_doc = "\
- ...
- ";
-
- ...
- }
-
- The ifdef-ed block is a template for class methods information
- to be added to the corresponding PyMethodDef structure array
- (your wrapper C++ source would have something like ZoneWriter_methods
- of this type). These lines should be copied there. As long as
- the method names and corresponding wrapper function (such as
- ZoneWriter_cleanup) are correct you shouldn't have to edit this part
- (and they would be normally correct, unless the guessed method name
- conversion was needed).
-
- The rest of the content is a sequence of constant C-string variables.
- Usually the first variable corresponds to the class description, and
- the rest are method descriptions (note that ZoneWriter_install_doc
- is referenced from the ifdef-ed block). The content of this part
- would generally make sense, but you'll often need to make some
- adjsutments by hand. A common examples of such adjustment is to
- replace "NULL" with "None". Also, it's not uncommon that some part
- of the description simply doesn't apply to the Python version or
- that Python specific notes are needed. So go through the description
- carefully and make necessary changes. A common practice is to add
- comments for a summary of adjustments like this:
-
- // Modifications:
- // NULL->None
- // - removed notes about derived classes (which doesn't apply for python)
- const char* const ZoneWriter_doc = "\
- ...
- ";
- This note will help next time you need to auto-generate and edit the
- template (probably because the original C++ document is updated).
-
- You can simply copy this part to the main C++ wrapper file, but since
- it's relatively large a common practice is to maintain it in a separate
- file that is exclusively included from the main file: if the name of
- the main file is zonewriter_python.cc, the pydoc strings would be copied
- in zonewriter_python_inc.cc, and the main file would have this line:
-
- #include "zonewriter_inc.cc"
-
- (In case you are C++ language police: it's okay to use the unnamed
- name space for a file to be included because it's essentially a part
- of the single .cc file, not expected to be included by others).
-
- In either case, the ifdef-ed part should be removed.
-
-ADVANCED FEATURES
-
-You can use a special "xmlonly" doxygen command in C++ doxygent document
-in order to include Python code excerpt (while hiding it from the doxygen
-output for the C++ version). This command will be converted to
-a special XML tag in the XML output.
-
-The block enclosed by \xmlonly and \endxmlonly should contain
-a verbatim XML tag named "pythonlisting", in which the python code should
-be placed.
-/// \code
-/// Name name("example.com");
-/// std::cout << name.toText() << std::endl;
-/// \endcode
-///
-/// \xmlonly <pythonlisting>
-/// name = Name("example.com")
-/// print(name.to_text())
-/// </pythonlisting> \endxmlonly
-
-Note that there must be a blank line between \endcode and \xmlonly.
-doxygen2pydoc assume the pythonlisting tag is in a separate <para> node.
-This blank ensures doxygen will produce the XML file that meets the
-assumption.
-
-INTERNAL MEMO (incomplete, and not very unredable yet)
-
-This simplified utility assumes the following structure:
-...
- <compounddef ...>
- <compoundname>isc::dns::TSIGError</compoundname>
- <sectiondef kind="user-defined">
- constructor, destructor
- </sectiondef>
- <sectiondef kind="public-type">
- ..
- </sectiondef>
- <sectiondef kind="public-func">
- <memberdef kind="function"...>
- <type>return type (if any)</type>
- <argsstring>(...) [const]</argsstring>
- <name>method name</name>
- <briefdescription>method's brief description</briefdescription>
- <detaileddescription>
- <para>...</para>...
- <para>
- <parameterlist kind="exception">
- <parameteritem>
- <parameternamelist>
- <parametername>Exception name</parametername>
- </parameternamelist>
- <parameterdescription>
- <para>exception desc</para>
- </parameterdescription>
- </parameteritem>
- </parameterlist>
- <parameterlist kind="param">
- <parameteritem>
- <parameternamelist>
- <parametername>param name</parametername>
- </parameternamelist>
- <parameterdescription>
- <para>param desc</para>
- </parameterdescription>
- </parameteritem>
- ...
- </parameterlist>
- <simplesect kind="return">Return value</simplesect>
- </para>
- </detaileddescription>
- </memberdef>
- </sectiondef>
- <sectiondef kind="public-static-attrib|user-defined">
- <memberdef kind="variable"...>
- <name>class-specific-constant</name>
- <initializer>value</initializer>
- <brief|detaileddescription>paragraph(s)</brief|detaileddescription>
- </sectiondef>
- <briefdescription>
- class's brief description
- </briefdescription>
- <detaileddescription>
- class's detailed description
- </detaileddescription>
- </compounddef>
-"""
-
-import re, string, sys, textwrap
-from xml.dom.minidom import parse
-from textwrap import fill, dedent, TextWrapper
-
-camel_replacements = {}
-member_functions = []
-constructors = []
-class_variables = []
-
-RE_CAMELTERM = re.compile('([\s\.]|^)[a-z]+[A-Z]\S*')
-RE_SIMPLECAMEL = re.compile("([a-z])([A-Z])")
-RE_CAMELAFTERUPPER = re.compile("([A-Z])([A-Z])([a-z])")
-
-class Paragraph:
- TEXT = 0
- ITEMIZEDLIST = 1
- CPPLISTING = 2
- PYLISTING = 3
- VERBATIM = 4
-
- def __init__(self, xml_node):
- if len(xml_node.getElementsByTagName("pythonlisting")) > 0:
- self.type = self.PYLISTING
- self.text = re.sub("///", "", get_text(xml_node))
- elif len(xml_node.getElementsByTagName("verbatim")) > 0:
- self.type = self.VERBATIM
- self.text = get_text(xml_node)
- elif len(xml_node.getElementsByTagName("programlisting")) > 0:
- # We ignore node containing a "programlisting" tag.
- # They are C++ example code, and we are not interested in them
- # in pydoc.
- self.type = self.CPPLISTING
- elif len(xml_node.getElementsByTagName("itemizedlist")) > 0:
- self.type = self.ITEMIZEDLIST
- self.items = []
- for item in xml_node.getElementsByTagName("listitem"):
- self.items.append(get_text(item))
- else:
- self.type = self.TEXT
-
- # A single textual paragraph could have multiple simple sections
- # if it contains notes.
-
- self.texts = []
- subnodes = []
- for child in xml_node.childNodes:
- if child.nodeType == child.ELEMENT_NODE and \
- child.nodeName == 'simplesect' and \
- child.getAttribute('kind') == 'note':
- if len(subnodes) > 0:
- self.texts.append(get_text_fromnodelist(subnodes))
- subnodes = []
- subtext = 'Note: '
- for t in child.childNodes:
- subtext += get_text(t)
- self.texts.append(subtext)
- else:
- subnodes.append(child)
- if len(subnodes) > 0:
- self.texts.append(get_text_fromnodelist(subnodes))
-
- def dump(self, f, wrapper):
- if self.type == self.CPPLISTING:
- return
- elif self.type == self.ITEMIZEDLIST:
- for item in self.items:
- item_wrapper = TextWrapper(\
- initial_indent=wrapper.initial_indent + "- ",
- subsequent_indent=wrapper.subsequent_indent + " ")
- dump_filled_text(f, item_wrapper, item)
- f.write("\\n\\\n")
- elif self.type == self.TEXT:
- for text in self.texts:
- if text != self.texts[0]:
- f.write("\\n\\\n")
- dump_filled_text(f, wrapper, text)
- f.write("\\n\\\n")
- else:
- dump_filled_text(f, None, self.text)
- f.write("\\n\\\n")
- f.write("\\n\\\n")
-
-class NamedItem:
- def __init__(self, name, desc):
- self.name = name
- self.desc = desc
-
- def dump(self, f, wrapper):
- # we use deeper indent inside the item list.
- new_initial_indent = wrapper.initial_indent + " " * 2
- new_subsequent_indent = wrapper.subsequent_indent + " " * (2 + 11)
- local_wrapper = TextWrapper(initial_indent=new_initial_indent,
- subsequent_indent=new_subsequent_indent)
-
- # concatenate name and description with a fixed width (up to 10 chars)
- # for the name, and wrap the entire text, then dump it to file.
- dump_filled_text(f, local_wrapper, "%-10s %s" % (self.name, self.desc))
- f.write("\\n\\\n")
-
-class FunctionDefinition:
- # function types
- CONSTRUCTOR = 0
- COPY_CONSTRUCTOR = 1
- DESTRUCTOR = 2
- ASSIGNMENT_OP = 3
- OTHER = 4
-
- def __init__(self):
- self.type = self.OTHER
- self.name = None
- self.pyname = None
- self.args = ""
- self.ret_type = None
- self.brief_desc = None
- self.detailed_desc = []
- self.exceptions = []
- self.parameters = []
- self.returns = None
- self.have_param = False
-
- def dump_doc(self, f, wrapper=TextWrapper()):
- f.write(self.pyname + "(" + self.args + ")")
- if self.ret_type is not None:
- f.write(" -> " + self.ret_type)
- f.write("\\n\\\n\\n\\\n")
-
- if self.brief_desc is not None:
- dump_filled_text(f, wrapper, self.brief_desc)
- f.write("\\n\\\n\\n\\\n")
-
- for para in self.detailed_desc:
- para.dump(f, wrapper)
-
- if len(self.exceptions) > 0:
- f.write(wrapper.fill("Exceptions:") + "\\n\\\n")
- for ex_desc in self.exceptions:
- ex_desc.dump(f, wrapper)
- f.write("\\n\\\n")
- if len(self.parameters) > 0:
- f.write(wrapper.fill("Parameters:") + "\\n\\\n")
- for param_desc in self.parameters:
- param_desc.dump(f, wrapper)
- f.write("\\n\\\n")
- if self.returns is not None:
- dump_filled_text(f, wrapper, "Return Value(s): " + self.returns)
- f.write("\\n\\\n")
-
- def dump_pymethod_def(self, f, class_name):
- f.write(' { "' + self.pyname + '", ')
- f.write(class_name + '_' + self.name + ', ')
- if len(self.parameters) == 0:
- f.write('METH_NOARGS,\n')
- else:
- f.write('METH_VARARGS,\n')
- f.write(' ' + class_name + '_' + self.name + '_doc },\n')
-
-class VariableDefinition:
- def __init__(self, nodelist):
- self.value = None
- self.brief_desc = None
- self.detailed_desc = []
-
- for node in nodelist:
- if node.nodeName == "name":
- self.name = get_text(node)
- elif node.nodeName == "initializer":
- self.value = get_text(node)
- elif node.nodeName == "briefdescription":
- self.brief_desc = get_text(node)
- elif node.nodeName == "detaileddescription":
- for para in node.childNodes:
- if para.nodeName != "para":
- # ignore surrounding empty nodes
- continue
- self.detailed_desc.append(Paragraph(para))
-
- def dump_doc(self, f, wrapper=TextWrapper()):
- name_value = self.name
- if self.value is not None:
- name_value += ' = ' + self.value
- dump_filled_text(f, wrapper, name_value)
- f.write('\\n\\\n')
-
- desc_initial_indent = wrapper.initial_indent + " "
- desc_subsequent_indent = wrapper.subsequent_indent + " "
- desc_wrapper = TextWrapper(initial_indent=desc_initial_indent,
- subsequent_indent=desc_subsequent_indent)
- if self.brief_desc is not None:
- dump_filled_text(f, desc_wrapper, self.brief_desc)
- f.write("\\n\\\n\\n\\\n")
-
- for para in self.detailed_desc:
- para.dump(f, desc_wrapper)
-
-def dump_filled_text(f, wrapper, text):
- """Fill given text using wrapper, and dump it to the given file
- appending an escaped CR at each end of line.
- """
- filled_text = wrapper.fill(text) if wrapper is not None else text
- f.write("".join(re.sub("\n", r"\\n\\\n", filled_text)))
-
-def camel_to_lowerscores(matchobj):
- oldtext = matchobj.group(0)
- newtext = re.sub(RE_SIMPLECAMEL, r"\1_\2", oldtext)
- newtext = re.sub(RE_CAMELAFTERUPPER, r"\1_\2\3", newtext)
- newtext = newtext.lower()
- camel_replacements[oldtext] = newtext
- return newtext.lower()
-
-def cpp_to_python(text):
- text = text.replace("::", ".")
- text = text.replace('"', '\\"')
-
- # convert camelCase to "_"-concatenated format
- # (e.g. getLength -> get_length)
- return re.sub(RE_CAMELTERM, camel_to_lowerscores, text)
-
-def convert_type_name(type_name):
- """Convert C++ type name to python type name using common conventions"""
- # strip off leading 'const' and trailing '&/*'
- type_name = re.sub("^const\S*", "", type_name)
- type_name = re.sub("\S*[&\*]$", "", type_name)
-
- # We often typedef smart pointers as [Const]TypePtr. Convert them to
- # just "Type"
- type_name = re.sub("^Const", "", type_name)
- type_name = re.sub("Ptr$", "", type_name)
-
- if type_name == "std::string":
- return "string"
- if re.search(r"(int\d+_t|size_t)", type_name):
- return "integer"
- return type_name
-
-def get_text(root, do_convert=True):
- """Recursively extract bare text inside the specified node (root),
- concatenate all extracted text and return the result.
- """
- nodelist = root.childNodes
- rc = []
- for node in nodelist:
- if node.nodeType == node.TEXT_NODE:
- if do_convert:
- rc.append(cpp_to_python(node.data))
- else:
- rc.append(node.data)
- elif node.nodeType == node.ELEMENT_NODE:
- rc.append(get_text(node))
- # return the result, removing any leading newlines (that often happens for
- # brief descriptions, which will cause lines not well aligned)
- return re.sub("^(\n*)", "", ''.join(rc))
-
-def get_text_fromnodelist(nodelist, do_convert=True):
- """Recursively extract bare text inside the specified node (root),
- concatenate all extracted text and return the result.
- """
- rc = []
- for node in nodelist:
- if node.nodeType == node.TEXT_NODE:
- if do_convert:
- rc.append(cpp_to_python(node.data))
- else:
- rc.append(node.data)
- elif node.nodeType == node.ELEMENT_NODE:
- rc.append(get_text(node))
- # return the result, removing any leading newlines (that often happens for
- # brief descriptions, which will cause lines not well aligned)
- return re.sub("^(\n*)", "", ''.join(rc))
-
-def parse_parameters(nodelist):
- rc = []
- for node in nodelist:
- if node.nodeName != "parameteritem":
- continue
- # for simplicity, we assume one parametername and one
- # parameterdescription for each parameter.
- name = get_text(node.getElementsByTagName("parametername")[0])
- desc = get_text(node.getElementsByTagName("parameterdescription")[0])
- rc.append(NamedItem(name, desc))
- return rc
-
-def parse_function_description(func_def, nodelist):
- for node in nodelist:
- # nodelist contains beginning and ending empty text nodes.
- # ignore them (otherwise they cause disruption below).
- if node.nodeName != "para":
- continue
-
- if node.getElementsByTagName("parameterlist"):
- # within this node there may be exception list, parameter list,
- # and description for return value. parse and store them
- # seprately.
- for paramlist in node.getElementsByTagName("parameterlist"):
- if paramlist.getAttribute("kind") == "exception":
- func_def.exceptions = \
- parse_parameters(paramlist.childNodes)
- elif paramlist.getAttribute("kind") == "param":
- func_def.parameters = \
- parse_parameters(paramlist.childNodes)
- if node.getElementsByTagName("simplesect"):
- simplesect = node.getElementsByTagName("simplesect")[0]
- if simplesect.getAttribute("kind") == "return":
- func_def.returns = get_text(simplesect)
- else:
- # for normal text, python listing and itemized list, append them
- # to the list of paragraphs
- func_def.detailed_desc.append(Paragraph(node))
-
-def parse_function(func_def, class_name, nodelist):
- for node in nodelist:
- if node.nodeName == "name":
- func_def.name = get_text(node, False)
- func_def.pyname = cpp_to_python(func_def.name)
- elif node.nodeName == "argsstring":
- # extract parameter names only, assuming they immediately follow
- # their type name + space, and are immeidatelly followed by
- # either "," or ")". If it's a pointer or reference, */& is
- # prepended to the parameter name without a space:
- # e.g. (int var1, char *var2, Foo &var3)
- args = get_text(node, False)
- # extract parameter names, possibly with */&
- func_def.args = ', '.join(re.findall(r"\s(\S+)[,)]", args))
- # then remove any */& symbols
- func_def.args = re.sub("[\*&]", "", func_def.args)
- elif node.nodeName == "type" and node.hasChildNodes():
- func_def.ret_type = convert_type_name(get_text(node, False))
- elif node.nodeName == "param":
- func_def.have_param = True
- elif node.nodeName == "briefdescription":
- func_def.brief_desc = get_text(node)
- elif node.nodeName == "detaileddescription":
- parse_function_description(func_def, node.childNodes)
- # identify the type of function using the name and arg
- if func_def.name == class_name and \
- re.search("^\(const " + class_name + " &[^,]*$", args):
- # This function is ClassName(const ClassName& param), which is
- # the copy constructor.
- func_def.type = func_def.COPY_CONSTRUCTOR
- elif func_def.name == class_name:
- # if it's not the copy ctor but the function name == class name,
- # it's a constructor.
- func_def.type = func_def.CONSTRUCTOR
- elif func_def.name == "~" + class_name:
- func_def.type = func_def.DESTRUCTOR
- elif func_def.name == "operator=":
- func_def.type = func_def.ASSIGNMENT_OP
-
- # register the definition to the approriate list
- if func_def.type == func_def.CONSTRUCTOR:
- constructors.append(func_def)
- elif func_def.type == func_def.OTHER:
- member_functions.append(func_def)
-
-def parse_functions(class_name, nodelist):
- for node in nodelist:
- if node.nodeName == "memberdef" and \
- node.getAttribute("kind") == "function":
- func_def = FunctionDefinition()
- parse_function(func_def, class_name, node.childNodes)
-
-def parse_class_variables(class_name, nodelist):
- for node in nodelist:
- if node.nodeName == "memberdef" and \
- node.getAttribute("kind") == "variable":
- class_variables.append(VariableDefinition(node.childNodes))
-
-def dump(f, class_name, class_brief_doc, class_detailed_doc):
- f.write("namespace {\n")
-
- f.write('#ifdef COPY_THIS_TO_MAIN_CC\n')
- for func in member_functions:
- func.dump_pymethod_def(f, class_name)
- f.write('#endif // COPY_THIS_TO_MAIN_CC\n\n')
-
- f.write("const char* const " + class_name + '_doc = "\\\n')
- if class_brief_doc is not None:
- f.write("".join(re.sub("\n", r"\\n\\\n", fill(class_brief_doc))))
- f.write("\\n\\\n")
- f.write("\\n\\\n")
- if len(class_detailed_doc) > 0:
- for para in class_detailed_doc:
- para.dump(f, wrapper=TextWrapper())
-
- # dump constructors
- for func in constructors:
- indent = " " * 4
- func.dump_doc(f, wrapper=TextWrapper(initial_indent=indent,
- subsequent_indent=indent))
-
- # dump class variables
- if len(class_variables) > 0:
- f.write("Class constant data:\\n\\\n")
- for var in class_variables:
- var.dump_doc(f)
-
- f.write("\";\n")
-
- for func in member_functions:
- f.write("\n")
- f.write("const char* const " + class_name + "_" + func.name + \
- "_doc = \"\\\n");
- func.dump_doc(f)
- f.write("\";\n")
-
- f.write("} // unnamed namespace") # close namespace
-
-if __name__ == '__main__':
- dom = parse(sys.argv[1])
- class_elements = dom.getElementsByTagName("compounddef")[0].childNodes
- class_brief_doc = None
- class_detailed_doc = []
- for node in class_elements:
- if node.nodeName == "compoundname":
- # class name is the last portion of the period-separated fully
- # qualified class name. (this should exist)
- class_name = re.split("\.", get_text(node))[-1]
- if node.nodeName == "briefdescription":
- # we assume a brief description consists at most one para
- class_brief_doc = get_text(node)
- elif node.nodeName == "detaileddescription":
- # a detaild description consists of one or more paragraphs
- for para in node.childNodes:
- if para.nodeName != "para": # ignore surrounding empty nodes
- continue
- class_detailed_doc.append(Paragraph(para))
- elif node.nodeName == "sectiondef" and \
- node.getAttribute("kind") == "public-func":
- parse_functions(class_name, node.childNodes)
- elif node.nodeName == "sectiondef" and \
- node.getAttribute("kind") == "public-static-attrib":
- parse_class_variables(class_name, node.childNodes)
- elif node.nodeName == "sectiondef" and \
- node.getAttribute("kind") == "user-defined":
- # there are two possiblities: functions and variables
- for child in node.childNodes:
- if child.nodeName != "memberdef":
- continue
- if child.getAttribute("kind") == "function":
- parse_function(FunctionDefinition(), class_name,
- child.childNodes)
- elif child.getAttribute("kind") == "variable":
- class_variables.append(VariableDefinition(child.childNodes))
-
- dump(sys.stdout, class_name, class_brief_doc, class_detailed_doc)
-
- if len(camel_replacements) > 0:
- sys.stderr.write("Replaced camelCased terms:\n")
- for oldterm in camel_replacements.keys():
- sys.stderr.write("%s => %s\n" % (oldterm,
- camel_replacements[oldterm]))
diff --git a/src/lib/util/python/mkpywrapper.py.in b/src/lib/util/python/mkpywrapper.py.in
deleted file mode 100755
index 4bf7752edd..0000000000
--- a/src/lib/util/python/mkpywrapper.py.in
+++ /dev/null
@@ -1,100 +0,0 @@
-#!@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)
diff --git a/src/lib/util/python/pycppwrapper_util.h b/src/lib/util/python/pycppwrapper_util.h
deleted file mode 100644
index 31041baa3e..0000000000
--- a/src/lib/util/python/pycppwrapper_util.h
+++ /dev/null
@@ -1,335 +0,0 @@
-// 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.
-///
-/// In some other cases, it would be convenient if it's possible to create
-/// an "empty" container and reset it with a Python object later.
-/// For example, we may want to create a temporary Python object in the
-/// middle of a function and make sure that it's valid within the rest of
-/// the function scope, while we want to make sure its reference is released
-/// when the function returns (either normally or as a result of exception).
-/// To allow this scenario, this class defines the default constructor
-/// and the \c reset() method. The default constructor allows the class
-/// object with an "empty" (NULL) Python object, while \c reset() allows
-/// the stored object to be replaced with a new one. If there's a valid
-/// object was already set, \c reset() releases its reference.
-/// In general, it's safer to construct the container object with a valid
-/// Python object pointer. The use of the default constructor and
-/// \c reset() should therefore be restricted to cases where it's
-/// absolutely necessary.
-///
-/// 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() : obj_(NULL) {}
- PyObjectContainer(PyObject* obj) : obj_(obj) {
- if (obj_ == NULL) {
- isc_throw(PyCPPWrapperException, "Unexpected NULL PyObject, "
- "probably due to short memory");
- }
- }
- ~PyObjectContainer() {
- if (obj_ != NULL) {
- Py_DECREF(obj_);
- }
- }
- void reset(PyObject* obj) {
- if (obj == NULL) {
- isc_throw(PyCPPWrapperException, "Unexpected NULL PyObject, "
- "probably due to short memory");
- }
- if (obj_ != NULL) {
- Py_DECREF(obj_);
- }
- obj_ = 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 {
- explicit 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/pythonize_constants.py b/src/lib/util/python/pythonize_constants.py
deleted file mode 100644
index cc6d9b23ff..0000000000
--- a/src/lib/util/python/pythonize_constants.py
+++ /dev/null
@@ -1,57 +0,0 @@
-# Copyright (C) 2013 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 script takes a C++ file with constants and converts it to a python
-module. However, the syntax it parses is very limited (it doesn't understand
-C++ at all, it just looks for lines containing the equal sign and strips
-what it thinks might be type).
-
-The purpose is to keep the same values of constants in C++ and python. This
-saves the work of keeping the constants in sync manually and is less error
-prone.
-'''
-
-import sys
-import re
-
-if len(sys.argv) != 3:
- sys.stderr.write("Usage: python3 ./pythonize_constants.py input.cc output.py\n")
- sys.exit(1)
-
-[filename_in, filename_out] = sys.argv[1:3]
-
-# Ignore preprocessor, namespaces and the ends of namespaces.
-ignore = re.compile('^(#|namespace|})')
-comment = re.compile('^//(.*)')
-constant = re.compile('^[a-zA-Z].*?([a-zA-Z_0-9]+\\s*=.*);')
-
-with open(filename_in) as file_in, open(filename_out, "w") as file_out:
- file_out.write("# This file is generated from " + filename_in + "\n" +
- "# by the pythonize_constants.py script.\n" +
- "# Do not edit, all changes will be lost.\n\n")
- for line in file_in:
- if ignore.match(line):
- continue
- # Mangle comments to be python-like
- line = comment.sub('#\\1', line)
- # Extract the constant.
-
- # TODO: We may want to do something with the true vs. True and
- # NULL vs. None and such. Left out for now, since none are in
- # the input file currently.
- line = constant.sub('\\1', line)
-
- file_out.write(line)
diff --git a/src/lib/util/python/wrapper_template.cc b/src/lib/util/python/wrapper_template.cc
deleted file mode 100644
index 780e69521c..0000000000
--- a/src/lib/util/python/wrapper_template.cc
+++ /dev/null
@@ -1,291 +0,0 @@
-// 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.
-
-// Enable this if you use s# variants with PyArg_ParseTuple(), see
-// http://docs.python.org/py3k/c-api/arg.html#strings-and-buffers
-//#define PY_SSIZE_T_CLEAN
-
-// Python.h needs to be placed at the head of the program file, see:
-// http://docs.python.org/py3k/extending/extending.html#a-simple-example
-#include <Python.h>
-
-#include <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;
-
-//
-// @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;
-
-@REMOVE_THIS_ON_RELEASE@
-// 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(PyObject* po_self, PyObject* args, PyObject*) {
- s_@CPPCLASS@* self = static_cast<s_@CPPCLASS@*>(po_self);
- try {
- if (PyArg_ParseTuple(args, "REPLACE ME")) {
- @REMOVE_THIS_ON_RELEASE@
- // 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(PyExc_SystemError, "Unexpected C++ exception");
- return (-1);
- }
-
- @REMOVE_THIS_ON_RELEASE@
- // If we are here PyArg_ParseTuple() failed and TypeError should have
- // been set. If the constructor is more complicated and the control
- // could reach this point for other reasons, an appropriate Python
- // exception should be set by PyErr_SetString.
-
- return (-1);
-}
-
-@REMOVE_THIS_ON_RELEASE@
-// 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(PyObject* po_self) {
- s_@CPPCLASS@* self = static_cast<s_@CPPCLASS@*>(po_self);
- delete self->cppobj;
- self->cppobj = NULL;
- Py_TYPE(self)->tp_free(self);
-}
-
-@REMOVE_THIS_ON_RELEASE@
-// This should be able to be used without modification as long as the
-// underlying C++ class has toText().
-PyObject*
-@CPPCLASS@_toText(PyObject* po_self) {
- const s_@CPPCLASS@* self = static_cast<const s_@CPPCLASS@*>(po_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*>("")));
-}
-
-@REMOVE_THIS_ON_RELEASE@
-// This is quite specific isc.dns. For other wrappers this should probably
-// be removed.
-PyObject* @CPPCLASS@_toWire(PyObject* self, PyObject* args) {
-}
-
-PyObject*
-@CPPCLASS@_richcmp(PyObject* po_self, PyObject* po_other, const int op) {
- const s_@CPPCLASS@* const self = static_cast<const s_@CPPCLASS@*>(po_self);
- const s_@CPPCLASS@* const other =
- static_cast<const s_@CPPCLASS@*>(po_other);
-
- 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;
- }
-}
-
-// 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", @CPPCLASS@_toText, METH_NOARGS,
- @CPPCLASS@_toText_doc },
-
- @REMOVE_THIS_ON_RELEASE@
- // This is quite specific isc.dns. For other wrappers this should probably
- // be removed:
- { "to_wire", @CPPCLASS@_toWire, METH_VARARGS,
- @CPPCLASS@_toWire_doc },
- { NULL, NULL, 0, NULL }
-};
-} // 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)
- "@MODULE@.@CPPCLASS@",
- sizeof(s_@CPPCLASS@), // tp_basicsize
- 0, // tp_itemsize
- @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
- @CPPCLASS@_doc,
- NULL, // tp_traverse
- NULL, // tp_clear
- // THIS MAY HAVE TO BE CHANGED TO NULL:
- @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
- @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);
-
- @REMOVE_THIS_ON_RELEASE@
- // 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);
-}
-
-PyObject*
-create@CPPCLASS@Object(const @CPPCLASS@& source) {
- @CPPCLASS@Container container(PyObject_New(s_@CPPCLASS@,
- &@cppclass@_type));
- container.set(new @CPPCLASS@(source));
- return (container.release());
-}
-} // namespace python
-} // namespace @MODULE@
-} // namespace isc
diff --git a/src/lib/util/python/wrapper_template.h b/src/lib/util/python/wrapper_template.h
deleted file mode 100644
index 787a2969e2..0000000000
--- a/src/lib/util/python/wrapper_template.h
+++ /dev/null
@@ -1,59 +0,0 @@
-// 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);
-
-// Note: this utility function works only when @CPPCLASS@ is a copy
-// constructable.
-// And, it would only be useful when python binding needs to create this
-// object frequently. Otherwise, it would (or should) probably be better to
-// remove the declaration and definition of this function.
-//
-/// This is a simple shortcut to create a python @CPPCLASS@ object (in the
-/// form of a pointer to PyObject) with minimal exception safety.
-/// On success, it returns a valid pointer to PyObject with a reference
-/// counter of 1; if something goes wrong it throws an exception (it never
-/// returns a NULL pointer).
-/// This function is expected to be called within a try block
-/// followed by necessary setup for python exception.
-PyObject* create@CPPCLASS@Object(const @CPPCLASS@& source);
-
-} // namespace python
-} // namespace @MODULE@
-} // namespace isc
-#endif // PYTHON_@CPPCLASS@_H
-
-// Local Variables:
-// mode: c++
-// End:
diff --git a/src/lib/util/pyunittests/Makefile.am b/src/lib/util/pyunittests/Makefile.am
deleted file mode 100644
index 5ab868d852..0000000000
--- a/src/lib/util/pyunittests/Makefile.am
+++ /dev/null
@@ -1,22 +0,0 @@
-AM_CPPFLAGS = -I$(top_srcdir)/src/lib -I$(top_builddir)/src/lib
-AM_CPPFLAGS += $(BOOST_INCLUDES)
-AM_CXXFLAGS = $(B10_CXXFLAGS)
-
-noinst_LTLIBRARIES = pyunittests_util.la
-
-pyunittests_util_la_SOURCES = pyunittests_util.cc
-pyunittests_util_la_CPPFLAGS = $(AM_CPPFLAGS) $(PYTHON_INCLUDES)
-pyunittests_util_la_LDFLAGS = $(PYTHON_LDFLAGS)
-# Note: PYTHON_CXXFLAGS may have some -Wno... workaround, which must be
-# placed after -Wextra defined in AM_CXXFLAGS
-pyunittests_util_la_CXXFLAGS = $(AM_CXXFLAGS) $(PYTHON_CXXFLAGS)
-
-# Python prefers .so, while some OSes (specifically MacOS) use a different
-# suffix for dynamic objects. -module is necessary to work this around.
-pyunittests_util_la_LDFLAGS += -module -avoid-version
-pyunittests_util_la_LIBADD = $(top_builddir)/src/lib/util/libkea-util.la
-pyunittests_util_la_LIBADD += $(PYTHON_LIB)
-
-# hack to trigger libtool to not create a convenience archive,
-# resulting in shared modules
-pyunittests_util_la_LDFLAGS += -rpath /nowhere
diff --git a/src/lib/util/pyunittests/pyunittests_util.cc b/src/lib/util/pyunittests/pyunittests_util.cc
deleted file mode 100644
index d266c84404..0000000000
--- a/src/lib/util/pyunittests/pyunittests_util.cc
+++ /dev/null
@@ -1,84 +0,0 @@
-// 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 <stdint.h>
-
-// see util/time_utilities.h
-namespace isc {
-namespace util {
-namespace detail {
-extern int64_t (*gettimeFunction)();
-}
-}
-}
-
-namespace {
-int64_t fake_current_time;
-
-int64_t
-getFakeTime() {
- return (fake_current_time);
-}
-
-PyObject*
-fixCurrentTime(PyObject*, PyObject* args) {
- PyObject* maybe_none;
- if (PyArg_ParseTuple(args, "L", &fake_current_time)) {
- isc::util::detail::gettimeFunction = getFakeTime;
- } else if (PyArg_ParseTuple(args, "O", &maybe_none) &&
- maybe_none == Py_None) {
- isc::util::detail::gettimeFunction = NULL;
- } else {
- PyErr_SetString(PyExc_TypeError, "Invalid arguments to "
- "pyunittests_util.fix_current_time");
- return (NULL);
- }
-
- PyErr_Clear();
- Py_RETURN_NONE;
-}
-
-PyMethodDef PyUnittestsUtilMethods[] = {
- { "fix_current_time", fixCurrentTime, METH_VARARGS,
- "Fix the current system time at the specified (fake) value.\n\n"
- "This is useful for testing modules that depend on the current time.\n"
- "Note that it only affects C++ modules that use gettimeWrapper() "
- "defined in libutil, which allows a hook for testing.\n"
- "If an integer (signed 64bit) is given, the current time will be fixed "
- "to that value; if None is specified (which is the default) the use of "
- "faked time will be canceled."
- },
- { NULL, NULL, 0, NULL}
-};
-
-PyModuleDef pyunittests_util = {
- { PyObject_HEAD_INIT(NULL) NULL, 0, NULL},
- "pyunittests_util",
- "This module is a collection of utilities useful for testing "
- "the BIND 10 C++ binding modules.",
- -1,
- PyUnittestsUtilMethods,
- NULL,
- NULL,
- NULL,
- NULL
-};
-} // end of unnamed namespace
-
-PyMODINIT_FUNC
-PyInit_pyunittests_util(void) {
- return (PyModule_Create(&pyunittests_util));
-}