Merge branch 'collectd-5.7' into collectd-5.8
[collectd.git] / src / python.c
index 5274262..e60ba45 100644 (file)
 #include "cpython.h"
 
 typedef struct cpy_callback_s {
-       char *name;
-       PyObject *callback;
-       PyObject *data;
-       struct cpy_callback_s *next;
+  char *name;
+  PyObject *callback;
+  PyObject *data;
+  struct cpy_callback_s *next;
 } cpy_callback_t;
 
 static char log_doc[] = "This function sends a string to all logging plugins.";
 
-static char get_ds_doc[] = "get_dataset(name) -> definition\n"
-               "\n"
-               "Returns the definition of a dataset specified by name.\n"
-               "\n"
-               "'name' is a string specifying the dataset to query.\n"
-               "'definition' is a list of 4-tuples. Every tuple represents a \n"
-               "    data source within the data set and its 4 values are the \n"
-               "    name, type, min and max value.\n"
-               "    'name' is a string.\n"
-               "    'type' is a string that is equal to either DS_TYPE_COUNTER,\n"
-               "        DS_TYPE_GAUGE, DS_TYPE_DERIVE or DS_TYPE_ABSOLUTE.\n"
-               "    'min' and 'max' are either a float or None.";
+static char get_ds_doc[] =
+    "get_dataset(name) -> definition\n"
+    "\n"
+    "Returns the definition of a dataset specified by name.\n"
+    "\n"
+    "'name' is a string specifying the dataset to query.\n"
+    "'definition' is a list of 4-tuples. Every tuple represents a \n"
+    "    data source within the data set and its 4 values are the \n"
+    "    name, type, min and max value.\n"
+    "    'name' is a string.\n"
+    "    'type' is a string that is equal to either DS_TYPE_COUNTER,\n"
+    "        DS_TYPE_GAUGE, DS_TYPE_DERIVE or DS_TYPE_ABSOLUTE.\n"
+    "    'min' and 'max' are either a float or None.";
 
 static char flush_doc[] = "flush([plugin][, timeout][, identifier]) -> None\n"
-               "\n"
-               "Flushes the cache of another plugin.";
-
-static char unregister_doc[] = "Unregisters a callback. This function needs exactly one parameter either\n"
-               "the function to unregister or the callback identifier to unregister.";
-
-static char reg_log_doc[] = "register_log(callback[, data][, name]) -> identifier\n"
-               "\n"
-               "Register a callback function for log messages.\n"
-               "\n"
-               "'callback' is a callable object that will be called every time something\n"
-               "    is logged.\n"
-               "'data' is an optional object that will be passed back to the callback\n"
-               "    function every time it is called.\n"
-               "'name' is an optional identifier for this callback. The default name\n"
-               "    is 'python.<module>'.\n"
-               "    Every callback needs a unique identifier, so if you want to\n"
-               "    register this callback multiple time from the same module you need\n"
-               "    to specify a name here.\n"
-               "'identifier' is the full identifier assigned to this callback.\n"
-               "\n"
-               "The callback function will be called with two or three parameters:\n"
-               "severity: An integer that should be compared to the LOG_ constants.\n"
-               "message: The text to be logged.\n"
-               "data: The optional data parameter passed to the register function.\n"
-               "    If the parameter was omitted it will be omitted here, too.";
-
-static char reg_init_doc[] = "register_init(callback[, data][, name]) -> identifier\n"
-               "\n"
-               "Register a callback function that will be executed once after the config.\n"
-               "file has been read, all plugins heve been loaded and the collectd has\n"
-               "forked into the background.\n"
-               "\n"
-               "'callback' is a callable object that will be executed.\n"
-               "'data' is an optional object that will be passed back to the callback\n"
-               "    function when it is called.\n"
-               "'name' is an optional identifier for this callback. The default name\n"
-               "    is 'python.<module>'.\n"
-               "    Every callback needs a unique identifier, so if you want to\n"
-               "    register this callback multiple time from the same module you need\n"
-               "    to specify a name here.\n"
-               "'identifier' is the full identifier assigned to this callback.\n"
-               "\n"
-               "The callback function will be called without parameters, except for\n"
-               "data if it was supplied.";
-
-static char reg_config_doc[] = "register_config(callback[, data][, name]) -> identifier\n"
-               "\n"
-               "Register a callback function for config file entries.\n"
-               "'callback' is a callable object that will be called for every config block.\n"
-               "'data' is an optional object that will be passed back to the callback\n"
-               "    function every time it is called.\n"
-               "'name' is an optional identifier for this callback. The default name\n"
-               "    is 'python.<module>'.\n"
-               "    Every callback needs a unique identifier, so if you want to\n"
-               "    register this callback multiple time from the same module you need\n"
-               "    to specify a name here.\n"
-               "'identifier' is the full identifier assigned to this callback.\n"
-               "\n"
-               "The callback function will be called with one or two parameters:\n"
-               "config: A Config object.\n"
-               "data: The optional data parameter passed to the register function.\n"
-               "    If the parameter was omitted it will be omitted here, too.";
-
-static char reg_read_doc[] = "register_read(callback[, interval][, data][, name]) -> identifier\n"
-               "\n"
-               "Register a callback function for reading data. It will just be called\n"
-               "in a fixed interval to signal that it's time to dispatch new values.\n"
-               "'callback' is a callable object that will be called every time something\n"
-               "    is logged.\n"
-               "'interval' is the number of seconds between between calls to the callback\n"
-               "    function. Full float precision is supported here.\n"
-               "'data' is an optional object that will be passed back to the callback\n"
-               "    function every time it is called.\n"
-               "'name' is an optional identifier for this callback. The default name\n"
-               "    is 'python.<module>'.\n"
-               "    Every callback needs a unique identifier, so if you want to\n"
-               "    register this callback multiple time from the same module you need\n"
-               "    to specify a name here.\n"
-               "'identifier' is the full identifier assigned to this callback.\n"
-               "\n"
-               "The callback function will be called without parameters, except for\n"
-               "data if it was supplied.";
-
-static char reg_write_doc[] = "register_write(callback[, data][, name]) -> identifier\n"
-               "\n"
-               "Register a callback function to receive values dispatched by other plugins.\n"
-               "'callback' is a callable object that will be called every time a value\n"
-               "    is dispatched.\n"
-               "'data' is an optional object that will be passed back to the callback\n"
-               "    function every time it is called.\n"
-               "'name' is an optional identifier for this callback. The default name\n"
-               "    is 'python.<module>'.\n"
-               "    Every callback needs a unique identifier, so if you want to\n"
-               "    register this callback multiple time from the same module you need\n"
-               "    to specify a name here.\n"
-               "'identifier' is the full identifier assigned to this callback.\n"
-               "\n"
-               "The callback function will be called with one or two parameters:\n"
-               "values: A Values object which is a copy of the dispatched values.\n"
-               "data: The optional data parameter passed to the register function.\n"
-               "    If the parameter was omitted it will be omitted here, too.";
-
-static char reg_notification_doc[] = "register_notification(callback[, data][, name]) -> identifier\n"
-               "\n"
-               "Register a callback function for notifications.\n"
-               "'callback' is a callable object that will be called every time a notification\n"
-               "    is dispatched.\n"
-               "'data' is an optional object that will be passed back to the callback\n"
-               "    function every time it is called.\n"
-               "'name' is an optional identifier for this callback. The default name\n"
-               "    is 'python.<module>'.\n"
-               "    Every callback needs a unique identifier, so if you want to\n"
-               "    register this callback multiple time from the same module you need\n"
-               "    to specify a name here.\n"
-               "'identifier' is the full identifier assigned to this callback.\n"
-               "\n"
-               "The callback function will be called with one or two parameters:\n"
-               "notification: A copy of the notification that was dispatched.\n"
-               "data: The optional data parameter passed to the register function.\n"
-               "    If the parameter was omitted it will be omitted here, too.";
-
-static char reg_flush_doc[] = "register_flush(callback[, data][, name]) -> identifier\n"
-               "\n"
-               "Register a callback function for flush messages.\n"
-               "'callback' is a callable object that will be called every time a plugin\n"
-               "    requests a flush for either this or all plugins.\n"
-               "'data' is an optional object that will be passed back to the callback\n"
-               "    function every time it is called.\n"
-               "'name' is an optional identifier for this callback. The default name\n"
-               "    is 'python.<module>'.\n"
-               "    Every callback needs a unique identifier, so if you want to\n"
-               "    register this callback multiple time from the same module you need\n"
-               "    to specify a name here.\n"
-               "'identifier' is the full identifier assigned to this callback.\n"
-               "\n"
-               "The callback function will be called with two or three parameters:\n"
-               "timeout: Indicates that only data older than 'timeout' seconds is to\n"
-               "    be flushed.\n"
-               "id: Specifies which values are to be flushed. Might be None.\n"
-               "data: The optional data parameter passed to the register function.\n"
-               "    If the parameter was omitted it will be omitted here, too.";
-
-static char reg_shutdown_doc[] = "register_shutdown(callback[, data][, name]) -> identifier\n"
-               "\n"
-               "Register a callback function for collectd shutdown.\n"
-               "'callback' is a callable object that will be called once collectd is\n"
-               "    shutting down.\n"
-               "'data' is an optional object that will be passed back to the callback\n"
-               "    function if it is called.\n"
-               "'name' is an optional identifier for this callback. The default name\n"
-               "    is 'python.<module>'.\n"
-               "    Every callback needs a unique identifier, so if you want to\n"
-               "    register this callback multiple time from the same module you need\n"
-               "    to specify a name here.\n"
-               "'identifier' is the full identifier assigned to this callback.\n"
-               "\n"
-               "The callback function will be called with no parameters except for\n"
-               "    data if it was supplied.";
-
+                          "\n"
+                          "Flushes the cache of another plugin.";
+
+static char unregister_doc[] =
+    "Unregisters a callback. This function needs exactly one parameter either\n"
+    "the function to unregister or the callback identifier to unregister.";
+
+static char reg_log_doc[] =
+    "register_log(callback[, data][, name]) -> identifier\n"
+    "\n"
+    "Register a callback function for log messages.\n"
+    "\n"
+    "'callback' is a callable object that will be called every time something\n"
+    "    is logged.\n"
+    "'data' is an optional object that will be passed back to the callback\n"
+    "    function every time it is called.\n"
+    "'name' is an optional identifier for this callback. The default name\n"
+    "    is 'python.<module>'.\n"
+    "    Every callback needs a unique identifier, so if you want to\n"
+    "    register this callback multiple time from the same module you need\n"
+    "    to specify a name here.\n"
+    "'identifier' is the full identifier assigned to this callback.\n"
+    "\n"
+    "The callback function will be called with two or three parameters:\n"
+    "severity: An integer that should be compared to the LOG_ constants.\n"
+    "message: The text to be logged.\n"
+    "data: The optional data parameter passed to the register function.\n"
+    "    If the parameter was omitted it will be omitted here, too.";
+
+static char reg_init_doc[] =
+    "register_init(callback[, data][, name]) -> identifier\n"
+    "\n"
+    "Register a callback function that will be executed once after the "
+    "config.\n"
+    "file has been read, all plugins heve been loaded and the collectd has\n"
+    "forked into the background.\n"
+    "\n"
+    "'callback' is a callable object that will be executed.\n"
+    "'data' is an optional object that will be passed back to the callback\n"
+    "    function when it is called.\n"
+    "'name' is an optional identifier for this callback. The default name\n"
+    "    is 'python.<module>'.\n"
+    "    Every callback needs a unique identifier, so if you want to\n"
+    "    register this callback multiple time from the same module you need\n"
+    "    to specify a name here.\n"
+    "'identifier' is the full identifier assigned to this callback.\n"
+    "\n"
+    "The callback function will be called without parameters, except for\n"
+    "data if it was supplied.";
+
+static char reg_config_doc[] =
+    "register_config(callback[, data][, name]) -> identifier\n"
+    "\n"
+    "Register a callback function for config file entries.\n"
+    "'callback' is a callable object that will be called for every config "
+    "block.\n"
+    "'data' is an optional object that will be passed back to the callback\n"
+    "    function every time it is called.\n"
+    "'name' is an optional identifier for this callback. The default name\n"
+    "    is 'python.<module>'.\n"
+    "    Every callback needs a unique identifier, so if you want to\n"
+    "    register this callback multiple time from the same module you need\n"
+    "    to specify a name here.\n"
+    "'identifier' is the full identifier assigned to this callback.\n"
+    "\n"
+    "The callback function will be called with one or two parameters:\n"
+    "config: A Config object.\n"
+    "data: The optional data parameter passed to the register function.\n"
+    "    If the parameter was omitted it will be omitted here, too.";
+
+static char reg_read_doc[] =
+    "register_read(callback[, interval][, data][, name]) -> identifier\n"
+    "\n"
+    "Register a callback function for reading data. It will just be called\n"
+    "in a fixed interval to signal that it's time to dispatch new values.\n"
+    "'callback' is a callable object that will be called every time something\n"
+    "    is logged.\n"
+    "'interval' is the number of seconds between between calls to the "
+    "callback\n"
+    "    function. Full float precision is supported here.\n"
+    "'data' is an optional object that will be passed back to the callback\n"
+    "    function every time it is called.\n"
+    "'name' is an optional identifier for this callback. The default name\n"
+    "    is 'python.<module>'.\n"
+    "    Every callback needs a unique identifier, so if you want to\n"
+    "    register this callback multiple time from the same module you need\n"
+    "    to specify a name here.\n"
+    "'identifier' is the full identifier assigned to this callback.\n"
+    "\n"
+    "The callback function will be called without parameters, except for\n"
+    "data if it was supplied.";
+
+static char reg_write_doc[] =
+    "register_write(callback[, data][, name]) -> identifier\n"
+    "\n"
+    "Register a callback function to receive values dispatched by other "
+    "plugins.\n"
+    "'callback' is a callable object that will be called every time a value\n"
+    "    is dispatched.\n"
+    "'data' is an optional object that will be passed back to the callback\n"
+    "    function every time it is called.\n"
+    "'name' is an optional identifier for this callback. The default name\n"
+    "    is 'python.<module>'.\n"
+    "    Every callback needs a unique identifier, so if you want to\n"
+    "    register this callback multiple time from the same module you need\n"
+    "    to specify a name here.\n"
+    "'identifier' is the full identifier assigned to this callback.\n"
+    "\n"
+    "The callback function will be called with one or two parameters:\n"
+    "values: A Values object which is a copy of the dispatched values.\n"
+    "data: The optional data parameter passed to the register function.\n"
+    "    If the parameter was omitted it will be omitted here, too.";
+
+static char reg_notification_doc[] =
+    "register_notification(callback[, data][, name]) -> identifier\n"
+    "\n"
+    "Register a callback function for notifications.\n"
+    "'callback' is a callable object that will be called every time a "
+    "notification\n"
+    "    is dispatched.\n"
+    "'data' is an optional object that will be passed back to the callback\n"
+    "    function every time it is called.\n"
+    "'name' is an optional identifier for this callback. The default name\n"
+    "    is 'python.<module>'.\n"
+    "    Every callback needs a unique identifier, so if you want to\n"
+    "    register this callback multiple time from the same module you need\n"
+    "    to specify a name here.\n"
+    "'identifier' is the full identifier assigned to this callback.\n"
+    "\n"
+    "The callback function will be called with one or two parameters:\n"
+    "notification: A copy of the notification that was dispatched.\n"
+    "data: The optional data parameter passed to the register function.\n"
+    "    If the parameter was omitted it will be omitted here, too.";
+
+static char reg_flush_doc[] =
+    "register_flush(callback[, data][, name]) -> identifier\n"
+    "\n"
+    "Register a callback function for flush messages.\n"
+    "'callback' is a callable object that will be called every time a plugin\n"
+    "    requests a flush for either this or all plugins.\n"
+    "'data' is an optional object that will be passed back to the callback\n"
+    "    function every time it is called.\n"
+    "'name' is an optional identifier for this callback. The default name\n"
+    "    is 'python.<module>'.\n"
+    "    Every callback needs a unique identifier, so if you want to\n"
+    "    register this callback multiple time from the same module you need\n"
+    "    to specify a name here.\n"
+    "'identifier' is the full identifier assigned to this callback.\n"
+    "\n"
+    "The callback function will be called with two or three parameters:\n"
+    "timeout: Indicates that only data older than 'timeout' seconds is to\n"
+    "    be flushed.\n"
+    "id: Specifies which values are to be flushed. Might be None.\n"
+    "data: The optional data parameter passed to the register function.\n"
+    "    If the parameter was omitted it will be omitted here, too.";
+
+static char reg_shutdown_doc[] =
+    "register_shutdown(callback[, data][, name]) -> identifier\n"
+    "\n"
+    "Register a callback function for collectd shutdown.\n"
+    "'callback' is a callable object that will be called once collectd is\n"
+    "    shutting down.\n"
+    "'data' is an optional object that will be passed back to the callback\n"
+    "    function if it is called.\n"
+    "'name' is an optional identifier for this callback. The default name\n"
+    "    is 'python.<module>'.\n"
+    "    Every callback needs a unique identifier, so if you want to\n"
+    "    register this callback multiple time from the same module you need\n"
+    "    to specify a name here.\n"
+    "'identifier' is the full identifier assigned to this callback.\n"
+    "\n"
+    "The callback function will be called with no parameters except for\n"
+    "    data if it was supplied.";
+
+static char CollectdError_doc[] =
+    "Basic exception for collectd Python scripts.\n"
+    "\n"
+    "Throwing this exception will not cause a stacktrace to be logged, \n"
+    "even if LogTraces is enabled in the config.";
 
 static pthread_t main_thread;
 static PyOS_sighandler_t python_sigint_handler;
