]> git.ipfire.org Git - people/ms/python-rrdtool.git/blame - rrdtool-py3k.c
* On POSIX systems, the 'graph' function will now cache data in memory
[people/ms/python-rrdtool.git] / rrdtool-py3k.c
CommitLineData
ce6564c2 1/*
618986c4
CJ
2 * rrdtool-py3k, rrdtool bindings for Python 3.
3 * Based on the rrdtool Python bindings for Python 2 from
4 * Hye-Shik Chang <perky@fallin.lv>.
ce6564c2
CJ
5 *
6 * Copyright 2012 Christian Jurk <commx@commx.ws>
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
21 * MA 02110-1301, USA.
22 *
ce6564c2
CJ
23 */
24
25#include <Python.h>
26#include <rrd.h>
27
06fe4e5b
CJ
28static const char *_version = "0.1.0";
29
ce6564c2
CJ
30/* Exception types */
31static PyObject *rrdtool_OperationalError;
32static PyObject *rrdtool_ProgrammingError;
33
618986c4
CJ
34static char **rrdtool_argv = NULL;
35static int rrdtool_argc = 0;
36
37static void
38destroy_args(void)
39{
40 PyMem_Del(rrdtool_argv);
41 rrdtool_argv = NULL;
42}
43
44/* Helper function to convert Python objects into a representation that the
45 * rrdtool functions can work with.
46 */
47static int
48convert_args(char *command, PyObject *args)
49{
50 PyObject *o, *lo;
51 int i, j, args_count, argv_count, element_count;
52
53 args_count = PyTuple_Size(args);
54 element_count = 0;
55 for (i = 0; i < args_count; i++)
56 {
57 o = PyTuple_GET_ITEM(args, i);
58 if (PyUnicode_Check(o) || PyBytes_Check(o))
59 element_count++;
60 else if (PyList_CheckExact(o))
61 element_count += PyList_Size(o);
62 else {
63 PyErr_Format(rrdtool_ProgrammingError,
64 "Argument %d must be string, bytes or list of " \
65 "string/bytes", i);
66 return -1;
67 }
68 }
69
70 rrdtool_argv = PyMem_New(char *, element_count + 1);
71
72 if (rrdtool_argv == NULL)
73 return -1;
74
75 argv_count = 0;
76 for (i = 0; i < args_count; i++) {
77 o = PyTuple_GET_ITEM(args, i);
78
79 if (PyUnicode_Check(o))
80 rrdtool_argv[++argv_count] = PyBytes_AsString(
81 PyUnicode_AsUTF8String(o));
82 else if (PyBytes_Check(o))
83 rrdtool_argv[++argv_count] = PyBytes_AS_STRING(o);
84 else if (PyList_CheckExact(o)) {
85 for (j = 0; j < PyList_Size(o); j++) {
86 lo = PyList_GetItem(o, j);
87 if (PyUnicode_Check(lo))
88 rrdtool_argv[++argv_count] = PyBytes_AS_STRING(
89 PyUnicode_AsUTF8String(lo));
90 else if (PyBytes_Check(lo))
91 rrdtool_argv[++argv_count] = PyBytes_AS_STRING(lo);
92 else {
93 PyMem_Del(rrdtool_argv);
94 PyErr_Format(rrdtool_ProgrammingError,
95 "Element %d in argument %d must be string", j, i);
96 return -1;
97 }
98 }
99 } else {
100 PyMem_Del(rrdtool_argv);
101 PyErr_Format(rrdtool_ProgrammingError,
102 "Argument %d must be string or list of strings", i);
103 return -1;
104 }
105 }
106
107 rrdtool_argv[0] = command;
108 rrdtool_argc = element_count + 1;
109
110 return 0;
111}
112
06fe4e5b
CJ
113static PyObject *
114_rrdtool_util_info2dict(const rrd_info_t *data)
115{
116 PyObject *dict, *val = NULL;
117
118 dict = PyDict_New();
119
120 while (data) {
121 switch (data->type) {
122 case RD_I_VAL:
123 if (isnan(data->value.u_val)) {
124 Py_INCREF(Py_None);
125 val = Py_None;
126 } else
127 PyFloat_FromDouble(data->value.u_val);
128 break;
129
130 case RD_I_CNT:
131 val = PyLong_FromUnsignedLong(data->value.u_cnt);
132 break;
133
134 case RD_I_INT:
135 val = PyLong_FromLong(data->value.u_int);
136 break;
137
138 case RD_I_STR:
139 val = PyUnicode_FromString(data->value.u_str);
140 break;
141
142 case RD_I_BLO:
143 val = PyUnicode_FromStringAndSize(
144 (char *)data->value.u_blo.ptr, data->value.u_blo.size);
145 break;
146 default:
147 val = NULL;
148 break;
149 }
150
151 if (val) {
152 PyDict_SetItemString(dict, data->key, val);
153 Py_DECREF(val);
154 }
155
156 data = data->next;
157 }
158
159 return dict;
160}
161
618986c4
CJ
162static char _rrdtool_create__doc__[] = "Create a new Round Robin Database.\n\n\
163 Usage: create(args...)\n\
164 Arguments:\n\n\
165 filename\n\
166 [--start|-b start time]\n\
167 [--step|-s step]\n\
168 [DS:ds-name:DST:heartbeat:min:max]\n\
169 [RRA:CF:xff:steps:rows]\n\n\
170 Full documentation can be found at:\n\
171 http://oss.oetiker.ch/rrdtool/doc/rrdcreate.en.html";
172
ce6564c2
CJ
173static PyObject *
174_rrdtool_create(PyObject *self, PyObject *args)
175{
618986c4 176 PyObject *ret;
ce6564c2 177
618986c4
CJ
178 if (convert_args("create", args) == -1)
179 return NULL;
ce6564c2 180
618986c4 181 if (rrd_create(rrdtool_argc, rrdtool_argv) == -1) {
ce6564c2
CJ
182 PyErr_SetString(rrdtool_OperationalError, rrd_get_error());
183 rrd_clear_error();
618986c4
CJ
184 ret = NULL;
185 } else {
186 Py_INCREF(Py_None);
187 ret = Py_None;
188 }
189
190 destroy_args();
191 return ret;
192}
193
194static char _rrdtool_update__doc__[] = "Store a new set of values into\
195 the RRD.\n\n\
196 Usage: update(args..)\n\
197 Arguments:\n\n\
198 filename\n\
199 [--template|-t ds-name[:ds-name]...]\n\
200 N|timestamp:value[:value...]\n\
201 [timestamp:value[:value...] ...]\n\n\
202 Full documentation can be found at:\n\
203 http://oss.oetiker.ch/rrdtool/doc/rrdupdate.en.html";
204
205static PyObject *
206_rrdtool_update(PyObject *self, PyObject *args)
207{
208 PyObject *ret;
209
210 if (convert_args("update", args) == -1)
211 return NULL;
212
213 if (rrd_update(rrdtool_argc, rrdtool_argv) == -1) {
214 PyErr_SetString(rrdtool_OperationalError, rrd_get_error());
215 rrd_clear_error();
216 ret = NULL;
217 } else {
218 Py_INCREF(Py_None);
219 ret = Py_None;
220 }
221
222 destroy_args();
223 return ret;
224}
225
06fe4e5b
CJ
226static char _rrdtool_updatev__doc__[] = "Store a new set of values into "\
227 "the Round Robin Database and return an info dictionary.\n\n\
228 This function works in the same manner as 'update', but will return an\n\
229 info dictionary instead of None.";
230
231static PyObject *
232_rrdtool_updatev(PyObject *self, PyObject *args)
233{
234 PyObject *ret;
235 rrd_info_t *data;
236
237 if (convert_args("updatev", args) == -1)
238 return NULL;
239
240 if ((data = rrd_update_v(rrdtool_argc, rrdtool_argv)) == NULL) {
241 PyErr_SetString(rrdtool_OperationalError, rrd_get_error());
242 rrd_clear_error();
243 ret = NULL;
244 } else {
245 ret = _rrdtool_util_info2dict(data);
246 rrd_info_free(data);
247 }
248
249 destroy_args();
250 return ret;
251}
252
618986c4
CJ
253static char _rrdtool_fetch__doc__[] = "Fetch data from an RRD.\n\n\
254 Usage: fetch(args..)\n\
255 Arguments:\n\n\
256 filename\n\
257 CF\n\
258 [--resolution|-r resolution]\n\
259 [--start|-s start]\n\
260 [--end|-e end]\n\n\
261 Full documentation can be found at:\n\
262 http://oss.oetiker.ch/rrdtool/doc/rrdfetch.en.html";
263
264static PyObject *
265_rrdtool_fetch(PyObject *self, PyObject *args)
266{
267 PyObject *ret, *range_tup, *dsnam_tup, *data_list, *t;
268 rrd_value_t *data, *datai, dv;
269 unsigned long step, ds_cnt, i, j, row;
270 time_t start, end;
271 char **ds_namv;
272
273 if (convert_args("fetch", args) == -1)
274 return NULL;
275
276 if (rrd_fetch(rrdtool_argc, rrdtool_argv, &start, &end, &step, &ds_cnt,
277 &ds_namv, &data) == -1) {
278 PyErr_SetString(rrdtool_OperationalError, rrd_get_error());
279 rrd_clear_error();
280 ret = NULL;
281 } else {
282 row = (end - start) / step;
283 ret = PyTuple_New(3);
284 range_tup = PyTuple_New(3);
285 dsnam_tup = PyTuple_New(ds_cnt);
286 data_list = PyList_New(row);
287
288 PyTuple_SET_ITEM(ret, 0, range_tup);
289 PyTuple_SET_ITEM(ret, 1, dsnam_tup);
290 PyTuple_SET_ITEM(ret, 2, data_list);
291
292 datai = data;
293
294 PyTuple_SET_ITEM(range_tup, 0, PyLong_FromLong((long)start));
295 PyTuple_SET_ITEM(range_tup, 1, PyLong_FromLong((long)end));
296 PyTuple_SET_ITEM(range_tup, 2, PyLong_FromLong((long)step));
297
298 for (i = 0; i < ds_cnt; i++)
299 PyTuple_SET_ITEM(dsnam_tup, i, PyUnicode_FromString(ds_namv[i]));
300
301 for (i = 0; i < row; i++) {
302 t = PyTuple_New(ds_cnt);
303 PyList_SET_ITEM(data_list, i, t);
304
305 for (j = 0; j < ds_cnt; j++) {
306 dv = *(datai++);
307 if (isnan(dv)) {
308 PyTuple_SET_ITEM(t, j, Py_None);
309 Py_INCREF(Py_None);
310 } else
311 PyTuple_SET_ITEM(t, j, PyFloat_FromDouble((double)dv));
312 }
313 }
314
315 for (i = 0; i < ds_cnt; i++)
316 rrd_freemem(ds_namv[i]);
317
318 rrd_freemem(ds_namv);
319 rrd_freemem(data);
320 }
321
322 destroy_args();
323 return ret;
324}
325
06fe4e5b
CJ
326static char _rrdtool_flushcached__doc__[] = "Flush RRD files from memory.\n\n\
327 Usage: flushcached(args..)\n\
328 Arguments:\n\n\
329 [--daemon address]\n\
330 filename\n\
331 [filename ...]\n\n\
332 Full documentation can be found at:\n\
333 http://oss.oetiker.ch/rrdtool/doc/rrdflushcached.en.html";
334
335static PyObject *
336_rrdtool_flushcached(PyObject *self, PyObject *args)
337{
338 PyObject *ret;
339
340 if (convert_args("flushcached", args) == -1)
341 return NULL;
342
343 if (rrd_flushcached(rrdtool_argc, rrdtool_argv) != 0) {
344 PyErr_SetString(rrdtool_OperationalError, rrd_get_error());
345 rrd_clear_error();
346 ret = NULL;
347 } else {
348 Py_INCREF(Py_None);
349 ret = Py_None;
350 }
351
352 destroy_args();
353 return ret;
354}
355
618986c4
CJ
356static char _rrdtool_graph__doc__[] = "Create a graph based on one or more " \
357 "RRDs.\n\n\
358 Usage: graph(args..)\n\
359 Arguments:\n\n\
360 filename | -\n\
361 [-s|--start start]\n\
362 [-e|--end end]\n\
363 [-S|--step step]\n\
364 [-t|--title string]\n\
365 [-v|--vertical-label string]\n\
366 [-w|--width pixels]\n\
367 [-h|--height pixels]\n\
368 [-j|--only-graph]\n\
369 [-D|--full-size-mode]\n\
370 [-u|--upper-limit value]\n\
371 [-l|--lower-limit value]\n\
372 [-r|--rigid]\n\
373 [-A|--alt-autoscale]\n\
374 [-J|--alt-autoscale-min]\n\
375 [-M|--alt-autoscale-max]\n\
376 [-N|--no-gridfit]\n\
377 [-x|--x-grid (GTM:GST:MTM:MST:LTM:LST:LPR:LFM|none)]\n\
378 [-y|--y-grid (grid step:label factor|none)]\n\
379 [-Y|--alt-y-grid]\n\
380 [-o|--logarithmic]\n\
381 [-X|--units-exponent value]\n\
382 [-L|--units-length value]\n\
383 [--units=si]\n\
384 [--right-axis scale:shift]\n\
385 [--right-axis-label label]\n\
386 [--right-axis-format format-string]\n\
387 [-g|--no-legend]\n\
388 [-F|--force-rules-legend]\n\
389 [--legend-position=(north|south|west|east)]\n\
390 [--legend-direction=(topdown|bottomup)]\n\
391 [-z|--lazy]\n\
392 [--daemon address]\n\
393 [-f|--imginfo printfstr]\n\
394 [-c|--color COLORTAG#rrggbb[aa]]\n\
395 [--grid-dash on:off]\n\
396 [--border width]\n\
397 [--dynamic-labels]\n\
398 [-m|--zoom factor]\n\
399 [-n|--font FONTTAG:size:[font]]\n\
400 [-R|--font-render-mode {normal,light,mono}]\n\
401 [-B|--font-smoothing-threshold size]\n\
402 [-P|--pango-markup]\n\
403 [-G|--graph-render-mode {normal,mono}]\n\
404 [-E|--slope-mode]\n\
405 [-a|--imgformat {PNG,SVG,EPS,PDF}]\n\
406 [-T|--tabwidth value]\n\
407 [-b|--base value]\n\
408 [-W|--watermark string]\n\
409 DEF:vname=rrdfile:ds-name:CF[:step=step][:start=time][:end=time]\n\
410 CDEF:vname=RPN expression\n\
411 VDEF=vname:RPN expression\n\n\
412 Full documentation can be found at:\n\
413 http://oss.oetiker.ch/rrdtool/doc/rrdgraph.en.html";
414
415static PyObject *
416_rrdtool_graph(PyObject *self, PyObject *args, PyObject *kwargs)
417{
418 PyObject *ret;
06fe4e5b 419 int xsize, ysize, i;
618986c4
CJ
420 int keep_in_mem = 0;
421 double ymin, ymax;
06fe4e5b
CJ
422 char **calcpr, *bp;
423#ifdef _POSIX_C_SOURCE
424 FILE *orig_stdout = stdout;
425 size_t bsize;
426#endif
618986c4
CJ
427
428 if (convert_args("graph", args) == -1)
429 return NULL;
430
06fe4e5b
CJ
431 if (rrdtool_argc >= 2 && strcmp(rrdtool_argv[1], "-") == 0) {
432#ifdef _POSIX_C_SOURCE
618986c4 433 keep_in_mem = 1;
06fe4e5b
CJ
434#else
435 PyErr_SetString(rrdtool_ProgrammingError,
436 "Output filename cannot be '-', because this platform does not "\
437 "support output buffering");
438 destroy_args();
439 return NULL;
440#endif
618986c4
CJ
441 }
442
06fe4e5b
CJ
443#ifdef _POSIX_C_SOURCE
444 if (keep_in_mem)
445 stdout = open_memstream(&bp, &bsize);
446#endif
447
618986c4
CJ
448 if (rrd_graph(rrdtool_argc, rrdtool_argv, &calcpr, &xsize, &ysize, NULL,
449 &ymin, &ymax) == -1) {
450 PyErr_SetString(rrdtool_OperationalError, rrd_get_error());
451 rrd_clear_error();
452 ret = NULL;
453 } else {
454 ret = PyTuple_New(keep_in_mem ? 4 : 3);
455
456 PyTuple_SET_ITEM(ret, 0, PyLong_FromLong((long)xsize));
457 PyTuple_SET_ITEM(ret, 1, PyLong_FromLong((long)ysize));
458
459 if (calcpr) {
460 PyObject *e, *t;
461
462 e = PyList_New(0);
463 PyTuple_SET_ITEM(ret, 2, e);
464
465 for (i = 0; calcpr[i]; i++) {
466 t = PyUnicode_FromString(calcpr[i]);
467 PyList_Append(e, t);
468 Py_DECREF(t);
469 rrd_freemem(calcpr[i]);
470 }
471 } else {
472 Py_INCREF(Py_None);
473 PyTuple_SET_ITEM(ret, 2, Py_None);
474 }
475
476 /* feed buffered contents into a PyBytes object */
477 if (keep_in_mem) {
478 PyObject *pb;
06fe4e5b
CJ
479
480 fflush(stdout);
481 pb = PyBytes_FromStringAndSize(bp, bsize);
618986c4
CJ
482
483 PyTuple_SET_ITEM(ret, 3, pb);
484 }
485 }
486
06fe4e5b
CJ
487 if (keep_in_mem) {
488 fclose(stdout);
489 stdout = orig_stdout;
490 }
491
492 destroy_args();
493 return ret;
494}
495
496static char _rrdtool_graphv__doc__[] = "Create a graph based on one or more "\
497 "RRDs and return header information.\n\n\
498 This function works in the same manner as 'graph', but will return a info\n\
499 dictionary instead of None.";
500
501static PyObject *
502_rrdtool_graphv(PyObject *self, PyObject *args)
503{
504 PyObject *ret;
505 rrd_info_t *data;
506
507 if (convert_args("graphv", args) == -1)
508 return NULL;
509
510 if ((data = rrd_graph_v(rrdtool_argc, rrdtool_argv)) == NULL) {
511 PyErr_SetString(rrdtool_OperationalError, rrd_get_error());
512 rrd_clear_error();
513 ret = NULL;
514 } else {
515 ret = _rrdtool_util_info2dict(data);
516 rrd_info_free(data);
517 }
518
519 destroy_args();
520 return ret;
521}
522
523static char _rrdtool_tune__doc__[] = "Modify some basic properties of a " \
524 "Round Robin Database.\n\n\
525 Usage: tune(args..)\n\
526 Arguments:\n\n\
527 filename\n\
528 [-h|--heartbeat ds-name:heartbeat]\n\
529 [-i|--minimum ds-name:min]\n\
530 [-a|--maximum ds-name:max]\n\
531 [-d|--data-source-type ds-name:DST]\n\
532 [-r|--data-source-rename old-name:new-name]\n\n\
533 Full documentation can be found at:\n\
534 http://oss.oetiker.ch/rrdtool/doc/rrdtune.en.html";
535
536static PyObject *
537_rrdtool_tune(PyObject *self, PyObject *args)
538{
539 PyObject *ret;
540
541 if (convert_args("tune", args) == -1)
542 return NULL;
543
544 if (rrd_tune(rrdtool_argc, rrdtool_argv) == -1) {
545 PyErr_SetString(rrdtool_OperationalError, rrd_get_error());
546 rrd_clear_error();
547 ret = NULL;
548 } else {
549 Py_INCREF(Py_None);
550 ret = Py_None;
551 }
552
553 destroy_args();
554 return ret;
555}
556
557static char _rrdtool_first__doc__[] = "Get the first UNIX timestamp of the "\
558 "first data sample in an Round Robin Database.\n\n\
559 Usage: first(args..)\n\
560 Arguments:\n\n\
561 filename\n\
562 [--rraindex number]\n\n\
563 Full documentation can be found at:\n\
564 http://oss.oetiker.ch/rrdtool/doc/rrdfirst.en.html";
565
566static PyObject *
567_rrdtool_first(PyObject *self, PyObject *args)
568{
569 PyObject *ret;
570 int ts;
571
572 if (convert_args("first", args) == -1)
573 return NULL;
574
575 if ((ts = rrd_first(rrdtool_argc, rrdtool_argv)) == -1) {
576 PyErr_SetString(rrdtool_OperationalError, rrd_get_error());
577 rrd_clear_error();
578 ret = NULL;
579 } else
580 ret = PyLong_FromLong((long)ts);
581
582 destroy_args();
583 return ret;
584}
585
586static char _rrdtool_last__doc__[] = "Get the UNIX timestamp of the most "\
587 "recent data sample in an Round Robin Database.\n\n\
588 Usage: last(args..)\n\
589 Arguments:\n\n\
590 filename\n\
591 [--daemon address]\n\n\
592 Full documentation can be found at:\n\
593 http://oss.oetiker.ch/rrdtool/doc/rrdlast.en.html";
594
595static PyObject *
596_rrdtool_last(PyObject *self, PyObject *args)
597{
598 PyObject *ret;
599 int ts;
600
601 if (convert_args("last", args) == -1)
602 return NULL;
603
604 if ((ts = rrd_last(rrdtool_argc, rrdtool_argv)) == -1) {
605 PyErr_SetString(rrdtool_OperationalError, rrd_get_error());
606 rrd_clear_error();
607 ret = NULL;
608 } else
609 ret = PyLong_FromLong((long)ts);
610
611 destroy_args();
612 return ret;
613}
614
615static char _rrdtool_resize__doc__[] = "Modify the number of rows in a "\
616 "Round Robin Database.\n\n\
617 Usage: resize(args..)\n\
618 Arguments:\n\n\
619 filename\n\
620 rra-num\n\
621 GROW|SHRINK\n\
622 rows\n\n\
623 Full documentation can be found at:\n\
624 http://oss.oetiker.ch/rrdtool/doc/rrdlast.en.html";
625
626static PyObject *
627_rrdtool_resize(PyObject *self, PyObject *args)
628{
629 PyObject *ret;
630 int ts;
631
632 if (convert_args("resize", args) == -1)
633 return NULL;
634
635 if ((ts = rrd_resize(rrdtool_argc, rrdtool_argv)) == -1) {
636 PyErr_SetString(rrdtool_OperationalError, rrd_get_error());
637 rrd_clear_error();
638 ret = NULL;
639 } else {
640 Py_INCREF(Py_None);
641 ret = Py_None;
642 }
643
644 destroy_args();
645 return ret;
646}
647
648static char _rrdtool_info__doc__[] = "Extract header information from an "\
649 "Round Robin Database.\n\n\
650 Usage: info(filename)\n\
651 Arguments:\n\n\
652 filename\n\n\
653 Full documentation can be found at:\n\
654 http://oss.oetiker.ch/rrdtool/doc/rrdinfo.en.html";
655
656static PyObject *
657_rrdtool_info(PyObject *self, PyObject *args)
658{
659 PyObject *ret;
660 rrd_info_t *data;
661
662 if (convert_args("info", args) == -1)
663 return NULL;
664
665 if ((data = rrd_info(rrdtool_argc, rrdtool_argv)) == NULL) {
666 PyErr_SetString(rrdtool_OperationalError, rrd_get_error());
667 rrd_clear_error();
668 ret = NULL;
669 } else {
670 ret = _rrdtool_util_info2dict(data);
671 rrd_info_free(data);
672 }
ce6564c2 673
618986c4
CJ
674 destroy_args();
675 return ret;
ce6564c2
CJ
676}
677
678static PyMethodDef rrdtool_methods[] = {
618986c4
CJ
679 {"create", (PyCFunction)_rrdtool_create,
680 METH_VARARGS, _rrdtool_create__doc__},
681 {"update", (PyCFunction)_rrdtool_update,
682 METH_VARARGS, _rrdtool_update__doc__},
06fe4e5b
CJ
683 {"updatev", (PyCFunction)_rrdtool_updatev,
684 METH_VARARGS, _rrdtool_updatev__doc__},
618986c4
CJ
685 {"fetch", (PyCFunction)_rrdtool_fetch,
686 METH_VARARGS, _rrdtool_fetch__doc__},
06fe4e5b
CJ
687 {"flushcached", (PyCFunction)_rrdtool_flushcached,
688 METH_VARARGS, _rrdtool_flushcached__doc__},
618986c4 689 {"graph", (PyCFunction)_rrdtool_graph,
06fe4e5b
CJ
690 METH_VARARGS, _rrdtool_graph__doc__},
691 {"graphv", (PyCFunction)_rrdtool_graphv,
692 METH_VARARGS, _rrdtool_graphv__doc__},
693 {"tune", (PyCFunction)_rrdtool_tune,
694 METH_VARARGS, _rrdtool_tune__doc__},
695 {"first", (PyCFunction)_rrdtool_first,
696 METH_VARARGS, _rrdtool_first__doc__},
697 {"last", (PyCFunction)_rrdtool_last,
698 METH_VARARGS, _rrdtool_last__doc__},
699 {"resize", (PyCFunction)_rrdtool_resize,
700 METH_VARARGS, _rrdtool_resize__doc__},
701 {"info", (PyCFunction)_rrdtool_info,
702 METH_VARARGS, _rrdtool_info__doc__},
ce6564c2
CJ
703 {NULL, NULL, 0, NULL}
704};
705
706static struct PyModuleDef rrdtoolmodule = {
707 PyModuleDef_HEAD_INIT,
708 "rrdtool",
709 "rrdtool bindings for Python 3",
710 -1,
711 rrdtool_methods
712};
713
714PyMODINIT_FUNC
715PyInit_rrdtool(void)
716{
717 PyObject *m;
718
719 m = PyModule_Create(&rrdtoolmodule);
720 if (m == NULL)
721 return NULL;
722
723 rrdtool_ProgrammingError = PyErr_NewException("rrdtool.ProgrammingError",
724 NULL, NULL);
725 Py_INCREF(rrdtool_ProgrammingError);
726 PyModule_AddObject(m, "ProgrammingError", rrdtool_ProgrammingError);
727
728 rrdtool_OperationalError = PyErr_NewException("rrdtool.OperationalError",
729 NULL, NULL);
730 Py_INCREF(rrdtool_OperationalError);
731 PyModule_AddObject(m, "OperationalError", rrdtool_OperationalError);
06fe4e5b 732 PyModule_AddObject(m, "__version__", PyUnicode_FromString(_version));
ce6564c2
CJ
733
734 return m;
735}