Merge branch 'collectd-5.5'
[collectd.git] / src / pyvalues.c
index e25353e..1097f7f 100644 (file)
@@ -1,3 +1,29 @@
+/**
+ * collectd - src/pyvalues.c
+ * Copyright (C) 2009  Sven Trenkel
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *
+ * Authors:
+ *   Sven Trenkel <collectd at semidefinite.de>  
+ **/
+
 #include <Python.h>
 #include <structmember.h>
 
 
 #include "cpython.h"
 
-static char time_doc[] = "This is the Unix timestap of the time this value was read.\n"
+#define FreeAll() do {\
+       PyMem_Free(type);\
+       PyMem_Free(plugin_instance);\
+       PyMem_Free(type_instance);\
+       PyMem_Free(plugin);\
+       PyMem_Free(host);\
+} while(0)
+
+static PyObject *cpy_common_repr(PyObject *s) {
+       PyObject *ret, *tmp;
+       static PyObject *l_type = NULL, *l_type_instance = NULL, *l_plugin = NULL, *l_plugin_instance = NULL;
+       static PyObject *l_host = NULL, *l_time = NULL;
+       PluginData *self = (PluginData *) s;
+       
+       if (l_type == NULL)
+               l_type = cpy_string_to_unicode_or_bytes("(type=");
+       if (l_type_instance == NULL)
+               l_type_instance = cpy_string_to_unicode_or_bytes(",type_instance=");
+       if (l_plugin == NULL)
+               l_plugin = cpy_string_to_unicode_or_bytes(",plugin=");
+       if (l_plugin_instance == NULL)
+               l_plugin_instance = cpy_string_to_unicode_or_bytes(",plugin_instance=");
+       if (l_host == NULL)
+               l_host = cpy_string_to_unicode_or_bytes(",host=");
+       if (l_time == NULL)
+               l_time = cpy_string_to_unicode_or_bytes(",time=");
+       
+       if (!l_type || !l_type_instance || !l_plugin || !l_plugin_instance || !l_host || !l_time)
+               return NULL;
+       
+       ret = cpy_string_to_unicode_or_bytes(s->ob_type->tp_name);
+
+       CPY_STRCAT(&ret, l_type);
+       tmp = cpy_string_to_unicode_or_bytes(self->type);
+       CPY_SUBSTITUTE(PyObject_Repr, tmp, tmp);
+       CPY_STRCAT_AND_DEL(&ret, tmp);
+
+       if (self->type_instance[0] != 0) {
+               CPY_STRCAT(&ret, l_type_instance);
+               tmp = cpy_string_to_unicode_or_bytes(self->type_instance);
+               CPY_SUBSTITUTE(PyObject_Repr, tmp, tmp);
+               CPY_STRCAT_AND_DEL(&ret, tmp);
+       }
+
+       if (self->plugin[0] != 0) {
+               CPY_STRCAT(&ret, l_plugin);
+               tmp = cpy_string_to_unicode_or_bytes(self->plugin);
+               CPY_SUBSTITUTE(PyObject_Repr, tmp, tmp);
+               CPY_STRCAT_AND_DEL(&ret, tmp);
+       }
+
+       if (self->plugin_instance[0] != 0) {
+               CPY_STRCAT(&ret, l_plugin_instance);
+               tmp = cpy_string_to_unicode_or_bytes(self->plugin_instance);
+               CPY_SUBSTITUTE(PyObject_Repr, tmp, tmp);
+               CPY_STRCAT_AND_DEL(&ret, tmp);
+       }
+
+       if (self->host[0] != 0) {
+               CPY_STRCAT(&ret, l_host);
+               tmp = cpy_string_to_unicode_or_bytes(self->host);
+               CPY_SUBSTITUTE(PyObject_Repr, tmp, tmp);
+               CPY_STRCAT_AND_DEL(&ret, tmp);
+       }
+
+       if (self->time != 0) {
+               CPY_STRCAT(&ret, l_time);
+               tmp = PyFloat_FromDouble(self->time);
+               CPY_SUBSTITUTE(PyObject_Repr, tmp, tmp);
+               CPY_STRCAT_AND_DEL(&ret, tmp);
+       }
+       return ret;
+}
+
+static char time_doc[] = "This is the Unix timestamp of the time this value was read.\n"
                "For dispatching values this can be set to 0 which means \"now\".\n"
                "This means the time the value is actually dispatched, not the time\n"
                "it was set to 0.";
 
 static char host_doc[] = "The hostname of the host this value was read from.\n"
                "For dispatching this can be set to an empty string which means\n"
-               "the local hostname as defined in the collectd.conf.";
+               "the local hostname as defined in collectd.conf.";
 
 static char type_doc[] = "The type of this value. This type has to be defined\n"
-               "in your types.db. Attempting to set it to any other value will\n"
-               "raise a TypeError exception.\n"
-               "Assigning a type is mandetory, calling dispatch without doing\n"
+               "in the types.db file. Attempting to set it to any other value\n"
+               "will raise a TypeError exception.\n"
+               "Assigning a type is mandatory, calling dispatch without doing\n"
                "so will raise a RuntimeError exception.";
 
 static char type_instance_doc[] = "";
@@ -29,7 +129,7 @@ static char plugin_doc[] = "The name of the plugin that read the data. Setting t
 static char plugin_instance_doc[] = "";
 
 static char PluginData_doc[] = "This is an internal class that is the base for Values\n"