@@ -230,7 +250,7 @@ static _Bool do_interactive = 0;
 
 static PyThreadState *state;
 
-static PyObject *sys_path, *cpy_format_exception;
+static PyObject *sys_path, *cpy_format_exception, *CollectdError;
 
 static cpy_callback_t *cpy_config_callbacks;
 static cpy_callback_t *cpy_init_callbacks;
@@ -241,1068 +261,1224 @@ static int cpy_shutdown_triggered = 0;
 static int cpy_num_callbacks = 0;
 
 static void cpy_destroy_user_data(void *data) {
-       cpy_callback_t *c = data;
-       free(c->name);
-       CPY_LOCK_THREADS
-       Py_DECREF(c->callback);
-       Py_XDECREF(c->data);
-       free(c);
-       --cpy_num_callbacks;
-       if (!cpy_num_callbacks && cpy_shutdown_triggered) {
-               Py_Finalize();
-               return;
-       }
-       CPY_RELEASE_THREADS
+  cpy_callback_t *c = data;
+  free(c->name);
+  CPY_LOCK_THREADS
+  Py_DECREF(c->callback);
+  Py_XDECREF(c->data);
+  free(c);
+  --cpy_num_callbacks;
+  if (!cpy_num_callbacks && cpy_shutdown_triggered) {
+    Py_Finalize();
+    return;
+  }
+  CPY_RELEASE_THREADS
 }
 
 /* You must hold the GIL to call this function!
- * But if you managed to extract the callback parameter then you probably already do. */
-
-static void cpy_build_name(char *buf, size_t size, PyObject *callback, const char *name) {
-       const char *module = NULL;
-       PyObject *mod = NULL;
-
-       if (name != NULL) {
-               snprintf(buf, size, "python.%s", name);
-               return;
-       }
-
-       mod = PyObject_GetAttrString(callback, "__module__"); /* New reference. */
-       if (mod != NULL)
-               module = cpy_unicode_or_bytes_to_string(&mod);
-
-       if (module != NULL) {
-               snprintf(buf, size, "python.%s", module);
-               Py_XDECREF(mod);
-               PyErr_Clear();
-               return;
-       }
-       Py_XDECREF(mod);
-
-       snprintf(buf, size, "python.%p", callback);
-       PyErr_Clear();
+ * But if you managed to extract the callback parameter then you probably
+ * already do. */
+
+static void cpy_build_name(char *buf, size_t size, PyObject *callback,
+                           const char *name) {
+  const char *module = NULL;
+  PyObject *mod = NULL;
+
+  if (name != NULL) {
+    snprintf(buf, size, "python.%s", name);
+    return;
+  }
+
+  mod = PyObject_GetAttrString(callback, "__module__"); /* New reference. */
+  if (mod != NULL)
+    module = cpy_unicode_or_bytes_to_string(&mod);
+
+  if (module != NULL) {
+    snprintf(buf, size, "python.%s", module);
+    Py_XDECREF(mod);
+    PyErr_Clear();
+    return;
+  }
+  Py_XDECREF(mod);
+
+  snprintf(buf, size, "python.%p", callback);
+  PyErr_Clear();
 }
 
 void cpy_log_exception(const char *context) {
-       int l = 0;
-       const char *typename = NULL, *message = NULL;
-       PyObject *type, *value, *traceback, *tn, *m, *list;
-
-       PyErr_Fetch(&type, &value, &traceback);
-       PyErr_NormalizeException(&type, &value, &traceback);
-       if (type == NULL) return;
-       tn = PyObject_GetAttrString(type, "__name__"); /* New reference. */
-       m = PyObject_Str(value); /* New reference. */
-       if (tn != NULL)
-               typename = cpy_unicode_or_bytes_to_string(&tn);
-       if (m != NULL)
-               message = cpy_unicode_or_bytes_to_string(&m);
-       if (typename == NULL)
-               typename = "NamelessException";
-       if (message == NULL)
-               message = "N/A";
-       Py_BEGIN_ALLOW_THREADS
-       ERROR("Unhandled python exception in %s: %s: %s", context, typename, message);
-       Py_END_ALLOW_THREADS
-       Py_XDECREF(tn);
-       Py_XDECREF(m);
-       if (!cpy_format_exception || !traceback) {
-               PyErr_Clear();
-               Py_DECREF(type);
-               Py_XDECREF(value);
-               Py_XDECREF(traceback);
-               return;
-       }
-       list = PyObject_CallFunction(cpy_format_exception, "NNN", type, value, traceback); /* New reference. Steals references from "type", "value" and "traceback". */
-       if (list)
-               l = PyObject_Length(list);
-
-       for (int i = 0; i < l; ++i) {
-               PyObject *line;
-               char const *msg;
-               char *cpy;
-
-               line = PyList_GET_ITEM(list, i); /* Borrowed reference. */
-               Py_INCREF(line);
-
-               msg = cpy_unicode_or_bytes_to_string(&line);
-               Py_DECREF(line);
-               if (msg == NULL)
-                       continue;
-
-               cpy = strdup(msg);
-               if (cpy == NULL)
-                       continue;
-
-               if (cpy[strlen(cpy) - 1] == '\n')
-                       cpy[strlen(cpy) - 1] = 0;
-
-               Py_BEGIN_ALLOW_THREADS
-               ERROR("%s", cpy);
-               Py_END_ALLOW_THREADS
-
-               free(cpy);
-       }
-
-       Py_XDECREF(list);
-       PyErr_Clear();
+  int l = 0, collectd_error;
+  const char *typename = NULL, *message = NULL;
+  PyObject *type, *value, *traceback, *tn, *m, *list;
+
+  PyErr_Fetch(&type, &value, &traceback);
+  PyErr_NormalizeException(&type, &value, &traceback);
+  if (type == NULL)
+    return;
+  collectd_error = PyErr_GivenExceptionMatches(value, CollectdError);
+  tn = PyObject_GetAttrString(type, "__name__"); /* New reference. */
+  m = PyObject_Str(value);                       /* New reference. */
+  if (tn != NULL)
+    typename = cpy_unicode_or_bytes_to_string(&tn);
+  if (m != NULL)
+    message = cpy_unicode_or_bytes_to_string(&m);
+  if (typename == NULL)
+    typename = "NamelessException";
+  if (message == NULL)
+    message = "N/A";
+  Py_BEGIN_ALLOW_THREADS;
+  if (collectd_error) {
+    WARNING("%s in %s: %s", typename, context, message);
+  } else {
+    ERROR("Unhandled python exception in %s: %s: %s", context, typename,
+          message);
+  }
+  Py_END_ALLOW_THREADS;
+  Py_XDECREF(tn);
+  Py_XDECREF(m);
+  if (!cpy_format_exception || !traceback || collectd_error) {
+    PyErr_Clear();
+    Py_DECREF(type);
+    Py_XDECREF(value);
+    Py_XDECREF(traceback);
+    return;
+  }
+  list = PyObject_CallFunction(cpy_format_exception, "NNN", type, value,
+                               traceback); /* New reference. Steals references
+                                              from "type", "value" and
+                                              "traceback". */
+  if (list)
+    l = PyObject_Length(list);
+
+  for (int i = 0; i < l; ++i) {
+    PyObject *line;
+    char const *msg;
+    char *cpy;
+
+    line = PyList_GET_ITEM(list, i); /* Borrowed reference. */
+    Py_INCREF(line);
+
+    msg = cpy_unicode_or_bytes_to_string(&line);
+    Py_DECREF(line);
+    if (msg == NULL)
+      continue;
+
+    cpy = strdup(msg);
+    if (cpy == NULL)
+      continue;
+
+    if (cpy[strlen(cpy) - 1] == '\n')
+      cpy[strlen(cpy) - 1] = 0;
+
+    Py_BEGIN_ALLOW_THREADS;
+    ERROR("%s", cpy);
+    Py_END_ALLOW_THREADS;
+
+    free(cpy);
+  }
+
+  Py_XDECREF(list);
+  PyErr_Clear();
 }
 
 static int cpy_read_callback(user_data_t *data) {
-       cpy_callback_t *c = data->data;
-       PyObject *ret;
-
-       CPY_LOCK_THREADS
-               ret = PyObject_CallFunctionObjArgs(c->callback, c->data, (void *) 0); /* New reference. */
-               if (ret == NULL) {
-                       cpy_log_exception("read callback");
-               } else {
-                       Py_DECREF(ret);
-               }
-       CPY_RELEASE_THREADS
-       if (ret == NULL)
-               return 1;
-       return 0;
+  cpy_callback_t *c = data->data;
+  PyObject *ret;
+
+  CPY_LOCK_THREADS
+  ret = PyObject_CallFunctionObjArgs(c->callback, c->data,
+                                     (void *)0); /* New reference. */
+  if (ret == NULL) {
+    cpy_log_exception("read callback");
+  } else {
+    Py_DECREF(ret);
+  }
+  CPY_RELEASE_THREADS
+  if (ret == NULL)
+    return 1;
+  return 0;
 }
 
-static int cpy_write_callback(const data_set_t *ds, const value_list_t *value_list, user_data_t *data) {
-       cpy_callback_t *c = data->data;
-       PyObject *ret, *list, *temp, *dict = NULL;
-       Values *v;
-
-       CPY_LOCK_THREADS
-               list = PyList_New(value_list->values_len); /* New reference. */
-               if (list == NULL) {
-                       cpy_log_exception("write callback");
-                       CPY_RETURN_FROM_THREADS 0;
-               }
-               for (size_t i = 0; i < value_list->values_len; ++i) {
-                       if (ds->ds[i].type == DS_TYPE_COUNTER) {
-                               PyList_SetItem(list, i, PyLong_FromUnsignedLongLong(value_list->values[i].counter));
-                       } else if (ds->ds[i].type == DS_TYPE_GAUGE) {
-                               PyList_SetItem(list, i, PyFloat_FromDouble(value_list->values[i].gauge));
-                       } else if (ds->ds[i].type == DS_TYPE_DERIVE) {
-                               PyList_SetItem(list, i, PyLong_FromLongLong(value_list->values[i].derive));
-                       } else if (ds->ds[i].type == DS_TYPE_ABSOLUTE) {
-                               PyList_SetItem(list, i, PyLong_FromUnsignedLongLong(value_list->values[i].absolute));
-                       } else {
-                               Py_BEGIN_ALLOW_THREADS
-                               ERROR("cpy_write_callback: Unknown value type %d.", ds->ds[i].type);
-                               Py_END_ALLOW_THREADS
-                               Py_DECREF(list);
-                               CPY_RETURN_FROM_THREADS 0;
-                       }
-                       if (PyErr_Occurred() != NULL) {
-                               cpy_log_exception("value building for write callback");
-                               Py_DECREF(list);
-                               CPY_RETURN_FROM_THREADS 0;
-                       }
-               }
-               dict = PyDict_New();  /* New reference. */
-               if (value_list->meta) {
-                       char **table;
-                       meta_data_t *meta = value_list->meta;
-
-                       int num = meta_data_toc(meta, &table);
-                       for (int i = 0; i < num; ++i) {
-                               int type;
-                               char *string;
-                               int64_t si;
-                               uint64_t ui;
-                               double d;
-                               _Bool b;
-
-                               type = meta_data_type(meta, table[i]);
-                               if (type == MD_TYPE_STRING) {
-                                       if (meta_data_get_string(meta, table[i], &string))
-                                               continue;
-                                       temp = cpy_string_to_unicode_or_bytes(string);  /* New reference. */
-                                       free(string);
-                                       PyDict_SetItemString(dict, table[i], temp);
-                                       Py_XDECREF(temp);
-                               } else if (type == MD_TYPE_SIGNED_INT) {
-                                       if (meta_data_get_signed_int(meta, table[i], &si))
-                                               continue;
-                                       temp = PyObject_CallFunctionObjArgs((void *) &SignedType, PyLong_FromLongLong(si), (void *) 0);  /* New reference. */
-                                       PyDict_SetItemString(dict, table[i], temp);
-                                       Py_XDECREF(temp);
-                               } else if (type == MD_TYPE_UNSIGNED_INT) {
-                                       if (meta_data_get_unsigned_int(meta, table[i], &ui))
-                                               continue;
-                                       temp = PyObject_CallFunctionObjArgs((void *) &UnsignedType, PyLong_FromUnsignedLongLong(ui), (void *) 0);  /* New reference. */
-                                       PyDict_SetItemString(dict, table[i], temp);
-                                       Py_XDECREF(temp);
-                               } else if (type == MD_TYPE_DOUBLE) {
-                                       if (meta_data_get_double(meta, table[i], &d))
-                                               continue;
-                                       temp = PyFloat_FromDouble(d);  /* New reference. */
-                                       PyDict_SetItemString(dict, table[i], temp);
-                                       Py_XDECREF(temp);
-                               } else if (type == MD_TYPE_BOOLEAN) {
-                                       if (meta_data_get_boolean(meta, table[i], &b))
-                                               continue;
-                                       if (b)
-                                               PyDict_SetItemString(dict, table[i], Py_True);
-                                       else
-                                               PyDict_SetItemString(dict, table[i], Py_False);
-                               }
-                               free(table[i]);
-                       }
-                       free(table);
-               }
-               v = (Values *) Values_New(); /* New reference. */
-               sstrncpy(v->data.host, value_list->host, sizeof(v->data.host));
-               sstrncpy(v->data.type, value_list->type, sizeof(v->data.type));
-               sstrncpy(v->data.type_instance, value_list->type_instance, sizeof(v->data.type_instance));
-               sstrncpy(v->data.plugin, value_list->plugin, sizeof(v->data.plugin));
-               sstrncpy(v->data.plugin_instance, value_list->plugin_instance, sizeof(v->data.plugin_instance));
-               v->data.time = CDTIME_T_TO_DOUBLE(value_list->time);
-               v->interval = CDTIME_T_TO_DOUBLE(value_list->interval);
-               Py_CLEAR(v->values);
-               v->values = list;
-               Py_CLEAR(v->meta);
-               v->meta = dict;  /* Steals a reference. */
-               ret = PyObject_CallFunctionObjArgs(c->callback, v, c->data, (void *) 0); /* New reference. */
-               Py_XDECREF(v);
-               if (ret == NULL) {
-                       cpy_log_exception("write callback");
-               } else {
-                       Py_DECREF(ret);
-               }
-       CPY_RELEASE_THREADS
-       return 0;
+static int cpy_write_callback(const data_set_t *ds,
+                              const value_list_t *value_list,
+                              user_data_t *data) {
+  cpy_callback_t *c = data->data;
+  PyObject *ret, *list, *temp, *dict = NULL;
+  Values *v;
+
+  CPY_LOCK_THREADS
+  list = PyList_New(value_list->values_len); /* New reference. */
+  if (list == NULL) {
+    cpy_log_exception("write callback");
+    CPY_RETURN_FROM_THREADS 0;
+  }
+  for (size_t i = 0; i < value_list->values_len; ++i) {
+    if (ds->ds[i].type == DS_TYPE_COUNTER) {
+      PyList_SetItem(
+          list, i, PyLong_FromUnsignedLongLong(value_list->values[i].counter));
+    } else if (ds->ds[i].type == DS_TYPE_GAUGE) {
+      PyList_SetItem(list, i, PyFloat_FromDouble(value_list->values[i].gauge));
+    } else if (ds->ds[i].type == DS_TYPE_DERIVE) {
+      PyList_SetItem(list, i,
+                     PyLong_FromLongLong(value_list->values[i].derive));
+    } else if (ds->ds[i].type == DS_TYPE_ABSOLUTE) {
+      PyList_SetItem(
+          list, i, PyLong_FromUnsignedLongLong(value_list->values[i].absolute));
+    } else {
+      Py_BEGIN_ALLOW_THREADS;
+      ERROR("cpy_write_callback: Unknown value type %d.", ds->ds[i].type);
+      Py_END_ALLOW_THREADS;
+      Py_DECREF(list);
+      CPY_RETURN_FROM_THREADS 0;
+    }
+    if (PyErr_Occurred() != NULL) {
+      cpy_log_exception("value building for write callback");
+      Py_DECREF(list);
+      CPY_RETURN_FROM_THREADS 0;
+    }
+  }
+  dict = PyDict_New(); /* New reference. */
+  if (value_list->meta) {
+    char **table = NULL;
+    meta_data_t *meta = value_list->meta;
+
+    int num = meta_data_toc(meta, &table);
+    for (int i = 0; i < num; ++i) {
+      int type;
+      char *string;
+      int64_t si;
+      uint64_t ui;
+      double d;
+      _Bool b;
+
+      type = meta_data_type(meta, table[i]);
+      if (type == MD_TYPE_STRING) {
+        if (meta_data_get_string(meta, table[i], &string))
+          continue;
+        temp = cpy_string_to_unicode_or_bytes(string); /* New reference. */
+        free(string);
+        PyDict_SetItemString(dict, table[i], temp);
+        Py_XDECREF(temp);
+      } else if (type == MD_TYPE_SIGNED_INT) {
+        if (meta_data_get_signed_int(meta, table[i], &si))
+          continue;
+        PyObject *sival = PyLong_FromLongLong(si); /* New reference */
+        temp = PyObject_CallFunctionObjArgs((void *)&SignedType, sival,
+                                            (void *)0); /* New reference. */
+        PyDict_SetItemString(dict, table[i], temp);
+        Py_XDECREF(temp);
+        Py_XDECREF(sival);
+      } else if (type == MD_TYPE_UNSIGNED_INT) {
+        if (meta_data_get_unsigned_int(meta, table[i], &ui))
+          continue;
+        PyObject *uval = PyLong_FromUnsignedLongLong(ui); /* New reference */
+        temp = PyObject_CallFunctionObjArgs((void *)&UnsignedType, uval,
+                                            (void *)0); /* New reference. */
+        PyDict_SetItemString(dict, table[i], temp);
+        Py_XDECREF(temp);
+        Py_XDECREF(uval);
+      } else if (type == MD_TYPE_DOUBLE) {
+        if (meta_data_get_double(meta, table[i], &d))
+          continue;
+        temp = PyFloat_FromDouble(d); /* New reference. */
+        PyDict_SetItemString(dict, table[i], temp);
+        Py_XDECREF(temp);
+      } else if (type == MD_TYPE_BOOLEAN) {
+        if (meta_data_get_boolean(meta, table[i], &b))
+          continue;
+        if (b)
+          PyDict_SetItemString(dict, table[i], Py_True);
+        else
+          PyDict_SetItemString(dict, table[i], Py_False);
+      }
+      free(table[i]);
+    }
+    free(table);
+  }
+  v = (Values *)Values_New(); /* New reference. */
+  sstrncpy(v->data.host, value_list->host, sizeof(v->data.host));
+  sstrncpy(v->data.type, value_list->type, sizeof(v->data.type));
+  sstrncpy(v->data.type_instance, value_list->type_instance,
+           sizeof(v->data.type_instance));
+  sstrncpy(v->data.plugin, value_list->plugin, sizeof(v->data.plugin));
+  sstrncpy(v->data.plugin_instance, value_list->plugin_instance,
+           sizeof(v->data.plugin_instance));
+  v->data.time = CDTIME_T_TO_DOUBLE(value_list->time);
+  v->interval = CDTIME_T_TO_DOUBLE(value_list->interval);
+  Py_CLEAR(v->values);
+  v->values = list;
+  Py_CLEAR(v->meta);
+  v->meta = dict; /* Steals a reference. */
+  ret = PyObject_CallFunctionObjArgs(c->callback, v, c->data,
+                                     (void *)0); /* New reference. */
+  Py_XDECREF(v);
+  if (ret == NULL) {
+    cpy_log_exception("write callback");
+  } else {
+    Py_DECREF(ret);
+  }
+  CPY_RELEASE_THREADS
+  return 0;
 }
 
-static int cpy_notification_callback(const notification_t *notification, user_data_t *data) {
-       cpy_callback_t *c = data->data;
-       PyObject *ret, *notify;
-       Notification *n;
-
-       CPY_LOCK_THREADS
-               notify = Notification_New(); /* New reference. */
-               n = (Notification *) notify;
-               sstrncpy(n->data.host, notification->host, sizeof(n->data.host));
-               sstrncpy(n->data.type, notification->type, sizeof(n->data.type));
-               sstrncpy(n->data.type_instance, notification->type_instance, sizeof(n->data.type_instance));
-               sstrncpy(n->data.plugin, notification->plugin, sizeof(n->data.plugin));
-               sstrncpy(n->data.plugin_instance, notification->plugin_instance, sizeof(n->data.plugin_instance));
-               n->data.time = CDTIME_T_TO_DOUBLE(notification->time);
-               sstrncpy(n->message, notification->message, sizeof(n->message));
-               n->severity = notification->severity;
-               ret = PyObject_CallFunctionObjArgs(c->callback, n, c->data, (void *) 0); /* New reference. */
-               Py_XDECREF(notify);
-               if (ret == NULL) {
-                       cpy_log_exception("notification callback");
-               } else {
-                       Py_DECREF(ret);
-               }
-       CPY_RELEASE_THREADS
-       return 0;
+static int cpy_notification_callback(const notification_t *notification,
+                                     user_data_t *data) {
+  cpy_callback_t *c = data->data;
+  PyObject *ret, *notify;
+  Notification *n;
+
+  CPY_LOCK_THREADS
+  PyObject *dict = PyDict_New(); /* New reference. */
+  for (notification_meta_t *meta = notification->meta; meta != NULL;
+       meta = meta->next) {
+    PyObject *temp = NULL;
+    if (meta->type == NM_TYPE_STRING) {
+      temp = cpy_string_to_unicode_or_bytes(
+          meta->nm_value.nm_string); /* New reference. */
+      PyDict_SetItemString(dict, meta->name, temp);
+      Py_XDECREF(temp);
+    } else if (meta->type == NM_TYPE_SIGNED_INT) {
+      PyObject *sival = PyLong_FromLongLong(meta->nm_value.nm_signed_int);
+      temp = PyObject_CallFunctionObjArgs((void *)&SignedType, sival,
+                                          (void *)0); /* New reference. */
+      PyDict_SetItemString(dict, meta->name, temp);
+      Py_XDECREF(temp);
+      Py_XDECREF(sival);
+    } else if (meta->type == NM_TYPE_UNSIGNED_INT) {
+      PyObject *uval =
+          PyLong_FromUnsignedLongLong(meta->nm_value.nm_unsigned_int);
+      temp = PyObject_CallFunctionObjArgs((void *)&UnsignedType, uval,
+                                          (void *)0); /* New reference. */
+      PyDict_SetItemString(dict, meta->name, temp);
+      Py_XDECREF(temp);
+      Py_XDECREF(uval);
+    } else if (meta->type == NM_TYPE_DOUBLE) {
+      temp = PyFloat_FromDouble(meta->nm_value.nm_double); /* New reference. */
+      PyDict_SetItemString(dict, meta->name, temp);
+      Py_XDECREF(temp);
+    } else if (meta->type == NM_TYPE_BOOLEAN) {
+      PyDict_SetItemString(dict, meta->name,
+                           meta->nm_value.nm_boolean ? Py_True : Py_False);
+    }
+  }
+  notify = Notification_New(); /* New reference. */
+  n = (Notification *)notify;
+  sstrncpy(n->data.host, notification->host, sizeof(n->data.host));
+  sstrncpy(n->data.type, notification->type, sizeof(n->data.type));
+  sstrncpy(n->data.type_instance, notification->type_instance,
+           sizeof(n->data.type_instance));
+  sstrncpy(n->data.plugin, notification->plugin, sizeof(n->data.plugin));
+  sstrncpy(n->data.plugin_instance, notification->plugin_instance,
+           sizeof(n->data.plugin_instance));
+  n->data.time = CDTIME_T_TO_DOUBLE(notification->time);
+  sstrncpy(n->message, notification->message, sizeof(n->message));
+  n->severity = notification->severity;
+  Py_CLEAR(n->meta);
+  n->meta = dict; /* Steals a reference. */
+  ret = PyObject_CallFunctionObjArgs(c->callback, n, c->data,
+                                     (void *)0); /* New reference. */
+  Py_XDECREF(notify);
+  if (ret == NULL) {
+    cpy_log_exception("notification callback");
+  } else {
+    Py_DECREF(ret);
+  }
+  CPY_RELEASE_THREADS
+  return 0;
 }
 
-static void cpy_log_callback(int severity, const char *message, user_data_t *data) {
-       cpy_callback_t * c = data->data;
-       PyObject *ret, *text;
-
-       CPY_LOCK_THREADS
-       text = cpy_string_to_unicode_or_bytes(message);  /* New reference. */
-       if (c->data == NULL)
-               ret = PyObject_CallFunction(c->callback, "iN", severity, text); /* New reference. Steals a reference from "text". */
-       else
-               ret = PyObject_CallFunction(c->callback, "iNO", severity, text, c->data); /* New reference. Steals a reference from "text". */
-
-       if (ret == NULL) {
-               /* FIXME */
-               /* Do we really want to trigger a log callback because a log callback failed?
-                * Probably not. */
-               PyErr_Print();
-               /* In case someone wanted to be clever, replaced stderr and failed at that. */
-               PyErr_Clear();
-       } else {
-               Py_DECREF(ret);
-       }
-       CPY_RELEASE_THREADS
+static void cpy_log_callback(int severity, const char *message,
+                             user_data_t *data) {
+  cpy_callback_t *c = data->data;
+  PyObject *ret, *text;
+
+  CPY_LOCK_THREADS
+  text = cpy_string_to_unicode_or_bytes(message); /* New reference. */
+  if (c->data == NULL)
+    ret = PyObject_CallFunction(
+        c->callback, "iN", severity,
+        text); /* New reference. Steals a reference from "text". */
+  else
+    ret = PyObject_CallFunction(
+        c->callback, "iNO", severity, text,
+        c->data); /* New reference. Steals a reference from "text". */
+
+  if (ret == NULL) {
+    /* FIXME */
+    /* Do we really want to trigger a log callback because a log callback
+     * failed?
+     * Probably not. */
+    PyErr_Print();
+    /* In case someone wanted to be clever, replaced stderr and failed at that.
+     */
+    PyErr_Clear();
+  } else {
+    Py_DECREF(ret);
+  }
+  CPY_RELEASE_THREADS
 }
 
 static void cpy_flush_callback(int timeout, const char *id, user_data_t *data) {
-       cpy_callback_t * c = data->data;
-       PyObject *ret, *text;
-
-       CPY_LOCK_THREADS
-       if (id) {
-               text = cpy_string_to_unicode_or_bytes(id);
-       } else {
-               text = Py_None;
-               Py_INCREF(text);
-       }
-       if (c->data == NULL)
-               ret = PyObject_CallFunction(c->callback, "iN", timeout, text); /* New reference. */
-       else
-               ret = PyObject_CallFunction(c->callback, "iNO", timeout, text, c->data); /* New reference. */
-
-       if (ret == NULL) {
-               cpy_log_exception("flush callback");
-       } else {
-               Py_DECREF(ret);
-       }
-       CPY_RELEASE_THREADS
+  cpy_callback_t *c = data->data;
+  PyObject *ret, *text;
+
+  CPY_LOCK_THREADS
+  if (id) {
+    text = cpy_string_to_unicode_or_bytes(id);
+  } else {
+    text = Py_None;
+    Py_INCREF(text);
+  }
+  if (c->data == NULL)
+    ret = PyObject_CallFunction(c->callback, "iN", timeout,
+                                text); /* New reference. */
+  else
+    ret = PyObject_CallFunction(c->callback, "iNO", timeout, text,
+                                c->data); /* New reference. */
+
+  if (ret == NULL) {
+    cpy_log_exception("flush callback");
+  } else {
+    Py_DECREF(ret);
+  }
+  CPY_RELEASE_THREADS
 }
 
-static PyObject *cpy_register_generic(cpy_callback_t **list_head, PyObject *args, PyObject *kwds) {
-       char buf[512];
-       cpy_callback_t *c;
-       char *name = NULL;
-       PyObject *callback = NULL, *data = NULL, *mod = NULL;
-       static char *kwlist[] = {"callback", "data", "name", NULL};
-
-       if (PyArg_ParseTupleAndKeywords(args, kwds, "O|Oet", kwlist, &callback, &data, NULL, &name) == 0) return NULL;
-       if (PyCallable_Check(callback) == 0) {
-               PyMem_Free(name);
-               PyErr_SetString(PyExc_TypeError, "callback needs a be a callable object.");
-               return NULL;
-       }
-       cpy_build_name(buf, sizeof(buf), callback, name);
-
-       Py_INCREF(callback);
-       Py_XINCREF(data);
-
-       c = calloc(1, sizeof(*c));
-       if (c == NULL)
-               return NULL;
-
-       c->name = strdup(buf);
-       c->callback = callback;
-       c->data = data;
-       c->next = *list_head;
-       ++cpy_num_callbacks;
-       *list_head = c;
-       Py_XDECREF(mod);
-       PyMem_Free(name);
-       return cpy_string_to_unicode_or_bytes(buf);
+static PyObject *cpy_register_generic(cpy_callback_t **list_head,
+                                      PyObject *args, PyObject *kwds) {
+  char buf[512];
+  cpy_callback_t *c;
+  char *name = NULL;
+  PyObject *callback = NULL, *data = NULL, *mod = NULL;
+  static char *kwlist[] = {"callback", "data", "name", NULL};
+
+  if (PyArg_ParseTupleAndKeywords(args, kwds, "O|Oet", kwlist, &callback, &data,
+                                  NULL, &name) == 0)
+    return NULL;
+  if (PyCallable_Check(callback) == 0) {
+    PyMem_Free(name);
+    PyErr_SetString(PyExc_TypeError, "callback needs a be a callable object.");
+    return NULL;
+  }
+  cpy_build_name(buf, sizeof(buf), callback, name);
+
+  Py_INCREF(callback);
+  Py_XINCREF(data);
+
+  c = calloc(1, sizeof(*c));
+  if (c == NULL)
+    return NULL;
+
+  c->name = strdup(buf);
+  c->callback = callback;
+  c->data = data;
+  c->next = *list_head;
+  ++cpy_num_callbacks;
+  *list_head = c;
+  Py_XDECREF(mod);
+  PyMem_Free(name);
+  return cpy_string_to_unicode_or_bytes(buf);
 }
 
 static PyObject *float_or_none(float number) {
-       if (isnan(number)) {
-               Py_RETURN_NONE;
-       }
-       return PyFloat_FromDouble(number);
+  if (isnan(number)) {
+    Py_RETURN_NONE;
+  }
+  return PyFloat_FromDouble(number);
 }
 
 static PyObject *cpy_get_dataset(PyObject *self, PyObject *args) {
-       char *name;
-       const data_set_t *ds;
-       PyObject *list, *tuple;
-
-       if (PyArg_ParseTuple(args, "et", NULL, &name) == 0) return NULL;
-       ds = plugin_get_ds(name);
-       PyMem_Free(name);
-       if (ds == NULL) {
-               PyErr_Format(PyExc_TypeError, "Dataset %s not found", name);
-               return NULL;
-       }
-       list = PyList_New(ds->ds_num); /* New reference. */
-       for (size_t i = 0; i < ds->ds_num; ++i) {
-               tuple = PyTuple_New(4);
-               PyTuple_SET_ITEM(tuple, 0, cpy_string_to_unicode_or_bytes(ds->ds[i].name));
-               PyTuple_SET_ITEM(tuple, 1, cpy_string_to_unicode_or_bytes(DS_TYPE_TO_STRING(ds->ds[i].type)));
-               PyTuple_SET_ITEM(tuple, 2, float_or_none(ds->ds[i].min));
-               PyTuple_SET_ITEM(tuple, 3, float_or_none(ds->ds[i].max));
-               PyList_SET_ITEM(list, i, tuple);
-       }
-       return list;
+  char *name;
+  const data_set_t *ds;
+  PyObject *list, *tuple;
+
+  if (PyArg_ParseTuple(args, "et", NULL, &name) == 0)
+    return NULL;
+  ds = plugin_get_ds(name);
+  PyMem_Free(name);
+  if (ds == NULL) {
+    PyErr_Format(PyExc_TypeError, "Dataset %s not found", name);
+    return NULL;
+  }
+  list = PyList_New(ds->ds_num); /* New reference. */
+  for (size_t i = 0; i < ds->ds_num; ++i) {
+    tuple = PyTuple_New(4);
+    PyTuple_SET_ITEM(tuple, 0, cpy_string_to_unicode_or_bytes(ds->ds[i].name));
+    PyTuple_SET_ITEM(
+        tuple, 1,
+        cpy_string_to_unicode_or_bytes(DS_TYPE_TO_STRING(ds->ds[i].type)));
+    PyTuple_SET_ITEM(tuple, 2, float_or_none(ds->ds[i].min));
+    PyTuple_SET_ITEM(tuple, 3, float_or_none(ds->ds[i].max));
+    PyList_SET_ITEM(list, i, tuple);
+  }
+  return list;
 }
 
 static PyObject *cpy_flush(PyObject *self, PyObject *args, PyObject *kwds) {
-       int timeout = -1;
-       char *plugin = NULL, *identifier = NULL;
-       static char *kwlist[] = {"plugin", "timeout", "identifier", NULL};
-
-       if (PyArg_ParseTupleAndKeywords(args, kwds, "|etiet", kwlist, NULL, &plugin, &timeout, NULL, &identifier) == 0) return NULL;
-       Py_BEGIN_ALLOW_THREADS
-       plugin_flush(plugin, timeout, identifier);
-       Py_END_ALLOW_THREADS
-       PyMem_Free(plugin);
-       PyMem_Free(identifier);
-       Py_RETURN_NONE;
+  int timeout = -1;
+  char *plugin = NULL, *identifier = NULL;
+  static char *kwlist[] = {"plugin", "timeout", "identifier", NULL};
+
+  if (PyArg_ParseTupleAndKeywords(args, kwds, "|etiet", kwlist, NULL, &plugin,
+                                  &timeout, NULL, &identifier) == 0)
+    return NULL;
+  Py_BEGIN_ALLOW_THREADS;
+  plugin_flush(plugin, timeout, identifier);
+  Py_END_ALLOW_THREADS;
+  PyMem_Free(plugin);
+  PyMem_Free(identifier);
+  Py_RETURN_NONE;
 }
 
-static PyObject *cpy_register_config(PyObject *self, PyObject *args, PyObject *kwds) {
-       return cpy_register_generic(&cpy_config_callbacks, args, kwds);
+static PyObject *cpy_register_config(PyObject *self, PyObject *args,
+                                     PyObject *kwds) {
+  return cpy_register_generic(&cpy_config_callbacks, args, kwds);
 }
 
-static PyObject *cpy_register_init(PyObject *self, PyObject *args, PyObject *kwds) {
-       return cpy_register_generic(&cpy_init_callbacks, args, kwds);
+static PyObject *cpy_register_init(PyObject *self, PyObject *args,
+                                   PyObject *kwds) {
+  return cpy_register_generic(&cpy_init_callbacks, args, kwds);
 }
 
 typedef int reg_function_t(const char *name, void *callback, void *data);
 
-static PyObject *cpy_register_generic_userdata(void *reg, void *handler, PyObject *args, PyObject *kwds) {
-       char buf[512];
-       reg_function_t *register_function = (reg_function_t *) reg;
-       cpy_callback_t *c = NULL;
-       char *name = NULL;
-       PyObject *callback = NULL, *data = NULL;
-       static char *kwlist[] = {"callback", "data", "name", NULL};
-
-       if (PyArg_ParseTupleAndKeywords(args, kwds, "O|Oet", kwlist, &callback, &data, NULL, &name) == 0) return NULL;
-       if (PyCallable_Check(callback) == 0) {
-               PyMem_Free(name);
-               PyErr_SetString(PyExc_TypeError, "callback needs a be a callable object.");
-               return NULL;
-       }
-       cpy_build_name(buf, sizeof(buf), callback, name);
-       PyMem_Free(name);
-
-       Py_INCREF(callback);
-       Py_XINCREF(data);
-
-       c = calloc(1, sizeof(*c));
-       if (c == NULL)
-               return NULL;
-
-       c->name = strdup(buf);
-       c->callback = callback;
-       c->data = data;
-       c->next = NULL;
-
-       register_function(buf, handler, &(user_data_t) {
-                               .data = c,
-                               .free_func = cpy_destroy_user_data,
-                       });
-
-       ++cpy_num_callbacks;
-       return cpy_string_to_unicode_or_bytes(buf);
+static PyObject *cpy_register_generic_userdata(void *reg, void *handler,
+                                               PyObject *args, PyObject *kwds) {
+  char buf[512];
+  reg_function_t *register_function = (reg_function_t *)reg;
+  cpy_callback_t *c = NULL;
+  char *name = NULL;
+  PyObject *callback = NULL, *data = NULL;
+  static char *kwlist[] = {"callback", "data", "name", NULL};
+
+  if (PyArg_ParseTupleAndKeywords(args, kwds, "O|Oet", kwlist, &callback, &data,
+                                  NULL, &name) == 0)
+    return NULL;
+  if (PyCallable_Check(callback) == 0) {
+    PyMem_Free(name);
+    PyErr_SetString(PyExc_TypeError, "callback needs a be a callable object.");
+    return NULL;
+  }
+  cpy_build_name(buf, sizeof(buf), callback, name);
+  PyMem_Free(name);
+
+  Py_INCREF(callback);
+  Py_XINCREF(data);
+
+  c = calloc(1, sizeof(*c));
+  if (c == NULL)
+    return NULL;
+
+  c->name = strdup(buf);
+  c->callback = callback;
+  c->data = data;
+  c->next = NULL;
+
+  register_function(buf, handler,
+                    &(user_data_t){
+                        .data = c,
+                        .free_func = cpy_destroy_user_data,
+                    });
+
+  ++cpy_num_callbacks;
+  return cpy_string_to_unicode_or_bytes(buf);
 }
 
-static PyObject *cpy_register_read(PyObject *self, PyObject *args, PyObject *kwds) {
-       char buf[512];
-       cpy_callback_t *c = NULL;
-       double interval = 0;
-       char *name = NULL;
-       PyObject *callback = NULL, *data = NULL;
-       static char *kwlist[] = {"callback", "interval", "data", "name", NULL};
-
-       if (PyArg_ParseTupleAndKeywords(args, kwds, "O|dOet", kwlist, &callback, &interval, &data, NULL, &name) == 0) return NULL;
-       if (PyCallable_Check(callback) == 0) {
-               PyMem_Free(name);
-               PyErr_SetString(PyExc_TypeError, "callback needs a be a callable object.");
-               return NULL;
-       }
-       cpy_build_name(buf, sizeof(buf), callback, name);
-       PyMem_Free(name);
-
-       Py_INCREF(callback);
-       Py_XINCREF(data);
-
-       c = calloc(1, sizeof(*c));
-       if (c == NULL)
-               return NULL;
-
-       c->name = strdup(buf);
-       c->callback = callback;
-       c->data = data;
-       c->next = NULL;
-
-       plugin_register_complex_read(/* group = */ "python", buf,
-                       cpy_read_callback, DOUBLE_TO_CDTIME_T (interval),
-                       &(user_data_t) {
-                               .data = c,
-                               .free_func = cpy_destroy_user_data,
-                       });
-       ++cpy_num_callbacks;
-       return cpy_string_to_unicode_or_bytes(buf);
+static PyObject *cpy_register_read(PyObject *self, PyObject *args,
+                                   PyObject *kwds) {
+  char buf[512];
+  cpy_callback_t *c = NULL;
+  double interval = 0;
+  char *name = NULL;
+  PyObject *callback = NULL, *data = NULL;
+  static char *kwlist[] = {"callback", "interval", "data", "name", NULL};
+
+  if (PyArg_ParseTupleAndKeywords(args, kwds, "O|dOet", kwlist, &callback,
+                                  &interval, &data, NULL, &name) == 0)
+    return NULL;
+  if (PyCallable_Check(callback) == 0) {
+    PyMem_Free(name);
+    PyErr_SetString(PyExc_TypeError, "callback needs a be a callable object.");
+    return NULL;
+  }
+  cpy_build_name(buf, sizeof(buf), callback, name);
+  PyMem_Free(name);
+
+  Py_INCREF(callback);
+  Py_XINCREF(data);
+
+  c = calloc(1, sizeof(*c));
+  if (c == NULL)
+    return NULL;
+
+  c->name = strdup(buf);
+  c->callback = callback;
+  c->data = data;
+  c->next = NULL;
+
+  plugin_register_complex_read(
+      /* group = */ "python", buf, cpy_read_callback,
+      DOUBLE_TO_CDTIME_T(interval),
+      &(user_data_t){
+          .data = c,
+          .free_func = cpy_destroy_user_data,
+      });
+  ++cpy_num_callbacks;
+  return cpy_string_to_unicode_or_bytes(buf);
 }
 
-static PyObject *cpy_register_log(PyObject *self, PyObject *args, PyObject *kwds) {
-       return cpy_register_generic_userdata((void *) plugin_register_log,
-                       (void *) cpy_log_callback, args, kwds);
+static PyObject *cpy_register_log(PyObject *self, PyObject *args,
+                                  PyObject *kwds) {
+  return cpy_register_generic_userdata((void *)plugin_register_log,
+                                       (void *)cpy_log_callback, args, kwds);
 }
 
-static PyObject *cpy_register_write(PyObject *self, PyObject *args, PyObject *kwds) {
-       return cpy_register_generic_userdata((void *) plugin_register_write,
-                       (void *) cpy_write_callback, args, kwds);
+static PyObject *cpy_register_write(PyObject *self, PyObject *args,
+                                    PyObject *kwds) {
+  return cpy_register_generic_userdata((void *)plugin_register_write,
+                                       (void *)cpy_write_callback, args, kwds);
 }
 
-static PyObject *cpy_register_notification(PyObject *self, PyObject *args, PyObject *kwds) {
-       return cpy_register_generic_userdata((void *) plugin_register_notification,
-                       (void *) cpy_notification_callback, args, kwds);
+static PyObject *cpy_register_notification(PyObject *self, PyObject *args,
+                                           PyObject *kwds) {
+  return cpy_register_generic_userdata((void *)plugin_register_notification,
+                                       (void *)cpy_notification_callback, args,
+                                       kwds);
 }
 
-static PyObject *cpy_register_flush(PyObject *self, PyObject *args, PyObject *kwds) {
-       return cpy_register_generic_userdata((void *) plugin_register_flush,
-                       (void *) cpy_flush_callback, args, kwds);
+static PyObject *cpy_register_flush(PyObject *self, PyObject *args,
+                                    PyObject *kwds) {
+  return cpy_register_generic_userdata((void *)plugin_register_flush,
+                                       (void *)cpy_flush_callback, args, kwds);
 }
 
-static PyObject *cpy_register_shutdown(PyObject *self, PyObject *args, PyObject *kwds) {
-       return cpy_register_generic(&cpy_shutdown_callbacks, args, kwds);
+static PyObject *cpy_register_shutdown(PyObject *self, PyObject *args,
+                                       PyObject *kwds) {
+  return cpy_register_generic(&cpy_shutdown_callbacks, args, kwds);
 }
 
 static PyObject *cpy_error(PyObject *self, PyObject *args) {
-       char *text;
-       if (PyArg_ParseTuple(args, "et", NULL, &text) == 0) return NULL;
-       Py_BEGIN_ALLOW_THREADS
-       plugin_log(LOG_ERR, "%s", text);
-       Py_END_ALLOW_THREADS
-       PyMem_Free(text);
-       Py_RETURN_NONE;
+  char *text;
+  if (PyArg_ParseTuple(args, "et", NULL, &text) == 0)
+    return NULL;
+  Py_BEGIN_ALLOW_THREADS;
+  plugin_log(LOG_ERR, "%s", text);
+  Py_END_ALLOW_THREADS;
+  PyMem_Free(text);
+  Py_RETURN_NONE;
 }
 
 static PyObject *cpy_warning(PyObject *self, PyObject *args) {
-       char *text;
-       if (PyArg_ParseTuple(args, "et", NULL, &text) == 0) return NULL;
-       Py_BEGIN_ALLOW_THREADS
-       plugin_log(LOG_WARNING, "%s", text);
-       Py_END_ALLOW_THREADS
-       PyMem_Free(text);
-       Py_RETURN_NONE;
+  char *text;
+  if (PyArg_ParseTuple(args, "et", NULL, &text) == 0)
+    return NULL;
+  Py_BEGIN_ALLOW_THREADS;
+  plugin_log(LOG_WARNING, "%s", text);
+  Py_END_ALLOW_THREADS;
+  PyMem_Free(text);
+  Py_RETURN_NONE;
 }
 
 static PyObject *cpy_notice(PyObject *self, PyObject *args) {
-       char *text;
-       if (PyArg_ParseTuple(args, "et", NULL, &text) == 0) return NULL;
-       Py_BEGIN_ALLOW_THREADS
-       plugin_log(LOG_NOTICE, "%s", text);
-       Py_END_ALLOW_THREADS
-       PyMem_Free(text);
-       Py_RETURN_NONE;
+  char *text;
+  if (PyArg_ParseTuple(args, "et", NULL, &text) == 0)
+    return NULL;
+  Py_BEGIN_ALLOW_THREADS;
+  plugin_log(LOG_NOTICE, "%s", text);
+  Py_END_ALLOW_THREADS;
+  PyMem_Free(text);
+  Py_RETURN_NONE;
 }
 
 static PyObject *cpy_info(PyObject *self, PyObject *args) {
-       char *text;
-       if (PyArg_ParseTuple(args, "et", NULL, &text) == 0) return NULL;
-       Py_BEGIN_ALLOW_THREADS
-       plugin_log(LOG_INFO, "%s", text);
-       Py_END_ALLOW_THREADS
-       PyMem_Free(text);
-       Py_RETURN_NONE;
+  char *text;
+  if (PyArg_ParseTuple(args, "et", NULL, &text) == 0)
+    return NULL;
+  Py_BEGIN_ALLOW_THREADS;
+  plugin_log(LOG_INFO, "%s", text);
+  Py_END_ALLOW_THREADS;
+  PyMem_Free(text);
+  Py_RETURN_NONE;
 }
 
 static PyObject *cpy_debug(PyObject *self, PyObject *args) {
 #ifdef COLLECT_DEBUG
-       char *text;
-       if (PyArg_ParseTuple(args, "et", NULL, &text) == 0) return NULL;
-       Py_BEGIN_ALLOW_THREADS
-       plugin_log(LOG_DEBUG, "%s", text);
-       Py_END_ALLOW_THREADS
-       PyMem_Free(text);
+  char *text;
+  if (PyArg_ParseTuple(args, "et", NULL, &text) == 0)
+    return NULL;
+  Py_BEGIN_ALLOW_THREADS;
+  plugin_log(LOG_DEBUG, "%s", text);
+  Py_END_ALLOW_THREADS;
+  PyMem_Free(text);
 #endif
-       Py_RETURN_NONE;
+  Py_RETURN_NONE;
 }
 
-static PyObject *cpy_unregister_generic(cpy_callback_t **list_head, PyObject *arg, const char *desc) {
-       char buf[512];
-       const char *name;
-       cpy_callback_t *prev = NULL, *tmp;
-
-       Py_INCREF(arg);
-       name = cpy_unicode_or_bytes_to_string(&arg);
-       if (name == NULL) {
-               PyErr_Clear();
-               if (!PyCallable_Check(arg)) {
-                       PyErr_SetString(PyExc_TypeError, "This function needs a string or a callable object as its only parameter.");
-                       Py_DECREF(arg);
-                       return NULL;
-               }
-               cpy_build_name(buf, sizeof(buf), arg, NULL);
-               name = buf;
-       }
-       for (tmp = *list_head; tmp; prev = tmp, tmp = tmp->next)
-               if (strcmp(name, tmp->name) == 0)
-                       break;
-
-       Py_DECREF(arg);
-       if (tmp == NULL) {
-               PyErr_Format(PyExc_RuntimeError, "Unable to unregister %s callback '%s'.", desc, name);
-               return NULL;
-       }
-       /* Yes, this is actually safe. To call this function the caller has to
-        * hold the GIL. Well, safe as long as there is only one GIL anyway ... */
-       if (prev == NULL)
-               *list_head = tmp->next;
-       else
-               prev->next = tmp->next;
-       cpy_destroy_user_data(tmp);
-       Py_RETURN_NONE;
+static PyObject *cpy_unregister_generic(cpy_callback_t **list_head,
+                                        PyObject *arg, const char *desc) {
+  char buf[512];
+  const char *name;
+  cpy_callback_t *prev = NULL, *tmp;
+
+  Py_INCREF(arg);
+  name = cpy_unicode_or_bytes_to_string(&arg);
+  if (name == NULL) {
+    PyErr_Clear();
+    if (!PyCallable_Check(arg)) {
+      PyErr_SetString(PyExc_TypeError, "This function needs a string or a "
+                                       "callable object as its only "
+                                       "parameter.");
+      Py_DECREF(arg);
+      return NULL;
+    }
+    cpy_build_name(buf, sizeof(buf), arg, NULL);
+    name = buf;
+  }
+  for (tmp = *list_head; tmp; prev = tmp, tmp = tmp->next)
+    if (strcmp(name, tmp->name) == 0)
+      break;
+
+  Py_DECREF(arg);
+  if (tmp == NULL) {
+    PyErr_Format(PyExc_RuntimeError, "Unable to unregister %s callback '%s'.",
+                 desc, name);
+    return NULL;
+  }
+  /* Yes, this is actually safe. To call this function the caller has to
+   * hold the GIL. Well, safe as long as there is only one GIL anyway ... */
+  if (prev == NULL)
+    *list_head = tmp->next;
+  else
+    prev->next = tmp->next;
+  cpy_destroy_user_data(tmp);
+  Py_RETURN_NONE;
 }
 
 static void cpy_unregister_list(cpy_callback_t **list_head) {
-       cpy_callback_t *cur, *next;
-       for (cur = *list_head; cur; cur = next) {
-               next = cur->next;
-               cpy_destroy_user_data(cur);
-       }
-       *list_head = NULL;
+  cpy_callback_t *cur, *next;
+  for (cur = *list_head; cur; cur = next) {
+    next = cur->next;
+    cpy_destroy_user_data(cur);
+  }
+  *list_head = NULL;
 }
 
 typedef int cpy_unregister_function_t(const char *name);
 
-static PyObject *cpy_unregister_generic_userdata(cpy_unregister_function_t *unreg, PyObject *arg, const char *desc) {
-       char buf[512];
-       const char *name;
-
-       Py_INCREF(arg);
-       name = cpy_unicode_or_bytes_to_string(&arg);
-       if (name == NULL) {
-               PyErr_Clear();
-               if (!PyCallable_Check(arg)) {
-                       PyErr_SetString(PyExc_TypeError, "This function needs a string or a callable object as its only parameter.");
-                       Py_DECREF(arg);
-                       return NULL;
-               }
-               cpy_build_name(buf, sizeof(buf), arg, NULL);
-               name = buf;
-       }
-       if (unreg(name) == 0) {
-               Py_DECREF(arg);
-               Py_RETURN_NONE;
-       }
-       PyErr_Format(PyExc_RuntimeError, "Unable to unregister %s callback '%s'.", desc, name);
-       Py_DECREF(arg);
-       return NULL;
+static PyObject *
+cpy_unregister_generic_userdata(cpy_unregister_function_t *unreg, PyObject *arg,
+                                const char *desc) {
+  char buf[512];
+  const char *name;
+
+  Py_INCREF(arg);
+  name = cpy_unicode_or_bytes_to_string(&arg);
+  if (name == NULL) {
+    PyErr_Clear();
+    if (!PyCallable_Check(arg)) {
+      PyErr_SetString(PyExc_TypeError, "This function needs a string or a "
+                                       "callable object as its only "
+                                       "parameter.");
+      Py_DECREF(arg);
+      return NULL;
+    }
+    cpy_build_name(buf, sizeof(buf), arg, NULL);
+    name = buf;
+  }
+  if (unreg(name) == 0) {
+    Py_DECREF(arg);
+    Py_RETURN_NONE;
+  }
+  PyErr_Format(PyExc_RuntimeError, "Unable to unregister %s callback '%s'.",
+               desc, name);
+  Py_DECREF(arg);
+  return NULL;
 }
 
 static PyObject *cpy_unregister_log(PyObject *self, PyObject *arg) {
-       return cpy_unregister_generic_userdata(plugin_unregister_log, arg, "log");
+  return cpy_unregister_generic_userdata(plugin_unregister_log, arg, "log");
 }
 
 static PyObject *cpy_unregister_init(PyObject *self, PyObject *arg) {
-       return cpy_unregister_generic(&cpy_init_callbacks, arg, "init");
+  return cpy_unregister_generic(&cpy_init_callbacks, arg, "init");
 }
 
 static PyObject *cpy_unregister_config(PyObject *self, PyObject *arg) {
-       return cpy_unregister_generic(&cpy_config_callbacks, arg, "config");
+  return cpy_unregister_generic(&cpy_config_callbacks, arg, "config");
 }
 
 static PyObject *cpy_unregister_read(PyObject *self, PyObject *arg) {
-       return cpy_unregister_generic_userdata(plugin_unregister_read, arg, "read");
+  return cpy_unregister_generic_userdata(plugin_unregister_read, arg, "read");
 }
 
 static PyObject *cpy_unregister_write(PyObject *self, PyObject *arg) {
-       return cpy_unregister_generic_userdata(plugin_unregister_write, arg, "write");
+  return cpy_unregister_generic_userdata(plugin_unregister_write, arg, "write");
 }
 
 static PyObject *cpy_unregister_notification(PyObject *self, PyObject *arg) {
-       return cpy_unregister_generic_userdata(plugin_unregister_notification, arg, "notification");
+  return cpy_unregister_generic_userdata(plugin_unregister_notification, arg,
+                                         "notification");
 }
 
 static PyObject *cpy_unregister_flush(PyObject *self, PyObject *arg) {
-       return cpy_unregister_generic_userdata(plugin_unregister_flush, arg, "flush");
+  return cpy_unregister_generic_userdata(plugin_unregister_flush, arg, "flush");
 }
 
 static PyObject *cpy_unregister_shutdown(PyObject *self, PyObject *arg) {
-       return cpy_unregister_generic(&cpy_shutdown_callbacks, arg, "shutdown");
+  return cpy_unregister_generic(&cpy_shutdown_callbacks, arg, "shutdown");
 }
 
 static PyMethodDef cpy_methods[] = {
-       {"debug", cpy_debug, METH_VARARGS, log_doc},
-       {"info", cpy_info, METH_VARARGS, log_doc},
-       {"notice", cpy_notice, METH_VARARGS, log_doc},
-       {"warning", cpy_warning, METH_VARARGS, log_doc},
-       {"error", cpy_error, METH_VARARGS, log_doc},
-       {"get_dataset", (PyCFunction) cpy_get_dataset, METH_VARARGS, get_ds_doc},
-       {"flush", (PyCFunction) cpy_flush, METH_VARARGS | METH_KEYWORDS, flush_doc},
-       {"register_log", (PyCFunction) cpy_register_log, METH_VARARGS | METH_KEYWORDS, reg_log_doc},
-       {"register_init", (PyCFunction) cpy_register_init, METH_VARARGS | METH_KEYWORDS, reg_init_doc},
-       {"register_config", (PyCFunction) cpy_register_config, METH_VARARGS | METH_KEYWORDS, reg_config_doc},
-       {"register_read", (PyCFunction) cpy_register_read, METH_VARARGS | METH_KEYWORDS, reg_read_doc},
-       {"register_write", (PyCFunction) cpy_register_write, METH_VARARGS | METH_KEYWORDS, reg_write_doc},
-       {"register_notification", (PyCFunction) cpy_register_notification, METH_VARARGS | METH_KEYWORDS, reg_notification_doc},
-       {"register_flush", (PyCFunction) cpy_register_flush, METH_VARARGS | METH_KEYWORDS, reg_flush_doc},
-       {"register_shutdown", (PyCFunction) cpy_register_shutdown, METH_VARARGS | METH_KEYWORDS, reg_shutdown_doc},
-       {"unregister_log", cpy_unregister_log, METH_O, unregister_doc},
-       {"unregister_init", cpy_unregister_init, METH_O, unregister_doc},
-       {"unregister_config", cpy_unregister_config, METH_O, unregister_doc},
-       {"unregister_read", cpy_unregister_read, METH_O, unregister_doc},
-       {"unregister_write", cpy_unregister_write, METH_O, unregister_doc},
-       {"unregister_notification", cpy_unregister_notification, METH_O, unregister_doc},
-       {"unregister_flush", cpy_unregister_flush, METH_O, unregister_doc},
-       {"unregister_shutdown", cpy_unregister_shutdown, METH_O, unregister_doc},
-       {0, 0, 0, 0}
-};
+    {"debug", cpy_debug, METH_VARARGS, log_doc},
+    {"info", cpy_info, METH_VARARGS, log_doc},
+    {"notice", cpy_notice, METH_VARARGS, log_doc},
+    {"warning", cpy_warning, METH_VARARGS, log_doc},
+    {"error", cpy_error, METH_VARARGS, log_doc},
+    {"get_dataset", (PyCFunction)cpy_get_dataset, METH_VARARGS, get_ds_doc},
+    {"flush", (PyCFunction)cpy_flush, METH_VARARGS | METH_KEYWORDS, flush_doc},
+    {"register_log", (PyCFunction)cpy_register_log,
+     METH_VARARGS | METH_KEYWORDS, reg_log_doc},
+    {"register_init", (PyCFunction)cpy_register_init,
+     METH_VARARGS | METH_KEYWORDS, reg_init_doc},
+    {"register_config", (PyCFunction)cpy_register_config,
+     METH_VARARGS | METH_KEYWORDS, reg_config_doc},
+    {"register_read", (PyCFunction)cpy_register_read,
+     METH_VARARGS | METH_KEYWORDS, reg_read_doc},
+    {"register_write", (PyCFunction)cpy_register_write,
+     METH_VARARGS | METH_KEYWORDS, reg_write_doc},
+    {"register_notification", (PyCFunction)cpy_register_notification,
+     METH_VARARGS | METH_KEYWORDS, reg_notification_doc},
+    {"register_flush", (PyCFunction)cpy_register_flush,
+     METH_VARARGS | METH_KEYWORDS, reg_flush_doc},
+    {"register_shutdown", (PyCFunction)cpy_register_shutdown,
+     METH_VARARGS | METH_KEYWORDS, reg_shutdown_doc},
+    {"unregister_log", cpy_unregister_log, METH_O, unregister_doc},
+    {"unregister_init", cpy_unregister_init, METH_O, unregister_doc},
+    {"unregister_config", cpy_unregister_config, METH_O, unregister_doc},
+    {"unregister_read", cpy_unregister_read, METH_O, unregister_doc},
+    {"unregister_write", cpy_unregister_write, METH_O, unregister_doc},
+    {"unregister_notification", cpy_unregister_notification, METH_O,
+     unregister_doc},
+    {"unregister_flush", cpy_unregister_flush, METH_O, unregister_doc},
+    {"unregister_shutdown", cpy_unregister_shutdown, METH_O, unregister_doc},
+    {0, 0, 0, 0}};
 
 static int cpy_shutdown(void) {
-       PyObject *ret;
-
-       if (!state) {
-               printf("================================================================\n");
-               printf("collectd shutdown while running an interactive session. This will\n");
-               printf("probably leave your terminal in a mess.\n");
-               printf("Run the command \"reset\" to get it back into a usable state.\n");
-               printf("You can press Ctrl+D in the interactive session to\n");
-               printf("close collectd and avoid this problem in the future.\n");
-               printf("================================================================\n");
-       }
-
-       CPY_LOCK_THREADS
-
-       for (cpy_callback_t *c = cpy_shutdown_callbacks; c; c = c->next) {
-               ret = PyObject_CallFunctionObjArgs(c->callback, c->data, (void *) 0); /* New reference. */
-               if (ret == NULL)
-                       cpy_log_exception("shutdown callback");
-               else
-                       Py_DECREF(ret);
-       }
-       PyErr_Print();
-
-       Py_BEGIN_ALLOW_THREADS
-       cpy_unregister_list(&cpy_config_callbacks);
-       cpy_unregister_list(&cpy_init_callbacks);
-       cpy_unregister_list(&cpy_shutdown_callbacks);
-       cpy_shutdown_triggered = 1;
-       Py_END_ALLOW_THREADS
-
-       if (!cpy_num_callbacks) {
-               Py_Finalize();
-               return 0;
-       }
-
-       CPY_RELEASE_THREADS
-       return 0;
+  PyObject *ret;
+
+  if (!state) {
+    printf(
+        "================================================================\n");
+    printf(
+        "collectd shutdown while running an interactive session. This will\n");
+    printf("probably leave your terminal in a mess.\n");
+    printf("Run the command \"reset\" to get it back into a usable state.\n");
+    printf("You can press Ctrl+D in the interactive session to\n");
+    printf("close collectd and avoid this problem in the future.\n");
+    printf(
+        "================================================================\n");
+  }
+
+  CPY_LOCK_THREADS
+
+  for (cpy_callback_t *c = cpy_shutdown_callbacks; c; c = c->next) {
+    ret = PyObject_CallFunctionObjArgs(c->callback, c->data,
+                                       (void *)0); /* New reference. */
+    if (ret == NULL)
+      cpy_log_exception("shutdown callback");
+    else
+      Py_DECREF(ret);
+  }
+  PyErr_Print();
+
+  Py_BEGIN_ALLOW_THREADS;
+  cpy_unregister_list(&cpy_config_callbacks);
+  cpy_unregister_list(&cpy_init_callbacks);
+  cpy_unregister_list(&cpy_shutdown_callbacks);
+  cpy_shutdown_triggered = 1;
+  Py_END_ALLOW_THREADS;
+
+  if (!cpy_num_callbacks) {
+    Py_Finalize();
+    return 0;
+  }
+
+  CPY_RELEASE_THREADS
+  return 0;
 }
 
 static void *cpy_interactive(void *pipefd) {
-       PyOS_sighandler_t cur_sig;
-
-       /* Signal handler in a plugin? Bad stuff, but the best way to
-        * handle it I guess. In an interactive session people will
-        * press Ctrl+C at some time, which will generate a SIGINT.
-        * This will cause collectd to shutdown, thus killing the
-        * interactive interpreter, and leaving the terminal in a
-        * mess. Chances are, this isn't what the user wanted to do.
-        *
-        * So this is the plan:
-        * 1. Restore Python's own signal handler
-        * 2. Tell Python we just forked so it will accept this thread
-        *    as the main one. No version of Python will ever handle
-        *    interrupts anywhere but in the main thread.
-        * 3. After the interactive loop is done, restore collectd's
-        *    SIGINT handler.
-        * 4. Raise SIGINT for a clean shutdown. The signal is sent to
-        *    the main thread to ensure it wakes up the main interval
-        *    sleep so that collectd shuts down immediately not in 10
-        *    seconds.
-        *
-        * This will make sure that SIGINT won't kill collectd but
-        * still interrupt syscalls like sleep and pause. */
-
-       if (PyImport_ImportModule("readline") == NULL) {
-               /* This interactive session will suck. */
-               cpy_log_exception("interactive session init");
-       }
-       cur_sig = PyOS_setsig(SIGINT, python_sigint_handler);
-       PyOS_AfterFork();
-       PyEval_InitThreads();
-       close(*(int *) pipefd);
-       PyRun_InteractiveLoop(stdin, "<stdin>");
-       PyOS_setsig(SIGINT, cur_sig);
-       PyErr_Print();
-       state = PyEval_SaveThread();
-       NOTICE("python: Interactive interpreter exited, stopping collectd ...");
-       pthread_kill(main_thread, SIGINT);
-       return NULL;
+  PyOS_sighandler_t cur_sig;
+
+  /* Signal handler in a plugin? Bad stuff, but the best way to
+   * handle it I guess. In an interactive session people will
+   * press Ctrl+C at some time, which will generate a SIGINT.
+   * This will cause collectd to shutdown, thus killing the
+   * interactive interpreter, and leaving the terminal in a
+   * mess. Chances are, this isn't what the user wanted to do.
+   *
+   * So this is the plan:
+   * 1. Restore Python's own signal handler
+   * 2. Tell Python we just forked so it will accept this thread
+   *    as the main one. No version of Python will ever handle
+   *    interrupts anywhere but in the main thread.
+   * 3. After the interactive loop is done, restore collectd's
+   *    SIGINT handler.
+   * 4. Raise SIGINT for a clean shutdown. The signal is sent to
+   *    the main thread to ensure it wakes up the main interval
+   *    sleep so that collectd shuts down immediately not in 10
+   *    seconds.
+   *
+   * This will make sure that SIGINT won't kill collectd but
+   * still interrupt syscalls like sleep and pause. */
+
+  if (PyImport_ImportModule("readline") == NULL) {
+    /* This interactive session will suck. */
+    cpy_log_exception("interactive session init");
+  }
+  cur_sig = PyOS_setsig(SIGINT, python_sigint_handler);
+  PyOS_AfterFork();
+  PyEval_InitThreads();
+  close(*(int *)pipefd);
+  PyRun_InteractiveLoop(stdin, "<stdin>");
+  PyOS_setsig(SIGINT, cur_sig);
+  PyErr_Print();
+  state = PyEval_SaveThread();
+  NOTICE("python: Interactive interpreter exited, stopping collectd ...");
+  pthread_kill(main_thread, SIGINT);
+  return NULL;
 }
 
 static int cpy_init(void) {
-       PyObject *ret;
-       int pipefd[2];
-       char buf;
-       static pthread_t thread;
-
-       if (!Py_IsInitialized()) {
-               WARNING("python: Plugin loaded but not configured.");
-               plugin_unregister_shutdown("python");
-               Py_Finalize();
-               return 0;
-       }
-       main_thread = pthread_self();
-       if (do_interactive) {
-               if (pipe(pipefd)) {
-                       ERROR("python: Unable to create pipe.");
-                       return 1;
-               }
-               if (plugin_thread_create(&thread, NULL, cpy_interactive, pipefd + 1)) {
-                       ERROR("python: Error creating thread for interactive interpreter.");
-               }
-               if(read(pipefd[0], &buf, 1))
-                       ;
-               (void)close(pipefd[0]);
-       } else {
-               PyEval_InitThreads();
-               state = PyEval_SaveThread();
-       }
-       CPY_LOCK_THREADS
-       for (cpy_callback_t *c = cpy_init_callbacks; c; c = c->next) {
-               ret = PyObject_CallFunctionObjArgs(c->callback, c->data, (void *) 0); /* New reference. */
-               if (ret == NULL)
-                       cpy_log_exception("init callback");
-               else
-                       Py_DECREF(ret);
-       }
-       CPY_RELEASE_THREADS
-
-       return 0;
+  PyObject *ret;
+  int pipefd[2];
+  char buf;
+  static pthread_t thread;
+
+  if (!Py_IsInitialized()) {
+    WARNING("python: Plugin loaded but not configured.");
+    plugin_unregister_shutdown("python");
+    Py_Finalize();
+    return 0;
+  }
+  main_thread = pthread_self();
+  if (do_interactive) {
+    if (pipe(pipefd)) {
+      ERROR("python: Unable to create pipe.");
+      return 1;
+    }
+    if (plugin_thread_create(&thread, NULL, cpy_interactive, pipefd + 1,
+                             "python interpreter")) {
+      ERROR("python: Error creating thread for interactive interpreter.");
+    }
+    if (read(pipefd[0], &buf, 1))
+      ;
+    (void)close(pipefd[0]);
+  } else {
+    PyEval_InitThreads();
+    state = PyEval_SaveThread();
+  }
+  CPY_LOCK_THREADS
+  for (cpy_callback_t *c = cpy_init_callbacks; c; c = c->next) {
+    ret = PyObject_CallFunctionObjArgs(c->callback, c->data,
+                                       (void *)0); /* New reference. */
+    if (ret == NULL)
+      cpy_log_exception("init callback");
+    else
+      Py_DECREF(ret);
+  }
+  CPY_RELEASE_THREADS
+
+  return 0;
 }
 
 static PyObject *cpy_oconfig_to_pyconfig(oconfig_item_t *ci, PyObject *parent) {
-       PyObject *item, *values, *children, *tmp;
-
-       if (parent == NULL)
-               parent = Py_None;
-
-       values = PyTuple_New(ci->values_num); /* New reference. */
-       for (int i = 0; i < ci->values_num; ++i) {
-               if (ci->values[i].type == OCONFIG_TYPE_STRING) {
-                       PyTuple_SET_ITEM(values, i, cpy_string_to_unicode_or_bytes(ci->values[i].value.string));
-               } else if (ci->values[i].type == OCONFIG_TYPE_NUMBER) {
-                       PyTuple_SET_ITEM(values, i, PyFloat_FromDouble(ci->values[i].value.number));
-               } else if (ci->values[i].type == OCONFIG_TYPE_BOOLEAN) {
-                       PyTuple_SET_ITEM(values, i, PyBool_FromLong(ci->values[i].value.boolean));
-               }
-       }
-
-       tmp = cpy_string_to_unicode_or_bytes(ci->key);
-       item = PyObject_CallFunction((void *) &ConfigType, "NONO", tmp, parent, values, Py_None);
-       if (item == NULL)
-               return NULL;
-       children = PyTuple_New(ci->children_num); /* New reference. */
-       for (int i = 0; i < ci->children_num; ++i) {
-               PyTuple_SET_ITEM(children, i, cpy_oconfig_to_pyconfig(ci->children + i, item));
-       }
-       tmp = ((Config *) item)->children;
-       ((Config *) item)->children = children;
-       Py_XDECREF(tmp);
-       return item;
+  PyObject *item, *values, *children, *tmp;
+
+  if (parent == NULL)
+    parent = Py_None;
+
+  values = PyTuple_New(ci->values_num); /* New reference. */
+  for (int i = 0; i < ci->values_num; ++i) {
+    if (ci->values[i].type == OCONFIG_TYPE_STRING) {
+      PyTuple_SET_ITEM(
+          values, i,
+          cpy_string_to_unicode_or_bytes(ci->values[i].value.string));
+    } else if (ci->values[i].type == OCONFIG_TYPE_NUMBER) {
+      PyTuple_SET_ITEM(values, i,
+                       PyFloat_FromDouble(ci->values[i].value.number));
+    } else if (ci->values[i].type == OCONFIG_TYPE_BOOLEAN) {
+      PyTuple_SET_ITEM(values, i, PyBool_FromLong(ci->values[i].value.boolean));
+    }
+  }
+
+  tmp = cpy_string_to_unicode_or_bytes(ci->key);
+  item = PyObject_CallFunction((void *)&ConfigType, "NONO", tmp, parent, values,
+                               Py_None);
+  if (item == NULL)
+    return NULL;
+  children = PyTuple_New(ci->children_num); /* New reference. */
+  for (int i = 0; i < ci->children_num; ++i) {
+    PyTuple_SET_ITEM(children, i,
+                     cpy_oconfig_to_pyconfig(ci->children + i, item));
+  }
+  tmp = ((Config *)item)->children;
+  ((Config *)item)->children = children;
+  Py_XDECREF(tmp);
+  return item;
 }
 
 #ifdef IS_PY3K
 static struct PyModuleDef collectdmodule = {
-       PyModuleDef_HEAD_INIT,
-       "collectd",   /* name of module */
-       "The python interface to collectd", /* module documentation, may be NULL */
-       -1,
-       cpy_methods
-};
+    PyModuleDef_HEAD_INIT, "collectd",  /* name of module */
+    "The python interface to collectd", /* module documentation, may be NULL */
+    -1, cpy_methods};
 
 PyMODINIT_FUNC PyInit_collectd(void) {
-       return PyModule_Create(&collectdmodule);
+  return PyModule_Create(&collectdmodule);
 }
 #endif
 
 static int cpy_init_python(void) {
-       PyOS_sighandler_t cur_sig;
-       PyObject *sys;
-       PyObject *module;
+  PyOS_sighandler_t cur_sig;
+  PyObject *sys, *errordict;
+  PyObject *module;
 
 #ifdef IS_PY3K
-       wchar_t *argv = L"";
-       /* Add a builtin module, before Py_Initialize */
-       PyImport_AppendInittab("collectd", PyInit_collectd);
+  wchar_t *argv = L"";
+  /* Add a builtin module, before Py_Initialize */
+  PyImport_AppendInittab("collectd", PyInit_collectd);
 #else
-       char *argv = "";
+  char *argv = "";
 #endif
 
-       /* Chances are the current signal handler is already SIG_DFL, but let's make sure. */
-       cur_sig = PyOS_setsig(SIGINT, SIG_DFL);
-       Py_Initialize();
-       python_sigint_handler = PyOS_setsig(SIGINT, cur_sig);
-
-       PyType_Ready(&ConfigType);
-       PyType_Ready(&PluginDataType);
-       ValuesType.tp_base = &PluginDataType;
-       PyType_Ready(&ValuesType);
-       NotificationType.tp_base = &PluginDataType;
-       PyType_Ready(&NotificationType);
-       SignedType.tp_base = &PyLong_Type;
-       PyType_Ready(&SignedType);
-       UnsignedType.tp_base = &PyLong_Type;
-       PyType_Ready(&UnsignedType);
-       sys = PyImport_ImportModule("sys"); /* New reference. */
-       if (sys == NULL) {
-               cpy_log_exception("python initialization");
-               return 1;
-       }
-       sys_path = PyObject_GetAttrString(sys, "path"); /* New reference. */
-       Py_DECREF(sys);
-       if (sys_path == NULL) {
-               cpy_log_exception("python initialization");
-               return 1;
-       }
-       PySys_SetArgv(1, &argv);
-       PyList_SetSlice(sys_path, 0, 1, NULL);
+  /* Chances are the current signal handler is already SIG_DFL, but let's make
+   * sure. */
+  cur_sig = PyOS_setsig(SIGINT, SIG_DFL);
+  Py_Initialize();
+  python_sigint_handler = PyOS_setsig(SIGINT, cur_sig);
+
+  PyType_Ready(&ConfigType);
+  PyType_Ready(&PluginDataType);
+  ValuesType.tp_base = &PluginDataType;
+  PyType_Ready(&ValuesType);
+  NotificationType.tp_base = &PluginDataType;
+  PyType_Ready(&NotificationType);
+  SignedType.tp_base = &PyLong_Type;
+  PyType_Ready(&SignedType);
+  UnsignedType.tp_base = &PyLong_Type;
+  PyType_Ready(&UnsignedType);
+  errordict = PyDict_New();
+  PyDict_SetItemString(
+      errordict, "__doc__",
+      cpy_string_to_unicode_or_bytes(CollectdError_doc)); /* New reference. */
+  CollectdError = PyErr_NewException("collectd.CollectdError", NULL, errordict);
+  sys = PyImport_ImportModule("sys"); /* New reference. */
+  if (sys == NULL) {
+    cpy_log_exception("python initialization");
+    return 1;
+  }
+  sys_path = PyObject_GetAttrString(sys, "path"); /* New reference. */
+  Py_DECREF(sys);
+  if (sys_path == NULL) {
+    cpy_log_exception("python initialization");
+    return 1;
+  }
+  PySys_SetArgv(1, &argv);
+  PyList_SetSlice(sys_path, 0, 1, NULL);
 
 #ifdef IS_PY3K
-       module = PyImport_ImportModule("collectd");
+  module = PyImport_ImportModule("collectd");
 #else
-       module = Py_InitModule("collectd", cpy_methods); /* Borrowed reference. */
+  module = Py_InitModule("collectd", cpy_methods); /* Borrowed reference. */
 #endif
-       PyModule_AddObject(module, "Config", (void *) &ConfigType); /* Steals a reference. */
-       PyModule_AddObject(module, "Values", (void *) &ValuesType); /* Steals a reference. */
-       PyModule_AddObject(module, "Notification", (void *) &NotificationType); /* Steals a reference. */
-       PyModule_AddObject(module, "Signed", (void *) &SignedType); /* Steals a reference. */
-       PyModule_AddObject(module, "Unsigned", (void *) &UnsignedType); /* Steals a reference. */
-       PyModule_AddIntConstant(module, "LOG_DEBUG", LOG_DEBUG);
-       PyModule_AddIntConstant(module, "LOG_INFO", LOG_INFO);
-       PyModule_AddIntConstant(module, "LOG_NOTICE", LOG_NOTICE);
-       PyModule_AddIntConstant(module, "LOG_WARNING", LOG_WARNING);
-       PyModule_AddIntConstant(module, "LOG_ERROR", LOG_ERR);
-       PyModule_AddIntConstant(module, "NOTIF_FAILURE", NOTIF_FAILURE);
-       PyModule_AddIntConstant(module, "NOTIF_WARNING", NOTIF_WARNING);
-       PyModule_AddIntConstant(module, "NOTIF_OKAY", NOTIF_OKAY);
-       PyModule_AddStringConstant(module, "DS_TYPE_COUNTER", DS_TYPE_TO_STRING(DS_TYPE_COUNTER));
-       PyModule_AddStringConstant(module, "DS_TYPE_GAUGE", DS_TYPE_TO_STRING(DS_TYPE_GAUGE));
-       PyModule_AddStringConstant(module, "DS_TYPE_DERIVE", DS_TYPE_TO_STRING(DS_TYPE_DERIVE));
-       PyModule_AddStringConstant(module, "DS_TYPE_ABSOLUTE", DS_TYPE_TO_STRING(DS_TYPE_ABSOLUTE));
-       return 0;
+  PyModule_AddObject(module, "Config",
+                     (void *)&ConfigType); /* Steals a reference. */
+  PyModule_AddObject(module, "Values",
+                     (void *)&ValuesType); /* Steals a reference. */
+  PyModule_AddObject(module, "Notification",
+                     (void *)&NotificationType); /* Steals a reference. */
+  PyModule_AddObject(module, "Signed",
+                     (void *)&SignedType); /* Steals a reference. */
+  PyModule_AddObject(module, "Unsigned",
+                     (void *)&UnsignedType); /* Steals a reference. */
+  Py_XINCREF(CollectdError);
+  PyModule_AddObject(module, "CollectdError",
+                     CollectdError); /* Steals a reference. */
+  PyModule_AddIntConstant(module, "LOG_DEBUG", LOG_DEBUG);
+  PyModule_AddIntConstant(module, "LOG_INFO", LOG_INFO);
+  PyModule_AddIntConstant(module, "LOG_NOTICE", LOG_NOTICE);
+  PyModule_AddIntConstant(module, "LOG_WARNING", LOG_WARNING);
+  PyModule_AddIntConstant(module, "LOG_ERROR", LOG_ERR);
+  PyModule_AddIntConstant(module, "NOTIF_FAILURE", NOTIF_FAILURE);
+  PyModule_AddIntConstant(module, "NOTIF_WARNING", NOTIF_WARNING);
+  PyModule_AddIntConstant(module, "NOTIF_OKAY", NOTIF_OKAY);
+  PyModule_AddStringConstant(module, "DS_TYPE_COUNTER",
+                             DS_TYPE_TO_STRING(DS_TYPE_COUNTER));
+  PyModule_AddStringConstant(module, "DS_TYPE_GAUGE",
+                             DS_TYPE_TO_STRING(DS_TYPE_GAUGE));
+  PyModule_AddStringConstant(module, "DS_TYPE_DERIVE",
+                             DS_TYPE_TO_STRING(DS_TYPE_DERIVE));
+  PyModule_AddStringConstant(module, "DS_TYPE_ABSOLUTE",
+                             DS_TYPE_TO_STRING(DS_TYPE_ABSOLUTE));
+  return 0;
 }
 
 static int cpy_config(oconfig_item_t *ci) {
-       PyObject *tb;
-       int status = 0;
-
-       /* Ok in theory we shouldn't do initialization at this point
-        * but we have to. In order to give python scripts a chance
-        * to register a config callback we need to be able to execute
-        * python code during the config callback so we have to start
-        * the interpreter here. */
-       /* Do *not* use the python "thread" module at this point! */
-
-       if (!Py_IsInitialized() && cpy_init_python()) return 1;
-
-       for (int i = 0; i < ci->children_num; ++i) {
-               oconfig_item_t *item = ci->children + i;
-
-               if (strcasecmp(item->key, "Interactive") == 0) {
-                       if (cf_util_get_boolean(item, &do_interactive) != 0) {
-                               status = 1;
-                               continue;
-                       }
-               } else if (strcasecmp(item->key, "Encoding") == 0) {
-                       char *encoding = NULL;
-                       if (cf_util_get_string(item, &encoding) != 0) {
-                               status = 1;
-                               continue;
-                       }
+  PyObject *tb;
+  int status = 0;
+
+  /* Ok in theory we shouldn't do initialization at this point
+   * but we have to. In order to give python scripts a chance
+   * to register a config callback we need to be able to execute
+   * python code during the config callback so we have to start
+   * the interpreter here. */
+  /* Do *not* use the python "thread" module at this point! */
+
+  if (!Py_IsInitialized() && cpy_init_python())
+    return 1;
+
+  for (int i = 0; i < ci->children_num; ++i) {
+    oconfig_item_t *item = ci->children + i;
+
+    if (strcasecmp(item->key, "Interactive") == 0) {
+      if (cf_util_get_boolean(item, &do_interactive) != 0) {
+        status = 1;
+        continue;
+      }
+    } else if (strcasecmp(item->key, "Encoding") == 0) {
+      char *encoding = NULL;
+      if (cf_util_get_string(item, &encoding) != 0) {
+        status = 1;
+        continue;
+      }
 #ifdef IS_PY3K
-                       ERROR("python: \"Encoding\" was used in the config file but Python3 was used, which does not support changing encodings");
-                       status = 1;
-                       sfree(encoding);
-                       continue;
+      ERROR("python: \"Encoding\" was used in the config file but Python3 was "
+            "used, which does not support changing encodings");
+      status = 1;
+      sfree(encoding);
+      continue;
 #else
-                       /* Why is this even necessary? And undocumented? */
-                       if (PyUnicode_SetDefaultEncoding(encoding)) {
-                               cpy_log_exception("setting default encoding");
-                               status = 1;
-                       }
+      /* Why is this even necessary? And undocumented? */
+      if (PyUnicode_SetDefaultEncoding(encoding)) {
+        cpy_log_exception("setting default encoding");
+        status = 1;
+      }
 #endif
-                       sfree(encoding);
-               } else if (strcasecmp(item->key, "LogTraces") == 0) {
-                       _Bool log_traces;
-                       if (cf_util_get_boolean(item, &log_traces) != 0) {
-                               status = 1;
-                               continue;
-                       }
-                       if (!log_traces) {
-                               Py_XDECREF(cpy_format_exception);
-                               cpy_format_exception = NULL;
-                               continue;
-                       }
-                       if (cpy_format_exception)
-                               continue;
-                       tb = PyImport_ImportModule("traceback"); /* New reference. */
-                       if (tb == NULL) {
-                               cpy_log_exception("python initialization");
-                               status = 1;
-                               continue;
-                       }
-                       cpy_format_exception = PyObject_GetAttrString(tb, "format_exception"); /* New reference. */
-                       Py_DECREF(tb);
-                       if (cpy_format_exception == NULL) {
-                               cpy_log_exception("python initialization");
-                               status = 1;
-                       }
-               } else if (strcasecmp(item->key, "ModulePath") == 0) {
-                       char *dir = NULL;
-                       PyObject *dir_object;
-
-                       if (cf_util_get_string(item, &dir) != 0) {
-                               status = 1;
-                               continue;
-                       }
-                       dir_object = cpy_string_to_unicode_or_bytes(dir); /* New reference. */
-                       if (dir_object == NULL) {
-                               ERROR("python plugin: Unable to convert \"%s\" to "
-                                     "a python object.", dir);
-                               free(dir);
-                               cpy_log_exception("python initialization");
-                               status = 1;
-                               continue;
-                       }
-                       if (PyList_Insert(sys_path, 0, dir_object) != 0) {
-                               ERROR("python plugin: Unable to prepend \"%s\" to "
-                                     "python module path.", dir);
-                               cpy_log_exception("python initialization");
-                               status = 1;
-                       }
-                       Py_DECREF(dir_object);
-                       free(dir);
-               } else if (strcasecmp(item->key, "Import") == 0) {
-                       char *module_name = NULL;
-                       PyObject *module;
-
-                       if (cf_util_get_string(item, &module_name) != 0) {
-                               status = 1;
-                               continue;
-                       }
-                       module = PyImport_ImportModule(module_name); /* New reference. */
-                       if (module == NULL) {
-                               ERROR("python plugin: Error importing module \"%s\".", module_name);
-                               cpy_log_exception("importing module");
-                               status = 1;
-                       }
-                       free(module_name);
-                       Py_XDECREF(module);
-               } else if (strcasecmp(item->key, "Module") == 0) {
-                       char *name = NULL;
-                       cpy_callback_t *c;
-                       PyObject *ret;
-
-                       if (cf_util_get_string(item, &name) != 0) {
-                               status = 1;
-                               continue;
-                       }
-                       for (c = cpy_config_callbacks; c; c = c->next) {
-                               if (strcasecmp(c->name + 7, name) == 0)
-                                       break;
-                       }
-                       if (c == NULL) {
-                               WARNING("python plugin: Found a configuration for the \"%s\" plugin, "
-                                       "but the plugin isn't loaded or didn't register "
-                                       "a configuration callback.", name);
-                               free(name);
-                               continue;
-                       }
-                       free(name);
-                       if (c->data == NULL)
-                               ret = PyObject_CallFunction(c->callback, "N",
-                                       cpy_oconfig_to_pyconfig(item, NULL)); /* New reference. */
-                       else
-                               ret = PyObject_CallFunction(c->callback, "NO",
-                                       cpy_oconfig_to_pyconfig(item, NULL), c->data); /* New reference. */
-                       if (ret == NULL) {
-                               cpy_log_exception("loading module");
-                               status = 1;
-                       } else
-                               Py_DECREF(ret);
-               } else {
-                       ERROR("python plugin: Unknown config key \"%s\".", item->key);
-                       status = 1;
-               }
-       }
-       return (status);
+      sfree(encoding);
+    } else if (strcasecmp(item->key, "LogTraces") == 0) {
+      _Bool log_traces;
+      if (cf_util_get_boolean(item, &log_traces) != 0) {
+        status = 1;
+        continue;
+      }
+      if (!log_traces) {
+        Py_XDECREF(cpy_format_exception);
+        cpy_format_exception = NULL;
+        continue;
+      }
+      if (cpy_format_exception)
+        continue;
+      tb = PyImport_ImportModule("traceback"); /* New reference. */
+      if (tb == NULL) {
+        cpy_log_exception("python initialization");
+        status = 1;
+        continue;
+      }
+      cpy_format_exception =
+          PyObject_GetAttrString(tb, "format_exception"); /* New reference. */
+      Py_DECREF(tb);
+      if (cpy_format_exception == NULL) {
+        cpy_log_exception("python initialization");
+        status = 1;
+      }
+    } else if (strcasecmp(item->key, "ModulePath") == 0) {
+      char *dir = NULL;
+      PyObject *dir_object;
+
+      if (cf_util_get_string(item, &dir) != 0) {
+        status = 1;
+        continue;
+      }
+      dir_object = cpy_string_to_unicode_or_bytes(dir); /* New reference. */
+      if (dir_object == NULL) {
+        ERROR("python plugin: Unable to convert \"%s\" to "
+              "a python object.",
+              dir);
+        free(dir);
+        cpy_log_exception("python initialization");
+        status = 1;
+        continue;
+      }
+      if (PyList_Insert(sys_path, 0, dir_object) != 0) {
+        ERROR("python plugin: Unable to prepend \"%s\" to "
+              "python module path.",
+              dir);
+        cpy_log_exception("python initialization");
+        status = 1;
+      }
+      Py_DECREF(dir_object);
+      free(dir);
+    } else if (strcasecmp(item->key, "Import") == 0) {
+      char *module_name = NULL;
+      PyObject *module;
+
+      if (cf_util_get_string(item, &module_name) != 0) {
+        status = 1;
+        continue;
+      }
+      module = PyImport_ImportModule(module_name); /* New reference. */
+      if (module == NULL) {
+        ERROR("python plugin: Error importing module \"%s\".", module_name);
+        cpy_log_exception("importing module");
+        status = 1;
+      }
+      free(module_name);
+      Py_XDECREF(module);
+    } else if (strcasecmp(item->key, "Module") == 0) {
+      char *name = NULL;
+      cpy_callback_t *c;
+      PyObject *ret;
+
+      if (cf_util_get_string(item, &name) != 0) {
+        status = 1;
+        continue;
+      }
+      for (c = cpy_config_callbacks; c; c = c->next) {
+        if (strcasecmp(c->name + 7, name) == 0)
+          break;
+      }
+      if (c == NULL) {
+        WARNING("python plugin: Found a configuration for the \"%s\" plugin, "
+                "but the plugin isn't loaded or didn't register "
+                "a configuration callback.",
+                name);
+        free(name);
+        continue;
+      }
+      free(name);
+      if (c->data == NULL)
+        ret = PyObject_CallFunction(
+            c->callback, "N",
+            cpy_oconfig_to_pyconfig(item, NULL)); /* New reference. */
+      else
+        ret = PyObject_CallFunction(c->callback, "NO",
+                                    cpy_oconfig_to_pyconfig(item, NULL),
+                                    c->data); /* New reference. */
+      if (ret == NULL) {
+        cpy_log_exception("loading module");
+        status = 1;
+      } else
+        Py_DECREF(ret);
+    } else {
+      ERROR("python plugin: Unknown config key \"%s\".", item->key);
+      status = 1;
+    }
+  }
+  return status;
 }
 
 void module_register(void) {
-       plugin_register_complex_config("python", cpy_config);
-       plugin_register_init("python", cpy_init);
-       plugin_register_shutdown("python", cpy_shutdown);
+  plugin_register_complex_config("python", cpy_config);
+  plugin_register_init("python", cpy_init);
+  plugin_register_shutdown("python", cpy_shutdown);
 }