2 #include <structmember.h>
9 static char time_doc[] = "This is the Unix timestap of the time this value was read.\n"
10 "For dispatching values this can be set to 0 which means \"now\".\n"
11 "This means the time the value is actually dispatched, not the time\n"
14 static char host_doc[] = "The hostname of the host this value was read from.\n"
15 "For dispatching this can be set to an empty string which means\n"
16 "the local hostname as defined in the collectd.conf.";
18 static char type_doc[] = "The type of this value. This type has to be defined\n"
19 "in your types.db. Attempting to set it to any other value will\n"
20 "raise a TypeError exception.\n"
21 "Assigning a type is mandetory, calling dispatch without doing\n"
22 "so will raise a RuntimeError exception.";
24 static char type_instance_doc[] = "";
26 static char plugin_doc[] = "The name of the plugin that read the data. Setting this\n"
27 "member to an empty string will insert \"python\" upon dispatching.";
29 static char plugin_instance_doc[] = "";
31 static char PluginData_doc[] = "This is an internal class that is the base for Values\n"
32 "and Notification. It is pretty useless by itself and was therefore not\n"
33 "exported to the collectd module.";
35 static PyObject *PluginData_new(PyTypeObject *type, PyObject *args, PyObject *kwds) {
38 self = (PluginData *) type->tp_alloc(type, 0);
45 self->plugin_instance[0] = 0;
47 self->type_instance[0] = 0;
48 return (PyObject *) self;
51 static int PluginData_init(PyObject *s, PyObject *args, PyObject *kwds) {
52 PluginData *self = (PluginData *) s;
54 const char *type = "", *plugin_instance = "", *type_instance = "", *plugin = "", *host = "";
55 static char *kwlist[] = {"type", "plugin_instance", "type_instance",
56 "plugin", "host", "time", NULL};
58 if (!PyArg_ParseTupleAndKeywords(args, kwds, "|sssssd", kwlist, &type,
59 &plugin_instance, &type_instance, &plugin, &host, &time))
62 if (type[0] != 0 && plugin_get_ds(type) == NULL) {
63 PyErr_Format(PyExc_TypeError, "Dataset %s not found", type);
67 sstrncpy(self->host, host, sizeof(self->host));
68 sstrncpy(self->plugin, plugin, sizeof(self->plugin));
69 sstrncpy(self->plugin_instance, plugin_instance, sizeof(self->plugin_instance));
70 sstrncpy(self->type, type, sizeof(self->type));
71 sstrncpy(self->type_instance, type_instance, sizeof(self->type_instance));
77 static PyObject *PluginData_repr(PyObject *s) {
78 PluginData *self = (PluginData *) s;
80 return PyString_FromFormat("collectd.Values(type='%s%s%s%s%s%s%s%s%s',time=%lu)", self->type,
81 *self->type_instance ? "',type_instance='" : "", self->type_instance,
82 *self->plugin ? "',plugin='" : "", self->plugin,
83 *self->plugin_instance ? "',plugin_instance='" : "", self->plugin_instance,
84 *self->host ? "',host='" : "", self->host,
85 (long unsigned) self->time);
88 static PyMemberDef PluginData_members[] = {
89 {"time", T_DOUBLE, offsetof(PluginData, time), 0, time_doc},
93 static PyObject *PluginData_getstring(PyObject *self, void *data) {
94 const char *value = ((char *) self) + (intptr_t) data;
96 return PyString_FromString(value);
99 static int PluginData_setstring(PyObject *self, PyObject *value, void *data) {
104 PyErr_SetString(PyExc_TypeError, "Cannot delete this attribute");
107 new = PyString_AsString(value);
108 if (new == NULL) return -1;
109 old = ((char *) self) + (intptr_t) data;
110 sstrncpy(old, new, DATA_MAX_NAME_LEN);
114 static int PluginData_settype(PyObject *self, PyObject *value, void *data) {
119 PyErr_SetString(PyExc_TypeError, "Cannot delete this attribute");
122 new = PyString_AsString(value);
123 if (new == NULL) return -1;
125 if (plugin_get_ds(new) == NULL) {
126 PyErr_Format(PyExc_TypeError, "Dataset %s not found", new);
130 old = ((char *) self) + (intptr_t) data;
131 sstrncpy(old, new, DATA_MAX_NAME_LEN);
135 static PyGetSetDef PluginData_getseters[] = {
136 {"host", PluginData_getstring, PluginData_setstring, host_doc, (void *) offsetof(PluginData, host)},
137 {"plugin", PluginData_getstring, PluginData_setstring, plugin_doc, (void *) offsetof(PluginData, plugin)},
138 {"plugin_instance", PluginData_getstring, PluginData_setstring, plugin_instance_doc, (void *) offsetof(PluginData, plugin_instance)},
139 {"type_instance", PluginData_getstring, PluginData_setstring, type_instance_doc, (void *) offsetof(PluginData, type_instance)},
140 {"type", PluginData_getstring, PluginData_settype, type_doc, (void *) offsetof(PluginData, type)},
144 PyTypeObject PluginDataType = {
145 PyObject_HEAD_INIT(NULL)
147 "collectd.PluginData", /* tp_name */
148 sizeof(PluginData), /* tp_basicsize */
149 0, /* Will be filled in later */
155 PluginData_repr, /* tp_repr */
156 0, /* tp_as_number */
157 0, /* tp_as_sequence */
158 0, /* tp_as_mapping */
164 0, /* tp_as_buffer */
165 Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE /*| Py_TPFLAGS_HAVE_GC*/, /*tp_flags*/
166 PluginData_doc, /* tp_doc */
169 0, /* tp_richcompare */
170 0, /* tp_weaklistoffset */
174 PluginData_members, /* tp_members */
175 PluginData_getseters, /* tp_getset */
178 0, /* tp_descr_get */
179 0, /* tp_descr_set */
180 0, /* tp_dictoffset */
181 PluginData_init, /* tp_init */
183 PluginData_new /* tp_new */
186 static char interval_doc[] = "The interval is the timespan in seconds between two submits for\n"
187 "the same data source. This value has to be a positive integer, so you can't\n"
188 "submit more than one value per second. If this member is set to a\n"
189 "non-positive value, the default value as specified in the config file will\n"
190 "be used (default: 10).\n"
192 "If you submit values more often than the specified interval, the average\n"
193 "will be used. If you submit less values, your graphs will have gaps.";
195 static char values_doc[] = "These are the actual values that get dispatched to collectd.\n"
196 "It has to be a sequence (a tuple or list) of numbers.\n"
197 "The size of the sequence and the type of its content depend on the type\n"
198 "member your types.db file. For more information on this read the types.db\n"
201 "If the sequence does not have the correct size upon dispatch a RuntimeError\n"
202 "exception will be raised. If the content of the sequence is not a number,\n"
203 "a TypeError exception will be raised.";
205 static char dispatch_doc[] = "dispatch([type][, values][, plugin_instance][, type_instance]"
206 "[, plugin][, host][, time][, interval]) -> None. Dispatch a value list.\n"
208 "Dispatch this instance to the collectd process. The object has members\n"
209 "for each of the possible arguments for this method. For a detailed explanation\n"
210 "of these parameters see the member of the same same.\n"
212 "If you do not submit a parameter the value saved in its member will be submitted.\n"
213 "If you do provide a parameter it will be used instead, without altering the member.";
215 static char write_doc[] = "write([destination][, type][, values][, plugin_instance][, type_instance]"
216 "[, plugin][, host][, time][, interval]) -> None. Dispatch a value list.\n"
218 "Write this instance to a single plugin or all plugins if 'destination' is obmitted.\n"
219 "This will bypass the main collectd process and all filtering and caching.\n"
220 "Other than that it works similar to 'dispatch'. In most cases 'dispatch' should be\n"
221 "used instead of 'write'.\n";
223 static char Values_doc[] = "A Values object used for dispatching values to collectd and receiving values from write callbacks.";
225 static PyObject *Values_new(PyTypeObject *type, PyObject *args, PyObject *kwds) {
228 self = (Values *) PluginData_new(type, args, kwds);
232 self->values = PyList_New(0);
234 return (PyObject *) self;
237 static int Values_init(PyObject *s, PyObject *args, PyObject *kwds) {
238 Values *self = (Values *) s;
239 int interval = 0, ret;
241 PyObject *values = NULL, *tmp;
242 const char *type = "", *plugin_instance = "", *type_instance = "", *plugin = "", *host = "";
243 static char *kwlist[] = {"type", "values", "plugin_instance", "type_instance",
244 "plugin", "host", "time", "interval", NULL};
246 if (!PyArg_ParseTupleAndKeywords(args, kwds, "|sOssssdi", kwlist,
247 &type, &values, &plugin_instance, &type_instance,
248 &plugin, &host, &time, &interval))
251 tmp = Py_BuildValue("sssssd", type, plugin_instance, type_instance, plugin, host, time);
254 ret = PluginDataType.tp_init(s, tmp, NULL);
259 if (values == NULL) {
260 values = PyList_New(0);
267 self->values = values;
270 self->interval = interval;
274 static PyObject *Values_dispatch(Values *self, PyObject *args, PyObject *kwds) {
276 const data_set_t *ds;
279 value_list_t value_list = VALUE_LIST_INIT;
280 PyObject *values = self->values;
281 double time = self->data.time;
282 int interval = self->interval;
283 const char *host = self->data.host;
284 const char *plugin = self->data.plugin;
285 const char *plugin_instance = self->data.plugin_instance;
286 const char *type = self->data.type;
287 const char *type_instance = self->data.type_instance;
289 static char *kwlist[] = {"type", "values", "plugin_instance", "type_instance",
290 "plugin", "host", "time", "interval", NULL};
291 if (!PyArg_ParseTupleAndKeywords(args, kwds, "|sOssssdi", kwlist,
292 &type, &values, &plugin_instance, &type_instance,
293 &plugin, &host, &time, &interval))
297 PyErr_SetString(PyExc_RuntimeError, "type not set");
300 ds = plugin_get_ds(type);
302 PyErr_Format(PyExc_TypeError, "Dataset %s not found", type);
305 if (values == NULL || (PyTuple_Check(values) == 0 && PyList_Check(values) == 0)) {
306 PyErr_Format(PyExc_TypeError, "values must be list or tuple");
309 size = PySequence_Length(values);
310 if (size != ds->ds_num) {
311 PyErr_Format(PyExc_RuntimeError, "type %s needs %d values, got %zd", type, ds->ds_num, size);
314 value = malloc(size * sizeof(*value));
315 for (i = 0; i < size; ++i) {
316 PyObject *item, *num;
317 item = PySequence_GetItem(values, i);
318 if (ds->ds->type == DS_TYPE_COUNTER) {
319 num = PyNumber_Long(item);
321 value[i].counter = PyLong_AsUnsignedLongLong(num);
322 } else if (ds->ds->type == DS_TYPE_GAUGE) {
323 num = PyNumber_Float(item);
325 value[i].gauge = PyFloat_AsDouble(num);
326 } else if (ds->ds->type == DS_TYPE_DERIVE) {
327 /* This might overflow without raising an exception.
328 * Not much we can do about it */
329 num = PyNumber_Long(item);
331 value[i].derive = PyLong_AsLongLong(num);
332 } else if (ds->ds->type == DS_TYPE_ABSOLUTE) {
333 /* This might overflow without raising an exception.
334 * Not much we can do about it */
335 num = PyNumber_Long(item);
337 value[i].absolute = PyLong_AsUnsignedLongLong(num);
340 PyErr_Format(PyExc_RuntimeError, "unknown data type %d for %s", ds->ds->type, type);
343 if (PyErr_Occurred() != NULL) {
348 value_list.values = value;
349 value_list.values_len = size;
350 value_list.time = time;
351 value_list.interval = interval;
352 sstrncpy(value_list.host, host, sizeof(value_list.host));
353 sstrncpy(value_list.plugin, plugin, sizeof(value_list.plugin));
354 sstrncpy(value_list.plugin_instance, plugin_instance, sizeof(value_list.plugin_instance));
355 sstrncpy(value_list.type, type, sizeof(value_list.type));
356 sstrncpy(value_list.type_instance, type_instance, sizeof(value_list.type_instance));
357 value_list.meta = NULL;
358 if (value_list.host[0] == 0)
359 sstrncpy(value_list.host, hostname_g, sizeof(value_list.host));
360 if (value_list.plugin[0] == 0)
361 sstrncpy(value_list.plugin, "python", sizeof(value_list.plugin));
362 Py_BEGIN_ALLOW_THREADS;
363 ret = plugin_dispatch_values(&value_list);
364 Py_END_ALLOW_THREADS;
366 PyErr_SetString(PyExc_RuntimeError, "error dispatching values, read the logs");
373 static PyObject *Values_write(Values *self, PyObject *args, PyObject *kwds) {
375 const data_set_t *ds;
378 value_list_t value_list = VALUE_LIST_INIT;
379 PyObject *values = self->values;
380 double time = self->data.time;
381 int interval = self->interval;
382 const char *host = self->data.host;
383 const char *plugin = self->data.plugin;
384 const char *plugin_instance = self->data.plugin_instance;
385 const char *type = self->data.type;
386 const char *type_instance = self->data.type_instance;
387 const char *dest = NULL;
389 static char *kwlist[] = {"destination", "type", "values", "plugin_instance", "type_instance",
390 "plugin", "host", "time", "interval", NULL};
391 if (!PyArg_ParseTupleAndKeywords(args, kwds, "|sOssssdi", kwlist,
392 &type, &values, &plugin_instance, &type_instance,
393 &plugin, &host, &time, &interval))
397 PyErr_SetString(PyExc_RuntimeError, "type not set");
400 ds = plugin_get_ds(type);
402 PyErr_Format(PyExc_TypeError, "Dataset %s not found", type);
405 if (values == NULL || (PyTuple_Check(values) == 0 && PyList_Check(values) == 0)) {
406 PyErr_Format(PyExc_TypeError, "values must be list or tuple");
409 size = PySequence_Length(values);
410 if (size != ds->ds_num) {
411 PyErr_Format(PyExc_RuntimeError, "type %s needs %d values, got %zd", type, ds->ds_num, size);
414 value = malloc(size * sizeof(*value));
415 for (i = 0; i < size; ++i) {
416 PyObject *item, *num;
417 item = PySequence_GetItem(values, i);
418 if (ds->ds->type == DS_TYPE_COUNTER) {
419 num = PyNumber_Long(item);
421 value[i].counter = PyLong_AsUnsignedLongLong(num);
422 } else if (ds->ds->type == DS_TYPE_GAUGE) {
423 num = PyNumber_Float(item);
425 value[i].gauge = PyFloat_AsDouble(num);
426 } else if (ds->ds->type == DS_TYPE_DERIVE) {
427 /* This might overflow without raising an exception.
428 * Not much we can do about it */
429 num = PyNumber_Long(item);
431 value[i].derive = PyLong_AsLongLong(num);
432 } else if (ds->ds->type == DS_TYPE_ABSOLUTE) {
433 /* This might overflow without raising an exception.
434 * Not much we can do about it */
435 num = PyNumber_Long(item);
437 value[i].absolute = PyLong_AsUnsignedLongLong(num);
440 PyErr_Format(PyExc_RuntimeError, "unknown data type %d for %s", ds->ds->type, type);
443 if (PyErr_Occurred() != NULL) {
448 value_list.values = value;
449 value_list.values_len = size;
450 value_list.time = time;
451 value_list.interval = interval;
452 sstrncpy(value_list.host, host, sizeof(value_list.host));
453 sstrncpy(value_list.plugin, plugin, sizeof(value_list.plugin));
454 sstrncpy(value_list.plugin_instance, plugin_instance, sizeof(value_list.plugin_instance));
455 sstrncpy(value_list.type, type, sizeof(value_list.type));
456 sstrncpy(value_list.type_instance, type_instance, sizeof(value_list.type_instance));
457 value_list.meta = NULL;
458 if (value_list.host[0] == 0)
459 sstrncpy(value_list.host, hostname_g, sizeof(value_list.host));
460 if (value_list.plugin[0] == 0)
461 sstrncpy(value_list.plugin, "python", sizeof(value_list.plugin));
462 Py_BEGIN_ALLOW_THREADS;
463 ret = plugin_write(dest, NULL, &value_list);
464 Py_END_ALLOW_THREADS;
466 PyErr_SetString(PyExc_RuntimeError, "error dispatching values, read the logs");
473 static PyObject *Values_repr(PyObject *s) {
474 PyObject *ret, *valuestring = NULL;
475 Values *self = (Values *) s;
477 if (self->values != NULL)
478 valuestring = PyObject_Repr(self->values);
479 if (valuestring == NULL)
482 ret = PyString_FromFormat("collectd.Values(type='%s%s%s%s%s%s%s%s%s',time=%lu,interval=%i,values=%s)", self->data.type,
483 *self->data.type_instance ? "',type_instance='" : "", self->data.type_instance,
484 *self->data.plugin ? "',plugin='" : "", self->data.plugin,
485 *self->data.plugin_instance ? "',plugin_instance='" : "", self->data.plugin_instance,
486 *self->data.host ? "',host='" : "", self->data.host,
487 (long unsigned) self->data.time, self->interval,
488 valuestring ? PyString_AsString(valuestring) : "[]");
489 Py_XDECREF(valuestring);
493 static int Values_traverse(PyObject *self, visitproc visit, void *arg) {
494 Values *v = (Values *) self;
499 static int Values_clear(PyObject *self) {
500 Values *v = (Values *) self;
505 static void Values_dealloc(PyObject *self) {
507 self->ob_type->tp_free(self);
510 static PyMemberDef Values_members[] = {
511 {"interval", T_INT, offsetof(Values, interval), 0, interval_doc},
512 {"values", T_OBJECT_EX, offsetof(Values, values), 0, values_doc},
516 static PyMethodDef Values_methods[] = {
517 {"dispatch", (PyCFunction) Values_dispatch, METH_VARARGS | METH_KEYWORDS, dispatch_doc},
518 {"write", (PyCFunction) Values_write, METH_VARARGS | METH_KEYWORDS, write_doc},
522 PyTypeObject ValuesType = {
523 PyObject_HEAD_INIT(NULL)
525 "collectd.Values", /* tp_name */
526 sizeof(Values), /* tp_basicsize */
527 0, /* Will be filled in later */
528 Values_dealloc, /* tp_dealloc */
533 Values_repr, /* tp_repr */
534 0, /* tp_as_number */
535 0, /* tp_as_sequence */
536 0, /* tp_as_mapping */
542 0, /* tp_as_buffer */
543 Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HAVE_GC, /*tp_flags*/
544 Values_doc, /* tp_doc */
545 Values_traverse, /* tp_traverse */
546 Values_clear, /* tp_clear */
547 0, /* tp_richcompare */
548 0, /* tp_weaklistoffset */
551 Values_methods, /* tp_methods */
552 Values_members, /* tp_members */
556 0, /* tp_descr_get */
557 0, /* tp_descr_set */
558 0, /* tp_dictoffset */
559 Values_init, /* tp_init */
561 Values_new /* tp_new */
564 static char severity_doc[] = "The severity of this notification. Assign or compare to\n"
565 "NOTIF_FAILURE, NOTIF_WARNING or NOTIF_OKAY.";
567 static char message_doc[] = "Some kind of description what's going on and why this Notification was generated.";
569 static char Notification_doc[] = "The Notification class is a wrapper around the collectd notification.\n"
570 "It can be used to notify other plugins about bad stuff happening. It works\n"
571 "similar to Values but has a severity and a message instead of interval\n"
573 "Notifications can be dispatched at any time and can be received with register_notification.";
575 static int Notification_init(PyObject *s, PyObject *args, PyObject *kwds) {
576 Notification *self = (Notification *) s;
578 int severity = 0, ret;
580 const char *message = "";
581 const char *type = "", *plugin_instance = "", *type_instance = "", *plugin = "", *host = "";
582 static char *kwlist[] = {"type", "message", "plugin_instance", "type_instance",
583 "plugin", "host", "time", "severity", NULL};
585 if (!PyArg_ParseTupleAndKeywords(args, kwds, "|ssssssdi", kwlist,
586 &type, &message, &plugin_instance, &type_instance,
587 &plugin, &host, &time, &severity))
590 tmp = Py_BuildValue("sssssd", type, plugin_instance, type_instance, plugin, host, time);
593 ret = PluginDataType.tp_init(s, tmp, NULL);
598 sstrncpy(self->message, message, sizeof(self->message));
599 self->severity = severity;
603 static PyObject *Notification_dispatch(Notification *self, PyObject *args, PyObject *kwds) {
605 const data_set_t *ds;
606 notification_t notification;
607 double t = self->data.time;
608 int severity = self->severity;
609 const char *host = self->data.host;
610 const char *plugin = self->data.plugin;
611 const char *plugin_instance = self->data.plugin_instance;
612 const char *type = self->data.type;
613 const char *type_instance = self->data.type_instance;
614 const char *message = self->message;
616 static char *kwlist[] = {"type", "message", "plugin_instance", "type_instance",
617 "plugin", "host", "time", "severity", NULL};
618 if (!PyArg_ParseTupleAndKeywords(args, kwds, "|ssssssdi", kwlist,
619 &type, &message, &plugin_instance, &type_instance,
620 &plugin, &host, &t, &severity))
624 PyErr_SetString(PyExc_RuntimeError, "type not set");
627 ds = plugin_get_ds(type);
629 PyErr_Format(PyExc_TypeError, "Dataset %s not found", type);
633 notification.time = t;
634 notification.severity = severity;
635 sstrncpy(notification.message, message, sizeof(notification.message));
636 sstrncpy(notification.host, host, sizeof(notification.host));
637 sstrncpy(notification.plugin, plugin, sizeof(notification.plugin));
638 sstrncpy(notification.plugin_instance, plugin_instance, sizeof(notification.plugin_instance));
639 sstrncpy(notification.type, type, sizeof(notification.type));
640 sstrncpy(notification.type_instance, type_instance, sizeof(notification.type_instance));
641 notification.meta = NULL;
642 if (notification.time < 1)
643 notification.time = time(0);
644 if (notification.host[0] == 0)
645 sstrncpy(notification.host, hostname_g, sizeof(notification.host));
646 if (notification.plugin[0] == 0)
647 sstrncpy(notification.plugin, "python", sizeof(notification.plugin));
648 Py_BEGIN_ALLOW_THREADS;
649 ret = plugin_dispatch_notification(¬ification);
650 Py_END_ALLOW_THREADS;
652 PyErr_SetString(PyExc_RuntimeError, "error dispatching notification, read the logs");
658 static PyObject *Notification_new(PyTypeObject *type, PyObject *args, PyObject *kwds) {
661 self = (Notification *) PluginData_new(type, args, kwds);
665 self->message[0] = 0;
667 return (PyObject *) self;
670 static int Notification_setstring(PyObject *self, PyObject *value, void *data) {
675 PyErr_SetString(PyExc_TypeError, "Cannot delete this attribute");
678 new = PyString_AsString(value);
679 if (new == NULL) return -1;
680 old = ((char *) self) + (intptr_t) data;
681 sstrncpy(old, new, NOTIF_MAX_MSG_LEN);
685 static PyObject *Notification_repr(PyObject *s) {
687 Notification *self = (Notification *) s;
689 ret = PyString_FromFormat("collectd.Values(type='%s%s%s%s%s%s%s%s%s%s%s',time=%lu,interval=%i)", self->data.type,
690 *self->data.type_instance ? "',type_instance='" : "", self->data.type_instance,
691 *self->data.plugin ? "',plugin='" : "", self->data.plugin,
692 *self->data.plugin_instance ? "',plugin_instance='" : "", self->data.plugin_instance,
693 *self->data.host ? "',host='" : "", self->data.host,
694 *self->message ? "',message='" : "", self->message,
695 (long unsigned) self->data.time, self->severity);
699 static PyMethodDef Notification_methods[] = {
700 {"dispatch", (PyCFunction) Notification_dispatch, METH_VARARGS | METH_KEYWORDS, dispatch_doc},
704 static PyMemberDef Notification_members[] = {
705 {"severity", T_INT, offsetof(Notification, severity), 0, severity_doc},
709 static PyGetSetDef Notification_getseters[] = {
710 {"message", PluginData_getstring, Notification_setstring, message_doc, (void *) offsetof(Notification, message)},
714 PyTypeObject NotificationType = {
715 PyObject_HEAD_INIT(NULL)
717 "collectd.Notification", /* tp_name */
718 sizeof(Notification), /* tp_basicsize */
719 0, /* Will be filled in later */
725 Notification_repr, /* tp_repr */
726 0, /* tp_as_number */
727 0, /* tp_as_sequence */
728 0, /* tp_as_mapping */
734 0, /* tp_as_buffer */
735 Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/
736 Notification_doc, /* tp_doc */
739 0, /* tp_richcompare */
740 0, /* tp_weaklistoffset */
743 Notification_methods, /* tp_methods */
744 Notification_members, /* tp_members */
745 Notification_getseters, /* tp_getset */
748 0, /* tp_descr_get */
749 0, /* tp_descr_set */
750 0, /* tp_dictoffset */
751 Notification_init, /* tp_init */
753 Notification_new /* tp_new */