-               "and Notification. It is pretty useless by itself and was therefore not\n"
+               "and Notification. It is pretty useless by itself and is therefore not\n"
                "exported to the collectd module.";
 
 static PyObject *PluginData_new(PyTypeObject *type, PyObject *args, PyObject *kwds) {
@@ -51,38 +151,45 @@ static PyObject *PluginData_new(PyTypeObject *type, PyObject *args, PyObject *kw
 static int PluginData_init(PyObject *s, PyObject *args, PyObject *kwds) {
        PluginData *self = (PluginData *) s;
        double time = 0;
-       const char *type = "", *plugin_instance = "", *type_instance = "", *plugin = "", *host = "";
+       char *type = NULL, *plugin_instance = NULL, *type_instance = NULL, *plugin = NULL, *host = NULL;
        static char *kwlist[] = {"type", "plugin_instance", "type_instance",
                        "plugin", "host", "time", NULL};
        
-       if (!PyArg_ParseTupleAndKeywords(args, kwds, "|sssssd", kwlist, &type,
-                       &plugin_instance, &type_instance, &plugin, &host, &time))
+       if (!PyArg_ParseTupleAndKeywords(args, kwds, "|etetetetetd", kwlist, NULL, &type,
+                       NULL, &plugin_instance, NULL, &type_instance, NULL, &plugin, NULL, &host, &time))
                return -1;
        
-       if (type[0] != 0 && plugin_get_ds(type) == NULL) {
+       if (type && plugin_get_ds(type) == NULL) {
                PyErr_Format(PyExc_TypeError, "Dataset %s not found", type);
+               FreeAll();
                return -1;
        }
 
-       sstrncpy(self->host, host, sizeof(self->host));
-       sstrncpy(self->plugin, plugin, sizeof(self->plugin));
-       sstrncpy(self->plugin_instance, plugin_instance, sizeof(self->plugin_instance));
-       sstrncpy(self->type, type, sizeof(self->type));
-       sstrncpy(self->type_instance, type_instance, sizeof(self->type_instance));
-       
+       sstrncpy(self->host, host ? host : "", sizeof(self->host));
+       sstrncpy(self->plugin, plugin ? plugin : "", sizeof(self->plugin));
+       sstrncpy(self->plugin_instance, plugin_instance ? plugin_instance : "", sizeof(self->plugin_instance));
+       sstrncpy(self->type, type ? type : "", sizeof(self->type));
+       sstrncpy(self->type_instance, type_instance ? type_instance : "", sizeof(self->type_instance));
        self->time = time;
+
+       FreeAll();
+
        return 0;
 }
 
 static PyObject *PluginData_repr(PyObject *s) {
-       PluginData *self = (PluginData *) s;
+       PyObject *ret;
+       static PyObject *l_closing = NULL;
        
-       return PyString_FromFormat("collectd.Values(type='%s%s%s%s%s%s%s%s%s',time=%lu)", self->type,
-                       *self->type_instance ? "',type_instance='" : "", self->type_instance,
-                       *self->plugin ? "',plugin='" : "", self->plugin,
-                       *self->plugin_instance ? "',plugin_instance='" : "", self->plugin_instance,
-                       *self->host ? "',host='" : "", self->host,
-                       (long unsigned) self->time);
+       if (l_closing == NULL)
+               l_closing = cpy_string_to_unicode_or_bytes(")");
+       
+       if (l_closing == NULL)
+               return NULL;
+       
+       ret = cpy_common_repr(s);
+       CPY_STRCAT(&ret, l_closing);
+       return ret;
 }
 
 static PyMemberDef PluginData_members[] = {
@@ -93,7 +200,7 @@ static PyMemberDef PluginData_members[] = {
 static PyObject *PluginData_getstring(PyObject *self, void *data) {
        const char *value = ((char *) self) + (intptr_t) data;
        
-       return PyString_FromString(value);
+       return cpy_string_to_unicode_or_bytes(value);
 }
 
 static int PluginData_setstring(PyObject *self, PyObject *value, void *data) {
@@ -104,10 +211,15 @@ static int PluginData_setstring(PyObject *self, PyObject *value, void *data) {
                PyErr_SetString(PyExc_TypeError, "Cannot delete this attribute");
                return -1;
        }
-       new = PyString_AsString(value);
-       if (new == NULL) return -1;
+       Py_INCREF(value);
+       new = cpy_unicode_or_bytes_to_string(&value);
+       if (new == NULL) {
+               Py_DECREF(value);
+               return -1;
+       }
        old = ((char *) self) + (intptr_t) data;
        sstrncpy(old, new, DATA_MAX_NAME_LEN);
+       Py_DECREF(value);
        return 0;
 }
 
@@ -119,16 +231,22 @@ static int PluginData_settype(PyObject *self, PyObject *value, void *data) {
                PyErr_SetString(PyExc_TypeError, "Cannot delete this attribute");
                return -1;
        }
-       new = PyString_AsString(value);
-       if (new == NULL) return -1;
+       Py_INCREF(value);
+       new = cpy_unicode_or_bytes_to_string(&value);
+       if (new == NULL) {
+               Py_DECREF(value);
+               return -1;
+       }
 
        if (plugin_get_ds(new) == NULL) {
                PyErr_Format(PyExc_TypeError, "Dataset %s not found", new);
+               Py_DECREF(value);
                return -1;
        }
 
        old = ((char *) self) + (intptr_t) data;
        sstrncpy(old, new, DATA_MAX_NAME_LEN);
+       Py_DECREF(value);
        return 0;
 }
 
@@ -142,8 +260,7 @@ static PyGetSetDef PluginData_getseters[] = {
 };
 
 PyTypeObject PluginDataType = {
-       PyObject_HEAD_INIT(NULL)
-       0,                         /* Always 0 */
+       CPY_INIT_TYPE
        "collectd.PluginData",     /* tp_name */
        sizeof(PluginData),        /* tp_basicsize */
        0,                         /* Will be filled in later */
@@ -195,13 +312,21 @@ static char interval_doc[] = "The interval is the timespan in seconds between tw
 static char values_doc[] = "These are the actual values that get dispatched to collectd.\n"
                "It has to be a sequence (a tuple or list) of numbers.\n"
                "The size of the sequence and the type of its content depend on the type\n"
-               "member your types.db file. For more information on this read the types.db\n"
-               "man page.\n"
+               "member in the types.db file. For more information on this read the\n"
+               "types.db man page.\n"
                "\n"
                "If the sequence does not have the correct size upon dispatch a RuntimeError\n"
                "exception will be raised. If the content of the sequence is not a number,\n"
                "a TypeError exception will be raised.";
 
+static char meta_doc[] = "These are the meta data for this Value object.\n"
+               "It has to be a dictionary of numbers, strings or bools. All keys must be\n"
+               "strings. int and long objects will be dispatched as signed integers unless\n"
+               "they are between 2**63 and 2**64-1, which will result in an unsigned integer.\n"
+               "You can force one of these storage classes by using the classes\n"
+               "collectd.Signed and collectd.Unsigned. A meta object received by a write\n"
+               "callback will always contain Signed or Unsigned objects.";
+
 static char dispatch_doc[] = "dispatch([type][, values][, plugin_instance][, type_instance]"
                "[, plugin][, host][, time][, interval]) -> None.  Dispatch a value list.\n"
                "\n"
@@ -215,7 +340,7 @@ static char dispatch_doc[] = "dispatch([type][, values][, plugin_instance][, typ
 static char write_doc[] = "write([destination][, type][, values][, plugin_instance][, type_instance]"
                "[, plugin][, host][, time][, interval]) -> None.  Dispatch a value list.\n"
                "\n"
-               "Write this instance to a single plugin or all plugins if 'destination' is obmitted.\n"
+               "Write this instance to a single plugin or all plugins if 'destination' is omitted.\n"
                "This will bypass the main collectd process and all filtering and caching.\n"
                "Other than that it works similar to 'dispatch'. In most cases 'dispatch' should be\n"
                "used instead of 'write'.\n";
@@ -230,32 +355,39 @@ static PyObject *Values_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
                return NULL;
        
        self->values = PyList_New(0);
+       self->meta = PyDict_New();
        self->interval = 0;
        return (PyObject *) self;
 }
 
 static int Values_init(PyObject *s, PyObject *args, PyObject *kwds) {
        Values *self = (Values *) s;
-       int interval = 0, ret;
-       double time = 0;
-       PyObject *values = NULL, *tmp;
-       const char *type = "", *plugin_instance = "", *type_instance = "", *plugin = "", *host = "";
+       double interval = 0, time = 0;
+       PyObject *values = NULL, *meta = NULL, *tmp;
+       char *type = NULL, *plugin_instance = NULL, *type_instance = NULL, *plugin = NULL, *host = NULL;
        static char *kwlist[] = {"type", "values", "plugin_instance", "type_instance",
-                       "plugin", "host", "time", "interval", NULL};
+                       "plugin", "host", "time", "interval", "meta", NULL};
        
-       if (!PyArg_ParseTupleAndKeywords(args, kwds, "|sOssssdi", kwlist,
-                       &type, &values, &plugin_instance, &type_instance,
-                       &plugin, &host, &time, &interval))
+       if (!PyArg_ParseTupleAndKeywords(args, kwds, "|etOetetetetddO", kwlist,
+                       NULL, &type, &values, NULL, &plugin_instance, NULL, &type_instance,
+                       NULL, &plugin, NULL, &host, &time, &interval, &meta))
                return -1;
        
-       tmp = Py_BuildValue("sssssd", type, plugin_instance, type_instance, plugin, host, time);
-       if (tmp == NULL)
-               return -1;
-       ret = PluginDataType.tp_init(s, tmp, NULL);
-       Py_DECREF(tmp);
-       if (ret != 0)
+       if (type && plugin_get_ds(type) == NULL) {
+               PyErr_Format(PyExc_TypeError, "Dataset %s not found", type);
+               FreeAll();
                return -1;
-       
+       }
+
+       sstrncpy(self->data.host, host ? host : "", sizeof(self->data.host));
+       sstrncpy(self->data.plugin, plugin ? plugin : "", sizeof(self->data.plugin));
+       sstrncpy(self->data.plugin_instance, plugin_instance ? plugin_instance : "", sizeof(self->data.plugin_instance));
+       sstrncpy(self->data.type, type ? type : "", sizeof(self->data.type));
+       sstrncpy(self->data.type_instance, type_instance ? type_instance : "", sizeof(self->data.type_instance));
+       self->data.time = time;
+
+       FreeAll();
+
        if (values == NULL) {
                values = PyList_New(0);
                PyErr_Clear();
@@ -263,81 +395,193 @@ static int Values_init(PyObject *s, PyObject *args, PyObject *kwds) {
                Py_INCREF(values);
        }
        
+       if (meta == NULL) {
+               meta = PyDict_New();
+               PyErr_Clear();
+       } else {
+               Py_INCREF(meta);
+       }
+       
        tmp = self->values;
        self->values = values;
        Py_XDECREF(tmp);
        
+       tmp = self->meta;
+       self->meta = meta;
+       Py_XDECREF(tmp);
+
        self->interval = interval;
        return 0;
 }
 
+static meta_data_t *cpy_build_meta(PyObject *meta) {
+       int i, s;
+       meta_data_t *m = NULL;
+       PyObject *l;
+       
+       if ((meta == NULL) || (meta == Py_None))
+               return NULL;
+
+       l = PyDict_Items(meta); /* New reference. */
+       if (!l) {
+               cpy_log_exception("building meta data");
+               return NULL;
+       }
+       s = PyList_Size(l);
+       if (s <= 0) {
+               Py_XDECREF(l);
+               return NULL;
+       }
+
+       m = meta_data_create();
+       for (i = 0; i < s; ++i) {
+               const char *string, *keystring;
+               PyObject *key, *value, *item, *tmp;
+               
+               item = PyList_GET_ITEM(l, i);
+               key = PyTuple_GET_ITEM(item, 0);
+               Py_INCREF(key);
+               keystring = cpy_unicode_or_bytes_to_string(&key);
+               if (!keystring) {
+                       PyErr_Clear();
+                       Py_XDECREF(key);
+                       continue;
+               }
+               value = PyTuple_GET_ITEM(item, 1);
+               Py_INCREF(value);
+               if (value == Py_True) {
+                       meta_data_add_boolean(m, keystring, 1);
+               } else if (value == Py_False) {
+                       meta_data_add_boolean(m, keystring, 0);
+               } else if (PyFloat_Check(value)) {
+                       meta_data_add_double(m, keystring, PyFloat_AsDouble(value));
+               } else if (PyObject_TypeCheck(value, &SignedType)) {
+                       long long int lli;
+                       lli = PyLong_AsLongLong(value);
+                       if (!PyErr_Occurred() && (lli == (int64_t) lli))
+                               meta_data_add_signed_int(m, keystring, lli);
+               } else if (PyObject_TypeCheck(value, &UnsignedType)) {
+                       long long unsigned llu;
+                       llu = PyLong_AsUnsignedLongLong(value);
+                       if (!PyErr_Occurred() && (llu == (uint64_t) llu))
+                               meta_data_add_unsigned_int(m, keystring, llu);
+               } else if (PyNumber_Check(value)) {
+                       long long int lli;
+                       long long unsigned llu;
+                       tmp = PyNumber_Long(value);
+                       lli = PyLong_AsLongLong(tmp);
+                       if (!PyErr_Occurred() && (lli == (int64_t) lli)) {
+                               meta_data_add_signed_int(m, keystring, lli);
+                       } else {
+                               PyErr_Clear();
+                               llu = PyLong_AsUnsignedLongLong(tmp);
+                               if (!PyErr_Occurred() && (llu == (uint64_t) llu))
+                                       meta_data_add_unsigned_int(m, keystring, llu);
+                       }
+                       Py_XDECREF(tmp);
+               } else {
+                       string = cpy_unicode_or_bytes_to_string(&value);
+                       if (string) {
+                               meta_data_add_string(m, keystring, string);
+                       } else {
+                               PyErr_Clear();
+                               tmp = PyObject_Str(value);
+                               string = cpy_unicode_or_bytes_to_string(&tmp);
+                               if (string)
+                                       meta_data_add_string(m, keystring, string);
+                               Py_XDECREF(tmp);
+                       }
+               }
+               if (PyErr_Occurred())
+                       cpy_log_exception("building meta data");
+               Py_XDECREF(value);
+               Py_DECREF(key);
+       }
+       Py_XDECREF(l);
+       return m;
+}
+
 static PyObject *Values_dispatch(Values *self, PyObject *args, PyObject *kwds) {
-       int i, ret;
+       int ret;
        const data_set_t *ds;
-       int size;
+       size_t size, i;
        value_t *value;
        value_list_t value_list = VALUE_LIST_INIT;
-       PyObject *values = self->values;
-       double time = self->data.time;
-       int interval = self->interval;
-       const char *host = self->data.host;
-       const char *plugin = self->data.plugin;
-       const char *plugin_instance = self->data.plugin_instance;
-       const char *type = self->data.type;
-       const char *type_instance = self->data.type_instance;
+       PyObject *values = self->values, *meta = self->meta;
+       double time = self->data.time, interval = self->interval;
+       char *host = NULL, *plugin = NULL, *plugin_instance = NULL, *type = NULL, *type_instance = NULL;
        
        static char *kwlist[] = {"type", "values", "plugin_instance", "type_instance",
-                       "plugin", "host", "time", "interval", NULL};
-       if (!PyArg_ParseTupleAndKeywords(args, kwds, "|sOssssdi", kwlist,
-                       &type, &values, &plugin_instance, &type_instance,
-                       &plugin, &host, &time, &interval))
+                       "plugin", "host", "time", "interval", "meta", NULL};
+       if (!PyArg_ParseTupleAndKeywords(args, kwds, "|etOetetetetddO", kwlist,
+                       NULL, &type, &values, NULL, &plugin_instance, NULL, &type_instance,
+                       NULL, &plugin, NULL, &host, &time, &interval, &meta))
                return NULL;
 
-       if (type[0] == 0) {
+       sstrncpy(value_list.host, host ? host : self->data.host, sizeof(value_list.host));
+       sstrncpy(value_list.plugin, plugin ? plugin : self->data.plugin, sizeof(value_list.plugin));
+       sstrncpy(value_list.plugin_instance, plugin_instance ? plugin_instance : self->data.plugin_instance, sizeof(value_list.plugin_instance));
+       sstrncpy(value_list.type, type ? type : self->data.type, sizeof(value_list.type));
+       sstrncpy(value_list.type_instance, type_instance ? type_instance : self->data.type_instance, sizeof(value_list.type_instance));
+       FreeAll();
+       if (value_list.type[0] == 0) {
                PyErr_SetString(PyExc_RuntimeError, "type not set");
+               FreeAll();
                return NULL;
        }
-       ds = plugin_get_ds(type);
+       ds = plugin_get_ds(value_list.type);
        if (ds == NULL) {
-               PyErr_Format(PyExc_TypeError, "Dataset %s not found", type);
+               PyErr_Format(PyExc_TypeError, "Dataset %s not found", value_list.type);
                return NULL;
        }
        if (values == NULL || (PyTuple_Check(values) == 0 && PyList_Check(values) == 0)) {
                PyErr_Format(PyExc_TypeError, "values must be list or tuple");
                return NULL;
        }
-       size = (int) PySequence_Length(values);
+       if (meta != NULL && meta != Py_None && !PyDict_Check(meta)) {
+               PyErr_Format(PyExc_TypeError, "meta must be a dict");
+               return NULL;
+       }
+       size = (size_t) PySequence_Length(values);
        if (size != ds->ds_num) {
-               PyErr_Format(PyExc_RuntimeError, "type %s needs %d values, got %i", type, ds->ds_num, size);
+               PyErr_Format(PyExc_RuntimeError, "type %s needs %zu values, got %zu", value_list.type, ds->ds_num, size);
                return NULL;
        }
-       value = malloc(size * sizeof(*value));
+       value = calloc(size, sizeof(*value));
        for (i = 0; i < size; ++i) {
                PyObject *item, *num;
-               item = PySequence_GetItem(values, i);
+               item = PySequence_Fast_GET_ITEM(values, (int) i); /* Borrowed reference. */
                if (ds->ds->type == DS_TYPE_COUNTER) {
-                       num = PyNumber_Long(item);
-                       if (num != NULL)
+                       num = PyNumber_Long(item); /* New reference. */
+                       if (num != NULL) {
                                value[i].counter = PyLong_AsUnsignedLongLong(num);
+                               Py_XDECREF(num);
+                       }
                } else if (ds->ds->type == DS_TYPE_GAUGE) {
-                       num = PyNumber_Float(item);
-                       if (num != NULL)
+                       num = PyNumber_Float(item); /* New reference. */
+                       if (num != NULL) {
                                value[i].gauge = PyFloat_AsDouble(num);
+                               Py_XDECREF(num);
+                       }
                } else if (ds->ds->type == DS_TYPE_DERIVE) {
                        /* This might overflow without raising an exception.
                         * Not much we can do about it */
-                       num = PyNumber_Long(item);
-                       if (num != NULL)
+                       num = PyNumber_Long(item); /* New reference. */
+                       if (num != NULL) {
                                value[i].derive = PyLong_AsLongLong(num);
+                               Py_XDECREF(num);
+                       }
                } else if (ds->ds->type == DS_TYPE_ABSOLUTE) {
                        /* This might overflow without raising an exception.
                         * Not much we can do about it */
-                       num = PyNumber_Long(item);
-                       if (num != NULL)
+                       num = PyNumber_Long(item); /* New reference. */
+                       if (num != NULL) {
                                value[i].absolute = PyLong_AsUnsignedLongLong(num);
+                               Py_XDECREF(num);
+                       }
                } else {
                        free(value);
-                       PyErr_Format(PyExc_RuntimeError, "unknown data type %d for %s", ds->ds->type, type);
+                       PyErr_Format(PyExc_RuntimeError, "unknown data type %d for %s", ds->ds->type, value_list.type);
                        return NULL;
                }
                if (PyErr_Occurred() != NULL) {
@@ -346,15 +590,10 @@ static PyObject *Values_dispatch(Values *self, PyObject *args, PyObject *kwds) {
                }
        }
        value_list.values = value;
+       value_list.meta = cpy_build_meta(meta);
        value_list.values_len = size;
-       value_list.time = time;
-       value_list.interval = interval;
-       sstrncpy(value_list.host, host, sizeof(value_list.host));
-       sstrncpy(value_list.plugin, plugin, sizeof(value_list.plugin));
-       sstrncpy(value_list.plugin_instance, plugin_instance, sizeof(value_list.plugin_instance));
-       sstrncpy(value_list.type, type, sizeof(value_list.type));
-       sstrncpy(value_list.type_instance, type_instance, sizeof(value_list.type_instance));
-       value_list.meta = NULL;
+       value_list.time = DOUBLE_TO_CDTIME_T(time);
+       value_list.interval = DOUBLE_TO_CDTIME_T(interval);
        if (value_list.host[0] == 0)
                sstrncpy(value_list.host, hostname_g, sizeof(value_list.host));
        if (value_list.plugin[0] == 0)
@@ -362,82 +601,91 @@ static PyObject *Values_dispatch(Values *self, PyObject *args, PyObject *kwds) {
        Py_BEGIN_ALLOW_THREADS;
        ret = plugin_dispatch_values(&value_list);
        Py_END_ALLOW_THREADS;
+       meta_data_destroy(value_list.meta);
+       free(value);
        if (ret != 0) {
                PyErr_SetString(PyExc_RuntimeError, "error dispatching values, read the logs");
                return NULL;
        }
-       free(value);
        Py_RETURN_NONE;
 }
 
 static PyObject *Values_write(Values *self, PyObject *args, PyObject *kwds) {
-       int i, ret;
+       int ret;
        const data_set_t *ds;
-       int size;
+       size_t size, i;
        value_t *value;
        value_list_t value_list = VALUE_LIST_INIT;
-       PyObject *values = self->values;
-       double time = self->data.time;
-       int interval = self->interval;
-       const char *host = self->data.host;
-       const char *plugin = self->data.plugin;
-       const char *plugin_instance = self->data.plugin_instance;
-       const char *type = self->data.type;
-       const char *type_instance = self->data.type_instance;
-       const char *dest = NULL;
+       PyObject *values = self->values, *meta = self->meta;
+       double time = self->data.time, interval = self->interval;
+       char *host = NULL, *plugin = NULL, *plugin_instance = NULL, *type = NULL, *type_instance = NULL, *dest = NULL;
        
        static char *kwlist[] = {"destination", "type", "values", "plugin_instance", "type_instance",
-                       "plugin", "host", "time", "interval", NULL};
-       if (!PyArg_ParseTupleAndKeywords(args, kwds, "|sOssssdi", kwlist,
-                       &type, &values, &plugin_instance, &type_instance,
-                       &plugin, &host, &time, &interval))
+                       "plugin", "host", "time", "interval", "meta", NULL};
+       if (!PyArg_ParseTupleAndKeywords(args, kwds, "et|etOetetetetdiO", kwlist, NULL, &dest,
+                       NULL, &type, &values, NULL, &plugin_instance, NULL, &type_instance,
+                       NULL, &plugin, NULL, &host, &time, &interval, &meta))
                return NULL;
 
-       if (type[0] == 0) {
+       sstrncpy(value_list.host, host ? host : self->data.host, sizeof(value_list.host));
+       sstrncpy(value_list.plugin, plugin ? plugin : self->data.plugin, sizeof(value_list.plugin));
+       sstrncpy(value_list.plugin_instance, plugin_instance ? plugin_instance : self->data.plugin_instance, sizeof(value_list.plugin_instance));
+       sstrncpy(value_list.type, type ? type : self->data.type, sizeof(value_list.type));
+       sstrncpy(value_list.type_instance, type_instance ? type_instance : self->data.type_instance, sizeof(value_list.type_instance));
+       FreeAll();
+       if (value_list.type[0] == 0) {
                PyErr_SetString(PyExc_RuntimeError, "type not set");
                return NULL;
        }
-       ds = plugin_get_ds(type);
+       ds = plugin_get_ds(value_list.type);
        if (ds == NULL) {
-               PyErr_Format(PyExc_TypeError, "Dataset %s not found", type);
+               PyErr_Format(PyExc_TypeError, "Dataset %s not found", value_list.type);
                return NULL;
        }
        if (values == NULL || (PyTuple_Check(values) == 0 && PyList_Check(values) == 0)) {
                PyErr_Format(PyExc_TypeError, "values must be list or tuple");
                return NULL;
        }
-       size = (int) PySequence_Length(values);
+       size = (size_t) PySequence_Length(values);
        if (size != ds->ds_num) {
-               PyErr_Format(PyExc_RuntimeError, "type %s needs %d values, got %i", type, ds->ds_num, size);
+               PyErr_Format(PyExc_RuntimeError, "type %s needs %zu values, got %zu", value_list.type, ds->ds_num, size);
                return NULL;
        }
-       value = malloc(size * sizeof(*value));
+       value = calloc(size, sizeof(*value));
        for (i = 0; i < size; ++i) {
                PyObject *item, *num;
-               item = PySequence_GetItem(values, i);
+               item = PySequence_Fast_GET_ITEM(values, i); /* Borrowed reference. */
                if (ds->ds->type == DS_TYPE_COUNTER) {
-                       num = PyNumber_Long(item);
-                       if (num != NULL)
+                       num = PyNumber_Long(item); /* New reference. */
+                       if (num != NULL) {
                                value[i].counter = PyLong_AsUnsignedLongLong(num);
+                               Py_XDECREF(num);
+                       }
                } else if (ds->ds->type == DS_TYPE_GAUGE) {
-                       num = PyNumber_Float(item);
-                       if (num != NULL)
+                       num = PyNumber_Float(item); /* New reference. */
+                       if (num != NULL) {
                                value[i].gauge = PyFloat_AsDouble(num);
+                               Py_XDECREF(num);
+                       }
                } else if (ds->ds->type == DS_TYPE_DERIVE) {
                        /* This might overflow without raising an exception.
                         * Not much we can do about it */
-                       num = PyNumber_Long(item);
-                       if (num != NULL)
+                       num = PyNumber_Long(item); /* New reference. */
+                       if (num != NULL) {
                                value[i].derive = PyLong_AsLongLong(num);
+                               Py_XDECREF(num);
+                       }
                } else if (ds->ds->type == DS_TYPE_ABSOLUTE) {
                        /* This might overflow without raising an exception.
                         * Not much we can do about it */
-                       num = PyNumber_Long(item);
-                       if (num != NULL)
+                       num = PyNumber_Long(item); /* New reference. */
+                       if (num != NULL) {
                                value[i].absolute = PyLong_AsUnsignedLongLong(num);
+                               Py_XDECREF(num);
+                       }
                } else {
                        free(value);
-                       PyErr_Format(PyExc_RuntimeError, "unknown data type %d for %s", ds->ds->type, type);
+                       PyErr_Format(PyExc_RuntimeError, "unknown data type %d for %s", ds->ds->type, value_list.type);
                        return NULL;
                }
                if (PyErr_Occurred() != NULL) {
@@ -447,14 +695,9 @@ static PyObject *Values_write(Values *self, PyObject *args, PyObject *kwds) {
        }
        value_list.values = value;
        value_list.values_len = size;
-       value_list.time = time;
-       value_list.interval = interval;
-       sstrncpy(value_list.host, host, sizeof(value_list.host));
-       sstrncpy(value_list.plugin, plugin, sizeof(value_list.plugin));
-       sstrncpy(value_list.plugin_instance, plugin_instance, sizeof(value_list.plugin_instance));
-       sstrncpy(value_list.type, type, sizeof(value_list.type));
-       sstrncpy(value_list.type_instance, type_instance, sizeof(value_list.type_instance));
-       value_list.meta = NULL;
+       value_list.time = DOUBLE_TO_CDTIME_T(time);
+       value_list.interval = DOUBLE_TO_CDTIME_T(interval);
+       value_list.meta = cpy_build_meta(meta);;
        if (value_list.host[0] == 0)
                sstrncpy(value_list.host, hostname_g, sizeof(value_list.host));
        if (value_list.plugin[0] == 0)
@@ -462,43 +705,64 @@ static PyObject *Values_write(Values *self, PyObject *args, PyObject *kwds) {
        Py_BEGIN_ALLOW_THREADS;
        ret = plugin_write(dest, NULL, &value_list);
        Py_END_ALLOW_THREADS;
+       meta_data_destroy(value_list.meta);
+       free(value);
        if (ret != 0) {
                PyErr_SetString(PyExc_RuntimeError, "error dispatching values, read the logs");
                return NULL;
        }
-       free(value);
        Py_RETURN_NONE;
 }
 
 static PyObject *Values_repr(PyObject *s) {
-       PyObject *ret, *valuestring = NULL;
+       PyObject *ret, *tmp;
+       static PyObject *l_interval = NULL, *l_values = NULL, *l_meta = NULL, *l_closing = NULL;
        Values *self = (Values *) s;
        
-       if (self->values != NULL)
-               valuestring = PyObject_Repr(self->values);
-       if (valuestring == NULL)
+       if (l_interval == NULL)
+               l_interval = cpy_string_to_unicode_or_bytes(",interval=");
+       if (l_values == NULL)
+               l_values = cpy_string_to_unicode_or_bytes(",values=");
+       if (l_meta == NULL)
+               l_meta = cpy_string_to_unicode_or_bytes(",meta=");
+       if (l_closing == NULL)
+               l_closing = cpy_string_to_unicode_or_bytes(")");
+       
+       if (l_interval == NULL || l_values == NULL || l_meta == NULL || l_closing == NULL)
                return NULL;
        
-       ret = PyString_FromFormat("collectd.Values(type='%s%s%s%s%s%s%s%s%s',time=%lu,interval=%i,values=%s)", self->data.type,
-                       *self->data.type_instance ? "',type_instance='" : "", self->data.type_instance,
-                       *self->data.plugin ? "',plugin='" : "", self->data.plugin,
-                       *self->data.plugin_instance ? "',plugin_instance='" : "", self->data.plugin_instance,
-                       *self->data.host ? "',host='" : "", self->data.host,
-                       (long unsigned) self->data.time, self->interval,
-                       valuestring ? PyString_AsString(valuestring) : "[]");
-       Py_XDECREF(valuestring);
+       ret = cpy_common_repr(s);
+       if (self->interval != 0) {
+               CPY_STRCAT(&ret, l_interval);
+               tmp = PyFloat_FromDouble(self->interval);
+               CPY_SUBSTITUTE(PyObject_Repr, tmp, tmp);
+               CPY_STRCAT_AND_DEL(&ret, tmp);
+       }
+       if (self->values && (!PyList_Check(self->values) || PySequence_Length(self->values) > 0)) {
+               CPY_STRCAT(&ret, l_values);
+               tmp = PyObject_Repr(self->values);
+               CPY_STRCAT_AND_DEL(&ret, tmp);
+       }
+       if (self->meta && (!PyDict_Check(self->meta) || PyDict_Size(self->meta) > 0)) {
+               CPY_STRCAT(&ret, l_meta);
+               tmp = PyObject_Repr(self->meta);
+               CPY_STRCAT_AND_DEL(&ret, tmp);
+       }
+       CPY_STRCAT(&ret, l_closing);
        return ret;
 }
 
 static int Values_traverse(PyObject *self, visitproc visit, void *arg) {
        Values *v = (Values *) self;
        Py_VISIT(v->values);
+       Py_VISIT(v->meta);
        return 0;
 }
 
 static int Values_clear(PyObject *self) {
        Values *v = (Values *) self;
        Py_CLEAR(v->values);
+       Py_CLEAR(v->meta);
        return 0;
 }
 
@@ -508,8 +772,9 @@ static void Values_dealloc(PyObject *self) {
 }
 
 static PyMemberDef Values_members[] = {
-       {"interval", T_INT, offsetof(Values, interval), 0, interval_doc},
+       {"interval", T_DOUBLE, offsetof(Values, interval), 0, interval_doc},
        {"values", T_OBJECT_EX, offsetof(Values, values), 0, values_doc},
+       {"meta", T_OBJECT_EX, offsetof(Values, meta), 0, meta_doc},
        {NULL}
 };
 
@@ -520,8 +785,7 @@ static PyMethodDef Values_methods[] = {
 };
 
 PyTypeObject ValuesType = {
-       PyObject_HEAD_INIT(NULL)
-       0,                         /* Always 0 */
+       CPY_INIT_TYPE
        "collectd.Values",         /* tp_name */
        sizeof(Values),            /* tp_basicsize */
        0,                         /* Will be filled in later */
@@ -574,29 +838,36 @@ static char Notification_doc[] = "The Notification class is a wrapper around the
 
 static int Notification_init(PyObject *s, PyObject *args, PyObject *kwds) {
        Notification *self = (Notification *) s;
-       PyObject *tmp;
-       int severity = 0, ret;
+       int severity = 0;
        double time = 0;
-       const char *message = "";
-       const char *type = "", *plugin_instance = "", *type_instance = "", *plugin = "", *host = "";
+       char *message = NULL;
+       char *type = NULL, *plugin_instance = NULL, *type_instance = NULL, *plugin = NULL, *host = NULL;
        static char *kwlist[] = {"type", "message", "plugin_instance", "type_instance",
                        "plugin", "host", "time", "severity", NULL};
        
-       if (!PyArg_ParseTupleAndKeywords(args, kwds, "|ssssssdi", kwlist,
-                       &type, &message, &plugin_instance, &type_instance,
-                       &plugin, &host, &time, &severity))
+       if (!PyArg_ParseTupleAndKeywords(args, kwds, "|etetetetetetdi", kwlist,
+                       NULL, &type, NULL, &message, NULL, &plugin_instance, NULL, &type_instance,
+                       NULL, &plugin, NULL, &host, &time, &severity))
                return -1;
        
-       tmp = Py_BuildValue("sssssd", type, plugin_instance, type_instance, plugin, host, time);
-       if (tmp == NULL)
-               return -1;
-       ret = PluginDataType.tp_init(s, tmp, NULL);
-       Py_DECREF(tmp);
-       if (ret != 0)
+       if (type && plugin_get_ds(type) == NULL) {
+               PyErr_Format(PyExc_TypeError, "Dataset %s not found", type);
+               FreeAll();
+               PyMem_Free(message);
                return -1;
-       
-       sstrncpy(self->message, message, sizeof(self->message));
+       }
+
+       sstrncpy(self->data.host, host ? host : "", sizeof(self->data.host));
+       sstrncpy(self->data.plugin, plugin ? plugin : "", sizeof(self->data.plugin));
+       sstrncpy(self->data.plugin_instance, plugin_instance ? plugin_instance : "", sizeof(self->data.plugin_instance));
+       sstrncpy(self->data.type, type ? type : "", sizeof(self->data.type));
+       sstrncpy(self->data.type_instance, type_instance ? type_instance : "", sizeof(self->data.type_instance));
+       sstrncpy(self->message, message ? message : "", sizeof(self->message));
+       self->data.time = time;
        self->severity = severity;
+
+       FreeAll();
+       PyMem_Free(message);
        return 0;
 }
 
@@ -606,41 +877,40 @@ static PyObject *Notification_dispatch(Notification *self, PyObject *args, PyObj
        notification_t notification;
        double t = self->data.time;
        int severity = self->severity;
-       const char *host = self->data.host;
-       const char *plugin = self->data.plugin;
-       const char *plugin_instance = self->data.plugin_instance;
-       const char *type = self->data.type;
-       const char *type_instance = self->data.type_instance;
-       const char *message = self->message;
+       char *host = NULL, *plugin = NULL, *plugin_instance = NULL, *type = NULL, *type_instance = NULL;
+       char *message = NULL;
        
        static char *kwlist[] = {"type", "message", "plugin_instance", "type_instance",
                        "plugin", "host", "time", "severity", NULL};
-       if (!PyArg_ParseTupleAndKeywords(args, kwds, "|ssssssdi", kwlist,
-                       &type, &message, &plugin_instance, &type_instance,
-                       &plugin, &host, &t, &severity))
+       if (!PyArg_ParseTupleAndKeywords(args, kwds, "|etetetetetetdi", kwlist,
+                       NULL, &type, NULL, &message, NULL, &plugin_instance, NULL, &type_instance,
+                       NULL, &plugin, NULL, &host, &t, &severity))
                return NULL;
 
-       if (type[0] == 0) {
+       notification.time = DOUBLE_TO_CDTIME_T(t);
+       notification.severity = severity;
+       sstrncpy(notification.message, message ? message : self->message, sizeof(notification.message));
+       sstrncpy(notification.host, host ? host : self->data.host, sizeof(notification.host));
+       sstrncpy(notification.plugin, plugin ? plugin : self->data.plugin, sizeof(notification.plugin));
+       sstrncpy(notification.plugin_instance, plugin_instance ? plugin_instance : self->data.plugin_instance, sizeof(notification.plugin_instance));
+       sstrncpy(notification.type, type ? type : self->data.type, sizeof(notification.type));
+       sstrncpy(notification.type_instance, type_instance ? type_instance : self->data.type_instance, sizeof(notification.type_instance));
+       notification.meta = NULL;
+       FreeAll();
+       PyMem_Free(message);
+
+       if (notification.type[0] == 0) {
                PyErr_SetString(PyExc_RuntimeError, "type not set");
                return NULL;
        }
-       ds = plugin_get_ds(type);
+       ds = plugin_get_ds(notification.type);
        if (ds == NULL) {
-               PyErr_Format(PyExc_TypeError, "Dataset %s not found", type);
+               PyErr_Format(PyExc_TypeError, "Dataset %s not found", notification.type);
                return NULL;
        }
 
-       notification.time = t;
-       notification.severity = severity;
-       sstrncpy(notification.message, message, sizeof(notification.message));
-       sstrncpy(notification.host, host, sizeof(notification.host));
-       sstrncpy(notification.plugin, plugin, sizeof(notification.plugin));
-       sstrncpy(notification.plugin_instance, plugin_instance, sizeof(notification.plugin_instance));
-       sstrncpy(notification.type, type, sizeof(notification.type));
-       sstrncpy(notification.type_instance, type_instance, sizeof(notification.type_instance));
-       notification.meta = NULL;
-       if (notification.time < 1)
-               notification.time = time(0);
+       if (notification.time == 0)
+               notification.time = cdtime();
        if (notification.host[0] == 0)
                sstrncpy(notification.host, hostname_g, sizeof(notification.host));
        if (notification.plugin[0] == 0)
@@ -675,24 +945,47 @@ static int Notification_setstring(PyObject *self, PyObject *value, void *data) {
                PyErr_SetString(PyExc_TypeError, "Cannot delete this attribute");
                return -1;
        }
-       new = PyString_AsString(value);
-       if (new == NULL) return -1;
+       Py_INCREF(value);
+       new = cpy_unicode_or_bytes_to_string(&value);
+       if (new == NULL) {
+               Py_DECREF(value);
+               return -1;
+       }
        old = ((char *) self) + (intptr_t) data;
        sstrncpy(old, new, NOTIF_MAX_MSG_LEN);
+       Py_DECREF(value);
        return 0;
 }
 
 static PyObject *Notification_repr(PyObject *s) {
-       PyObject *ret;
+       PyObject *ret, *tmp;
+       static PyObject *l_severity = NULL, *l_message = NULL, *l_closing = NULL;
        Notification *self = (Notification *) s;
        
-       ret = PyString_FromFormat("collectd.Values(type='%s%s%s%s%s%s%s%s%s%s%s',time=%lu,interval=%i)", self->data.type,
-                       *self->data.type_instance ? "',type_instance='" : "", self->data.type_instance,
-                       *self->data.plugin ? "',plugin='" : "", self->data.plugin,
-                       *self->data.plugin_instance ? "',plugin_instance='" : "", self->data.plugin_instance,
-                       *self->data.host ? "',host='" : "", self->data.host,
-                       *self->message ? "',message='" : "", self->message,
-                       (long unsigned) self->data.time, self->severity);
+       if (l_severity == NULL)
+               l_severity = cpy_string_to_unicode_or_bytes(",severity=");
+       if (l_message == NULL)
+               l_message = cpy_string_to_unicode_or_bytes(",message=");
+       if (l_closing == NULL)
+               l_closing = cpy_string_to_unicode_or_bytes(")");
+       
+       if (l_severity == NULL || l_message == NULL || l_closing == NULL)
+               return NULL;
+       
+       ret = cpy_common_repr(s);
+       if (self->severity != 0) {
+               CPY_STRCAT(&ret, l_severity);
+               tmp = PyInt_FromLong(self->severity);
+               CPY_SUBSTITUTE(PyObject_Repr, tmp, tmp);
+               CPY_STRCAT_AND_DEL(&ret, tmp);
+       }
+       if (self->message[0] != 0) {
+               CPY_STRCAT(&ret, l_message);
+               tmp = cpy_string_to_unicode_or_bytes(self->message);
+               CPY_SUBSTITUTE(PyObject_Repr, tmp, tmp);
+               CPY_STRCAT_AND_DEL(&ret, tmp);
+       }
+       CPY_STRCAT(&ret, l_closing);
        return ret;
 }
 
@@ -712,8 +1005,7 @@ static PyGetSetDef Notification_getseters[] = {
 };
 
 PyTypeObject NotificationType = {
-       PyObject_HEAD_INIT(NULL)
-       0,                         /* Always 0 */
+       CPY_INIT_TYPE
        "collectd.Notification",   /* tp_name */
        sizeof(Notification),      /* tp_basicsize */
        0,                         /* Will be filled in later */
@@ -752,3 +1044,57 @@ PyTypeObject NotificationType = {
        0,                         /* tp_alloc */
        Notification_new           /* tp_new */
 };
+
+static char Signed_doc[] = "This is a long by another name. Use it in meta data dicts\n"
+               "to choose the way it is stored in the meta data.";
+
+PyTypeObject SignedType = {
+       CPY_INIT_TYPE
+       "collectd.Signed",         /* tp_name */
+       sizeof(Signed),            /* tp_basicsize */
+       0,                         /* Will be filled in later */
+       0,                         /* tp_dealloc */
+       0,                         /* tp_print */
+       0,                         /* tp_getattr */
+       0,                         /* tp_setattr */
+       0,                         /* tp_compare */
+       0,                         /* tp_repr */
+       0,                         /* tp_as_number */
+       0,                         /* tp_as_sequence */
+       0,                         /* tp_as_mapping */
+       0,                         /* tp_hash */
+       0,                         /* tp_call */
+       0,                         /* tp_str */
+       0,                         /* tp_getattro */
+       0,                         /* tp_setattro */
+       0,                         /* tp_as_buffer */
+       Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/
+       Signed_doc                 /* tp_doc */
+};
+
+static char Unsigned_doc[] = "This is a long by another name. Use it in meta data dicts\n"
+               "to choose the way it is stored in the meta data.";
+
+PyTypeObject UnsignedType = {
+       CPY_INIT_TYPE
+       "collectd.Unsigned",       /* tp_name */
+       sizeof(Unsigned),          /* tp_basicsize */
+       0,                         /* Will be filled in later */
+       0,                         /* tp_dealloc */
+       0,                         /* tp_print */
+       0,                         /* tp_getattr */
+       0,                         /* tp_setattr */
+       0,                         /* tp_compare */
+       0,                         /* tp_repr */
+       0,                         /* tp_as_number */
+       0,                         /* tp_as_sequence */
+       0,                         /* tp_as_mapping */
+       0,                         /* tp_hash */
+       0,                         /* tp_call */
+       0,                         /* tp_str */
+       0,                         /* tp_getattro */
+       0,                         /* tp_setattro */
+       0,                         /* tp_as_buffer */
+       Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/
+       Unsigned_doc               /* tp_doc */
+};